Jun 14

vlc Streaming Guide

Category: Linux,webserver,WIP   — Published by tengo on June 14, 2011 at 11:46 am

Table of Contents:

As a kind of follow-up to the FFserver streaming Guide, this guide here chronicles my work on getting vlc to act as medium size streaming server.

For reference, you might want to right-click on the official vlc streaming docs so you have them open in another browser-tab.

Also note, this article is an unfinished sketch and work-in-progress.

Continuus asf/wmv video streaming

Using the 2009-11-05 1.1.0-git snapshot version of vlc, the startup of the server and thus quality of the stream varied from time to time. I issued this command:

cvlc /var/media/playlist.m3u -I rc --sout="#gather:std{access=http,mux=asf,dst=<some.ip>:<some.port>}" --sout-keep --loop
Sometimes I had video only, no audio, sometimes the picture on the playing client froze. And after a few streamed videos vlc streamout broke with this error:

[0x80dd830] main mux error: cannot add a new stream (unsupported while muxing to this format). You can try increasing sout-mux-caching value
[0x80e9600] main decoder error: cannot create packetizer output (WMA2)

So I did what vlc suggested (added the "--sout-mux-caching 55000" flag) and subsequently raised the cache as you see to finally 55000, and everything got worse. It actually went better, when I removed the caching flag altogether again.

I had a breakthrough when I switched to using mms with this command:

cvlc /var/media/playlist.m3u -I rc --sout="#gather:std{access=mmsh,mux=asfh,dst=85.14.216.46:8090}" --sout-keep --loop

And gone was the "cannot add a new stream" mux error! Although I am still confused which the right syntax is. Some versions of vlc use mms, some mmsh (this one), sometimes you need to specify mux=asf, sometimes mux=ash - you might need to experiment a bit here.

1st lesson learned: vlc doesn't refer to the container-format as "format", but to the streaming protocol! So switching from http to mms solves the "unsupported while muxing to this format"-error.

2nd lessond learned: asf/wmv was designed byMicrosoft to be transported over mms, so streaming it over http is, at least in vlc, at best buggy. Stick to mms forstreaming and you will face less headaches.

3rd lesson: "Windows Media Player won't play my vlc asf stream!"

If you experience this problem, it seems that you've told vlc to provide the stream via mms but chose asf as mux (e.g. access=mmsh,mux=asf) but it is important to do as shown above, mmsh + asfh! Otherwise vlc and mplayer will play your stream (they don't seem to care) but Microsoft's Windows Media Player will tell you it can't open the stream!

Example asf streaming config/ command-line:

This was with VLC media player 2.0.1 Twoflower (revision 2.0.1-0-gf432547)

cvlc /var/www/site/playlist_asf.m3u \
--sout="#gather:std{access=mmsh,mux=asfh,dst=:8099/stream.asf}" \
--sout-keep --loop \
-I rc --rc-fake-tty --rc-unix /tmp/vlc_asf.sock \
--extraintf logger --logfile /var/log/site/vlc_asf.log \
--sout-asf-title "GoesZen.com example stream" --sout-asf-author "goeszen.com" \
--album-art 0 -vvv

We stream a m3u playlist here. All files are pre-transcoded - which means that vlc will have zero transcoding overhead and is only occupied with streaming and concatenating the files as required by the playlist. This way, vlc will have nearly no CPU impact! The files streamed here are transcoded like this:

Stream #0.0: Video: wmv2, yuv420p, 640x360, 25 tbr, 1k tbn, 1k tbc
Stream #0.1: Audio: wmav2, 44100 Hz, 2 channels, s16, 127 kb/s

And vlc now is serving the stream on port 8099 and path /stream.asf. You would embed this URL into an .asx file, and most players will pick it up properly then. We also embed a little bit of metadata, write a log-file and expose a control-interface via UNIX socket. cvlc is used so vlc is not tempted to fire up a graphical UI. If you are intersted in streaming Flash Video, there's a similar example further down below.

The http interface and "403 forbidden" errors

By adding the switch -I http --http-host <some ip>:<some port> to your vlc command, you are able to add a webbrowser accessible http interface to your vlc. A common problem when doing so and trying to access the interface from outside the localhost/ local network is that you get a more or less blank page telling you 403 Forbidden (/).
As you can read here, this tells you that vlc's whitelist .hosts file does not allow yourIP to access the interface. Thsi feature was introduces with versions around 0.9.0, the changes readme, the http interface docs and the related wiki page give additional insight.

