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)
#6 Pascal Giard (http://fofix.googlecode.com)
It now does send the sync messages (and my callback gets called) but my python thread gets stuck after ~5 seconds.
I'm working around this very hackishly by NOT using the code you suggest and by looking at the last-message every 41.666 ms :-(
Find the code there: http://tr.im/wvvO
See the vidSetup() and run() methods.
Any comment/suggestion is welcome.
#7 Sander Marechal (http://www.jejik.com)
But as I said, that's really a stab in the dark.
#8 splicer
#9 Eloff
#10 JoulusnefeSon
#11 Not Anonymous Coward
Immense thanks!
#12 Anonymous Coward
http://wiki.python.org/moin/DbusExamples
The first lines show how to enable threading with gobject. Hope that's helpful to anyone else who stumbles across this article.
#13 Anonymous Coward
* Python uses OS-level threads. It does not provide its own threading implementation.
* The GIL does not freeze threads in C. It just prevents multiple threads from accessing the Python interpreter simultaneously. A C thread can carry on merrily in the background as long as it doesn't need to touch any Python objects (which would require acquiring the GIL first).
#14 Anonymous Coward
#15 Flulleycoatty
#16 Sander Marechal (http://www.jejik.com)
Some people leave their e-mail address but I do not disclose e-mail addresses. But if you send me a message (use the contact form) then maybe I can forward the message for you.
#17 Anonymous Coward
#18 Anonymous Coward
#19 Gabriele Barchiesi
gobject.threads_init()
loop = gobject.MainLoop()
context = loop.get_context()
while True:
context.iteration(True)
#20 Rob van den Bogaard
#21 Matt (http://freedomboxfoundation.org)
Comments have been retired for this article.