encodebin and python

So I went down to FOSDEM this weekend. One of the things I was able to do while there was sit down with Edward Hervey and figure out how to use the new encodebin element with Transmageddon. The goal of the new encodebin element is to make encoding a much easier task with GStreamer, where you basically pass it a GStreamer caps value for your container format and audio and video streams, and encodebin figures out which elements it needs to create output with those caps. I have a minimal example below:

import sys
import os
import gobject; gobject.threads_init()
import gst
import pygst
import glib
import gst.pbutils
import gtk

class Transcoder:
   def __init__(self):
       # create GStreamer pipeline object
       self.pipeline = gst.Pipeline("TranscodingPipeline")
       self.pipeline.set_state(gst.STATE_PAUSED)

       self.uridecoder = gst.element_factory_make("uridecodebin", "uridecoder") 
       self.uridecoder.set_property("uri", "file:///home/cschalle/Videos/gravity.mpg")
       self.uridecoder.connect("pad-added", self.OnDynamicPad)
       self.pipeline.add(self.uridecoder)

       self.containerprofile = gst.pbutils.EncodingContainerProfile ("ogg", None , gst.Caps("application/ogg"), None)
       self.videoprofile = gst.pbutils.EncodingVideoProfile (gst.Caps("video/x-dirac"), None, gst.caps_new_any(), 0)
       self.audioprofile = gst.pbutils.EncodingAudioProfile (gst.Caps("audio/x-vorbis"), None, gst.caps_new_any(), 0)
       self.containerprofile.add_profile(self.videoprofile)
       self.containerprofile.add_profile(self.audioprofile)

       self.ebin = gst.element_factory_make ("encodebin", None)
       self.ebin.set_property("profile", self.containerprofile)
       self.pipeline.add(self.ebin)

       print "self.ebin is " + str(self.ebin)
       self.ebin.set_state(gst.STATE_PAUSED)
       self.uridecoder.set_state(gst.STATE_PAUSED)
       self.filesink = gst.element_factory_make("filesink", None)
       self.filesink.set_property("location", "/tmp/test.ogg")
       self.pipeline.add(self.filesink)
       self.filesink.set_state(gst.STATE_PAUSED)
       self.ebin.link(self.filesink)

       self.pipeline.set_state(gst.STATE_PLAYING)

   def OnDynamicPad(self, uridecodebin, src_pad):
       c = src_pad.get_caps().to_string()
       print c
       sinkpad = self.ebin.emit("request-pad", src_pad.get_caps())
       print "action signal returned", sinkpad
       src_pad.link(sinkpad)

if __name__ == "__main__":
	hwg = Transcoder()
	gtk.main()

The most important thing to notice about this code is the creation of the profiles, the adding of the audio and video profile to the container profile and then finally the setting of that profile onto the encodebin element. In my example I have extremely simple caps statements, basically just the codecs names, but you could add further things here like video height and width, framerate, audio bitrate, audio channels and so on, and encodebin would be able to give you the correct output.

We did find a couple of Python binding bugs though, which Edward fixed promptly, so if you want to try this code now, you need to grab gstreamer-python from git master.

New look for Transmageddon

Screenshot of git snapshot of Transmageddon

Screenshot of git snapshot of Transmageddon

Been preparing Transmageddon for adding a slew of new features, biggest visible change so far is replacing the radiobuttons with drop down lists. People suggested I do that from day one, but with the new features planned it has become a necessity, should also make Transmageddon nicer to use on small screens, like netbooks.

Back on Transmageddon and new GStreamer features

Been working on Transmageddon again this weekend as the work Edward has been doing on GES has been making its way into GStreamer-plugins-base and gst-python. The most import change for now is the new discoverer service. For those who used gst-python you might know the old version of it, which is basically an API for getting information about a multimedia file. Unfortunately the old Python version had some shortcomings, but thanks to the work that has been done for GES and Rygel we now have a new C based on in plugins-base which works a lot better. So those who had problems with Transmageddon in the past not recognizing files and thus not operating properly should now have more luck. Also the new discoverer process will tell me if a file is interlaced so I can easily now add support for deinterlacing in Transmageddon.

Anyway, I thought I share my half-done implementation of gst-discover in Python. You find a better version in C in gst-plugins-base/tools or a nice Vala version at live.gnome.org. But if you are familiar with Python the code below should at least give you an inkling on how to use the API from Python. Or check out the code of transmageddon for how to use the asynch version of the API.

