Chameleon: Separate the display-related functionality to DisplayUtility
The display-related methods are moved to DisplayUtility. We can add more
utilities later for audio and video.
BUG=chromium:407004
TEST=Ran the Chameleon test display_Resolution.mirrored and it passed.
Change-Id: Ic8c78d8a1a64760f616dd8f2492b9e05c268e7fd
Reviewed-on: https://chromium-review.googlesource.com/214423
Reviewed-by: Wai-Hong Tam <waihong@chromium.org>
Tested-by: Wai-Hong Tam <waihong@chromium.org>
Commit-Queue: Cheng-Yi Chiang <cychiang@chromium.org>
diff --git a/client/cros/constants.py b/client/cros/constants.py
index 9f96cee..2d48683 100644
--- a/client/cros/constants.py
+++ b/client/cros/constants.py
@@ -165,6 +165,8 @@
'./multimedia_xmlrpc_server.py')
MULTIMEDIA_XMLRPC_SERVER_CLEANUP_PATTERN = 'multimedia_xmlrpc_server'
MULTIMEDIA_XMLRPC_SERVER_READY_METHOD = 'ready'
+MULTIMEDIA_TEST_EXTENSION = (
+ '/usr/local/autotest/cros/multimedia/multimedia_test_extension')
# Bug filing
CHROME_VERSION = 'CHROME_VERSION'
diff --git a/client/cros/multimedia/__init__.py b/client/cros/multimedia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/cros/multimedia/__init__.py
diff --git a/client/cros/multimedia/display_utility.py b/client/cros/multimedia/display_utility.py
new file mode 100644
index 0000000..3542068
--- /dev/null
+++ b/client/cros/multimedia/display_utility.py
@@ -0,0 +1,349 @@
+# Copyright 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utility to access the display-related functionality."""
+
+import multiprocessing
+import os
+import re
+import time
+import telemetry
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.cros import constants, cros_ui, sys_power
+
+TimeoutException = telemetry.core.util.TimeoutException
+
+
+class DisplayUtility(object):
+ """Utility to access the display-related functionality."""
+
+ def __init__(self, chrome):
+ self._chrome = chrome
+ self._browser = chrome.browser
+
+
+ def get_display_info(self):
+ """Gets the display info from Chrome.system.display API.
+
+ @return array of dict for display info.
+ """
+
+ extension = self._chrome.get_extension(
+ constants.MULTIMEDIA_TEST_EXTENSION)
+ if not extension:
+ raise RuntimeError('Graphics test extension not found')
+ extension.ExecuteJavaScript('window.__display_info = null;')
+ extension.ExecuteJavaScript(
+ "chrome.system.display.getInfo(function(info) {"
+ "window.__display_info = info;})")
+ utils.wait_for_value(lambda: (
+ extension.EvaluateJavaScript("window.__display_info") != None),
+ expected_value=True)
+ return extension.EvaluateJavaScript("window.__display_info")
+
+
+ def _wait_for_display_options_to_appear(self, tab, display_index,
+ timeout=16):
+ """Waits for option.DisplayOptions to appear.
+
+ The function waits until options.DisplayOptions appears or is timed out
+ after the specified time.
+
+ @param tab: the tab where the display options dialog is shown.
+ @param display_index: index of the display.
+ @param timeout: time wait for display options appear.
+
+ @raise RuntimeError when display_index is out of range
+ @raise TimeoutException when the operation is timed out.
+ """
+
+ tab.WaitForJavaScriptExpression(
+ "typeof options !== 'undefined' &&"
+ "typeof options.DisplayOptions !== 'undefined' &&"
+ "typeof options.DisplayOptions.instance_ !== 'undefined' &&"
+ "typeof options.DisplayOptions.instance_"
+ " .displays_ !== 'undefined'", timeout)
+
+ if not tab.EvaluateJavaScript(
+ "options.DisplayOptions.instance_.displays_.length > %d"
+ % (display_index)):
+ raise RuntimeError('Display index out of range: '
+ + str(tab.EvaluateJavaScript(
+ "options.DisplayOptions.instance_.displays_.length")))
+
+ tab.WaitForJavaScriptExpression(
+ "typeof options.DisplayOptions.instance_"
+ " .displays_[%(index)d] !== 'undefined' &&"
+ "typeof options.DisplayOptions.instance_"
+ " .displays_[%(index)d].id !== 'undefined' &&"
+ "typeof options.DisplayOptions.instance_"
+ " .displays_[%(index)d].resolutions !== 'undefined'"
+ % {'index': display_index}, timeout)
+
+
+ def get_display_modes(self, display_index):
+ """Gets all the display modes for the specified display.
+
+ The modes are obtained from chrome://settings-frame/display via
+ telemetry.
+
+ @param display_index: index of the display to get modes from.
+
+ @return: A list of DisplayMode dicts.
+
+ @raise TimeoutException when the operation is timed out.
+ """
+
+ tab = self._browser.tabs.New()
+ try:
+ tab.Navigate('chrome://settings-frame/display')
+ tab.Activate()
+ self._wait_for_display_options_to_appear(tab, display_index)
+ return tab.EvaluateJavaScript(
+ "options.DisplayOptions.instance_"
+ " .displays_[%(index)d].resolutions"
+ % {'index': display_index})
+ finally:
+ tab.Close()
+
+
+ def set_resolution(self, display_index, width, height, timeout=3):
+ """Sets the resolution of the specified display.
+
+ @param display_index: index of the display to set resolution for.
+ @param width: width of the resolution
+ @param height: height of the resolution
+ @param timeout: maximal time in seconds waiting for the new resolution
+ to settle in.
+ @raise TimeoutException when the operation is timed out.
+ """
+
+ tab = self._browser.tabs.New()
+ try:
+ tab.Navigate('chrome://settings-frame/display')
+ tab.Activate()
+ self._wait_for_display_options_to_appear(tab, display_index)
+
+ tab.ExecuteJavaScript(
+ # Start from M38 (refer to CR:417113012), a DisplayMode dict
+ # contains 'originalWidth'/'originalHeight' in addition to
+ # 'width'/'height'. OriginalWidth/originalHeight is what is
+ # supported by the display while width/height is what is
+ # shown to users in the display setting.
+ """
+ var display = options.DisplayOptions.instance_
+ .displays_[%(index)d];
+ var modes = display.resolutions;
+ var is_m38 = modes.length > 0
+ && "originalWidth" in modes[0];
+ if (is_m38) {
+ for (index in modes) {
+ var mode = modes[index];
+ if (mode.originalWidth == %(width)d &&
+ mode.originalHeight == %(height)d) {
+ chrome.send('setDisplayMode', [display.id, mode]);
+ break;
+ }
+ }
+ } else {
+ chrome.send('setResolution',
+ [display.id, %(width)d, %(height)d]);
+ }
+ """
+ % {'index': display_index, 'width': width, 'height': height}
+ )
+
+ # TODO(tingyuan):
+ # Support for multiple external monitors (i.e. for chromebox)
+
+ end_time = time.time() + timeout
+ while time.time() < end_time:
+ r = self.get_resolution(self.get_external_connector_name())
+ if (width, height) == (r[0], r[1]):
+ return True
+ time.sleep(0.1)
+ raise TimeoutException("Failed to change resolution to %r (%r"
+ " detected)" % ((width, height), r))
+ finally:
+ tab.Close()
+
+
+ def get_resolution(self, output):
+ """Gets the resolution of the specified output.
+
+ @param output: The output name as a string.
+
+ @return The resolution of output as a tuple (width, height,
+ fb_offset_x, fb_offset_y) of ints.
+ """
+
+ regexp = re.compile(
+ r'^([-A-Za-z0-9]+)\s+connected\s+(\d+)x(\d+)\+(\d+)\+(\d+)',
+ re.M)
+ match = regexp.findall(utils.call_xrandr())
+ for m in match:
+ if m[0] == output:
+ return (int(m[1]), int(m[2]), int(m[3]), int(m[4]))
+ return (0, 0, 0, 0)
+
+
+ def take_tab_screenshot(self, url_pattern, output_suffix):
+ """Takes a screenshot of the tab specified by the given url pattern.
+
+ The captured screenshot is saved to:
+ /tmp/screenshot_<output_suffix>_<last_part_of_url>.png
+
+ @param url_pattern: A string of url pattern used to search for tabs.
+ @param output_suffix: A suffix appended to the file name of captured
+ PNG image.
+ """
+ if not url_pattern:
+ # If no URL pattern is provided, defaults to capture all the tabs
+ # that show PNG images.
+ url_pattern = '.png'
+
+ tabs = self._browser.tabs
+ screenshots = []
+ for i in xrange(0, len(tabs)):
+ if url_pattern in tabs[i].url:
+ screenshots.append((tabs[i].url, tabs[i].Screenshot(timeout=5)))
+
+ output_file = ('/tmp/screenshot_%s_%%s.png' % output_suffix)
+ for url, screenshot in screenshots:
+ image_filename = os.path.splitext(url.rsplit('/', 1)[-1])[0]
+ screenshot.WriteFile(output_file % image_filename)
+ return True
+
+
+ def toggle_mirrored(self):
+ """Toggles mirrored.
+
+ Emulates L_Ctrl + Maximize in X server to toggle mirrored.
+ """
+ self.press_key('ctrl+F4')
+ return True
+
+
+ def press_key(self, key_str):
+ """Presses the given key(s).
+
+ @param key_str: A string of the key(s), like 'ctrl+F4', 'Up'.
+ """
+ command = 'xdotool key %s' % key_str
+ cros_ui.xsystem(command)
+ return True
+
+
+ def set_mirrored(self, is_mirrored):
+ """Sets mirrored mode.
+
+ @param is_mirrored: True or False to indicate mirrored state.
+ """
+ def _is_mirrored_enabled():
+ return bool(self.get_display_info()[0]['mirroringSourceId'])
+
+ retries = 3
+ while _is_mirrored_enabled() != is_mirrored and retries > 0:
+ self.toggle_mirrored()
+ time.sleep(3)
+ retries -= 1
+ return _is_mirrored_enabled() == is_mirrored
+
+
+ def suspend_resume(self, suspend_time=10):
+ """Suspends the DUT for a given time in second.
+
+ @param suspend_time: Suspend time in second.
+ """
+ sys_power.do_suspend(suspend_time)
+ return True
+
+
+ def suspend_resume_bg(self, suspend_time=10):
+ """Suspends the DUT for a given time in second in the background.
+
+ @param suspend_time: Suspend time in second.
+ """
+ process = multiprocessing.Process(target=self.suspend_resume,
+ args=(suspend_time,))
+ process.start()
+ return True
+
+
+ def get_external_connector_name(self):
+ """Gets the name of the external output connector.
+
+ @return The external output connector name as a string, if any.
+ Otherwise, return False.
+ """
+ xrandr_output = utils.get_xrandr_output_state()
+ for output in xrandr_output.iterkeys():
+ if (output.startswith('HDMI') or
+ output.startswith('DP') or
+ output.startswith('DVI')):
+ return output
+ return False
+
+
+ def get_internal_connector_name(self):
+ """Gets the name of the internal output connector.
+
+ @return The internal output connector name as a string, if any.
+ Otherwise, return False.
+ """
+ xrandr_output = utils.get_xrandr_output_state()
+ for output in xrandr_output.iterkeys():
+ # reference: chromium_org/chromeos/display/output_util.cc
+ if (output.startswith('eDP') or
+ output.startswith('LVDS') or
+ output.startswith('DSI')):
+ return output
+ return False
+
+
+ def wait_output_connected(self, output):
+ """Wait for output to connect.
+
+ @param output: The output name as a string.
+
+ @return: True if output is connected; False otherwise.
+ """
+ def _is_connected(output):
+ xrandr_output = utils.get_xrandr_output_state()
+ if output not in xrandr_output:
+ return False
+ return xrandr_output[output]
+ return utils.wait_for_value(lambda: _is_connected(output),
+ expected_value=True)
+
+
+ def load_url(self, url):
+ """Loads the given url in a new tab.
+
+ @param url: The url to load as a string.
+ """
+ tab = self._browser.tabs.New()
+ tab.Navigate(url)
+ tab.Activate()
+ return True
+
+
+ def close_tab(self, index=-1):
+ """Closes the tab of the given index.
+
+ @param index: The tab index to close. Defaults to the last tab.
+ """
+ self._browser.tabs[index].Close()
+ return True
+
+
+ def reconnect_output(self, output):
+ """Reconnects output.
+
+ @param output: The output name as a string.
+ """
+ utils.set_xrandr_output(output, False)
+ utils.set_xrandr_output(output, True)
+ return True
diff --git a/client/cros/multimedia/multimedia_xmlrpc_server.py b/client/cros/multimedia/multimedia_xmlrpc_server.py
index cc592ad..0daecc3 100755
--- a/client/cros/multimedia/multimedia_xmlrpc_server.py
+++ b/client/cros/multimedia/multimedia_xmlrpc_server.py
@@ -8,352 +8,46 @@
import argparse
import code
import logging
-import multiprocessing
import os
-import re
-import time
import xmlrpclib
-import telemetry
import common # pylint: disable=W0611
-from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib.cros import chrome, xmlrpc_server
-from autotest_lib.client.cros import constants, cros_ui, sys_power
-
-EXT_PATH = os.path.join(os.path.dirname(__file__), 'multimedia_test_extension')
-TimeoutException = telemetry.core.util.TimeoutException
+from autotest_lib.client.cros import constants
+from autotest_lib.client.cros.multimedia import display_utility
class MultimediaXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
"""XML RPC delegate for multimedia testing."""
def __init__(self, chrome):
- self._chrome = chrome
- self._browser = chrome.browser
+ """Initializes the utility objects."""
+ self._utilities = {}
+ self._utilities['display'] = display_utility.DisplayUtility(chrome)
- def get_display_info(self):
- """Gets the display info from Chrome.system.display API.
+ def _dispatch(self, method, params):
+ """Dispatches the method to the proper utility.
- @return array of dict for display info.
+ We turn off allow_dotted_names option. The method handles the dot
+ and dispatches the method to the proper utility, like DisplayUtility.
+
"""
-
- extension = self._chrome.get_extension(EXT_PATH)
- if not extension:
- raise RuntimeError('Graphics test extension not found')
- extension.ExecuteJavaScript('window.__display_info = null;')
- extension.ExecuteJavaScript(
- "chrome.system.display.getInfo(function(info) {"
- "window.__display_info = info;})")
- utils.wait_for_value(lambda: (
- extension.EvaluateJavaScript("window.__display_info") != None),
- expected_value=True)
- return extension.EvaluateJavaScript("window.__display_info")
-
-
- def _wait_for_display_options_to_appear(self, tab, display_index,
- timeout=16):
- """Waits for option.DisplayOptions to appear.
-
- The function waits until options.DisplayOptions appears or is timed out
- after the specified time.
-
- @param tab: the tab where the display options dialog is shown.
- @param display_index: index of the display.
- @param timeout: time wait for display options appear.
-
- @raise RuntimeError when display_index is out of range
- @raise TimeoutException when the operation is timed out.
- """
-
- tab.WaitForJavaScriptExpression(
- "typeof options !== 'undefined' &&"
- "typeof options.DisplayOptions !== 'undefined' &&"
- "typeof options.DisplayOptions.instance_ !== 'undefined' &&"
- "typeof options.DisplayOptions.instance_"
- " .displays_ !== 'undefined'", timeout)
-
- if not tab.EvaluateJavaScript(
- "options.DisplayOptions.instance_.displays_.length > %d"
- % (display_index)):
- raise RuntimeError('Display index out of range: '
- + str(tab.EvaluateJavaScript(
- "options.DisplayOptions.instance_.displays_.length")))
-
- tab.WaitForJavaScriptExpression(
- "typeof options.DisplayOptions.instance_"
- " .displays_[%(index)d] !== 'undefined' &&"
- "typeof options.DisplayOptions.instance_"
- " .displays_[%(index)d].id !== 'undefined' &&"
- "typeof options.DisplayOptions.instance_"
- " .displays_[%(index)d].resolutions !== 'undefined'"
- % {'index': display_index}, timeout)
-
-
- def get_display_modes(self, display_index):
- """Gets all the display modes for the specified display.
-
- The modes are obtained from chrome://settings-frame/display via
- telemetry.
-
- @param display_index: index of the display to get modes from.
-
- @return: A list of DisplayMode dicts.
-
- @raise TimeoutException when the operation is timed out.
- """
-
- tab = self._browser.tabs.New()
try:
- tab.Navigate('chrome://settings-frame/display')
- tab.Activate()
- self._wait_for_display_options_to_appear(tab, display_index)
- return tab.EvaluateJavaScript(
- "options.DisplayOptions.instance_"
- " .displays_[%(index)d].resolutions"
- % {'index': display_index})
- finally:
- tab.Close()
+ if '.' not in method:
+ func = getattr(self, method)
+ else:
+ util_name, method_name = method.split('.', 1)
+ if util_name in self._utilities:
+ func = getattr(self._utilities[util_name], method_name)
+ else:
+ raise Exception('unknown utility: %s' % util_name)
+ except AttributeError:
+ raise Exception('method %s not supported' % method)
-
- def set_resolution(self, display_index, width, height, timeout=3):
- """Sets the resolution of the specified display.
-
- @param display_index: index of the display to set resolution for.
- @param width: width of the resolution
- @param height: height of the resolution
- @param timeout: maximal time in seconds waiting for the new resolution
- to settle in.
- @raise TimeoutException when the operation is timed out.
- """
-
- tab = self._browser.tabs.New()
- try:
- tab.Navigate('chrome://settings-frame/display')
- tab.Activate()
- self._wait_for_display_options_to_appear(tab, display_index)
-
- tab.ExecuteJavaScript(
- # Start from M38 (refer to CR:417113012), a DisplayMode dict
- # contains 'originalWidth'/'originalHeight' in addition to
- # 'width'/'height'. OriginalWidth/originalHeight is what is
- # supported by the display while width/height is what is
- # shown to users in the display setting.
- """
- var display = options.DisplayOptions.instance_
- .displays_[%(index)d];
- var modes = display.resolutions;
- var is_m38 = modes.length > 0
- && "originalWidth" in modes[0];
- if (is_m38) {
- for (index in modes) {
- var mode = modes[index];
- if (mode.originalWidth == %(width)d &&
- mode.originalHeight == %(height)d) {
- chrome.send('setDisplayMode', [display.id, mode]);
- break;
- }
- }
- } else {
- chrome.send('setResolution',
- [display.id, %(width)d, %(height)d]);
- }
- """
- % {'index': display_index, 'width': width, 'height': height}
- )
-
- # TODO(tingyuan):
- # Support for multiple external monitors (i.e. for chromebox)
-
- end_time = time.time() + timeout
- while time.time() < end_time:
- r = self.get_resolution(self.get_external_connector_name())
- if (width, height) == (r[0], r[1]):
- return True
- time.sleep(0.1)
- raise TimeoutException("Failed to change resolution to %r (%r"
- " detected)" % ((width, height), r))
- finally:
- tab.Close()
-
-
- def get_resolution(self, output):
- """Gets the resolution of the specified output.
-
- @param output: The output name as a string.
-
- @return The resolution of output as a tuple (width, height,
- fb_offset_x, fb_offset_y) of ints.
- """
-
- regexp = re.compile(
- r'^([-A-Za-z0-9]+)\s+connected\s+(\d+)x(\d+)\+(\d+)\+(\d+)',
- re.M)
- match = regexp.findall(utils.call_xrandr())
- for m in match:
- if m[0] == output:
- return (int(m[1]), int(m[2]), int(m[3]), int(m[4]))
- return (0, 0, 0, 0)
-
-
- def take_tab_screenshot(self, url_pattern, output_suffix):
- """Takes a screenshot of the tab specified by the given url pattern.
-
- The captured screenshot is saved to:
- /tmp/screenshot_<output_suffix>_<last_part_of_url>.png
-
- @param url_pattern: A string of url pattern used to search for tabs.
- @param output_suffix: A suffix appended to the file name of captured
- PNG image.
- """
- if not url_pattern:
- # If no URL pattern is provided, defaults to capture all the tabs
- # that show PNG images.
- url_pattern = '.png'
-
- tabs = self._browser.tabs
- screenshots = []
- for i in xrange(0, len(tabs)):
- if url_pattern in tabs[i].url:
- screenshots.append((tabs[i].url, tabs[i].Screenshot(timeout=5)))
-
- output_file = ('/tmp/screenshot_%s_%%s.png' % output_suffix)
- for url, screenshot in screenshots:
- image_filename = os.path.splitext(url.rsplit('/', 1)[-1])[0]
- screenshot.WriteFile(output_file % image_filename)
- return True
-
-
- def toggle_mirrored(self):
- """Toggles mirrored.
-
- Emulates L_Ctrl + Maximize in X server to toggle mirrored.
- """
- self.press_key('ctrl+F4')
- return True
-
-
- def press_key(self, key_str):
- """Presses the given key(s).
-
- @param key_str: A string of the key(s), like 'ctrl+F4', 'Up'.
- """
- command = 'xdotool key %s' % key_str
- cros_ui.xsystem(command)
- return True
-
-
- def set_mirrored(self, is_mirrored):
- """Sets mirrored mode.
-
- @param is_mirrored: True or False to indicate mirrored state.
- """
- def _is_mirrored_enabled():
- return bool(self.get_display_info()[0]['mirroringSourceId'])
-
- retries = 3
- while _is_mirrored_enabled() != is_mirrored and retries > 0:
- self.toggle_mirrored()
- time.sleep(3)
- retries -= 1
- return _is_mirrored_enabled() == is_mirrored
-
-
- def suspend_resume(self, suspend_time=10):
- """Suspends the DUT for a given time in second.
-
- @param suspend_time: Suspend time in second.
- """
- sys_power.do_suspend(suspend_time)
- return True
-
-
- def suspend_resume_bg(self, suspend_time=10):
- """Suspends the DUT for a given time in second in the background.
-
- @param suspend_time: Suspend time in second.
- """
- process = multiprocessing.Process(target=self.suspend_resume,
- args=(suspend_time,))
- process.start()
- return True
-
-
- def get_external_connector_name(self):
- """Gets the name of the external output connector.
-
- @return The external output connector name as a string, if any.
- Otherwise, return False.
- """
- xrandr_output = utils.get_xrandr_output_state()
- for output in xrandr_output.iterkeys():
- if (output.startswith('HDMI') or
- output.startswith('DP') or
- output.startswith('DVI')):
- return output
- return False
-
-
- def get_internal_connector_name(self):
- """Gets the name of the internal output connector.
-
- @return The internal output connector name as a string, if any.
- Otherwise, return False.
- """
- xrandr_output = utils.get_xrandr_output_state()
- for output in xrandr_output.iterkeys():
- # reference: chromium_org/chromeos/display/output_util.cc
- if (output.startswith('eDP') or
- output.startswith('LVDS') or
- output.startswith('DSI')):
- return output
- return False
-
-
- def wait_output_connected(self, output):
- """Wait for output to connect.
-
- @param output: The output name as a string.
-
- @return: True if output is connected; False otherwise.
- """
- def _is_connected(output):
- xrandr_output = utils.get_xrandr_output_state()
- if output not in xrandr_output:
- return False
- return xrandr_output[output]
- return utils.wait_for_value(lambda: _is_connected(output),
- expected_value=True)
-
-
- def load_url(self, url):
- """Loads the given url in a new tab.
-
- @param url: The url to load as a string.
- """
- tab = self._browser.tabs.New()
- tab.Navigate(url)
- tab.Activate()
- return True
-
-
- def close_tab(self, index=-1):
- """Closes the tab of the given index.
-
- @param index: The tab index to close. Defaults to the last tab.
- """
- self._browser.tabs[index].Close()
- return True
-
-
- def reconnect_output(self, output):
- """Reconnects output.
-
- @param output: The output name as a string.
- """
- utils.set_xrandr_output(output, False)
- utils.set_xrandr_output(output, True)
- return True
+ logging.info('Dispatching method %s with args %s',
+ str(func), str(params))
+ return func(*params)
if __name__ == '__main__':
@@ -376,8 +70,9 @@
extra_browser_args = ['--enable-gpu-benchmarking']
- with chrome.Chrome(extension_paths=[EXT_PATH],
- extra_browser_args=extra_browser_args) as cr:
+ with chrome.Chrome(
+ extension_paths=[constants.MULTIMEDIA_TEST_EXTENSION],
+ extra_browser_args=extra_browser_args) as cr:
server = xmlrpc_server.XmlRpcServer(
'localhost', constants.MULTIMEDIA_XMLRPC_SERVER_PORT)
server.register_delegate(MultimediaXmlRpcDelegate(cr))