Merge branch 'feature/modularised-output' into develop

This commit is contained in:
Thomas Adamcik 2011-05-02 21:44:45 +02:00
commit 0031239357
16 changed files with 240 additions and 236 deletions

View File

@ -12,7 +12,7 @@ from mopidy.backends.base import (Backend, CurrentPlaylistController,
BasePlaybackProvider, StoredPlaylistsController,
BaseStoredPlaylistsProvider)
from mopidy.models import Playlist, Track, Album
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
from .translator import parse_m3u, parse_mpd_tag_cache
@ -50,12 +50,12 @@ class LocalBackend(ThreadingActor, Backend):
self.uri_handlers = [u'file://']
self.output = None
self.gstreamer = None
def on_start(self):
output_refs = ActorRegistry.get_by_class(BaseOutput)
assert len(output_refs) == 1, 'Expected exactly one running output.'
self.output = output_refs[0].proxy()
gstreamer_refs = ActorRegistry.get_by_class(GStreamer)
assert len(gstreamer_refs) == 1, 'Expected exactly one running gstreamer.'
self.gstreamer = gstreamer_refs[0].proxy()
class LocalPlaybackController(PlaybackController):
@ -67,24 +67,24 @@ class LocalPlaybackController(PlaybackController):
@property
def time_position(self):
return self.backend.output.get_position().get()
return self.backend.gstreamer.get_position().get()
class LocalPlaybackProvider(BasePlaybackProvider):
def pause(self):
return self.backend.output.set_state('PAUSED').get()
return self.backend.gstreamer.set_state('PAUSED').get()
def play(self, track):
return self.backend.output.play_uri(track.uri).get()
return self.backend.gstreamer.play_uri(track.uri).get()
def resume(self):
return self.backend.output.set_state('PLAYING').get()
return self.backend.gstreamer.set_state('PLAYING').get()
def seek(self, time_position):
return self.backend.output.set_position(time_position).get()
return self.backend.gstreamer.set_position(time_position).get()
def stop(self):
return self.backend.output.set_state('READY').get()
return self.backend.gstreamer.set_state('READY').get()
class LocalStoredPlaylistsProvider(BaseStoredPlaylistsProvider):

View File

@ -6,7 +6,7 @@ from pykka.registry import ActorRegistry
from mopidy import settings
from mopidy.backends.base import (Backend, CurrentPlaylistController,
LibraryController, PlaybackController, StoredPlaylistsController)
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
logger = logging.getLogger('mopidy.backends.spotify')
@ -63,13 +63,13 @@ class SpotifyBackend(ThreadingActor, Backend):
self.uri_handlers = [u'spotify:', u'http://open.spotify.com/']
self.output = None
self.gstreamer = None
self.spotify = None
def on_start(self):
output_refs = ActorRegistry.get_by_class(BaseOutput)
assert len(output_refs) == 1, 'Expected exactly one running output.'
self.output = output_refs[0].proxy()
gstreamer_refs = ActorRegistry.get_by_class(GStreamer)
assert len(gstreamer_refs) == 1, 'Expected exactly one running gstreamer.'
self.gstreamer = gstreamer_refs[0].proxy()
self.spotify = self._connect()

View File

@ -8,10 +8,10 @@ logger = logging.getLogger('mopidy.backends.spotify.playback')
class SpotifyPlaybackProvider(BasePlaybackProvider):
def pause(self):
return self.backend.output.set_state('PAUSED')
return self.backend.gstreamer.set_state('PAUSED')
def play(self, track):
self.backend.output.set_state('READY')
self.backend.gstreamer.set_state('READY')
if self.backend.playback.state == self.backend.playback.PLAYING:
self.backend.spotify.session.play(0)
if track.uri is None:
@ -20,7 +20,8 @@ class SpotifyPlaybackProvider(BasePlaybackProvider):
self.backend.spotify.session.load(
Link.from_string(track.uri).as_track())
self.backend.spotify.session.play(1)
self.backend.output.play_uri('appsrc://')
self.backend.gstreamer.play_uri('appsrc://')
self.backend.gstreamer.set_metadata(track)
return True
except SpotifyError as e:
logger.info('Playback of %s failed: %s', track.uri, e)
@ -30,12 +31,12 @@ class SpotifyPlaybackProvider(BasePlaybackProvider):
return self.seek(self.backend.playback.time_position)
def seek(self, time_position):
self.backend.output.set_state('READY')
self.backend.gstreamer.set_state('READY')
self.backend.spotify.session.seek(time_position)
self.backend.output.set_state('PLAYING')
self.backend.gstreamer.set_state('PLAYING')
return True
def stop(self):
result = self.backend.output.set_state('READY')
result = self.backend.gstreamer.set_state('READY')
self.backend.spotify.session.play(0)
return result

