I've fallen in love with Python. I'm always on the lookout for a good excuse to learn a new programming language. Since Lua is backwards-incompatible (and most non-Debian based Linux distro's ship Lua in such a way that you cannot run multiple versions at the same time), I wanted to switch the AI script language for Gnome Hearts to Python. I'm still working on that and now I'm hooked. So, when I wanted something that lets me play my music collection from my server to my stereo I took python for a good test drive.
Anoher thing I wanted an excuse for was learning gstreamer, so naturally I picked python-gstreamer. Sadly there's a huge lack of documentation for it. There are only a few good tutorials around, first and foremost Jona Bacon's excellent tutorials [1][2][3]. I wanted my hack to work over TCP as well, because I have multiple music libraries. One on my home server, one on my desktop, a bit more on my laptop, etcetera. I want to be able to stream from other machines to my server which I'll hook up to my stereo. I could not find any tutorials on using tcpserversource and tcpclientsink elements, so honoring Jona's request that everyone “should write an article about something that you have discovered that isn’t particularly well documented”, here's mine about tcpserversource and tcpclientsink.
TCP streaming using gst-launch
Using gst-launch-0.10 is the easiest way to see if a pipeline will actually work or not. It's quicker to change a few commandline variables than it is to change your program every time. It took my a few hours before I found the right one due to two causes:
- Caps (format descriptors) are not automagically negotiated when you use tcp elements. According to the scarce documentation I could find, using setting the protocol to GST_TCP_PROTOCOL_GDP should enable negotiation, but I could not make this work.
- Apparently the caps for the audiotestsrc that I was trying to use most of the time could not be negotiated at all.
What I ended up doing was simply streaming the still compressed audiofile before any caps negotiation has taken place. Aside from making my pipeline actually work it has the added benefit for me that it uses less bandwidth. All caps negotiation then happens at the server side. Here are the server and client pipelines that actually worked for me. You can run them both on the same machine simply by setting the host to localhost and using two xterms. Make sure you start the server before starting the client.
- you@server$ gst-launch-0.10 tcpserversrc host=localhost port=3000 ! decodebin ! audioconvert ! alsasink
- you@client$ gst-launch-0.10 filesrc location=/home/you/song.ogg ! tcpclientsink host=localhost port=3000
Putting it all in Python
With the gst-launch pipelines sorted out it's trivial to implement the same thing in Python. The server code:
- #!/usr/bin/env python
- # This is some example code for python-gstreamer.
- # It's a gstreamer TCP/IP server that listens on
- # the localhost for a client to send data to it.
- # It shows how to use the tcpserversrc and tcpclientsink
- # elements.
- import gobject, pygst
- pygst.require("0.10")
- import gst
- # Callback for the decodebin source pad
- def new_decode_pad(dbin, pad, islast):
- pad.link(convert.get_pad("sink"))
- # create a pipeline and add [tcpserversrc ! decodebin ! audioconvert ! alsasink]
- pipeline = gst.Pipeline("server")
- tcpsrc = gst.element_factory_make("tcpserversrc", "source")
- pipeline.add(tcpsrc)
- tcpsrc.set_property("host", "127.0.0.1")
- tcpsrc.set_property("port", 3000)
- decode = gst.element_factory_make("decodebin", "decode")
- decode.connect("new-decoded-pad", new_decode_pad)
- pipeline.add(decode)
- tcpsrc.link(decode)
- convert = gst.element_factory_make("audioconvert", "convert")
- pipeline.add(convert)
- sink = gst.element_factory_make("alsasink", "sink")
- pipeline.add(sink)
- convert.link(sink)
- pipeline.set_state(gst.STATE_PLAYING)
- # enter into a mainloop
- loop = gobject.MainLoop()
- loop.run()
And the client code:
- #!/usr/bin/env python
- # This is some example code for python-gstreamer.
- # It's a gstreamer TCP/IP client that sends a file
- # through a tcpclientsink to a server on localhost
- import gobject, pygst
- pygst.require("0.10")
- import gst
- # create a pipeline and add [ filesrc ! tcpclientsink ]
- pipeline = gst.Pipeline("client")
- src = gst.element_factory_make("filesrc", "source")
- src.set_property("location", "/home/you/song.ogg")
- pipeline.add(src)
- client = gst.element_factory_make("tcpclientsink", "client")
- pipeline.add(client)
- client.set_property("host", "127.0.0.1")
- client.set_property("port", 3000)
- src.link(client)
- pipeline.set_state(gst.STATE_PLAYING)
- # enter into a mainloop
- loop = gobject.MainLoop()
- loop.run()
I hope this has been useful to you. Happy coding!
Comments
#1 blaxeep
I am a student in TUC (Greece) and your code was really helpful for me to understand what's going on!
It's about a project that implements a mail application in which users will send video-mail instead of e-mails :P
thnx!
#2 Diego Tejera
#3 Anonymous
Note that, if you want to stream data from one computer to another,
you have to fill the host property of the client AND the server with
the server's IP address...
#4 Anonymous Coward
very simple, easy to understand, appreciate you
a question: is there a similar simple way for live streaming?
#5 Sander Marechal (http://www.jejik.com)
#6 Hassan
I am a student from Pakistan. Your tutorial is pretty good, gave me some help for what I was looking for. Its not exactly this but put me on the right path.
Cheers :)
#7 Anonymous Coward (http://153.19.231.17)
#8 Tulga (http://melug.blogspot.com)
#9 Antoine Martin (http://devloop.org.uk)
One question though, how do I make it re-start after the first client disconnects?
It just seems to get stuck and you have to kill the tcpserversrc script and start it again.
I tried catching the EOS and unlink()ing the elements after the tcpserversrc (then re-adding new ones), but the exact incantions required are beyond me at this point.
How about allowing multiple concurrent connections? Is that even possible using the same port?
Also, when I run it across the network it seems to stutter a bit (despite the fact there is lots of bandwidth to spare), even more so when tunneled over ssh, how can I get rid of that problem? (increase buffering somehow?)
Many thanks
#10 Sander Marechal (http://www.jejik.com)
As for the stuttering, try introducing some queue elements to act as buffers.
#11 sam
thank you
#12 Sander Marechal (http://www.jejik.com)
Next, I suggest you start playing with the commandline tools such as gst-launch and build some working pipelines. Once you have something that works, try to implement the same pipeline in Python.
#13 sam
do you have any tutorial or any useful documentation on that??..
Thank you
#14 Sander Marechal (http://www.jejik.com)
Python tutorial
Gstreamer tutorials, especially this one
And read the code in my article. It shows the same GStreamer pipeline twice. First using gst-launch from the commandline and then the same pipeline implemented in Python.
#15 celia
thanks! and sorry for my english :)
#16 Sander Marechal (http://www.jejik.com)
I did something like that for my jukebox server but that was basically a cheat. I had two sound sources: a song and a dummy bin that produces a constant stream of silence. When I wanted to "pause", I only stopped the song. The "silence" still played, so the pipeline kept rolling.
#17 celia
#18 Sander Marechal (http://www.jejik.com)
The person in question was asking about a simple GStreamer pipeline tutorial. As it happens, Jono Bacon has written an excellent tutorial on implementing a simple pipeline. He implements it both using gst-launch and using Python.
See here: http://www.jonobacon.org/2006/08/28/getting-started-with-gstreamer-with-python/
#19 celia
#20 sam
yeah i have check the Jono Bacon tutorial its very very useful.. As i have a very basic knowledge about this.. it gave me idea to start with.. as i am very basic in this i still couldn't get how can i create a or develop a pipeline to play any mp3/mp4 files.. i might be asking for too much but i will really appreciate your help..
Many thanks
#21 Sander Marechal (http://www.jejik.com)
That article shows a minimal, basic pipeline that can play pretty much any audiofile (mp3, ogg, etcetera)
#22 Sam
Thank you
#23 Sander Marechal (http://www.jejik.com)
#24 sam
sam@ubuntu:~/Desktop$ ./server.py
./server.py: line 8: import: command not found
./server.py: line 9: syntax error near unexpected token `"0.10"'
./server.py: line 9: `pygst.require("0.10")'
can you help me with this...
#25 Sander Marechal (http://www.jejik.com)
#26 Matt Derlay
#27 Sander Marechal (http://www.jejik.com)
#28 sam
#29 Sander Marechal (http://www.jejik.com)
#30 sam
Please have a look, I will be really grateful if you can help me with this...
Thank you
#31 Sander Marechal (http://www.jejik.com)
#32 Samir Ghimire (http://samirghimire.wordpress.com)
And recently i have been trying for live streaming from my webcam.. here is the link:
http://samirghimire.wordpress.com/2011/03/04/webcam-streaming-using-python-and-gstreamer/
I can stream using just the gstreamer pipeline but when i bind with python its not showing any output although both server and client are running..
Please help me out with this..
Thank you very much
#33 Sander Marechal (http://www.jejik.com)
#34 Samir Ghimire (http://samirghimire.wordpress.com)
another question , how to write the call back like in
def new_decode_pad(dbin, pad, islast):
on what basis the dbin, pad, islast is written.. what is islast??
many thanks...
#35 Ionut
#36 Sander Marechal (http://www.jejik.com)
#37 Giovanni
#38 Sander Marechal (http://www.jejik.com)
#39 Abhishek (http://thezeroth.net)
but it doesnt work. am I using it correctly ?
Thanks
Comments have been retired for this article.