Switched to using decodebin2 to do the demuxing and (optionally) decoding. master
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Mon, 24 Jan 2011 03:07:34 +0000 (22:07 -0500)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Mon, 24 Jan 2011 03:07:34 +0000 (22:07 -0500)
hylia-transcoder.py

index aa4c028..f0f18be 100755 (executable)
@@ -1,6 +1,11 @@
 #!/usr/bin/env python
 # encoding: utf-8
 
+# known issues:
+#      - gstreamer demuxes xvid as video/x-xvid, but mpegtsmuxer can only accept video/mpeg. capssetter doesn't fix this.
+#      - MPEG-4 video can't seem to be muxed in properly, anyway.
+#      - Doesn't seem to work with the PS3...
+
 import sys, os, time, thread
 import glib, gobject, pygst
 import argparse
@@ -12,47 +17,42 @@ loop = glib.MainLoop()
 
 
 class Main(object):
-       def __init__(self):
+       def __init__(self, parser, args):
+               self.parser = parser
+               self.args = args
+               
                self.transcoder = gst.Pipeline('transcoder')
                
                self.input_file = gst.element_factory_make('filesrc', 'input-file')
+               self.input_file.set_property('location', self.args.input)
+               
                self.decoder = gst.element_factory_make('decodebin2', 'decoder')
+               self.decoder.connect('autoplug-continue', self.decoder_autoplug_continue)
                self.decoder.connect('pad-added', self.decoder_pad_added)
+               self.decoder.connect('no-more-pads', self.decoder_no_more_pads)
                
                queues = []
                
                self.video_input_queue = gst.element_factory_make('queue2', 'video-input-queue')
                queues.append(self.video_input_queue)
-               self.video_encoder = gst.element_factory_make('x264enc', 'video-encoder')
-               self.video_encoder.set_property('speed-preset', 1)
-               self.video_encoder.set_property('tune', 0x00000004)
-               #self.video_encoder.set_property('bitrate', 2048)
-               
-               h264parse = gst.element_factory_make('h264parse', 'h264parse')
-               h264parse.set_property('output-format', 1)
                self.video_output_queue = gst.element_factory_make('queue2', 'video-output-queue')
                queues.append(self.video_output_queue)
                
                self.audio_input_queue = gst.element_factory_make('queue2', 'audio-input-queue')
                queues.append(self.audio_input_queue)
-               audioconvert = gst.element_factory_make('audioconvert', 'audioconvert')
-               self.audio_encoder = gst.element_factory_make('ffenc_mp2', 'audio-encoder')
                self.audio_output_queue = gst.element_factory_make('queue2', 'audio-output-queue')
                queues.append(self.audio_output_queue)
                
-               self.muxer = gst.element_factory_make('ffmux_mpegts', 'muxer')
+               self.muxer = gst.element_factory_make('mpegtsmux', 'muxer')
                self.output_file = gst.element_factory_make('filesink', 'output-file')
