audio: Add about to finish callback support

This commit is contained in:
Thomas Adamcik 2014-01-27 23:43:11 +01:00
parent 99d581f7fc
commit 0d18bdea79
2 changed files with 61 additions and 8 deletions

View File

@ -58,6 +58,7 @@ class Audio(pykka.ThreadingActor):
self._playbin = None
self._signal_ids = {} # {(element, event): signal_id}
self._about_to_finish_callback = None
self._mixer = None
self._mixer_track = None
@ -108,15 +109,15 @@ class Audio(pykka.ThreadingActor):
def _on_about_to_finish(self, element):
source, self._appsrc = self._appsrc, None
if source is None:
return
self._appsrc_caps = None
if source is not None:
self._appsrc_caps = None
self._disconnect(source, 'need-data')
self._disconnect(source, 'enough-data')
self._disconnect(source, 'seek-data')
self._disconnect(source, 'need-data')
self._disconnect(source, 'enough-data')
self._disconnect(source, 'seek-data')
logger.debug('Ready to switch to new stream')
if self._about_to_finish_callback:
logger.debug('Calling about to finish callback.')
self._about_to_finish_callback()
def _on_new_source(self, element, pad):
uri = element.get_property('uri')
@ -432,6 +433,19 @@ class Audio(pykka.ThreadingActor):
# TODO: replace this with emit_data(None)?
self._playbin.get_property('source').emit('end-of-stream')
def set_about_to_finish_callback(self, callback):
"""
Configure audio to use an about to finish callback.
This should be used to achieve gapless playback. For this to work the
callback *MUST* call :meth:`set_uri` with the new URI to play and
block until this call has been made. :meth:`prepare_change` is not
needed before :meth:`set_uri` in this one special case.
:param callable callback: Callback to run when we need the next URI.
"""
self._about_to_finish_callback = callback
def get_position(self):
"""
Get position in milliseconds.

View File

@ -324,6 +324,45 @@ class AudioEventTest(unittest.TestCase):
if not event.wait(timeout=5.0):
self.fail('End of stream not reached within deadline')
# Make sure that gapless really works:
def test_gapless(self, send_mock):
song2_uri = path_to_uri(path_to_data_dir('song2.wav'))
uris = [song2_uri]
events = []
done = threading.Event()
def callback():
if uris:
self.audio.set_uri(uris.pop()).get()
def send(name, **kwargs):
events.append((name, kwargs))
if name == 'reached_end_of_stream':
done.set()
send_mock.side_effect = send
self.audio.set_about_to_finish_callback(callback).get()
self.audio.prepare_change()
self.audio.set_uri(self.song_uri)
self.audio.start_playback()
self.audio.wait_for_state_change().get()
if not done.wait(timeout=5.0):
self.fail('EOS not received')
excepted = [
('position_changed', {'position': 0}),
('stream_changed', {'uri': self.song_uri}),
('state_changed', {'old_state': PlaybackState.STOPPED,
'new_state': PlaybackState.PLAYING}),
('position_changed', {'position': 0}),
('stream_changed', {'uri': song2_uri}),
('reached_end_of_stream', {})]
self.assertEqual(excepted, events)
class AudioStateTest(unittest.TestCase):
def setUp(self):