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._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.
|
||||
|
||||
@ -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):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user