#!/usr/bin/env python
# gst-python

import os
import sys

import pygtk
pygtk.require('2.0')
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.pbutils

class discover:
    def __init__(self):
	self.audiostreams=[]
    def set_file(self,file):
        self.file_uri=("file://"+filepath)
	newitem = gst.pbutils.Discoverer(50000000000)
	self.info = newitem.discover_uri(self.file_uri)

	self.streaminfo=self.info.get_stream_info()
	self.duration= (self.info.get_duration()/1000000000.0)
	self.container = self.streaminfo.get_caps()
	seekbool = self.info.get_seekable()
	if seekbool is True:
		self.seekable="Yes"
	else:
		self.seekable="No"
	audiostreamcounter=-1

	for i in self.info.get_stream_list():
		audiostreamcounter=audiostreamcounter+1
		if isinstance(i, gst.pbutils.DiscovererAudioInfo):
			audiocaps=i.get_caps()
			self.audiostreams.append(gst.pbutils.get_codec_description(audiocaps))
			self.audiotags=i.get_tags()	

		if isinstance(i, gst.pbutils.DiscovererVideoInfo):
			self.videocaps=i.get_caps()
			self.videotags=i.get_tags()
			interlacedbool = i.is_interlaced()
			if interlacedbool is True:
				self.interlaced ="Yes"
			else:
				self.interlaced="No"
			self.videoheight=i.get_height()
			self.videowidth=i.get_width()


    def create_report(self): # Create properties report
		print "Analyzing " + str(self.file_uri)
		print "Topology:"
		print "  container: " + gst.pbutils.get_codec_description(self.container)
		beancounter=0
		for item in self.audiostreams:
			beancounter=beancounter+1
			print "    audio stream " +str(beancounter) + ": " + self.audiostreams[beancounter-1]

		print "    video stream: " + gst.pbutils.get_codec_description(self.videocaps)
		print " "
		print "Properties:"
		print "  Duration: " + str(self.duration)
		print "  Seekable: " + str(self.seekable)
		print "  Video interlaced: " + str(self.interlaced)
		print "  Video height: " + str(self.videoheight)
		print "  Video width: " + str(self.videowidth)
		print "  Audio Tags: " 
		audiofile_tags = {}
		for akey in self.audiotags.keys():
			audiofile_tags[akey] = self.audiotags[akey]
			print "      " + akey, '\t', self.audiotags[akey]
		print "  Video Tags: "
		videofile_tags = {}
		for vkey in self.videotags.keys():
			videofile_tags[vkey] = self.videotags[vkey]
			print "      " + vkey, '\t', self.videotags[vkey]

if __name__=="__main__":
    if len(sys.argv)>1:
        file = sys.argv[1]
        pwd = os.getcwd()
        filepath = os.path.join(pwd,file)
        discovering = discover()
	discovering.set_file(file)
        discovering.create_report()
    else:
        print "select an audio file"



This program should give you an output like this one:

python newdiscoverer.py /home/cschalle/Videos/diracpromo.vob 
Analyzing file:///home/cschalle/Videos/diracpromo.vob
Topology:
  container: MPEG-2 System Stream
    audio stream 1: MPEG-1 Layer 2 (MP2)
    video stream: MPEG-2 Video
 
Properties:
  Duration: 735.672
  Seekable: Yes
  Video interlaced: No
  Video height: 576
  Video width: 720
  Audio Tags: 
      audio-codec 	MPEG 1 Audio, Layer 2
      bitrate 	384000
      has-crc 	True
      channel-mode 	stereo
  Video Tags: 
      bitrate 	8000000

Transmageddon and WebM

So after a long hiatus I finally managed to push out a new Transmageddon release, 0.16. In addition to a lot of new translations (big thanks to all members of the GNOME translation team) there are a few bugfixes in there, but most importantly this release adds support for the new WebM format, enabling you to quickly and easily make such files. Thanks to the work Sebastian and David did this was very easy for me.

Mandatory screenshot:

Transmageddon encoding a WebM file

Transmageddon encoding a WebM file

To make this work however you will need a lot of bleeding edge software, including todays git master of gst-plugins-good and gst-plugins-bad and libvpx installed (libvpx adds VP8 support).

This release also lets you put the On VP8 codec into other container formats, such as Ogg, Quicktime and AVI.