View File

@ -10,7 +10,7 @@ from mopidy import get_version, settings
from mopidy.backends.base import Backend
from mopidy.backends.spotify.translator import SpotifyTranslator
from mopidy.models import Playlist
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
from mopidy.utils.process import BaseThread
logger = logging.getLogger('mopidy.backends.spotify.session_manager')
@ -29,7 +29,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager):
BaseThread.__init__(self)
self.name = 'SpotifySMThread'
self.output = None
self.gstreamer = None
self.backend = None
self.connected = threading.Event()
@ -40,9 +40,9 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager):
self.connect()
def setup(self):
output_refs = ActorRegistry.get_by_class(BaseOutput)
assert len(output_refs) == 1, 'Expected exactly one running output.'
self.output = output_refs[0].proxy()
gstreamer_refs = ActorRegistry.get_by_class(GStreamer)
assert len(gstreamer_refs) == 1, 'Expected exactly one running gstreamer.'
self.gstreamer = gstreamer_refs[0].proxy()
backend_refs = ActorRegistry.get_by_class(Backend)
assert len(backend_refs) == 1, 'Expected exactly one running backend.'
@ -106,7 +106,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager):
'sample_rate': sample_rate,
'channels': channels,
}
self.output.deliver_data(capabilites, bytes(frames))
self.gstreamer.deliver_data(capabilites, bytes(frames))
def play_token_lost(self, session):
"""Callback used by pyspotify"""
@ -120,7 +120,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager):
def end_of_track(self, session):
"""Callback used by pyspotify"""
logger.debug(u'End of data stream reached')
self.output.end_of_data_stream()
self.gstreamer.end_of_data_stream()
def refresh_stored_playlists(self):
"""Refresh the stored playlists in the backend with fresh meta data

View File

@ -5,6 +5,7 @@ import time
from pykka.registry import ActorRegistry
from mopidy import get_version, settings, OptionalDependencyError
from mopidy.gstreamer import GStreamer
from mopidy.utils import get_class
from mopidy.utils.log import setup_logging
from mopidy.utils.path import get_or_create_folder, get_or_create_file
@ -18,7 +19,7 @@ def main():
setup_logging(options.verbosity_level, options.save_debug_log)
setup_settings()
setup_gobject_loop()
setup_output()
setup_gstreamer()
setup_mixer()
setup_backend()
setup_frontends()
@ -56,8 +57,8 @@ def setup_gobject_loop():
gobject_loop.start()
return gobject_loop
def setup_output():
return get_class(settings.OUTPUT).start().proxy()
def setup_gstreamer():
return GStreamer().start().proxy()
def setup_mixer():
return get_class(settings.MIXER).start().proxy()

View File

@ -8,10 +8,10 @@ from pykka.actor import ThreadingActor
from pykka.registry import ActorRegistry
from mopidy import settings
from mopidy.utils import get_class
from mopidy.backends.base import Backend
from mopidy.outputs.base import BaseOutput
logger = logging.getLogger('mopidy.outputs.gstreamer')
logger = logging.getLogger('mopidy.gstreamer')
default_caps = gst.Caps("""
audio/x-raw-int,
@ -22,7 +22,48 @@ default_caps = gst.Caps("""
signed=(boolean)true,
rate=(int)44100""")
class GStreamerOutput(ThreadingActor, BaseOutput):
class BaseOutput(object):
def connect_bin(self, pipeline, element_to_link_to):
"""
Connect output bin to pipeline and given element.
"""
description = 'queue ! %s' % self.describe_bin()
logger.debug('Adding new output to tee: %s', description)
output = self.parse_bin(description)
self.modify_bin(output)
pipeline.add(output)
output.sync_state_with_parent()
gst.element_link_many(element_to_link_to, output)
def parse_bin(self, description):
return gst.parse_bin_from_description(description, True)
def modify_bin(self, output):
"""
Modifies bin before it is installed if needed
"""
pass
def describe_bin(self):
"""
Describe bin to be parsed.
Must be implemented by subclasses.
"""
raise NotImplementedError
def set_properties(self, element, properties):
"""
Set properties on element if they have a value.
"""
for key, value in properties.items():
if value:
element.set_property(key, value)
class GStreamer(ThreadingActor):
"""
Audio output through `GStreamer <http://gstreamer.freedesktop.org/>`_.
@ -44,22 +85,32 @@ class GStreamerOutput(ThreadingActor, BaseOutput):
:class:`mopidy.utils.process.GObjectEventThread` to be running. This is
not enforced by :class:`GStreamerOutput` itself.
"""
logger.debug(u'Setting up GStreamer pipeline')
self.gst_pipeline = gst.parse_launch(' ! '.join([
base_pipeline = ' ! '.join([
'audioconvert name=convert',
'volume name=volume',
settings.GSTREAMER_AUDIO_SINK,
]))
'taginject name=tag',
'tee name=tee',
])
pad = self.gst_pipeline.get_by_name('convert').get_pad('sink')
logger.debug(u'Setting up base GStreamer pipeline: %s', base_pipeline)
self.gst_pipeline = gst.parse_launch(base_pipeline)
self.gst_tee = self.gst_pipeline.get_by_name('tee')
self.gst_convert = self.gst_pipeline.get_by_name('convert')
self.gst_volume = self.gst_pipeline.get_by_name('volume')
self.gst_taginject = self.gst_pipeline.get_by_name('tag')
uridecodebin = gst.element_factory_make('uridecodebin', 'uri')
uridecodebin.connect('pad-added', self._process_new_pad, pad)
uridecodebin.connect('notify::source', self._process_new_source)
uridecodebin.connect('pad-added', self._process_new_pad,
self.gst_convert.get_pad('sink'))
self.gst_pipeline.add(uridecodebin)
for output in settings.OUTPUTS:
output_cls = get_class(output)()
output_cls.connect_bin(self.gst_pipeline, self.gst_tee)
# Setup bus and message processor
gst_bus = self.gst_pipeline.get_bus()
gst_bus.add_signal_watch()
@ -96,7 +147,7 @@ class GStreamerOutput(ThreadingActor, BaseOutput):
def play_uri(self, uri):
"""Play audio at URI"""
self.set_state('READY')
self.gst_pipeline.get_by_name('uri').set_property('uri', uri)
self.gst_uridecodebin.set_property('uri', uri)
return self.set_state('PLAYING')
def deliver_data(self, caps_string, data):
@ -160,11 +211,17 @@ class GStreamerOutput(ThreadingActor, BaseOutput):
def get_volume(self):
"""Get volume in range [0..100]"""
gst_volume = self.gst_pipeline.get_by_name('volume')
return int(gst_volume.get_property('volume') * 100)
return int(self.gst_volume.get_property('volume') * 100)
def set_volume(self, volume):
"""Set volume in range [0..100]"""
gst_volume = self.gst_pipeline.get_by_name('volume')
gst_volume.set_property('volume', volume / 100.0)
self.gst_volume.set_property('volume', volume / 100.0)
return True
def set_metadata(self, track):
tags = u'artist="%(artist)s",title="%(title)s"' % {
'artist': u', '.join([a.name for a in track.artists]),
'title': track.name,
}
logger.debug('Setting tags to: %s', tags)
self.gst_taginject.set_property('tags', tags)

