This article will guide you through the process of setting up live video
streaming from a DVB (ATSC) capture card in Linux to a DLNA UPnP Media Renderer
that supports MPEG-2 Transport Streams (such as a PS3).
What you need
=============
Mediatomb_
Mediatomb is a DLNA/UPnP media server for Linux. You'll want to set up
mediatomb to play regular files to your MediaRenderer before attempting the
configuration in this guide. It helps to know how the javascript import
and transcoding configuration in Mediatomb works, but this guide should be
adequate as long as you don't need to do anything fancy.
DVBStreamer_
This is the tool that directly talks to your DVB capture card and streams
the video. This would be all that you need if your MediaRenderer
understands how to listen to a UDP stream. The can't do this (or I
couldn't figure out how to do this) so we need one more tool...
NetCat_
The swiss-army knife of networking, this tool allows us to take the UDP
stream that DVBStreamer gets us and pipe it through a FIFO with a simple
script.
.. _Mediatomb: http://mediatomb.cc/
.. _DVBStreamer: http://dvbstreamer.sf.net/
.. _NetCat: http://netcat.sf.net/
Configuring Mediatomb
=====================
The tricky bit to get streaming working correctly is getting Mediatomb to do
the hard work of talking to the DLNA devices — in a language they understand.
You'll want to set up Mediatomb and get it talking to your UPnP media renderer
*before* trying to do this; you can try to do it all at once, but don't
say I didn't warn you.
Firstly, you need to use an import script rather than the built-in import
provided by mediatomb for scanning files. The XML config for this will look
something like the following:
.. sourcecode:: xml
/usr/share/mediatomb/js/common.js
/usr/share/mediatomb/js/playlists.js
/etc/mediatomb/import.js
This config goes in the "" tag of the "config.xml" file.
You also need to add a map from a file extension to a mimetype, I used ".live"
-> "video/x-dvb-stream", but you could use whatever you like, the mimetype
doesn't escape mediatomb, it's just used so we can tell mediatomb to
"transcode" the files into something else. Add the following to the
"" tag:
.. sourcecode:: xml
Finally, you need to add a transcoding profile for the "video/x-dvb-stream"
mimetype to transcode it into a "video/mpeg" stream. The ""
tag should look something like the following:
.. sourcecode:: xml
video/mpeg
no
yes
yes
Don't worry about the "dvbstream-live" script just yet, we need to get
DVBStreamer set up first.
The final piece to the puzzle is the "import.js" script. This is where having
some knowledge of javascript and what you want to do helps quite a bit. You
should copy the default import.js script from wherever it's installed, and then
add the following code:
.. sourcecode:: javascript
function videoIsLiveChannel(obj) {
var pattern = /(.*)\.live/;
var match = pattern.exec(obj.title);
if( match ) {
var container_chain = new Array('video', 'television' );
obj.title = match[1];
print("Parsed channel info: " + obj.title);
addCdsObject(obj, createContainerChain(container_chain));
return true;
} else {
return false;
}
}
You also need to add the following to the top of the "addVideo" function:
.. sourcecode:: javascript
if(videoIsLiveChannel(obj)) {
return;
}
This tells the import script that if the file to be imported is a ".live" file,
we want to add it to the "video/television" section of the UPnP media library,
sans ".live" ending. Strictly speaking, adding this to the import script is
not necessary, but it makes the listings look a bit nicer.
Setting up DVBStreamer
======================
DVBStreamer is a simple tool that reads from a DVB capture card and can stream
(and filter) the broadcast to a "udp://" url. To get DVBStreamer up and
running you will need to already have a "channels.conf" file, generated by a
program like "dvbscan". Then simply run the "setupdvbstreamer" command with
the appropriate options to get dvbstreamer working.
Use the `DVBStreamer Wiki`_ to look up how to do some basic tests, stream some
video to a file, or use mplayer to watch a UDP stream; essentially, you want to
verify that DVBStreamer is working correctly and doing what you expect.
.. _`DVBStreamer Wiki`: http://apps.sourceforge.net/mediawiki/dvbstreamer/index.php?title=Running
To stream using the scripts below you need to run DVBStreamer as a daemon and
set to stream to "udp://localhost:12346". Create a file "dvbstreamer.startup"
with a single line:
.. sourcecode::
setmrl udp://localhost:12346
Start DVBStreamer like this:
.. sourcecode:: bash
dvbstreamer -f dvbstreamer.startup -d
Now all you need to do is get the scripts to do the "transcoding" and create
some ".live" files for your television channels.
The "transcoding" script(s)
===========================
There are two scripts that are used by the streaming system, one changes the
channel in DVBStreamer so the correct video is streamed, the other pushes the
stream from the UDP stream to a file (FIFO) which mediatomb provides as an
argument.
Here's the first, the channel changing script. It requires python >=2.5 (and
<3.0). It expects the path to a ".live" file as it's first (and only)
argument, and connects to the DVBStreamer instance (using the default
authentication credentials) to select the DVBStreamer service corresponding to
the name of the file. e.g. "/home/media/21-1 WXXI-HD.live" would select the
"21-1 WXXI-HD" DVBStreamer service.
.. sourcecode:: python
#!/usr/bin/env python
from __future__ import with_statement
import sys
import socket
from contextlib import closing
from os import path
class DVBStreamerException(Exception):
pass
def get_response(streamer):
resp = []
while True:
try:
resp.append(streamer.recv(4096))
except socket.error, e:
if resp and e.args[0] == 11: # temp unavail
break
elif e.args[0] == 11:
pass
else:
raise
if not resp:
raise DVBStreamerException("Connection closed?")
resp = ''.join(resp).splitlines()
if not resp[-1].startswith('DVBStreamer'):
return resp + get_response(streamer)
code = resp[-1].rsplit('/', 1)[-1]
if int(code.split()[0]) != 0:
raise DVBStreamerException(resp)
else:
return resp[:-1]
def dvbstreamer_cmd(cmd, auth=False, adapter=0):
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
sock.connect(('localhost', 54197+adapter))
sock.setblocking(0)
#print "connected, waiting for ready...",
get_response(sock)
#print 'got ready: '
if auth:
#print 'sending auth...',
sock.send("auth dvbstreamer control\n")
get_response(sock)
#print 'auth success'
print 'sending cmd: ', cmd.strip()
sock.send(cmd)
return get_response(sock)
def main():
#l = open('/tmp/channel.log', 'w')
#print >>l, sys.argv
channel = sys.argv[1]
if channel.endswith(".live"):
channel = path.splitext(path.basename(channel))[0]
#print >>l, channel
dvbstreamer_cmd("select %s\n" % channel, auth=True)
#l.close()
if __name__=='__main__':
main()
Here's the (much simpler) script that calls the above script and hands off to
netcat to push the stream to the FIFO.
.. sourcecode:: bash
#!/bin/bash
/path/to/dvbstream-channel "$1"
exec nc -l -u -p 12346 localhost > "$2"
Put these two scripts someplace you can remember and ensure they can be
executed by the user running mediatomb.
Wrap-Up
=======
That should be all there is to it, you can download a tarball containing the
scripts_ and config snippets in the article to make life easier.
.. _scripts: /dvbstream-mediatomb-live.tbz2