Rip out rest of code that supported simulatnous outputs
This commit is contained in:
parent
f912164d39
commit
b2ccdec960
@ -17,10 +17,17 @@ v0.6.0 (in development)
|
|||||||
- Replace :attr:`mopidy.backends.base.Backend.uri_handlers` with
|
- Replace :attr:`mopidy.backends.base.Backend.uri_handlers` with
|
||||||
:attr:`mopidy.backends.base.Backend.uri_schemes`, which just takes the part
|
:attr:`mopidy.backends.base.Backend.uri_schemes`, which just takes the part
|
||||||
up to the colon of an URI, and not any prefix.
|
up to the colon of an URI, and not any prefix.
|
||||||
|
|
||||||
- Add Listener API, :mod:`mopidy.listeners`, to be implemented by actors
|
- Add Listener API, :mod:`mopidy.listeners`, to be implemented by actors
|
||||||
wanting to receive events from the backend. This is a formalization of the
|
wanting to receive events from the backend. This is a formalization of the
|
||||||
ad hoc events the Last.fm scrobbler has already been using for some time.
|
ad hoc events the Last.fm scrobbler has already been using for some time.
|
||||||
- Fix metadata update in Shoutcast streaming (Fixes: :issue:`122`)
|
|
||||||
|
- Fix metadata update in Shoutcast streaming (Fixes: :issue:`122`)
|
||||||
|
|
||||||
|
- Multiple simultaneously playing outputs was considered more trouble than what
|
||||||
|
it is worth maintnance wise. Thus, this feature has been axed for now.
|
||||||
|
Switching outputs is still posible, but only one can be active at a time, and
|
||||||
|
it is still the case that switching during playback does not funtion.
|
||||||
|
|
||||||
|
|
||||||
v0.5.0 (2011-06-15)
|
v0.5.0 (2011-06-15)
|
||||||
|
|||||||
@ -73,8 +73,8 @@ Using a custom audio sink
|
|||||||
=========================
|
=========================
|
||||||
|
|
||||||
If you for some reason want to use some other GStreamer audio sink than
|
If you for some reason want to use some other GStreamer audio sink than
|
||||||
``autoaudiosink``, you can add ``mopidy.outputs.custom.CustomOutput`` to the
|
``autoaudiosink``, you can set :attr:`mopidy.settings.OUTPUTS` to
|
||||||
:attr:`mopidy.settings.OUTPUTS` setting, and set the
|
``mopidy.outputs.custom.CustomOutput``, and set the
|
||||||
:attr:`mopidy.settings.CUSTOM_OUTPUT` setting to a partial GStreamer pipeline
|
:attr:`mopidy.settings.CUSTOM_OUTPUT` setting to a partial GStreamer pipeline
|
||||||
description describing the GStreamer sink you want to use.
|
description describing the GStreamer sink you want to use.
|
||||||
|
|
||||||
|
|||||||
@ -131,7 +131,7 @@ server simultaneously. To use the SHOUTcast output, do the following:
|
|||||||
#. Install, configure and start the Icecast server. It can be found in the
|
#. Install, configure and start the Icecast server. It can be found in the
|
||||||
``icecast2`` package in Debian/Ubuntu.
|
``icecast2`` package in Debian/Ubuntu.
|
||||||
|
|
||||||
#. Add ``mopidy.outputs.shoutcast.ShoutcastOutput`` output to the
|
#. Set ``mopidy.outputs.shoutcast.ShoutcastOutput`` as the first output in the
|
||||||
:attr:`mopidy.settings.OUTPUTS` setting.
|
:attr:`mopidy.settings.OUTPUTS` setting.
|
||||||
|
|
||||||
#. Check the default values for the following settings, and alter them to match
|
#. Check the default values for the following settings, and alter them to match
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class GStreamer(ThreadingActor):
|
|||||||
|
|
||||||
**Settings:**
|
**Settings:**
|
||||||
|
|
||||||
- :attr:`mopidy.settings.OUTPUTS`
|
- :attr:`mopidy.settings.OUTPUT`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -66,9 +66,9 @@ class GStreamer(ThreadingActor):
|
|||||||
self._pipeline.get_by_name('convert').get_pad('sink'))
|
self._pipeline.get_by_name('convert').get_pad('sink'))
|
||||||
|
|
||||||
def _setup_outputs(self):
|
def _setup_outputs(self):
|
||||||
for output in settings.OUTPUTS:
|
for klass in settings.OUTPUTS:
|
||||||
self._outputs.append(get_class(output)(self))
|
self._outputs.append(get_class(klass)())
|
||||||
self._outputs[0].connect()
|
self.connect_output(self._outputs[0].bin)
|
||||||
|
|
||||||
def _setup_message_processor(self):
|
def _setup_message_processor(self):
|
||||||
bus = self._pipeline.get_bus()
|
bus = self._pipeline.get_bus()
|
||||||
@ -87,10 +87,6 @@ class GStreamer(ThreadingActor):
|
|||||||
pad.link(target_pad)
|
pad.link(target_pad)
|
||||||
|
|
||||||
def _on_message(self, bus, message):
|
def _on_message(self, bus, message):
|
||||||
if message.src in self._handlers:
|
|
||||||
if self._handlers[message.src](message):
|
|
||||||
return # Message was handeled by output
|
|
||||||
|
|
||||||
if message.type == gst.MESSAGE_EOS:
|
if message.type == gst.MESSAGE_EOS:
|
||||||
logger.debug(u'GStreamer signalled end-of-stream. '
|
logger.debug(u'GStreamer signalled end-of-stream. '
|
||||||
'Telling backend ...')
|
'Telling backend ...')
|
||||||
@ -305,86 +301,4 @@ class GStreamer(ThreadingActor):
|
|||||||
gst.element_link_many(self._volume, output)
|
gst.element_link_many(self._volume, output)
|
||||||
logger.debug('Output set to %s', output.get_name())
|
logger.debug('Output set to %s', output.get_name())
|
||||||
|
|
||||||
def list_outputs(self):
|
# FIXME re-add disconnect / swap output code?
|
||||||
"""
|
|
||||||
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 the pipeline
|
|
||||||
:type output: :class:`gst.Bin`
|
|
||||||
"""
|
|
||||||
peersrc = output.get_pad('sink').get_peer()
|
|
||||||
handler = peersrc.add_event_probe(self._handle_event_probe)
|
|
||||||
|
|
||||||
struct = gst.Structure('mopidy-unlink')
|
|
||||||
struct.set_value('handler', handler)
|
|
||||||
|
|
||||||
event = gst.event_new_custom(gst.EVENT_CUSTOM_DOWNSTREAM, struct)
|
|
||||||
self._volume.send_event(event)
|
|
||||||
|
|
||||||
def _handle_event_probe(self, srcpad, event):
|
|
||||||
if event.type == gst.EVENT_CUSTOM_DOWNSTREAM and event.has_name('mopidy-unlink'):
|
|
||||||
data = self._get_structure_data(event.get_structure())
|
|
||||||
|
|
||||||
output = srcpad.get_peer().get_parent()
|
|
||||||
|
|
||||||
srcpad.unlink(srcpad.get_peer())
|
|
||||||
srcpad.remove_event_probe(data['handler'])
|
|
||||||
|
|
||||||
output.set_state(gst.STATE_NULL)
|
|
||||||
self._pipeline.remove(output)
|
|
||||||
|
|
||||||
logger.warning('Removed %s', output.get_name())
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _get_structure_data(self, struct):
|
|
||||||
# Ugly hack to get around missing get_value in pygst bindings :/
|
|
||||||
data = {}
|
|
||||||
def get_data(key, value):
|
|
||||||
data[key] = value
|
|
||||||
struct.foreach(get_data)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def connect_message_handler(self, element, handler):
|
|
||||||
"""
|
|
||||||
Attach custom message handler for given element.
|
|
||||||
|
|
||||||
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.BaseOutput.on_connect`
|
|
||||||
should be used to attach such handlers and care should be taken to
|
|
||||||
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 one handler per element.
|
|
||||||
|
|
||||||
:param element: element to watch messages from
|
|
||||||
:type element: :class:`gst.Element`
|
|
||||||
: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
|
|
||||||
|
|
||||||
def remove_message_handler(self, element):
|
|
||||||
"""
|
|
||||||
Remove custom message handler.
|
|
||||||
|
|
||||||
:param element: element to remove message handling from.
|
|
||||||
:type element: :class:`gst.Element`
|
|
||||||
"""
|
|
||||||
self._handlers.pop(element, None)
|
|
||||||
|
|||||||
@ -13,8 +13,7 @@ class BaseOutput(object):
|
|||||||
MESSAGE_ERROR = gst.MESSAGE_ERROR
|
MESSAGE_ERROR = gst.MESSAGE_ERROR
|
||||||
MESSAGE_WARNING = gst.MESSAGE_WARNING
|
MESSAGE_WARNING = gst.MESSAGE_WARNING
|
||||||
|
|
||||||
def __init__(self, gstreamer):
|
def __init__(self):
|
||||||
self.gstreamer = gstreamer
|
|
||||||
self.bin = self._build_bin()
|
self.bin = self._build_bin()
|
||||||
self.bin.set_name(self.get_name())
|
self.bin.set_name(self.get_name())
|
||||||
|
|
||||||
@ -25,32 +24,6 @@ class BaseOutput(object):
|
|||||||
logger.debug('Creating new output: %s', description)
|
logger.debug('Creating new output: %s', description)
|
||||||
return gst.parse_bin_from_description(description, True)
|
return gst.parse_bin_from_description(description, True)
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
"""Attach output to GStreamer pipeline."""
|
|
||||||
self.gstreamer.connect_output(self.bin)
|
|
||||||
self.on_connect()
|
|
||||||
|
|
||||||
def on_connect(self):
|
|
||||||
"""
|
|
||||||
Called after output has been connected to GStreamer pipeline.
|
|
||||||
|
|
||||||
*MAY be implemented by subclass.*
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove(self):
|
|
||||||
"""Remove output from GStreamer pipeline."""
|
|
||||||
self.gstreamer.remove_output(self.bin)
|
|
||||||
self.on_remove()
|
|
||||||
|
|
||||||
def on_remove(self):
|
|
||||||
"""
|
|
||||||
Called after output has been removed from GStreamer pipeline.
|
|
||||||
|
|
||||||
*MAY be implemented by subclass.*
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
Get name of the output. Defaults to the output's class name.
|
Get name of the output. Defaults to the output's class name.
|
||||||
|
|||||||
@ -40,19 +40,3 @@ class ShoutcastOutput(BaseOutput):
|
|||||||
u'username': settings.SHOUTCAST_OUTPUT_USERNAME,
|
u'username': settings.SHOUTCAST_OUTPUT_USERNAME,
|
||||||
u'password': settings.SHOUTCAST_OUTPUT_PASSWORD,
|
u'password': settings.SHOUTCAST_OUTPUT_PASSWORD,
|
||||||
})
|
})
|
||||||
|
|
||||||
def on_connect(self):
|
|
||||||
self.gstreamer.connect_message_handler(
|
|
||||||
self.bin.get_by_name('shoutcast'), self.message_handler)
|
|
||||||
|
|
||||||
def on_remove(self):
|
|
||||||
self.gstreamer.remove_message_handler(
|
|
||||||
self.bin.get_by_name('shoutcast'))
|
|
||||||
|
|
||||||
def message_handler(self, message):
|
|
||||||
if message.type != self.MESSAGE_ERROR:
|
|
||||||
return False
|
|
||||||
error, debug = message.parse_error()
|
|
||||||
logger.warning('%s (%s)', error, debug)
|
|
||||||
self.remove()
|
|
||||||
return True
|
|
||||||
|
|||||||
@ -173,12 +173,8 @@ MPD_SERVER_PASSWORD = None
|
|||||||
#:
|
#:
|
||||||
#: Default::
|
#: Default::
|
||||||
#:
|
#:
|
||||||
#: OUTPUTS = (
|
#: OUTPUTS = (u'mopidy.outputs.local.LocalOutput',)
|
||||||
#: u'mopidy.outputs.local.LocalOutput',
|
OUTPUTS = (u'mopidy.outputs.local.LocalOutput',)
|
||||||
#: )
|
|
||||||
OUTPUTS = (
|
|
||||||
u'mopidy.outputs.local.LocalOutput',
|
|
||||||
)
|
|
||||||
|
|
||||||
#: Hostname of the SHOUTcast server which Mopidy should stream audio to.
|
#: Hostname of the SHOUTcast server which Mopidy should stream audio to.
|
||||||
#:
|
#:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user