View File

@ -2,7 +2,7 @@ from pykka.actor import ThreadingActor
from pykka.registry import ActorRegistry
from mopidy.mixers.base import BaseMixer
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
class GStreamerSoftwareMixer(ThreadingActor, BaseMixer):
"""Mixer which uses GStreamer to control volume in software."""
@ -11,7 +11,7 @@ class GStreamerSoftwareMixer(ThreadingActor, BaseMixer):
self.output = None
def on_start(self):
output_refs = ActorRegistry.get_by_class(BaseOutput)
output_refs = ActorRegistry.get_by_class(GStreamer)
assert len(output_refs) == 1, 'Expected exactly one running output.'
self.output = output_refs[0].proxy()

31
mopidy/outputs.py Normal file
View File

@ -0,0 +1,31 @@
from mopidy import settings
from mopidy.gstreamer import BaseOutput
class LocalOutput(BaseOutput):
def describe_bin(self):
if settings.LOCAL_OUTPUT_OVERRIDE:
return settings.LOCAL_OUTPUT_OVERRIDE
return 'autoaudiosink'
class NullOutput(BaseOutput):
def describe_bin(self):
return 'fakesink'
class ShoutcastOutput(BaseOutput):
def describe_bin(self):
if settings.SHOUTCAST_OUTPUT_OVERRIDE:
return settings.SHOUTCAST_OUTPUT_OVERRIDE
return 'audioconvert ! %s ! shout2send name=shoutcast' \
% settings.SHOUTCAST_OUTPUT_ENCODER
def modify_bin(self, output):
if settings.SHOUTCAST_OUTPUT_OVERRIDE:
return
self.set_properties(output.get_by_name('shoutcast'), {
u'ip': settings.SHOUTCAST_OUTPUT_SERVER,
u'mount': settings.SHOUTCAST_OUTPUT_MOUNT,
u'port': settings.SHOUTCAST_OUTPUT_PORT,
u'username': settings.SHOUTCAST_OUTPUT_USERNAME,
u'password': settings.SHOUTCAST_OUTPUT_PASSWORD,
})

