audio: Add about to finish callback support
This commit is contained in:
parent
99d581f7fc
commit
0d18bdea79
@ -58,6 +58,7 @@ class Audio(pykka.ThreadingActor):
|
|||||||
|
|
||||||
self._playbin = None
|
self._playbin = None
|
||||||
self._signal_ids = {} # {(element, event): signal_id}
|
self._signal_ids = {} # {(element, event): signal_id}
|
||||||
|
self._about_to_finish_callback = None
|
||||||
|
|
||||||
self._mixer = None
|
self._mixer = None
|
||||||
self._mixer_track = None
|
self._mixer_track = None
|
||||||
@ -108,15 +109,15 @@ class Audio(pykka.ThreadingActor):
|
|||||||
|
|
||||||
def _on_about_to_finish(self, element):
|
def _on_about_to_finish(self, element):
|
||||||
source, self._appsrc = self._appsrc, None
|
source, self._appsrc = self._appsrc, None
|
||||||
if source is None:
|
if source is not None:
|
||||||
return
|
self._appsrc_caps = 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')
|
if self._about_to_finish_callback:
|
||||||
self._disconnect(source, 'enough-data')
|
logger.debug('Calling about to finish callback.')
|
||||||
self._disconnect(source, 'seek-data')
|
self._about_to_finish_callback()
|
||||||
|
|
||||||
logger.debug('Ready to switch to new stream')
|
|
||||||
|
|
||||||
def _on_new_source(self, element, pad):
|
def _on_new_source(self, element, pad):
|
||||||
uri = element.get_property('uri')
|
uri = element.get_property('uri')
|
||||||
@ -432,6 +433,19 @@ class Audio(pykka.ThreadingActor):
|
|||||||
# TODO: replace this with emit_data(None)?
|
# TODO: replace this with emit_data(None)?
|
||||||
self._playbin.get_property('source').emit('end-of-stream')
|
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):
|
def get_position(self):
|
||||||
"""
|
"""
|
||||||
Get position in milliseconds.
|
Get position in milliseconds.
|
||||||
|
|||||||
@ -324,6 +324,45 @@ class AudioEventTest(unittest.TestCase):
|
|||||||
if not event.wait(timeout=5.0):
|
if not event.wait(timeout=5.0):
|
||||||
self.fail('End of stream not reached within deadline')
|
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):
|
class AudioStateTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user