Update docs on GStreamer module. Some refactoring of (mostly internal) methods.

This commit is contained in:
Stein Magnus Jodal 2011-05-19 19:31:14 +02:00
parent 1503caa03d
commit 31aaec8830
2 changed files with 66 additions and 40 deletions

View File

@ -106,7 +106,7 @@ class SpotifySessionManager(BaseThread, PyspotifySessionManager):
'sample_rate': sample_rate,
'channels': channels,
}
self.gstreamer.deliver_data(capabilites, bytes(frames))
self.gstreamer.emit_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.gstreamer.end_of_data_stream()
self.gstreamer.emit_end_of_stream()
def refresh_stored_playlists(self):
"""Refresh the stored playlists in the backend with fresh meta data

View File

@ -44,14 +44,14 @@ class GStreamer(ThreadingActor):
self._handlers = {}
def on_start(self):
self._setup_gstreamer()
# **Warning:** :class:`GStreamer` requires
# :class:`mopidy.utils.process.GObjectEventThread` to be running. This
# is not enforced by :class:`GStreamer` itself.
self._setup_pipeline()
self._setup_outputs()
self._setup_message_processor()
def _setup_gstreamer(self):
"""
**Warning:** :class:`GStreamer` requires
:class:`mopidy.utils.process.GObjectEventThread` to be running. This is
not enforced by :class:`GStreamer` itself.
"""
def _setup_pipeline(self):
description = ' ! '.join([
'uridecodebin name=uri',
'audioconvert name=convert',
@ -67,31 +67,31 @@ class GStreamer(ThreadingActor):
self._volume = self._pipeline.get_by_name('volume')
self._uridecodebin = self._pipeline.get_by_name('uri')
self._uridecodebin.connect('notify::source', self._process_new_source)
self._uridecodebin.connect('pad-added', self._process_new_pad,
self._uridecodebin.connect('notify::source', self._on_new_source)
self._uridecodebin.connect('pad-added', self._on_new_pad,
self._pipeline.get_by_name('convert').get_pad('sink'))
def _setup_outputs(self):
for output in settings.OUTPUTS:
get_class(output)(self).connect()
# Setup bus and message processor
def _setup_message_processor(self):
bus = self._pipeline.get_bus()
bus.add_signal_watch()
bus.connect('message', self._process_gstreamer_message)
bus.connect('message', self._on_message)
def _process_new_source(self, element, pad):
def _on_new_source(self, element, pad):
self._source = element.get_by_name('source')
try:
self._source.set_property('caps', default_caps)
except TypeError:
pass
def _process_new_pad(self, source, pad, target_pad):
def _on_new_pad(self, source, pad, target_pad):
if not pad.is_linked():
pad.link(target_pad)
def _process_gstreamer_message(self, bus, message):
"""Process messages from GStreamer."""
def _on_message(self, bus, message):
if message.src in self._handlers:
if self._handlers[message.src](message):
return # Message was handeled by output
@ -115,16 +115,18 @@ class GStreamer(ThreadingActor):
def set_uri(self, uri):
"""
Change internal uridecodebin's URI
Set URI of audio to be played.
You *MUST* call :meth:`prepare_change` before calling this method.
:param uri: the URI to play
:type uri: string
"""
self._uridecodebin.set_property('uri', uri)
def deliver_data(self, capabilities, data):
def emit_data(self, capabilities, data):
"""
Deliver audio data to be played
Call this to deliver raw audio data to be played.
:param capabilities: a GStreamer capabilities string
:type capabilities: string
@ -136,9 +138,10 @@ class GStreamer(ThreadingActor):
self._source.set_property('caps', caps)
self._source.emit('push-buffer', buffer_)
def end_of_data_stream(self):
def emit_end_of_stream(self):
"""
Add end-of-stream token to source.
Put an end-of-stream token on the pipeline. This is typically used in
combination with :meth:`emit_data`.
We will get a GStreamer message when the stream playback reaches the
token, and can then do any end-of-stream related tasks.
@ -175,18 +178,26 @@ class GStreamer(ThreadingActor):
return handeled
def start_playback(self):
"""Notify GStreamer that it should start playback"""
"""
Notify GStreamer that it should start playback.
:rtype: :class:`True` if successfull, else :class:`False`
"""
return self._set_state(gst.STATE_PLAYING)
def pause_playback(self):
"""Notify GStreamer that it should pause playback"""
"""
Notify GStreamer that it should pause playback.
:rtype: :class:`True` if successfull, else :class:`False`
"""
return self._set_state(gst.STATE_PAUSED)
def prepare_change(self):
"""
Notify GStreamer that we are about to change state of playback.
This function always needs to be called before changing URIS or doing
This function *MUST* be called before changing URIs or doing
changes like updating data that is being pushed. The reason for this
is that GStreamer will reset all its state when it changes to
:attr:`gst.STATE_READY`.
@ -194,15 +205,22 @@ class GStreamer(ThreadingActor):
return self._set_state(gst.STATE_READY)
def stop_playback(self):
"""Notify GStreamer that is should stop playback"""
"""
Notify GStreamer that is should stop playback.
:rtype: :class:`True` if successfull, else :class:`False`
"""
return self._set_state(gst.STATE_NULL)
def _set_state(self, state):
"""
Set the GStreamer state. Returns :class:`True` if successful.
Internal method for setting the raw GStreamer state.
.. digraph:: gst_state_transitions
graph [rankdir="LR"];
node [fontsize=10];
"NULL" -> "READY"
"PAUSED" -> "PLAYING"
"PAUSED" -> "READY"
@ -213,7 +231,7 @@ class GStreamer(ThreadingActor):
:param state: State to set pipeline to. One of: `gst.STATE_NULL`,
`gst.STATE_READY`, `gst.STATE_PAUSED` and `gst.STATE_PLAYING`.
:type state: :class:`gst.State`
:rtype: :class:`True` or :class:`False`
:rtype: :class:`True` if successfull, else :class:`False`
"""
result = self._pipeline.set_state(state)
if result == gst.STATE_CHANGE_FAILURE:
@ -231,7 +249,7 @@ class GStreamer(ThreadingActor):
def get_volume(self):
"""
Get volume level for software mixer.
Get volume level of the GStreamer software mixer.
:rtype: int in range [0..100]
"""
@ -239,7 +257,7 @@ class GStreamer(ThreadingActor):
def set_volume(self, volume):
"""
Set volume level for software mixer.
Set volume level of the GStreamer software mixer.
:param volume: the volume in the range [0..100]
:type volume: int
@ -252,10 +270,11 @@ class GStreamer(ThreadingActor):
"""
Set track metadata for currently playing song.
Only needs to be called by sources such as appsrc which don't already
inject tags in pipeline.
Only needs to be called by sources such as `appsrc` which do not
already inject tags in pipeline, e.g. when using :meth:`emit_data` to
deliver raw audio data to GStreamer.
:param track: Track containing metadata for current song.
:param track: the current track
:type track: :class:`mopidy.modes.Track`
"""
# FIXME what if we want to unset taginject tags?
@ -270,7 +289,7 @@ class GStreamer(ThreadingActor):
"""
Connect output to pipeline.
:param output: output to connect to our pipeline.
:param output: output to connect to the pipeline
:type output: :class:`gst.Bin`
"""
self._pipeline.add(output)
@ -280,13 +299,18 @@ class GStreamer(ThreadingActor):
logger.info('Added %s', output.get_name())
def list_outputs(self):
"""
Get list with the name of all active outputs.
:rtype: list of strings
"""
return [output.get_name() for output in self._outputs]
def remove_output(self, output):
"""
Remove output from our pipeline.
:param output: output to remove from our pipeline.
:param output: output to remove from the pipeline
:type output: :class:`gst.Bin`
"""
if output not in self._outputs:
@ -332,21 +356,23 @@ class GStreamer(ThreadingActor):
Hook to allow outputs (or other code) to register custom message
handlers for all messages coming from the element in question.
In the case of outputs :meth:`mopidy.outputs.BaseOuptut.on_connect`
In the case of outputs, :meth:`mopidy.outputs.BaseOutput.on_connect`
should be used to attach such handlers and care should be taken to
remove them in :meth:`mopidy.outputs.BaseOuptut.on_remove`.
remove them in :meth:`mopidy.outputs.BaseOutput.on_remove` using
:meth:`remove_message_handler`.
The handler callback will only be given the message in question, and
is free to ignore the message. However, if the handler wants to prevent
the default handling of the message it should return :class:`True`
indicating that the message has been handled.
(Note that there can only be on handler per element)
Note that there can only be one handler per element.
:param element: element to watch messages from
:type element: :class:`gst.Element`
:param handler: function that expects `gst.Message`, should return
``True`` if message has been handeled.
:param handler: callable that takes :class:`gst.Message` and returns
:class:`True` if the message has been handeled
:type handler: callable
"""
self._handlers[element] = handler