* The HTTP Interface is now only available on the local machine by default.
If you want to make it available from other machines, you will have to edit the ".hosts" file.
* On UNIX/Linux, the file is in /usr/share/vlc/http/.hosts
If you're using the old http interface, it's located in /usr/share/vlc/http/old/.hosts
* On Windows they are in C:Program FilesVideoLANVLChttp.hosts and C:Program FilesVideoLANVLChttpold.hosts
* On Mac OS X, you can find it in VLC.app/Contents/MacOS/share/http/.hosts and respectively in VLC.app/Contents/MacOS/share/http/old/.hosts

The default file in /usr/share/vlc/http/.hosts looks like this:

#
# Default access-list for VLC HTTP interface
# $Id: .hosts 13985 2006-01-22 16:18:39Z xtophe $
#
# localhost
::1
127.0.0.1
# link-local addresses
#fe80::/64
# private addresses
#fc00::/7
#fec0::/10
#10.0.0.0/8
#172.16.0.0/12
#192.168.0.0/16
# The world
# (comment this out to obtain a safe default)
::/0
0.0.0.0/0

Running vlc or cvlc as daemon service

Do so by adding the --daemon switch to your command

Adding metadata to asf streams

Many container formats support the embedding of metadata, and so does asf. We are used to using ID3 Tags in mp3s and know it from asf, but associate it with files. But the meta tags are also embeddable into streaming media. Player clients read the metadata as well from streams as from files.

When using asf as your streaming format, the vlc asf docs tell you how to add this metainformation to the streamvia global options. For example, a command that worked for me was: vlc ... --sout-asf-author "Some Author String"

If you are streaming multiple streams via one vlc, it should be save to say :sout-asf-author="Some Author String" to tell vlc to use this option just for one stream. I haven't tried this.

So much about static metadata that is added to thewhole stream as global metadata. Is it possible to add dynamic metadata, that changes when playing titles change? So that we can build a "Now Playing" feature?

The forums tells us this is possible with marquee text: --marq-marquee="Currently playing: $t". A quick test with: --sout-asf-title "Currently playing: $t" just ended up as  "Currently playing: $t" in the player's metadata display. Of course, escaping $ weith a slash just means "print $", and so it did. But omitting the slash didn't work either. But the $t was replaced, replaced by <nothing>. Thus we know vlc recognizes these variables, but does not dynamically fill them with content.
Another suggestion from the forums, the variable $N also didn't work.

vlc as Relay-Server for an asf stream

Relaying a mpeg ts stream via vlc works, for example within a local test setup, for the Relay:

vlc udp://@:8777 --sout-mux-caching 45000 --sout "#standard{access=http,mux=ts,dst=127.0.0.1:9070}" --intf rc

and the Streamer uses:

vlc test.m3u --sout"#transcode{vcodec=mp2v,vb=250,width=300,height=140,acodec=mp3,ab=96,channels=2,samplerate=22050}:gather:std{access=http,mux=ts,dst=localhost:8777}" --sout-keep --intf rc  --sout-ts-use-key-frames

Applying this knowledge to relay/ forward/ repeat an asf stream doesn't work. Why? A look at the vlc streaming features matrix helps: ASF via UDP is not possible. And using HTTP would need the Streamer to be reachable via its own public IP (which in most scenarios - where the Relay is on a WAN server and the Streamer is a client on your e.g. local Windows machine - is impossible).

The solution appears to be: embedding the readily transcoded wmv+wma into a ts mux (possible according to the matrix) and letting the relay server do a swift re-mux into an asf stream. Testing result: does not work!

Still, there's some hope for later testing: a vlc-devel post tells us why this specific setup might not work: the muxer can't determine the target bitrate from the ts. So bringing the --sout-asf-packet-size flag into the mix might actually solve this. Todo, so untested.

Flash Video streaming with vlc

According to the docs, beginning with revision 18876 (which means 0.9.0-svn after 17/02/2007) vlc is able to stream Flash videos to web based flash players like JW or Flowplayer or to players like vlc itself or mplayer. This thread shows a working setup.

% ./vlc  --sout "#transcode{vcodec=FLV1,acodec=mp3}:std{access=http,dst=0.0.0.0:8081/stream.flv}"

is the suggested command. Wheater it makes sense to still add the {mime=video/x-flv} attribute, I dont know. This would look like this:

./vlc --sout
"#transcode{vcodec=FLV1,acodec=mp3,sameplerate=44100}:std{access=http{mime=video/x-flv},mux=ffmpeg{mux=flv},dst=
0.0.0.0:8080/stream.flv}" somefile.avi --loop -vvv

To play this stream as an embedded video on a webpage, use the basic flowplayer-free example:

1. Add the example's .js file to the html header:

<script type="text/javascript" src="http://example.com/flowplayer/example/flowplayer-3.1.4.min.js"></script>

2. and use this code to embed the player:

<!-- this A tag is where your Flowplayer will be placed. it can be anywhere -->
<a
 href="http://example.com:8080/stream.flv"
	style="display:block;width:640px;height:360px"
	id="player">
