This is just a quick heads-up for people who try to use python-gstreamer in a thread. Threading in Python has a few unexpected problems if you're new to it and it took me quite a few hours on Google searching for some scraps of documentation (and even diving into the source) before I got it right.
My jukebox hack that I touched upon in my last post uses threads. The main thread doesn't do much of anything right now except watch two child threads. One thread will hold a simple twisted TCP server to control the jukebox. The other will hold the gstreamer audio player. The trouble started with the gstreamer mainloop. When I started the gobject mainloop below in the player thread, all my other threads froze.
- loop = gobject.MainLoop()
- loop.run()
I had to reimplement that anyway because the player thread also needs to see occasionally if there are commands from the twisted thread. Here's the new loop:
- loop = gobject.MainLoop()
- context = loop.get_context()
- while 1:
- # Handle commands here
- ...
- context.iteration(True)
The result was the same: thread freeze. The True in the above code makes context.iteration() idle until there is work to do. Setting context.iteration() to False makes it return immediately. That stopped the freeze but ramped up my CPU usage to 100%. As it turns out, Python doesn't use Operating System threads but implements it's own threading because of portability, but that threading only works within python. When a thread calls a function that's written in C, such as gobject.context.iteration(), then Python freezes all threads until that function returns (unless that C function is wrapped in Py_BEGIN_ALLOW_THREADS/Py_BEGIN_ALLOW_THREADS statements). This is called Global Interpreter Lock or GIL.
Initially I tried getting rid of the gobject.MainLoop altogether, but in Python you can't have a thread simply idle so I had to have some sort of mainloop. This blog post suggest that any loop will do, even a while-true loop, but everything I came up with ramped my CPU usage to 100%.
I finally found out that Python's gobject module has a function to enable/disable threading when it's calling C functions: gobject.threads_init(). By default this is off so you have to turn it on before iterating the gobject mainloop:
- loop = gobject.MainLoop()
- gobject.threads_init()
- context = loop.get_context()
- while 1:
- # Handle commands here
- ...
- context.iteration(True)
The above code makes gstreamer play smooth and lets all other threads do what they must. I hope that this saves someone a couple of hours breaking their head over their frozen threads. Happy coding!


Comments
#1 ndr k (http://aendruk.wordpress.com/)
#2 Anonymous Coward
#3 Anonymous Coward
#4 Anonymous Coward Olivier
I just hope that it is not an unsupported hack that is going to stop working with the next gobject release ..
#5 Sander Marechal (http://www.jejik.com)