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