</a>

<!-- this will install flowplayer inside previous A- tag. -->
<script>
	flowplayer("player", "http://example.com/flowplayer/flowplayer-3.1.4.swf", {
		play: null,
		plugins: { controls: null },
	});
</script>

I've used two tricks here:

  1. controls: null - permanently hides the flowplayer controls, the shuttle control bar etc., it's cleaner and they are useless on streaming content nevertheless.
  2. play: null - hides the 'progress indicator', the spinning circle that would be permanently visible as the player is permanently buffering on streaming video.

Example FLV streaming config/ command-line:

This was with VLC media player 2.0.1 Twoflower (revision 2.0.1-0-gf432547)

cvlc /var/www/site/playlist_flv.m3u \
--sout="#gather:std{access=http{mime=video/x-flv},mux=ffmpeg{mux=flv},dst=:8099/stream.flv}" \
--sout-keep --loop \
-I rc --rc-fake-tty --rc-unix /tmp/vlc_flv.sock \
--extraintf logger --logfile /var/log/site/vlc_flv.log \
--album-art 0 -vvv

Streaming MP3 and publish via shoutcast

A common setup is to stream music or some other audio via Shoutcast server. But Shoutcast is only a stream server, not a playlist manager. Good news, you can use vlc to handle and stream your playlist, and then you can use Shoutcast to deflect / relay / repeat this stream to your users. For that to work, you would first start:

vlc mp3 streaming and shoutcast serving example config:

This was with VLC media player 2.0.1 Twoflower (revision 2.0.1-0-gf432547)

cvlc /var/www/site/playlist_mp3.m3u \
--sout="#gather:std{access=shout{mp3=1,bitrate=128,samplerate=44100,channels=2,name=\'goeszen.com example stream\',genre=\'Spoken Word,Pop,Rock,Top100\', \
url=\'http://www.goeszen.com/\'},mux=raw,dst=admin:example-password@localhost:8099/}" \
--sout-keep --loop --no-sout-video \
-I rc --rc-fake-tty --rc-unix /tmp/vlc_mp3.sock \
--extraintf logger --logfile /var/log/vlc/vlc_mp3.log \
--album-art 0 -vvv --daemon

As you can see, we stream a 128K MP3 stream here in shoutcast format, with basic metadata. The admin= stuff are the shoutcast credentials. We also write a log and run the command-line variant of vlc, cvlc, as daemon here.

Usually, after starting vlc, you would then start the shoutcast relay with something like: $ bin/shoutcast/sc_serv /var/www/site/sc_serv.conf

(live) HTTP Streaming (iPhone-style streaming)

There are two ways to do this:

  • a more legacy approach, a combination of streamer and segmenter, where vlc can be used as the feeder/playlist-manager/streamer
  • and a new approach, where vlc does the playback and segmenting via the new livehttp output module

The legacy setup: Stream to the iPhone using vlc + segmenter + webserver
You should be able to use vlc as a video feeder to a segmenter/webserver combination, which is a stack many open-source based iterations of HTTP live streaming used. You can read more about this here: iPhone HTTP Live Streaming standard use.

The new method: vlc + livehttp output
The livehttp output module was a patch first, but is now included (livehttp.c) in more recent vlcs. It's a very nice implementation which prepares the m3u8 file, and keeps track of currently live segments as well as garbage collecting outdated .ts segments.

The videolan Wiki has examples and details about "streaming for the iphone". This blogpost here has another example. Both show a live transcoding instance of vlc, but it is also possible to prepare transcoded playlist items out-of-band, and use the "gather" input to assemble them to a stream, accoding to a playlist, thus reducing CPU load.

vlc and the Video Lan Manager vlm

Most flags and switches passed to vlc in endless commands can also be passed to vlc in a config file, a VLM configuration file. By doing so you are able to stream multiple streams with just one instance of vlc. This functionality is called the vlm and is described here. An example of a complex vlm config file is:

show
    media : ( 2 broadcast - 0 vod )
        channel1
            type : broadcast
            enabled : yes
            loop : no
            inputs
                1 : http://127.0.0.1:9025
            output :
#transcode{scale=1,vcodec=FLV1,vb=500}:std{access=http,dst=:8082/test.flv}
            options
            instances
                instance
                    name : default
                    state : playing
                    position : 0.000000
                    time : 0
                    length : 0
                    rate : 1000
                    title : 0
                    chapter : 0
                    seekable : 0
                    playlistindex : 1
        channel2
            type : broadcast
            enabled : yes
            loop : no
            inputs
                1 : http://*.*.*.*:9025
            output :
