Video from audio file and picture. Filesink does not get any data

Flavian Hautbois flavian at trax-air.com
Fri Aug 21 03:12:45 PDT 2015


To mix together an audio file and a picture, one has to use imagefreeze to
make the image into an infinite video. The issue is to stop the pipeline
when the audio file was finished reading. This can be achieved through the
'drained' signal from decodebin2 (catch it and then send EOS to mp4mux).

I've written the code for the above, however I have an issue about filesink
not getting any data. I can see in the debug logs that mp4mux does get
data, but filesink does not flush anything. Do you know why?

Enclosing the code and a graphviz of the pipeline

class AudioCoverVideo:
    def __init__(self,
                 cover_path,
                 audio_path,
                 output_path):
        """
        Outputs a video which is a composite of a cover image and an audio
medium.
        Output format is set to mp4 H.264 AAC following the youtube's
guidelines for videos.

        Args:
            cover_path: path to the cover file (jpeg and png only)
            audio_path: path to the audio file (any format)
            output_path: path to the output video (it has to be mp4)
        """
        import pygst
        pygst.require('0.10')
        import gst
        import gobject
        gobject.threads_init()

        cover_decoder = None

        if cover_path.lower().endswith('jpg') or
cover_path.lower().endswith('jpeg'):
            cover_decoder = 'jpegdec'
        elif cover_path.lower().endswith('png'):
            cover_decoder = 'pngdec'
        else:
            raise ValueError()

        self.pipeline = gst.Pipeline()

        # Create bus and connect handlers for EOS and error
        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message::eos', self.on_eos)
        self.bus.connect('message::error', self.on_error)

        self.mux = gst.element_factory_make("mp4mux")
        self.mux.set_property('faststart', True)
        self.filesink = gst.element_factory_make("filesink")
        self.filesink.set_property('location', output_path)

        self.pipeline.add(self.mux, self.filesink)
        gst.element_link_many(self.mux, self.filesink)

        self.ch_audio_pad = self.mux.get_request_pad('audio_%d')
        self.ch_video_pad = self.mux.get_request_pad('video_%d')

        self.audio_pipeline(audio_path)
        self.video_pipeline(cover_path)

    def start(self):
        import gst
        import gobject
        # The MainLoop
        self.mainloop = gobject.MainLoop()
        # And off we go!
        self.pipeline.set_state(gst.STATE_PLAYING)
        self.mainloop.run()

    def audio_pipeline(self, audio_path):
        import gst
        src = gst.element_factory_make('filesrc', 'audiofilesrc')
        self.decaudio = gst.element_factory_make('decodebin2',
'decodebinaudio')
        self.audioconvert = gst.element_factory_make('audioconvert')
        enc = gst.element_factory_make('voaacenc')
        queue = gst.element_factory_make('queue')

        self.audioconvertpad = self.audioconvert.get_pad('sink')

        # Add to pipeline
        self.pipeline.add(src, self.decaudio, self.audioconvert, queue, enc)

        # Link
        gst.element_link_many(src, self.decaudio)
        gst.element_link_many(self.audioconvert, queue, enc)

        # Change parameters
        src.set_property('location', audio_path)
        enc.set_property('bitrate', 384000)

        # Add pads
        self.decaudio.connect('new-decoded-pad', self.on_new_decoded_pad)
        enc.get_pad("src").link(self.ch_audio_pad)

        # Create bus and connect handlers for EOS and error
        self.decaudio.connect('drained', self.on_drained)

    def video_pipeline(self, cover_path):
        import gst
        src = gst.element_factory_make('filesrc', 'videofilesrc')
        self.decvideo = gst.element_factory_make('decodebin2',
'decodebinvideo')
        ffmpegcs1 = gst.element_factory_make('ffmpegcolorspace',
'ffmpegcsp0')
        videoscale = gst.element_factory_make('videoscale', 'videoscale0')
        ffmpegcs2 = gst.element_factory_make('ffmpegcolorspace',
'ffmpegcsp1')
        imagefreeze = gst.element_factory_make('imagefreeze')
        capsfilter = gst.element_factory_make('capsfilter')
        ffmpegcs3 = gst.element_factory_make('ffmpegcolorspace',
'ffmpegcsp2')
        x264enc = gst.element_factory_make('x264enc')
        queue = gst.element_factory_make('queue')

        self.ffmpegpad = ffmpegcs1.get_pad('sink')

        caps = gst.Caps('video/x-raw-yuv,width=1080,height=1080')
        capsfilter.set_property("caps", caps)

        # Add to pipeline
        self.pipeline.add(src, self.decvideo, ffmpegcs1, imagefreeze,
ffmpegcs2,
                          videoscale, capsfilter, ffmpegcs3, queue, x264enc)

        # Link
        gst.element_link_many(src, self.decvideo)
        gst.element_link_many(ffmpegcs1, imagefreeze, ffmpegcs2,
                              videoscale, capsfilter, ffmpegcs3, queue,
x264enc)

        # Change parameters
        src.set_property('location', cover_path)
        x264enc.set_property('bframes', 2)
        x264enc.set_property('profile', 'high')
        x264enc.set_property('pass', 'pass1')

        # Add pads
        self.decvideo.connect('new-decoded-pad', self.on_new_decoded_pad)
        x264enc.get_pad("src").link(self.ch_video_pad)

    def on_eos(self, bus, msg):
        import gst
        print "EOS"
        self.pipeline.set_state(gst.STATE_NULL)
        self.mainloop.quit()

    def on_drained(self, decodebin):
        import gst
        print "DRAINED"
        self.mux.send_event(gst.event_new_eos())

    def on_error(self, bus, msg):
        error = msg.parse_error()
        self.mainloop.quit()
        raise IOError(u"GStreamer error: {}".format(error[1]))

    def on_new_decoded_pad(self, element, pad, last):
        print 'Decoded'
        if element == self.decaudio:
            apad = self.audioconvertpad
        elif element == self.decvideo:
            apad = self.ffmpegpad
        else:
            raise ValueError('Cannot connect this element:
{0}'.format(element))
        if not apad.is_linked():  # Only link once
            pad.link(apad)

F
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20150821/dd50a68a/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pipeline.png
Type: image/png
Size: 310768 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20150821/dd50a68a/attachment-0001.png>


More information about the gstreamer-devel mailing list