For Fedora users I recommend grabbing the latest libvpx SRPM created by Tom "spot" Callaway from Red Hat bugzilla. It should work perfectly with the GStreamer configure scripts.

For Ubuntu users I recommend the GStreamer PPA repository for getting latest libvpx. You probably need a newer gst-plugins-good than you find there right now though, but hopefully we will get that PPA updated today.

I also think libvpx has been packaged into Debian.

Totem playing WebM file

Totem playing WebM file

So I think that with this release Transmageddon is the first free software encoding GUI tool supporting the WebM format. At minimum the first I care about ;)

You can download Transmageddon 0.16 from the usual location on the Transmageddon homepage.

Note: Some long standing bugs, like trouble identifying some input files and no deinterlacing support, are still not fixed. I am waiting for Gst-conveniece to be ready with Python bindings, in order to fix those two bugs.

Winding down for yuletide

Going back home for the Yuletide celebrations this Friday and are trying to tie up all lose ends before heading off to Norway. We had a board meeting here yesterday, with Wim, Tim and Edward coming to Cambridge. Started the meeting by summarizing the last few years since the founding of Collabora Multimedia and how we are in a very good place now to take on the next few years. Apart from the general company related stuff we also had a long discussion about how we can help the GStreamer community get GStreamer 1.0 out the door in the coming year. Think we got some workable ideas on how we, and especially Wim, can get to do some of the heavy lifting needed for GStreamer 1.0 and at the same time enable the GStreamer community as a whole to get into gear for pushing 1.0 out through the door. A lot of the general thinking for GStreamer 1.0 was done at this years Gran Canaria Desktop summit, so our discussion was more about the practical issues of who, how and when. Will need to take those discussions to the mailing list once we have crystallized our thoughts a little more first.

There was also the Cambridge Collabora Christmas party yesterday. Every had a great time with good food and drink being served. Also gathered afterwards at Rob McQueens place for some further celebration, although I think Robs neighbours didn’t seem to appreciate the Christmas carols as much as the season might warrant :)

Ended up having a relaxing lunch with Wim, Tim and Edward around midday today at one of our favourite Cambridge hangouts, The Snug, their Chicken, Brie, Bacon and Cranberry burger always me a happy camper (although probably warrants an extra half hour at the gym too). Afterwards Wim and Edward scurried off towards to train and Stanstead, while Tim jumped in his car to drive home to Bristol.

Anyway, looking foward to a relaxing Christmas vacation and to see my little niece for the first time ever, as she popped out a few weeks early a week ago :) Also hope to get some Transmageddon hacking done, while some of the Transmageddon plans I have is waiting for the GStreamer Editing Services to get python bindings I do plan on looking into various types of subtitle support as that code will likely be useful also after switching to GES.

Transmageddon 0.14 released

Pushed out a new release of Transmageddon today. It is mostly
about fixing bugs and trying to make things more robust. But I also added the PSP and Google G1 profiles to this release.
Remuxing should be more robust now and if it lacks the plugins it needs it will let you know and let you choose something else instead.

My next step is going to be to combine the device profiles with remuxing, so that if the device you are targeting supports
for instance the audio and/or video format used in the incoming media Transmageddon will just remux it instead of decode and re-encode it. Should eventually in combination with a AC3 parser plugin enable you to just remux Matroska files with H264 and AC3 audio to MPEG TS when you choose the PS3 profile to get a playable file. Only problem there of course is the bitrate requirements of AC3 when used in MPEG TS on the PS3.

Writing code that does nothing

My Transmageddon hacking has slowed down a little over the last few Months. But I am still working on it, fixing bugs and adding little features. However a lot of the stuff I have been doing recently is adding code to work around or detect errors. Error handling is nice in the sense its code that help my application work on computers other than my own, but it is also something which I guess people find rather uninteresting. Its like you compare the last 3 versions and from a feature standpoint they are almost identical, even though I added quite a bit of code to handle all the kind of problems people reported to me.

For instance I spent quite a few hours yesterday adding code to make sure I could handle the situation of missing audio and video parsers. Currently if you choose passthrough mode and missed the needed parser plugin the application would just hang, with a lot of ugly spew on the command line. Well thanks to changing 70 lines of code and spending hours coming up with those lines the application now handles it gracefully. Of course for someone not running into this problem the application does nothing it didn’t before.