#transcode{scale=1,vcodec=FLV1,vb=400,hurry-up}:std{access=http,dst=:8083/test.flv}
            options
            instances
                instance
                    name : default
                    state : playing
                    position : 0.000000
                    time : 0
                    length : 0
                    rate : 1000
                    title : 0
                    chapter : 0
                    seekable : 0
                    playlistindex : 1
    schedule

(via)
Still, this format seems make problems with newer versions of vlc. The docs suggest this tighter format:

new channel1 broadcast enabled
setup channel1 input http://host.mydomain/movie.mpeg
setup channel1 output #rtp{mux=ts,dst=239.255.1.1,sap,name="Channel 1"}
new channel2 broadcast enabled
setup channel2 input rtp://@239.255.12.42
setup channel2 output #rtp{mux=ts,dst=239.255.1.2,sap,name="Channel 2"}
control channel1 play
control channel2 play

Anyway, such a vlm file is then started/executed, for example, by typing: ./vlc <path to file> --extraintf telnet --telnet-password <somepass> --security-policy=1
You may use the --vlm-conf switch to explicitely pass a path to a vlm config file to vlc.
The --config=<path to file> switch is NOT meant to point to vlm config files!

Some issues seem to exist when using playlists as input, see here and here.

Programmatically controlling vlc via rc, a socket file and perl

As you can read in the docs and here, vlc can be controlled by multiple interfaces. What I am interested here, is the rc interface. (rc's source code here)

Start vlc with switches -I rc and to use a socket instead of the standard stdin, add --rc-unix /path/to/file.sock or whatever socket-file you'd like.

In order to test/debug your rc-socket-connection, you can then send commands to vlc via the socket file interface for example by using netcat:

/bin/echo -n "help" | nc -U /path/to/file.sock

Some versions of Linux, like my Debian Etch, don't provide a nc/netcat command that has the -U option (Unix Domain Socket Communication). In this case, socat is an even mightier replacement. apt-get it and fire a connection between STDIO ("-", minux sign) and your socket file like this:

socat - /path/to/file.sock

The same in perl is quite simple using IO::Socket, more specific the socket-file variant IO::Socket::UNIX. Connect with IO::Socket to the socket-file and use the $socket scalar just like any standard filehandle, you can read, write,print, etc:

my $socket = IO::Socket::UNIX->new(
     Peer    => '/path/to/socket.file.sock',
)
 if(!defined($socket)){
     print "Socket: connection failed: $@";
}

You can then issue commands by doing a simple print or using the OOP style $socket->send(). Commands are terminated, as they would be on the rc's STDIO, with a newline n (simulating the ENTER key).

while ( <$socket> ) { print $_ }

Reading from vlc's socket can be a bit troublesome, as our while loop does not know when it has read everything. This sub:

sub sockread {
        my $socket = shift;
        my $content;
        while (my $line=<$socket>){
                $content .= $line;
                last if $line =~ /[ End of /i;         # vlc's rc is a bit inconsistent, some end like this
                last if $line =~ / returned 0 /i;       # others like this
        }
        return $content;
}

tries to detect the end of the answer by waiting for the "[End of <command>" string each rc response seems to come with. A bit hacky, but it works.

Now, parsing the rc interface's return content is another issue...

One note: if you are running vlc with the rc interface in --daemon mode, you need to add the --rc-fake-tty flag, see this thread. Otherwise vlc won't create the rc interface and/or a socket file! (The problem arises because vlc needs a primary tty interface and daemons normally lack one, so you need to set rc to be the "fake tty" or another workaround would be to bring the dummy interface into the mix.)

If you read this rc centered section here, you might also be interested in VLMa, the Video Lan Manager based on Java.

Listeners/ viewers statistics with vlc

As far as I know, vlc is a bit mute in terms of statistics. There is no current listeners / viewers / clients count, and overall data transfer measurements appear to be broken - at least via rc. This is a bit disappointing when you plan to use vlc as a streaming server. The log file alone is of little help here. Still, in combination with nifty networking tools, one can squeeze out a bit of usage stats from vlc.

There is this little tool called ss,  it comes as part of the iproute package. A command as simple as ss -n | grep '1234' will give you a printout of all connected clients on a certain port. Keep in mind that protocols like mms open two simultaneous connections, so you need to further filter outputs that also list "Listening..." or "Establishing..." connections, like ss -a (-a for all) does. Simply counting "Established" connections can be done with ss -n | grep -c '1234'. See the ss manpage for more details.

For the sake of completeness: of course, you could also use tcpdump for that, but I found it to be overkill for such a simple task and think that live network traffic snooping is where tcpdump really shines. For a simple "print output" task, let alone programmatically via a script, ss is a good enough choice.

Stream Motion-Jpeg with vlc

vlc can do many things. One is streaming a video input as M-JPEG, also called motion-jpeg or mjpg. The corresponding videocodec is mjpg and the muxer is mpjpeg.

TODO