Merge branch 'feature/multi-backend' into develop
This commit is contained in:
commit
c8639f48da
@ -17,4 +17,5 @@ Outputs are responsible for playing audio.
|
||||
Output implementations
|
||||
======================
|
||||
|
||||
* :mod:`mopidy.outputs.dummy`
|
||||
* :mod:`mopidy.outputs.gstreamer`
|
||||
|
||||
10
docs/modules/outputs/dummy.rst
Normal file
10
docs/modules/outputs/dummy.rst
Normal file
@ -0,0 +1,10 @@
|
||||
*******************************************************
|
||||
:mod:`mopidy.outputs.dummy` -- Dummy output for testing
|
||||
*******************************************************
|
||||
|
||||
.. inheritance-diagram:: mopidy.outputs.dummy
|
||||
|
||||
.. automodule:: mopidy.outputs.dummy
|
||||
:synopsis: Dummy output for testing
|
||||
:members:
|
||||
:undoc-members:
|
||||
@ -5,6 +5,7 @@ import time
|
||||
from pykka.registry import ActorRegistry
|
||||
|
||||
from mopidy.frontends.base import BaseFrontend
|
||||
from mopidy.outputs.base import BaseOutput
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.base')
|
||||
|
||||
@ -288,6 +289,9 @@ class PlaybackController(object):
|
||||
@property
|
||||
def time_position(self):
|
||||
"""Time position in milliseconds."""
|
||||
output_position = self._time_position_from_output()
|
||||
if output_position is not None:
|
||||
return output_position
|
||||
if self.state == self.PLAYING:
|
||||
time_since_started = (self._current_wall_time -
|
||||
self.play_time_started)
|
||||
@ -297,6 +301,13 @@ class PlaybackController(object):
|
||||
elif self.state == self.STOPPED:
|
||||
return 0
|
||||
|
||||
def _time_position_from_output(self):
|
||||
output_refs = ActorRegistry.get_by_class(BaseOutput)
|
||||
if not output_refs:
|
||||
return None
|
||||
output = output_refs[0].proxy()
|
||||
return output.get_position()
|
||||
|
||||
def _play_time_start(self):
|
||||
self.play_time_accumulated = 0
|
||||
self.play_time_started = self._current_wall_time
|
||||
|
||||
@ -41,7 +41,7 @@ class LocalBackend(ThreadingActor, Backend):
|
||||
provider=library_provider)
|
||||
|
||||
playback_provider = LocalPlaybackProvider(backend=self)
|
||||
self.playback = LocalPlaybackController(backend=self,
|
||||
self.playback = PlaybackController(backend=self,
|
||||
provider=playback_provider)
|
||||
|
||||
stored_playlists_provider = LocalStoredPlaylistsProvider(backend=self)
|
||||
@ -58,18 +58,6 @@ class LocalBackend(ThreadingActor, Backend):
|
||||
self.output = output_refs[0].proxy()
|
||||
|
||||
|
||||
class LocalPlaybackController(PlaybackController):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalPlaybackController, self).__init__(*args, **kwargs)
|
||||
|
||||
# XXX Why do we call stop()? Is it to set GStreamer state to 'READY'?
|
||||
self.stop()
|
||||
|
||||
@property
|
||||
def time_position(self):
|
||||
return self.backend.output.get_position().get()
|
||||
|
||||
|
||||
class LocalPlaybackProvider(BasePlaybackProvider):
|
||||
def pause(self):
|
||||
return self.backend.output.set_state('PAUSED').get()
|
||||
|
||||
@ -40,7 +40,7 @@ class BaseOutput(object):
|
||||
|
||||
*MUST be implemented by subclass.*
|
||||
|
||||
:rtype: int
|
||||
:rtype: int or :class:`None` if unknown
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ class DummyOutput(ThreadingActor, BaseOutput):
|
||||
end_of_data_stream_called = False
|
||||
|
||||
#: For testing. Contains the current position.
|
||||
position = 0
|
||||
position = None
|
||||
|
||||
#: For testing. Contains the current state.
|
||||
state = 'NULL'
|
||||
|
||||
@ -165,3 +165,18 @@ class GStreamerOutput(ThreadingActor, BaseOutput):
|
||||
gst_volume = self.gst_pipeline.get_by_name('volume')
|
||||
gst_volume.set_property('volume', volume / 100.0)
|
||||
return True
|
||||
|
||||
def set_position(self, position):
|
||||
self.gst_pipeline.get_state() # block until state changes are done
|
||||
handeled = self.gst_pipeline.seek_simple(gst.Format(gst.FORMAT_TIME),
|
||||
gst.SEEK_FLAG_FLUSH, position * gst.MSECOND)
|
||||
self.gst_pipeline.get_state() # block until seek is done
|
||||
return handeled
|
||||
|
||||
def get_position(self):
|
||||
try:
|
||||
position = self.gst_pipeline.query_position(gst.FORMAT_TIME)[0]
|
||||
return position // gst.MSECOND
|
||||
except gst.QueryError, e:
|
||||
logger.debug(u'GStreamer time position: %s', e)
|
||||
return None
|
||||
|
||||
@ -386,5 +386,3 @@ class MusicDatabaseSearchTest(unittest.TestCase):
|
||||
def test_search_else_should_fail(self):
|
||||
result = self.h.handle_request(u'search "sometype" "something"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments')
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user