audio: Cleanup playlists elements docs and interace.

This commit is contained in:
Thomas Adamcik 2013-05-28 23:54:23 +02:00
parent acbaab59e5
commit a2b95c3a3a

View File

@ -40,7 +40,7 @@ def detect_xspf_header(typefind):
def parse_m3u(data):
# TODO: convert non uris to file uris.
# TODO: convert non URIs to file URIs.
for line in data.readlines():
if not line.startswith('#') and line.strip():
yield line
@ -48,7 +48,7 @@ def parse_m3u(data):
def parse_pls(data):
# TODO: error handling of bad playlists.
# TODO: convert non uris to file uris.
# TODO: convert non URIs to file URIs.
cp = configparser.RawConfigParser()
cp.readfp(data)
for i in xrange(1, cp.getint('playlist', 'numberofentries')):
@ -87,31 +87,64 @@ def register_typefinders():
class BasePlaylistElement(gst.Bin):
sinktemplate = None
srctemplate = None
ghostsrc = False
"""Base class for creating GStreamer elements for playlist support.
This element performs the following steps:
1. Initializes src and sink pads for the element.
2. Collects data from the sink until EOS is reached.
3. Passes the collected data to :meth:`convert` to get a list of URIs.
4. Passes the list of URIs to :meth:`handle`, default handling is to pass
the URIs to the src element as a uri-list.
5. If handle returned true, the EOS consumed and nothing more happens, if
it is not consumed it flows on to the next element downstream, which is
likely our uri-list consumer which needs the EOS to know we are done
sending URIs.
"""
sinkpad_template = None
"""GStreamer pad template to use for sink, must be overriden."""
srcpad_template = None
"""GStreamer pad template to use for src, must be overriden."""
ghost_srcpad = False
"""Indicates if src pad should be ghosted or not."""
def __init__(self):
"""Sets up src and sink pads plus behaviour."""
super(BasePlaylistElement, self).__init__()
self._data = io.BytesIO()
self._done = False
self.sink = gst.Pad(self.sinktemplate)
self.sink.set_chain_function(self._chain)
self.sink.set_event_function(self._event)
self.add_pad(self.sink)
self.sinkpad = gst.Pad(self.sinkpad_template)
self.sinkpad.set_chain_function(self._chain)
self.sinkpad.set_event_function(self._event)
self.add_pad(self.sinkpad)
if self.ghostsrc:
self.src = gst.ghost_pad_new_notarget('src', gst.PAD_SRC)
if self.ghost_srcpad:
self.srcpad = gst.ghost_pad_new_notarget('src', gst.PAD_SRC)
else:
self.src = gst.Pad(self.srctemplate)
self.add_pad(self.src)
self.srcpad = gst.Pad(self.srcpad_template)
self.add_pad(self.srcpad)
def convert(self, data):
"""Convert the data we have colleted to URIs.
:param data: Collected data buffer.
:type data: :class:`io.BytesIO`
:returns: iterable or generator of URIs.
"""
raise NotImplementedError
def handle(self, uris):
self.src.push(gst.Buffer('\n'.join(uris)))
"""Do something usefull with the URIs.
:param uris: List of URIs.
:type uris: :type:`list`
:returns: Boolean indicating if EOS should be consumed.
"""
self.srcpad.push(gst.Buffer('\n'.join(uris)))
return False
def _chain(self, pad, buf):
@ -129,6 +162,8 @@ class BasePlaylistElement(gst.Bin):
self._data.seek(0)
if self.handle(list(self.convert(self._data))):
return True
# Ensure we handle remaining events in a sane way.
return pad.event_default(event)
@ -138,17 +173,17 @@ class M3UDecoder(BasePlaylistElement):
'Convert .m3u to text/uri-list',
'Mopidy')
sinktemplate = gst.PadTemplate ('sink',
sinkpad_template = gst.PadTemplate ('sink',
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string('audio/x-mpegurl'))
srctemplate = gst.PadTemplate ('src',
srcpad_template = gst.PadTemplate ('src',
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string('text/uri-list'))
__gsttemplates__ = (sinktemplate, srctemplate)
__gsttemplates__ = (sinkpad_template, srcpad_template)
def convert(self, data):
return parse_m3u(data)
@ -160,17 +195,17 @@ class PLSDecoder(BasePlaylistElement):
'Convert .pls to text/uri-list',
'Mopidy')
sinktemplate = gst.PadTemplate ('sink',
sinkpad_template = gst.PadTemplate ('sink',
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string('audio/x-scpls'))
srctemplate = gst.PadTemplate ('src',
srcpad_template = gst.PadTemplate ('src',
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string('text/uri-list'))
__gsttemplates__ = (sinktemplate, srctemplate)
__gsttemplates__ = (sinkpad_template, srcpad_template)
def convert(self, data):
return parse_pls(data)
@ -182,17 +217,17 @@ class XSPFDecoder(BasePlaylistElement):
'Convert .pls to text/uri-list',
'Mopidy')
sinktemplate = gst.PadTemplate ('sink',
sinkpad_template = gst.PadTemplate ('sink',
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string('application/xspf+xml'))
srctemplate = gst.PadTemplate ('src',
srcpad_template = gst.PadTemplate ('src',
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string('text/uri-list'))
__gsttemplates__ = (sinktemplate, srctemplate)
__gsttemplates__ = (sinkpad_template, srcpad_template)
def convert(self, data):
return parse_xspf(data)
@ -204,19 +239,19 @@ class UriListElement(BasePlaylistElement):
'Convert a text/uri-list to a stream',
'Mopidy')
sinktemplate = gst.PadTemplate ('sink',
sinkpad_template = gst.PadTemplate ('sink',
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string('text/uri-list'))
srctemplate = gst.PadTemplate ('src',
srcpad_template = gst.PadTemplate ('src',
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_new_any())
ghostsrc = True # We need to hook this up to our internal decodebin
ghost_srcpad = True # We need to hook this up to our internal decodebin
__gsttemplates__ = (sinktemplate, srctemplate)
__gsttemplates__ = (sinkpad_template, srcpad_template)
def __init__(self):
super(UriListElement, self).__init__()
@ -227,10 +262,10 @@ class UriListElement(BasePlaylistElement):
self.uridecodebin.set_property('caps', gst.caps_new_any())
def pad_added(self, src, pad):
self.src.set_target(pad)
self.srcpad.set_target(pad)
def handle(self, uris):
# TODO: hookup about to finish and errors to rest of uris so we
# TODO: hookup about to finish and errors to rest of URIs so we
# round robin, only giving up once all have been tried.
self.add(self.uridecodebin)
self.uridecodebin.set_state(gst.STATE_READY)