View File

@ -1,91 +0,0 @@
class BaseOutput(object):
"""
Base class for audio outputs.
"""
def play_uri(self, uri):
"""
Play URI.
*MUST be implemented by subclass.*
:param uri: the URI to play
:type uri: string
:rtype: :class:`True` if successful, else :class:`False`
"""
raise NotImplementedError
def deliver_data(self, capabilities, data):
"""
Deliver audio data to be played.
*MUST be implemented by subclass.*
:param capabilities: a GStreamer capabilities string
:type capabilities: string
"""
raise NotImplementedError
def end_of_data_stream(self):
"""
Signal that the last audio data has been delivered.
*MUST be implemented by subclass.*
"""
raise NotImplementedError
def get_position(self):
"""
Get position in milliseconds.
*MUST be implemented by subclass.*
:rtype: int
"""
raise NotImplementedError
def set_position(self, position):
"""
Set position in milliseconds.
*MUST be implemented by subclass.*
:param position: the position in milliseconds
:type volume: int
:rtype: :class:`True` if successful, else :class:`False`
"""
raise NotImplementedError
def set_state(self, state):
"""
Set playback state.
*MUST be implemented by subclass.*
:param state: the state
:type state: string
:rtype: :class:`True` if successful, else :class:`False`
"""
raise NotImplementedError
def get_volume(self):
"""
Get volume level for software mixer.
*MUST be implemented by subclass.*
:rtype: int in range [0..100]
"""
raise NotImplementedError
def set_volume(self, volume):
"""
Set volume level for software mixer.
*MUST be implemented by subclass.*
:param volume: the volume in the range [0..100]
:type volume: int
:rtype: :class:`True` if successful, else :class:`False`
"""
raise NotImplementedError

View File

@ -1,63 +0,0 @@
from pykka.actor import ThreadingActor
from mopidy.outputs.base import BaseOutput
class DummyOutput(ThreadingActor, BaseOutput):
"""
Audio output used for testing.
"""
# pylint: disable = R0902
# Too many instance attributes (9/7)
#: For testing. Contains the last URI passed to :meth:`play_uri`.
uri = None
#: For testing. Contains the last capabilities passed to
#: :meth:`deliver_data`.
capabilities = None
#: For testing. Contains the last data passed to :meth:`deliver_data`.
data = None
#: For testing. :class:`True` if :meth:`end_of_data_stream` has been
#: called.
end_of_data_stream_called = False
#: For testing. Contains the current position.
position = 0
#: For testing. Contains the current state.
state = 'NULL'
#: For testing. Contains the current volume.
volume = 100
def play_uri(self, uri):
self.uri = uri
return True
def deliver_data(self, capabilities, data):
self.capabilities = capabilities
self.data = data
def end_of_data_stream(self):
self.end_of_data_stream_called = True
def get_position(self):
return self.position
def set_position(self, position):
self.position = position
return True
def set_state(self, state):
self.state = state
return True
def get_volume(self):
return self.volume
def set_volume(self, volume):
self.volume = volume
return True

View File

