With slowly increasing upload bandwidths (even in Germany), video and audio streaming for home servers become feasible. In Germany, currently the highest upload bandwidth you can get as a non-corporate is 10 Mbit/s with VDSL, enough to provide actual viewable video streaming in near DVD quality to a small number of concurrent viewers. However it was kind of hard to get a setup working that suits my requirements.
In my case the starting point are video and audio files, the video files are mostly XviD (MPEG4) encoded (with one or multiple MP3 or AC3 audio streams) in AVI containers. The audio files are mostly MP3 files with a constant or variable bitrate.
Because I want to stream (more or less) directly to the browser, possible player technologies are: Browser Plugins (WMP/ActiveX, Mplayer/VLC Mozilla Plugins, Quicktime), Flash or HTML5 Video and Audio Tags.
All of them support different codecs, containers and streaming protocols. I use HTTP as a streaming protocol for a numerous of reasons, but lets just say the decisive factors are that HTTP is supported by all mentioned players and is easy to integrate in existing web applications.
The different player plugins are a mess, there is no standard what so ever and it is hard to make any cross platform assumption on support of this. It has the theoretical capability to stream my video and audio file formats but in my experience it is most likely to fail out of the box so I would not count on that.
The popular flash plugin supports the mediocre Sorenson Spark and VP6 video codecs and most recently H.264 video. The audio codecs are MP3 and most recently AAC. Flash uses its own Container Format FLV, in more recent versions other containers are also supported.
There are two notable non-free open source flash players. The jwplayer (custom commercial/open source license) with a phone home logo/branding and flowplayer with a (In my mind) violating GPLv3 dual license and branding.
Maybe I should mention that Flash supports their own proprietary streaming protocol RTMP, since its DRM infected (RTMPe) and just evil, I ignore it, even through there is an open source server implementation.
The major problem of flash video is (besides of its proprietary status) the exceedingly modest video decoder that requires high end hardware and obscene resources to play even medium quality video.
Most recent and upcoming browsers implement the Video and Audio functionality of HTML5. Unfortunately and due to the nature of HTML5, w3c does not specify video and audio codec requirements. As a result every browser and even different versions have a different subset of supported codecs. The formats are (Video Codec/Audio Codec/Container): Theora/Vorbis/OGG, H.264/AAC/MP4 and VP8/Vorbis/WebM. For more information visit the excellent site diveintohtml5.org.
I want support for HTML5 and Flash for my home server streaming setup, this should support streaming for all modern browsers and operating systems (even mobile devices). As you may have noticed, none of them can handle my commonly used file format and the variety of formats used for HTML5 makes transcoding unavoidable.
Transcoding means the encoding from one digital video/audio file to another. So the solution would be to transcode all files to the required formats (at least Sorenson Spark, H264 and OGV) beforehand. And now comes the tricky part: Because for some reasons (too many files, no disk space to waste) this is not an option in my case. Now normally that would be the end of this article, but I came up with a solution although it has its own problems and limitations.
The basic idea is to start the transcoding process the same time the user requests streaming for a audio/video file. It works kind of like a live feed, but instead of using the video and audio stream from a camera and microphone, it reads video and audio files. Because it starts a resource hungry encoding process for each stream, the maximum possible concurrent viewers are very limited. In my case, because my upload bandwidth is limited anyway, this is not really a problem, but in other, larger setups, it sure is.
Another big limitation is seeking, although theoretically possible with pseudo HTTP streaming, it is very difficult to implement in this kind of live streaming. I have some vague ideas how this could work, but I’ve not implemented any of it. As a kind of workaround, the user can specify a timecode, FFmpeg should seek to, before the transcoding starts.
One last thing: I talked about the different formats supported by HTML5 capable browsers. Some of them only support H.264/AAC within MP4 container. The problem is that, the MP4 container is not capable of streaming this way because it needs to seek inside the output file. FFmpeg will just quit with the message
muxer does not support non seekable output. Theres a alternative for Apple stuff but I do not plan to implement that anytime soon.
Proof of Concept
I’ve created a proof of concept implementation using PHP, FFmpeg for transcoding and MPlayer to identify encodings and bitrates. The user can select player technology, audio/video bitrate (to transcode for lower bandwidths) and adjust the transcoding settings in detail. My solution is pointed to technical experienced users, depending on your audience you probably would use browser sniffing to hide technical details completely. The scripts need Linux, PHP5 and the Smarty Template Language installed. If you want to give it a try I’ve created a github repository: php-live-transcode. Make sure you met the requirements and change the
config.inc.php file to match your setup.
I’m well aware about the different problems of this peace of software, although some could be fixed, there are some fundamental problems that are just unavoidable, so it is important that you are aware of the limitations before you use any of this. Notice the configuration value
FFMPEG_MAX_INSTANCES, this defines how many ffmpeg instances should be allowed to run simultaneously, this is very important because this setup just calls out for a Denial of Service attack, so be careful.
I guess thats it for now, please leave a comment if you have tried it. Please use the issue tracker at github if you encounter a problem.