From 101b2a9817ff477c188b7851c7121dab87fa7db9 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 4 Aug 2014 21:41:25 +0200 Subject: [PATCH] audio: Make stream-changed correctly handle tee setup For the purposes of this event we consider the outputs sink the final element. If we don't do this we would get one event per branch, and we don't want to track when each of them actually switches any way. So just tracking when the tee/outputs bin gets the event is good enough for us. As part of this I've also added 'testoutput' as a special cased output value. This is now needed as outputs are always synced to the clock, making testing a lot less practical. --- mopidy/audio/actor.py | 37 ++++++++++++++++++++++++------------- tests/audio/test_actor.py | 12 +++++------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index fb8a0306..029913ac 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -155,7 +155,7 @@ class _Outputs(gst.Bin): # the actual switch, i.e. about to switch can block for longer thanks # to this queue. # TODO: make the min-max values a setting? - # TODO: move out of this class? + # TODO: this does not belong in this class. queue = gst.element_factory_make('queue') queue.set_property('max-size-buffers', 0) queue.set_property('max-size-bytes', 0) @@ -168,9 +168,11 @@ class _Outputs(gst.Bin): ghost_pad = gst.GhostPad('sink', queue.get_pad('sink')) self.add_pad(ghost_pad) - # Add an always connected fakesink so the tee doesn't fail. - # XXX disabled for now as we get one stream changed per sink... - # self._add(gst.element_factory_make('fakesink')) + # Add an always connected fakesink which respects the clock so the tee + # doesn't fail even if we don't have any outputs. + fakesink = gst.element_factory_make('fakesink') + fakesink.set_property('sync', True) + self._add(fakesink) def add_output(self, description): # XXX This only works for pipelines not in use until #790 gets done. @@ -346,12 +348,18 @@ class Audio(pykka.ThreadingActor): setup_proxy(source, self._config['proxy']) def _setup_output(self): - self._outputs = _Outputs() + # We don't want to test outputs for regular testing, so just instal + # an unsynced fakesink when someone asks for a testouput. + if self._config['audio']['output'] == 'testoutput': + self._outputs = gst.element_factory_make('fakesink') + else: + self._outputs = _Outputs() + try: + self._outputs.add_output(self._config['audio']['output']) + except exceptions.AudioException: + process.exit_process() # TODO: move this up the chain + self._outputs.get_pad('sink').add_event_probe(self._on_pad_event) - try: - self._outputs.add_output(self._config['audio']['output']) - except exceptions.AudioException: - process.exit_process() # TODO: move this up the chain self._playbin.set_property('audio-sink', self._outputs) def _setup_mixer(self): @@ -396,7 +404,12 @@ class Audio(pykka.ThreadingActor): pos_ms = pos // gst.MSECOND logger.debug('Triggering: position_changed(position=%s)', pos_ms) AudioListener.send('position_changed', position=pos_ms) - + elif event.type == gst.EVENT_SINK_MESSAGE: + # Handle stream changed messages when they reach our output bin. + # If we listen for it on the bus we get one per tee branch. + msg = event.parse_sink_message() + if msg.structure.has_name('playbin2-stream-changed'): + self._on_stream_changed(msg.structure['uri']) return True # TODO: consider splitting this out while we are at it. @@ -414,9 +427,7 @@ class Audio(pykka.ThreadingActor): elif msg.type == gst.MESSAGE_ASYNC_DONE: gst_logger.debug('Got async-done message.') elif msg.type == gst.MESSAGE_ELEMENT: - if msg.structure.has_name('playbin2-stream-changed'): - self._on_stream_changed(msg.structure['uri']) - elif gst.pbutils.is_missing_plugin_message(msg): + if gst.pbutils.is_missing_plugin_message(msg): self._on_missing_plugin(msg) def _on_playbin_state_changed(self, old_state, new_state, pending_state): diff --git a/tests/audio/test_actor.py b/tests/audio/test_actor.py index 2426f54e..fc3321d2 100644 --- a/tests/audio/test_actor.py +++ b/tests/audio/test_actor.py @@ -21,11 +21,9 @@ from mopidy.utils.path import path_to_uri from tests import path_to_data_dir -""" -We want to make sure both our real audio class and the fake one behave -correctly. So each test is first run against the real class, then repeated -against our dummy. -""" +# We want to make sure both our real audio class and the fake one behave +# correctly. So each test is first run against the real class, then repeated +# against our dummy. class BaseTest(unittest.TestCase): @@ -34,7 +32,7 @@ class BaseTest(unittest.TestCase): 'mixer': 'fakemixer track_max_volume=65536', 'mixer_track': None, 'mixer_volume': None, - 'output': 'fakesink', + 'output': 'testoutput', 'visualizer': None, } } @@ -49,7 +47,7 @@ class BaseTest(unittest.TestCase): 'audio': { 'mixer': 'foomixer', 'mixer_volume': None, - 'output': 'fakesink', + 'output': 'testoutput', 'visualizer': None, }, 'proxy': {