+               self.output_file.set_property('location', self.args.output)
                
                self.transcoder.add(
                        self.input_file,
                        self.decoder,
                        self.video_input_queue,
-                       self.video_encoder,
-                       h264parse,
                        self.video_output_queue,
                        self.audio_input_queue,
-                       audioconvert,
-                       self.audio_encoder,
                        self.audio_output_queue,
                        self.muxer,
                        self.output_file
@@ -60,45 +60,61 @@ class Main(object):
                
                for queue in queues:
                        queue.set_property('max-size-buffers', 0)
-                       queue.set_property('max-size-bytes', 0)
                        queue.set_property('max-size-time', 0)
                
-               gst.element_link_many(
-                       self.input_file,
-                       self.decoder
-               )
-               
-               gst.element_link_many(
-                       self.video_input_queue,
-                       self.video_encoder,
-                       h264parse,
-                       self.video_output_queue,
-                       self.muxer
-               )
-               
-               gst.element_link_many(
-                       self.audio_input_queue,
-                       audioconvert,
-                       self.audio_encoder,
-                       self.audio_output_queue,
-                       self.muxer
-               )
-               
-               gst.element_link_many(
-                       self.muxer,
-                       self.output_file
-               )
+               gst.element_link_many(self.input_file, self.decoder)
                
                bus = self.transcoder.get_bus()
                bus.add_signal_watch()
                bus.connect('message', self.on_message)
        
+       def decoder_autoplug_continue(self, decoder, pad, caps):
+               caps_string = caps.to_string()
+               if caps_string.startswith('video/x-h264'):
+                       return False
+               elif caps_string.startswith('audio/x-ac3'):
+                       return False
+               elif caps_string.startswith('audio/mpeg'): # could be AAC, MP3, MP2
+                       return False
+               return True
+       
        def decoder_pad_added(self, decoder, pad):
-               print pad.get_caps()
-               if pad.get_caps().to_string()[0:5] == 'video':
+               caps_string = pad.get_caps().to_string()
+               if caps_string.startswith('video'):
                        pad.link(self.video_input_queue.get_pad('sink'))
-               elif pad.get_caps().to_string()[0:5] == 'audio':
+                       if caps_string.startswith('video/x-h264'):
+                               #h264parse = gst.element_factory_make('h264parse', 'h264parse')
+                               #h264parse.set_property('output-format', 1)
+                               #self.transcoder.add(h264parse)
+                               #gst.element_link_many(self.video_input_queue, h264parse, self.video_output_queue, self.muxer)
+                               gst.element_link_many(self.video_input_queue, self.video_output_queue, self.muxer)
+                               #h264parse.set_state(gst.STATE_PLAYING)
+                       else:
+                               video_encoder = gst.element_factory_make('ffenc_mpeg4', 'video-encoder')
+                               video_encoder.set_property('bitrate', (2048*1000))
+                               self.transcoder.add(video_encoder)
+                               gst.element_link_many(self.video_input_queue, video_encoder, self.video_output_queue, self.muxer)
+                               video_encoder.set_state(gst.STATE_PLAYING)
+               elif caps_string.startswith('audio'):
                        pad.link(self.audio_input_queue.get_pad('sink'))
+                       if caps_string.startswith('audio/x-ac3'):
+                               ac3parse = gst.element_factory_make('ac3parse', 'ac3parse')
+                               self.transcoder.add(ac3parse)
+                               gst.element_link_many(self.audio_input_queue, ac3parse, self.audio_output_queue, self.muxer)
+                               ac3parse.set_state(gst.STATE_PLAYING)
+                       elif caps_string.startswith('audio/mpeg'):
+                               gst.element_link_many(self.audio_input_queue, self.audio_output_queue, self.muxer)
+                       else:
+                               audioconvert = gst.element_factory_make('audioconvert', 'audioconvert')
+                               audio_encoder = gst.element_factory_make('ffenc_mp2', 'audio-encoder')
+                               self.transcoder.add(audioconvert, audio_encoder)
+                               gst.element_link_many(self.audio_input_queue, audioconvert, audio_encoder, self.audio_output_queue, self.muxer)
+                               audioconvert.set_state(gst.STATE_PLAYING)
+                               audio_encoder.set_state(gst.STATE_PLAYING)
+       
+       def decoder_no_more_pads(self, decoder):
+               gst.element_link_many(self.muxer, self.output_file)
+               self.transcoder.set_state(gst.STATE_PLAYING)
        
        def on_message(self, bus, message):
                t = message.type
@@ -108,17 +124,14 @@ class Main(object):
                elif t == gst.MESSAGE_ERROR:
                        self.transcoder.set_state(gst.STATE_NULL)
                        err, debug = message.parse_error()
-                       print "Error: %s" % err, debug
+                       logging.debug("Error: %s" % err, debug)
                        self.playmode = False
        
-       def start(self, args):
-               if os.path.isfile(args.input):
-                       self.playmode = True
-                       self.transcoder.get_by_name('input-file').set_property('location', args.input)
-                       self.transcoder.get_by_name('output-file').set_property('location', args.output)
-                       self.transcoder.set_state(gst.STATE_PLAYING)
-                       while self.playmode:
-                               time.sleep(1)
+       def start(self):
+               self.playmode = True
+               self.transcoder.set_state(gst.STATE_PAUSED)
+               while self.playmode:
+                       time.sleep(1)
                
                time.sleep(1)
                loop.quit()
@@ -129,10 +142,10 @@ if __name__ == "__main__":
        parser.add_argument('input', help='the path or URL of media to be transcoded')
        parser.add_argument('output', help='the path where transcoded data will be sent (defaults to /dev/null for testing)', nargs='?', default='/dev/null')
        args = parser.parse_args()
-       
-       print args
-       
-       mainclass = Main()
-       thread.start_new_thread(mainclass.start, (args,))
-       gobject.threads_init()
-       loop.run()
\ No newline at end of file
+       if os.path.isfile(args.input):
+               mainclass = Main(parser, args)
+               thread.start_new_thread(mainclass.start, ())
+               gobject.threads_init()
+               loop.run()
+       else:
+               parser.error('Invalid input "%s"' % args.input)
\ No newline at end of file