@ -54,12 +54,12 @@ FRONTENDS = (
u'mopidy.frontends.lastfm.LastfmFrontend',
)
#: Which GStreamer audio sink to use in :mod:`mopidy.outputs.gstreamer`.
#: Which GStreamer bin description to use in :mod:`mopidy.outputs.CustomOutput`.
#:
#: Default::
#:
#: GSTREAMER_AUDIO_SINK = u'autoaudiosink'
GSTREAMER_AUDIO_SINK = u'autoaudiosink'
#: LOCAL_OUTPUT_OVERRIDE = None
LOCAL_OUTPUT_OVERRIDE = None
#: Your `Last.fm <http://www.last.fm/>`_ username.
#:
@ -143,13 +143,6 @@ MIXER_EXT_SPEAKERS_B = None
#: MIXER_MAX_VOLUME = 100
MIXER_MAX_VOLUME = 100
#: Audio output handler to use.
#:
#: Default::
#:
#: OUTPUT = u'mopidy.outputs.gstreamer.GStreamerOutput'
OUTPUT = u'mopidy.outputs.gstreamer.GStreamerOutput'
#: Which address Mopidy's MPD server should bind to.
#:
#:Examples:
@ -174,6 +167,77 @@ MPD_SERVER_PASSWORD = None
#: Default: 6600
MPD_SERVER_PORT = 6600
#: List of outputs to use. See :mod:`mopidy.outputs` for all available
#: backends
#:
#: Default::
#:
#: OUTPUTS = (
#: u'mopidy.outputs.LocalOutput',
#: )
OUTPUTS = (
u'mopidy.outputs.LocalOutput',
)
#: Servar that runs Shoutcast server to send stream to.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_SERVER = u'127.0.0.1'
SHOUTCAST_OUTPUT_SERVER = u'127.0.0.1'
#: User to authenticate as against Shoutcast server.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_USERNAME = u'source'
SHOUTCAST_OUTPUT_USERNAME = u'source'
#: Password to authenticate with against Shoutcast server.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_PASSWORD = u'hackme'
SHOUTCAST_OUTPUT_PASSWORD = u'hackme'
#: Port to use for streaming to Shoutcast server.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_PORT = 8000
SHOUTCAST_OUTPUT_PORT = 8000
#: Mountpoint to use for the stream on the Shoutcast server.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_MOUNT = u'/stream'
SHOUTCAST_OUTPUT_MOUNT = u'/stream'
#: Encoder to use to process audio data before streaming.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_ENCODER = u'lame mode=stereo bitrate=320'
SHOUTCAST_OUTPUT_ENCODER = u'lame mode=stereo bitrate=320'
#: Overrides to allow advanced setup of shoutcast. Using this settings implies
#: that all other SHOUTCAST_OUTPUT_* settings will be ignored.
#:
#: Examples:
#:
#: ``vorbisenc ! oggmux ! shout2send mount=/stream port=8000``
#: Encode with vorbis and use ogg mux.
#: ``lame bitrate=320 ! shout2send mount=/stream port=8000``
#: Encode with lame to bitrate=320.
#:
#: For all options see gst-inspect-0.10 lame, vorbisenc and shout2send.
#:
#: Default::
#:
#: SHOUTCAST_OUTPUT_OVERRIDE = None
SHOUTCAST_OUTPUT_OVERRIDE = None
#: Path to the Spotify cache.
#:
#: Used by :mod:`mopidy.backends.spotify`.

View File

@ -97,9 +97,11 @@ def validate_settings(defaults, settings):
'DUMP_LOG_FILENAME': 'DEBUG_LOG_FILENAME',
'DUMP_LOG_FORMAT': 'DEBUG_LOG_FORMAT',
'FRONTEND': 'FRONTENDS',
'GSTREAMER_AUDIO_SINK': 'LOCAL_OUTPUT_OVERRIDE',
'LOCAL_MUSIC_FOLDER': 'LOCAL_MUSIC_PATH',
'LOCAL_PLAYLIST_FOLDER': 'LOCAL_PLAYLIST_PATH',
'LOCAL_TAG_CACHE': 'LOCAL_TAG_CACHE_FILE',
'OUTPUT': None,
'SERVER': None,
'SERVER_HOSTNAME': 'MPD_SERVER_HOSTNAME',
'SERVER_PORT': 'MPD_SERVER_PORT',

View File

@ -3,7 +3,7 @@ import multiprocessing
import random
from mopidy.models import Playlist, Track
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
from tests.backends.base import populate_playlist
@ -12,7 +12,7 @@ class CurrentPlaylistControllerTest(object):
def setUp(self):
self.backend = self.backend_class()
self.backend.output = mock.Mock(spec=BaseOutput)
self.backend.gstreamer = mock.Mock(spec=GStreamer)
self.controller = self.backend.current_playlist
self.playback = self.backend.playback

View File

