[gst-devel] typefind and modifying a running stream (python bindings)

Daniel Lenski dlenski at gmail.com
Wed Sep 12 00:13:05 CEST 2007

On 9/11/07, Tim Müller <t.i.m at zen.co.uk> wrote:
> There should be a section about dynamic pads or "pad-added" signals or
> decodebin's "new-decoded-pad" signal somewhere, what you need to do
> follows the same pattern.  IIRC Jono Bacon also has a short python-based
> tutorial about dynamic pads in his blog somewhere.

Thanks again, Tim!  That tutorial was quite helpful

> It's not a bug, this is expected behaviour.  In this case we treat an
> ID3 tag like a container format - you only get the container type and
> not the media type of the stream(s) within the container.  In this case,
> id3demux will give you the media type of the content after the tag on
> its source pad (which it will add dynamically).

Gotcha!  So, with this advice I have been able to make a working
"autotranscoder" element... which is a bin with one sink and two
sources.  It uses typefind to identify the incoming stream, then:

(a) if that's an audio/mpeg stream, copies it verbatim to the filesrc
pad, and decodes it to the audiosrc pad
(b) if it's some other stream, decodes it with decodebin, then copies
the decoded audio to the audiosrc pad, and re-encodes in as MP3 to the
filesrc pad

So basically it's supposed to accept any incoming audio stream and
output both audio/x-raw-int and audio/mpeg.  It doesn't currently
recognize application/x-id3, so it decodes and then re-encodes it as

It works just great now *IF* the input is audio/mpeg or
application/ogg.  But if the input is application/x-id3... the stream
fails to start playing.  I'm going slightly nuts trying to understand
why.  If decodebin can decode OGG into a playable Vorbis stream,
shouldn't it do the exact same thing with ID3 containing MP3?  I have
posted the code below (~100 lines), and if anyone could try running it
on an MP3 file, or otherwise point out where I'm going wrong, I'd much
appreciate it.



Code for recorder.py follows:

import pygst
import gst
import gobject
import sys, time

# A gstreamer element with one sink and two sources
#   * sink should be some audio stream
#   * audiosrc will output audio/x-raw-int, for playback
#   * filesrc will output audio/mpeg, to be saved on disk
# Will do The Right Thing (tm) whether the source is an MP3
# stream or other format.

class autotranscoder(gst.Bin):
    def __init__(self):

        self.typefind = gst.element_factory_make('typefind')
        self.typefind.connect('have-type', self.have_type)

        # sink pad (to receive the audio stream)
        self.sink = gst.GhostPad('sink', self.typefind.get_pad('sink'))

        # source pads (to be connected once pipeline is complete)
        self.audiosrc = gst.ghost_pad_new_notarget('audiosrc', gst.PAD_SRC)
        self.filesrc = gst.ghost_pad_new_notarget('filesrc', gst.PAD_SRC)

        # add pads to self
        for p in (self.sink, self.audiosrc, self.filesrc): self.add_pad(p)

    def have_type(self, tf, prob, caps):
        print "got file type: %s" % caps[0].get_name()

        ismp3 = (caps[0].get_name() == 'audio/mpeg' and
caps[0]['mpegversion']==1 and caps[0]['layer']==3)
        if ismp3:
            tee = gst.element_factory_make('tee', 'sink')
            audio_q = gst.element_factory_make('queue')
            file_q = gst.element_factory_make('queue', 'filesrc')
            mad = gst.element_factory_make('mad', 'audiosrc')
            self.add(tee, audio_q, file_q, mad)

            gst.element_link_many(tee, audio_q, mad)
            decodebin = gst.element_factory_make('decodebin', 'sink')
            convert = gst.element_factory_make('audioconvert')
            tee = gst.element_factory_make('tee')
            audio_q = gst.element_factory_make('queue', 'audiosrc')
            file_q = gst.element_factory_make('queue')
            lame = gst.element_factory_make('lame', 'filesrc')
            self.add(decodebin, convert, tee, audio_q, file_q, lame)

            gst.element_link_many(convert, tee, file_q, lame)
            decodebin.connect('new-decoded-pad', self.new_decoded_pad,

        self.typefind.link( self.get_by_name('sink') )
        self.filesrc.set_target( self.get_by_name('filesrc').get_pad('src') )
        self.audiosrc.set_target( self.get_by_name('audiosrc').get_pad('src') )


    def new_decoded_pad(self, dbin, pad, islast, target):

class StreamRecorder:
    def __init__(self, uri, filename, audiosink):
        self.pipeline = gst.Pipeline()

        # create stream reader and automatic MP3 transcoder/decoder
        self.stream = gst.element_make_from_uri(gst.URI_SRC, uri)
        self.filter = autotranscoder()
        self.pipeline.add(self.stream, self.filter)

        # create audio and/or file sinks
        self.audiosink = audiosink or gst.element_factory_make('fakesink')
        if filename:
            self.filesink = gst.element_factory_make('filesink')
            self.filesink.set_property('location', filename)
            self.filesink = gst.element_factory_make('fakesink')
        self.pipeline.add(self.audiosink, self.filesink)

        # assemble pipeline
        self.filter.link_pads('audiosrc', self.audiosink, None)
        self.filter.link_pads('filesrc', self.filesink, None)

    def start(self):
        print "starting recorder..."
        print "started recorder."

    def stop(self):

def main(args):
    from time import sleep

    if len(args) != 4:
        print "usage: %s stream-uri output-filename
duration-in-seconds" % args[0]
        return -1
        uri, filename, duration = args[1:]

    streamer = StreamRecorder(uri, filename,

    mainloop = gobject.MainLoop()
    def end():
        time = streamer.pipeline.query_position(gst.Format(gst.FORMAT_TIME))[0]
/ 1000000000.0
        print time
        if time >= duration:
            return False
            return True
    gobject.timeout_add(1000, end)

if __name__=='__main__': sys.exit(main(sys.argv))

More information about the gstreamer-devel mailing list