Merge branch 'gstreamer', remote branch 'adamcik/gstreamer' into gstreamer
This commit is contained in:
commit
f448771027
@ -22,7 +22,10 @@ def main():
|
|||||||
get_or_create_folder('~/.mopidy/')
|
get_or_create_folder('~/.mopidy/')
|
||||||
core_queue = multiprocessing.Queue()
|
core_queue = multiprocessing.Queue()
|
||||||
get_class(settings.SERVER)(core_queue).start()
|
get_class(settings.SERVER)(core_queue).start()
|
||||||
core = CoreProcess(core_queue)
|
output_class = get_class(settings.OUTPUT)
|
||||||
|
backend_class = get_class(settings.BACKENDS[0])
|
||||||
|
frontend_class = get_class(settings.FRONTEND)
|
||||||
|
core = CoreProcess(core_queue, output_class, backend_class, frontend_class)
|
||||||
core.start()
|
core.start()
|
||||||
asyncore.loop()
|
asyncore.loop()
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,8 @@ class GStreamerProcess(BaseProcess):
|
|||||||
http://jameswestby.net/weblog/tech/14-caution-python-multiprocessing-and-glib-dont-mix.html.
|
http://jameswestby.net/weblog/tech/14-caution-python-multiprocessing-and-glib-dont-mix.html.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
pipeline_description = 'appsrc name=data ! volume name=volume ! autoaudiosink name=sink'
|
||||||
|
|
||||||
def __init__(self, core_queue, output_queue):
|
def __init__(self, core_queue, output_queue):
|
||||||
super(GStreamerProcess, self).__init__()
|
super(GStreamerProcess, self).__init__()
|
||||||
self.core_queue = core_queue
|
self.core_queue = core_queue
|
||||||
@ -65,8 +67,10 @@ class GStreamerProcess(BaseProcess):
|
|||||||
messages_thread.daemon = True
|
messages_thread.daemon = True
|
||||||
messages_thread.start()
|
messages_thread.start()
|
||||||
|
|
||||||
# A pipeline consisting of many elements
|
self.gst_pipeline = gst.parse_launch(self.pipeline_description)
|
||||||
self.gst_pipeline = gst.Pipeline("pipeline")
|
self.gst_data_src = self.gst_pipeline.get_by_name('data')
|
||||||
|
self.gst_volume = self.gst_pipeline.get_by_name('volume')
|
||||||
|
self.gst_sink = self.gst_pipeline.get_by_name('sink')
|
||||||
|
|
||||||
# Setup bus and message processor
|
# Setup bus and message processor
|
||||||
self.gst_bus = self.gst_pipeline.get_bus()
|
self.gst_bus = self.gst_pipeline.get_bus()
|
||||||
@ -74,42 +78,6 @@ class GStreamerProcess(BaseProcess):
|
|||||||
self.gst_bus_id = self.gst_bus.connect('message',
|
self.gst_bus_id = self.gst_bus.connect('message',
|
||||||
self.process_gst_message)
|
self.process_gst_message)
|
||||||
|
|
||||||
# Bin for playing audio URIs
|
|
||||||
#self.gst_uri_src = gst.element_factory_make('uridecodebin', 'uri_src')
|
|
||||||
#self.gst_pipeline.add(self.gst_uri_src)
|
|
||||||
|
|
||||||
# Bin for playing audio data
|
|
||||||
self.gst_data_src = gst.element_factory_make('appsrc', 'data_src')
|
|
||||||
self.gst_pipeline.add(self.gst_data_src)
|
|
||||||
|
|
||||||
# Volume filter
|
|
||||||
self.gst_volume = gst.element_factory_make('volume', 'volume')
|
|
||||||
self.gst_pipeline.add(self.gst_volume)
|
|
||||||
|
|
||||||
# Audio output sink
|
|
||||||
self.gst_sink = gst.element_factory_make('autoaudiosink', 'sink')
|
|
||||||
self.gst_pipeline.add(self.gst_sink)
|
|
||||||
|
|
||||||
# Add callback that will link uri_src output with volume filter input
|
|
||||||
# when the output pad is ready.
|
|
||||||
# See http://stackoverflow.com/questions/2993777 for details.
|
|
||||||
def on_new_decoded_pad(dbin, pad, is_last):
|
|
||||||
uri_src = pad.get_parent()
|
|
||||||
pipeline = uri_src.get_parent()
|
|
||||||
volume = pipeline.get_by_name('volume')
|
|
||||||
uri_src.link(volume)
|
|
||||||
logger.debug("Linked uri_src's new decoded pad to volume filter")
|
|
||||||
# FIXME uridecodebin got no new-decoded-pad signal, but it's
|
|
||||||
# subcomponent decodebin2 got that signal. Fixing this is postponed
|
|
||||||
# till after data_src is up and running perfectly
|
|
||||||
#self.gst_uri_src.connect('new-decoded-pad', on_new_decoded_pad)
|
|
||||||
|
|
||||||
# Link data source output with volume filter input
|
|
||||||
self.gst_data_src.link(self.gst_volume)
|
|
||||||
|
|
||||||
# Link volume filter output to audio sink input
|
|
||||||
self.gst_volume.link(self.gst_sink)
|
|
||||||
|
|
||||||
def process_mopidy_message(self, message):
|
def process_mopidy_message(self, message):
|
||||||
"""Process messages from the rest of Mopidy."""
|
"""Process messages from the rest of Mopidy."""
|
||||||
if message['command'] == 'play_uri':
|
if message['command'] == 'play_uri':
|
||||||
|
|||||||
@ -28,16 +28,23 @@ class BaseProcess(multiprocessing.Process):
|
|||||||
except SettingsError as e:
|
except SettingsError as e:
|
||||||
logger.error(e.message)
|
logger.error(e.message)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
except ImportError as e:
|
||||||
|
logger.error(e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def run_inside_try(self):
|
def run_inside_try(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class CoreProcess(BaseProcess):
|
class CoreProcess(BaseProcess):
|
||||||
def __init__(self, core_queue):
|
def __init__(self, core_queue, output_class, backend_class,
|
||||||
|
frontend_class):
|
||||||
super(CoreProcess, self).__init__()
|
super(CoreProcess, self).__init__()
|
||||||
self.core_queue = core_queue
|
self.core_queue = core_queue
|
||||||
self.output_queue = None
|
self.output_queue = None
|
||||||
|
self.output_class = output_class
|
||||||
|
self.backend_class = backend_class
|
||||||
|
self.frontend_class = frontend_class
|
||||||
self.output = None
|
self.output = None
|
||||||
self.backend = None
|
self.backend = None
|
||||||
self.frontend = None
|
self.frontend = None
|
||||||
@ -50,11 +57,9 @@ class CoreProcess(BaseProcess):
|
|||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.output_queue = multiprocessing.Queue()
|
self.output_queue = multiprocessing.Queue()
|
||||||
self.output = get_class(settings.OUTPUT)(self.core_queue,
|
self.output = self.output_class(self.core_queue, self.output_queue)
|
||||||
self.output_queue)
|
self.backend = self.backend_class(self.core_queue, self.output_queue)
|
||||||
self.backend = get_class(settings.BACKENDS[0])(self.core_queue,
|
self.frontend = self.frontend_class(self.backend)
|
||||||
self.output_queue)
|
|
||||||
self.frontend = get_class(settings.FRONTEND)(self.backend)
|
|
||||||
|
|
||||||
def process_message(self, message):
|
def process_message(self, message):
|
||||||
if message.get('to') == 'output':
|
if message.get('to') == 'output':
|
||||||
|
|||||||
@ -24,8 +24,11 @@ def get_class(name):
|
|||||||
module_name = name[:name.rindex('.')]
|
module_name = name[:name.rindex('.')]
|
||||||
class_name = name[name.rindex('.') + 1:]
|
class_name = name[name.rindex('.') + 1:]
|
||||||
logger.debug('Loading: %s', name)
|
logger.debug('Loading: %s', name)
|
||||||
module = import_module(module_name)
|
try:
|
||||||
class_object = getattr(module, class_name)
|
module = import_module(module_name)
|
||||||
|
class_object = getattr(module, class_name)
|
||||||
|
except (ImportError, AttributeError):
|
||||||
|
raise ImportError("Couldn't load: %s" % name)
|
||||||
return class_object
|
return class_object
|
||||||
|
|
||||||
def get_or_create_folder(folder):
|
def get_or_create_folder(folder):
|
||||||
|
|||||||
@ -11,6 +11,25 @@ from mopidy.models import Track, Artist, Album
|
|||||||
|
|
||||||
from tests import SkipTest, data_folder
|
from tests import SkipTest, data_folder
|
||||||
|
|
||||||
|
class GetClassTest(unittest.TestCase):
|
||||||
|
def test_loading_module_that_does_not_exist(self):
|
||||||
|
test = lambda: get_class('foo.bar.Baz')
|
||||||
|
self.assertRaises(ImportError, test)
|
||||||
|
|
||||||
|
def test_loading_class_that_does_not_exist(self):
|
||||||
|
test = lambda: get_class('unittest.FooBarBaz')
|
||||||
|
self.assertRaises(ImportError, test)
|
||||||
|
|
||||||
|
def test_import_error_message_contains_complete_class_path(self):
|
||||||
|
try:
|
||||||
|
get_class('foo.bar.Baz')
|
||||||
|
except ImportError as e:
|
||||||
|
self.assert_('foo.bar.Baz' in str(e))
|
||||||
|
|
||||||
|
def test_loading_existing_class(self):
|
||||||
|
cls = get_class('unittest.TestCase')
|
||||||
|
self.assertEqual(cls.__name__, 'TestCase')
|
||||||
|
|
||||||
class GetOrCreateFolderTest(unittest.TestCase):
|
class GetOrCreateFolderTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.parent = tempfile.mkdtemp()
|
self.parent = tempfile.mkdtemp()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user