diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 7437824c..eab8a3cc 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -50,6 +50,7 @@ class Audio(pykka.ThreadingActor): #: The GStreamer state mapped to :class:`mopidy.audio.PlaybackState` state = PlaybackState.STOPPED + _target_state = gst.STATE_NULL def __init__(self, config): super(Audio, self).__init__() @@ -101,6 +102,9 @@ class Audio(pykka.ThreadingActor): playbin = gst.element_factory_make('playbin2') playbin.set_property('flags', PLAYBIN_FLAGS) + playbin.set_property('buffer-size', 2*1024*1024) + playbin.set_property('buffer-duration', 2*gst.SECOND) + self._connect(playbin, 'about-to-finish', self._on_about_to_finish) self._connect(playbin, 'notify::source', self._on_new_source) @@ -279,6 +283,10 @@ class Audio(pykka.ThreadingActor): self._on_playbin_state_changed(old_state, new_state, pending_state) elif message.type == gst.MESSAGE_BUFFERING: percent = message.parse_buffering() + if percent < 10: + self._playbin.set_state(gst.STATE_PAUSED) + if percent == 100 and self._target_state == gst.STATE_PLAYING: + self._playbin.set_state(gst.STATE_PLAYING) logger.debug('Buffer %d%% full', percent) elif message.type == gst.MESSAGE_EOS: self._on_end_of_stream() @@ -471,6 +479,7 @@ class Audio(pykka.ThreadingActor): :type state: :class:`gst.State` :rtype: :class:`True` if successfull, else :class:`False` """ + self._target_state = state result = self._playbin.set_state(state) if result == gst.STATE_CHANGE_FAILURE: logger.warning( diff --git a/tests/audio/test_actor.py b/tests/audio/test_actor.py index 3e4fe694..4359f71a 100644 --- a/tests/audio/test_actor.py +++ b/tests/audio/test_actor.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from mock import Mock import unittest import gobject @@ -158,3 +159,35 @@ class AudioStateTest(unittest.TestCase): # gst.STATE_READY, gst.STATE_NULL, gst.STATE_VOID_PENDING) self.assertEqual(audio.PlaybackState.STOPPED, self.audio.state) + + +class AudioBufferingTest(unittest.TestCase): + def setUp(self): + self.audio = audio.Audio(config=None) + self.audio._playbin = Mock(spec=['set_state']) + + self.buffer_full_message = Mock() + self.buffer_full_message.type = gst.MESSAGE_BUFFERING + self.buffer_full_message.parse_buffering = Mock(return_value=100) + + self.buffer_empty_message = Mock() + self.buffer_empty_message.type = gst.MESSAGE_BUFFERING + self.buffer_empty_message.parse_buffering = Mock(return_value=0) + + def test_pause_when_buffer_empty(self): + playbin = self.audio._playbin + self.audio.start_playback() + playbin.set_state.assert_called_with(gst.STATE_PLAYING) + playbin.set_state.reset_mock() + + self.audio._on_message(None, self.buffer_empty_message) + playbin.set_state.assert_called_with(gst.STATE_PAUSED) + + def test_stay_paused_when_buffering_finished(self): + playbin = self.audio._playbin + self.audio.pause_playback() + playbin.set_state.assert_called_with(gst.STATE_PAUSED) + playbin.set_state.reset_mock() + + self.audio._on_message(None, self.buffer_full_message) + self.assertEqual(playbin.set_state.call_count, 0)