So part of my feel that these sort of fixes are quite boring and uninteresting, but on the other hand I guess they are exactly the stuff that is the difference between an application that obviously was never meant to work on any system apart from that of the application writer and an application that most people can actually use. And when people tell me they successfully used Transmageddon it do make me more happy than when I am told they tried it and it failed horribly. I mean the point of releasing Transmageddon to the public was not to make them familiar with python error messages :)

I have a small question to the more python savvy people out there though. I have been trying to set a environment variable for Transmageddon in python, but so far it doesn’t seem to work. If I in the shell do:
export GST_DEBUG_DUMP_DOT_DIR= "/tmp"
that works fine. But if I in my python code do
os.environ["GST_DEBUG_DUMP_DOT_DIR"] = "/tmp"
os.putenv('GST_DEBUG_DUMP_DIR_DIR', '/tmp')

neither of them seem to have any effect. Anyone got a clue to what I am doing wrong?
Edit: Turns out I was setting the environment variables to late in my file, I needed to do it before import gst was called :) Thanks Edward.

Urgent bugfix release of Transmageddon

Seems I did a very classic mistake with the new Transmageddon release I did this weekend. I was about to release, but at the last minute I decided to add MPEG PS support as it was ‘only a few lines of code’. As it turns out those few lines where enough to break the profile support and thus things like the iPod profile ended up broken.

So a new 0.13 release is now available fixing the profiles again. And to show the world I haven’t learned the lesson I also added drag and drop support in this version. So you can drag and drop a file from Nautilus onto Transmageddon and it will be put into the filechooser for you. This feature came about due to extensive nagging from Stuart Langridge and cut and paste code from the pygtk FAQ :)

So if you tried 0.12 and the profiles didn’t work for you, try 0.13

New Transmageddon release

Finally managed to sit down today and clear up all the lose ends I had left in the code since GUADEC. For instance in my mind remuxing was ‘done, but when I started testing and looking at the code this was not the case at all :). To celebrate these cleanups I pushed Transmageddon 0.12 today and I hope it will work reasonably well for people. The major new feature available is the remuxing support, which will let you keep the original audio or video track in the file. Stability should also be a little bit better as I added more error checking, but there is still some way to go there. Things should work fine as long as you got all the GStreamer plugins needed, but if you don’t your success will vary on what type of plugin is missing :)

Also added ASF and MPEG PS support in this release, but in order to create those you need either latest release of gst-plugins-bad for ASF or git master for MPEG PS.

Transmageddon now also sports its own icon thanks to Emily and Liam of fightingcrane.com, a big thanks for that.

Screenshot of the latest release also available of course.

Sorry for taking so long to get this, but I was trying to get DVD ripping support in, but that effort stalled unfortunately. Focus for next release will be to switch from Glade to GtkBuilder and add some more device profiles.

AMR in Transmageddon

So after the successful integration of the new AMR plugin into GStreamer, I went about doing some testing with Transmageddon. Discovered that it didn’t work so well. Turned out to be two different issues. First after some communication with Iago and then Martin Storsjö, Martin managed to fix some bugs in the original encoder which meant it was broken on x86_64 systems. Then even when that was fixed I couldn’t get Transmageddon to create a working file. I realized I should use the graph creation system to debug it, but this time I decided to add it as a permanent feature to Transmageddon, so that anything I or anyone else needed a graphical output of the transcoding process it would be very easy to do. So in the current git master version of Transmageddon you now have a ‘debug’ option under the Help menu. If you choose it, graphwiz and Eye of Gnome will be called, giving you a graphical representation of the pipeline at the time of choosing ‘debug’. With that in place I could quickly see that my audioresampler had ended up outside the standard pipeline and was able to fix the issue. After that encoding into amr-nb works without a hitch.

This screenshot of my desktop shows Transmageddon after having created a 3gpp file with AMR-NB audio, Totem playing that file and finally EOG displaying the pipeline used to create the file in Transmageddon. In fact you can create such images to view even if you don’t have any issues as it will not affect the successful completion of your transcoding process.

Great thing now is that any user of Transmageddon who experience a hang or similar can just choose ‘debug’ and add the debug image to a bug report. In a lot of cases I would be able to see exactly what goes wrong just from that. Having such a debug tool in the UI might not be the most usability PC thing to do, but at this stage in the development process I think there will be enough issues faced to warrant it.

A big thanks to Martin Storsjö for his effort today in fixing this issue, very much appreciated.