How to "live stream" a media file
by Thorsten Pferdekämper
I have tried a while to setup a free (open source etc.) live streaming solution which is able to stream “anything” to a flash frontend. The basic idea is to stream TV from v4l2 (and similar), but I also wanted to stream files (movies). I found that most tutorials only show how to setup the streaming or only show how to get a flash player up and running. The whole roundtrip is not really described and has its own difficulties. This tutorial describes the whole “roundtrip” from a media file on your disk to displaying it in a browser. I know that there are easier ways to send a media file to some player in a browser and I also know that playing a file is not really live streaming. (This is why I have put it in double quotes.) However, it shows the principle and it might be easier to set this up as a first step.
Maybe important to note that this is targeted to a Linux server, I don’t know how and if it works with other operating systems.
The whole roundtrip looks as follows:
file → ffmpeg → crtmpserver → jwplayer/haxe ( → apache)
ffpmeg is used to convert the input to h264/aac in an MPEG-TS container and sends it to crtmpserver using UDP
crtmpserver acts as the RTMP server (you might have guessed this)
jwplayer can then play the stream. With using haXe, it is possible to create your own players
apache is the webserver
The following command line converts <filename-of-movie> to h264/aac, puts it into an TS container and sends it using UDP. (I have split this on several lines for readability.)
> ffmpeg -i "<filename-of-movie>" -re -vcodec libx264 -vpre default -vpre baseline -b 500000 -s 320x180 -strict experimental -g 25 -me_method zero -acodec aac -ab 96000 -ar 48000 -ac 2 -vbsf h264_mp4toannexb -f mpegts udp://127.0.0.1:10000?pkt_size=1316
This command line simulates a live stream. It does not wait for anything to receive the stream, it just pumps the data through port 10000 at the speed the movie should play. (You should see some ffmpeg output about frames, fps etc.) This is achieved by the ”-re” which makes ffmpeg to read the data with (maximum) the framerate which the movie has. In addition, the UDP protokol does not wait for the answer of any receiver like TCP would do. (As far as I understand.)
In my tests, <filename-of-movie> always is in an AVI container, with DivX/XviD and MP3. It should, however, work as well with any other movie formats ffmpeg understands properly.
The ffmpeg command line above was taken from the wowza documentation. I have only experimented a bit to enhance the performance. So I have changed the ”-g 60” to ”-g 25” as it seems more appropriate to me and added the ”-me_method zero” as it was considered in the ffmpeg documentation as being faster. The ffmpeg documentation also mentions using ”-intra”, but this did not work. The stream never played in the flash player. (I don't have an explanation for that.)
I have checked out and compiled the sources from svn as described on the crtmpserver website. After that, the server can be run. I have tried the same with other free rtmp servers and it did not work. E.g. I did never succeed with compiling red5. With crtmpserver, it just worked. It is also not that complicated to understand the coding. (Even if there could be some more comments…)
I.e. just get and compile crtmpserver as described in the crtmpserver documentation.
To enable it to receive an UPD TS stream, the following needs to be added to the file crtmpserver/builders/cmake/crtmpserver/crtmpserver.lua
{ ip="0.0.0.0", port=10000, protocol="inboundUdpTs" },
This should be added to the acceptors part of the flvplayback application. This tells crtmpserver that there should be a stream in a TS container sent via UDP on port 10000. (See alsortmpserver.lua for the whole file.)
After changing this and running the ffmpeg command line above, run the server with the config file as argument, like
> cd crtmpserver/builders/cmake > ./crtmpserver/crtmpserver crtmpserver/crtmpserver.lua
(I don't know yet why it only works from the cmake directory. Running from the crtmpserver directory itself did not work for me.)
UPDATE: This happens because cmake is used for building the development version of the server. It has all the binaries/libraries paths hardcoded in by the GCC linker. This is why you can't move around any binaries/libraries. If the production version is required (independent paths, maximum optimization, etc), make
or packing
scripts should be used instead. The documentation about installing/compiling the server will be changed to reflect that.
I keep having the issue that crtmpserver does not start up when I try it the first time after booting the machine. This is because there is another rtmp server (red5) installed as well. This needs to be stopped first, at least as long as it runs on the same port. I.e. all other rtmp servers should be stopped. On my sytsem, this is done like this:
> /etc/rc2.d/S20red5-server stop
Apparently, this needs to be done as root.
Apart from some (maybe a lot) warnings, the crtmpserver output should contain something like
...basetsappprotocolhandler.cpp:49 Stream available (1): ts_2_257_256
Remember the “ts_2_257_256”, or whatever it exactly looks like. This is the name of the stream which is later needed in the flash player. The name might depend on the ffmpeg version and on the crtmpserver config and version. Generally, it has the format “ts_<id>_<audio-pid>_<video-pid>”.
crtmpserver now sends the stream as crtmpserver://<your-ip>/flvplayback/ts_2_257_256. This URI can be used to point a flash player to the stream. I have successfully done this with a very simple player written in Haxe and with JWPlayer as well.
But first, we need a webserver. Playing the streams from an embedded player using local files seems to have some security issues, so flash does not allow it easily.
There's not much to say here. Install apache and find where the content needs to go. On my system, this is /var/www, which I believe is the default.
The JWPlayer is a full-blown Flash video player. You can get it from here: http://www.longtailvideo.com/players/jw-flv-player/
Copy the file player.swf in a directory which can be reached by the web server (e.g. a subdirectory of /var/www) and embed it into an html file as described on the longtailvideo website. Alternatively, just use my test_jwplayer.html and put it in the same directory as player.swf. When using it, you need to change this part:
streamer:'rtmp://zeus/flvplayback',file:'ts_2_257_256'
“zeus” is the name of my server, you need to change it to the name of your server. If unsure or it does not work, just use 127.0.0.1. Apparently, it will only work locally then…
You might also need to change the stream name (ts_2_257_256) to the one from crtmpserver's output.
Now make sure that ffmpeg and crtmpserver are running. Open a browser and type an url like
http://zeus/thorsten/jwplayer/test_jwplayer.html
Apparently, you need to change “zeus” to your machine's name or try 127.0.0.1. The rest is the path to the test_jwplayer.html file, which will also be different on your box.
After a few seconds of buffering, you should be able to watch the movie. It might not have the correct aspect ratio and it also might be a bit choppy, but it works in principle. (If it does not, restart ffmpeg and crtmpserver and reload the page.)
It seems that it can do even more, but for me, haXe (http://haxe.org/) is a free (open source) alternative to “develop flash”. I.e. you can write actionscript-like programs and compile them to .swf format. (I sometimes need to do things myself to really understand them.) I like this especially because I could write my own little flash stream player which I could experiment with. In theory, this could also be done with JWPlayer as this is open source as well. However, JWPlayer source is a bit complicated and I am not sure if you can compile it without the commercial flash development kit.
If you want to try the haXe player, install haxe (at least on debian, there is a package). Then create a file like Test.hx. Open it in an editor and change the appearances of “zeus” to your own server's name and change “ts_2_257_256” to the correct stream name. (I know that hardcoding these names is not a programming style which deserves to be called “style”, but again, it shows the principle.)
The file can then be compiled using the following command line from the directory where Test.hx sits in.
> haxe -swf9 test.swf -main Test
This results in the file test.swf. This can then be embedded as any other flash player into a website, see e.g. test_haxe.html.
Put test.swf and test_haxe.html in a directory accessible by the web server. Open a browser and point it to the test_haxe.html page. Again, the movie should come up after a few seconds. (…if crtmpserver and ffmpeg are still running)