@ -4,7 +4,7 @@ import random
import time
from mopidy.models import Track
from mopidy.outputs.base import BaseOutput
from mopidy.gstreamer import GStreamer
from tests import SkipTest
from tests.backends.base import populate_playlist
@ -16,7 +16,7 @@ class PlaybackControllerTest(object):
def setUp(self):
self.backend = self.backend_class()
self.backend.output = mock.Mock(spec=BaseOutput)
self.backend.gstreamer = mock.Mock(spec=GStreamer)
self.playback = self.backend.playback
self.current_playlist = self.backend.current_playlist
@ -520,7 +520,7 @@ class PlaybackControllerTest(object):
self.assert_(wrapper.called)
@SkipTest # Blocks for 10ms and does not work with DummyOutput
@SkipTest # Blocks for 10ms
@populate_playlist
def test_end_of_track_callback_gets_called(self):
self.playback.play()
@ -599,7 +599,7 @@ class PlaybackControllerTest(object):
self.playback.pause()
self.assertEqual(self.playback.resume(), None)
@SkipTest # Uses sleep and does not work with DummyOutput+LocalBackend
@SkipTest # Uses sleep and might not work with LocalBackend
@populate_playlist
def test_resume_continues_from_right_position(self):
self.playback.play()
@ -729,7 +729,7 @@ class PlaybackControllerTest(object):
def test_time_position_when_stopped(self):
future = mock.Mock()
future.get = mock.Mock(return_value=0)
self.backend.output.get_position = mock.Mock(return_value=future)
self.backend.gstreamer.get_position = mock.Mock(return_value=future)
self.assertEqual(self.playback.time_position, 0)
@ -737,11 +737,11 @@ class PlaybackControllerTest(object):
def test_time_position_when_stopped_with_playlist(self):
future = mock.Mock()
future.get = mock.Mock(return_value=0)
self.backend.output.get_position = mock.Mock(return_value=future)
self.backend.gstreamer.get_position = mock.Mock(return_value=future)
self.assertEqual(self.playback.time_position, 0)
@SkipTest # Uses sleep and does not work with LocalBackend+DummyOutput
@SkipTest # Uses sleep and does might not work with LocalBackend
@populate_playlist
def test_time_position_when_playing(self):
self.playback.play()

View File

@ -9,26 +9,28 @@ if sys.platform == 'win32':
raise SkipTest
from mopidy import settings
from mopidy.outputs.gstreamer import GStreamerOutput
from mopidy.gstreamer import GStreamer
from mopidy.utils.path import path_to_uri
from tests import path_to_data_dir
class GStreamerOutputTest(unittest.TestCase):
# TODO BaseOutputTest?
class GStreamerTest(unittest.TestCase):
def setUp(self):
settings.BACKENDS = ('mopidy.backends.local.LocalBackend',)
self.song_uri = path_to_uri(path_to_data_dir('song1.wav'))
self.output = GStreamerOutput()
self.output.on_start()
self.gstreamer = GStreamer()
self.gstreamer.on_start()
def tearDown(self):
settings.runtime.clear()
def test_play_uri_existing_file(self):
self.assertTrue(self.output.play_uri(self.song_uri))
self.assertTrue(self.gstreamer.play_uri(self.song_uri))
def test_play_uri_non_existing_file(self):
self.assertFalse(self.output.play_uri(self.song_uri + 'bogus'))
self.assertFalse(self.gstreamer.play_uri(self.song_uri + 'bogus'))
@SkipTest
def test_deliver_data(self):
@ -39,19 +41,19 @@ class GStreamerOutputTest(unittest.TestCase):
pass # TODO
def test_default_get_volume_result(self):
self.assertEqual(100, self.output.get_volume())
self.assertEqual(100, self.gstreamer.get_volume())
def test_set_volume(self):
self.assertTrue(self.output.set_volume(50))
self.assertEqual(50, self.output.get_volume())
self.assertTrue(self.gstreamer.set_volume(50))
self.assertEqual(50, self.gstreamer.get_volume())
def test_set_volume_to_zero(self):
self.assertTrue(self.output.set_volume(0))
self.assertEqual(0, self.output.get_volume())
self.assertTrue(self.gstreamer.set_volume(0))
self.assertEqual(0, self.gstreamer.get_volume())
def test_set_volume_to_one_hundred(self):
self.assertTrue(self.output.set_volume(100))
self.assertEqual(100, self.output.get_volume())
self.assertTrue(self.gstreamer.set_volume(100))
self.assertEqual(100, self.gstreamer.get_volume())
@SkipTest
def test_set_state(self):