How to setup video on demand (pseudo-streaming) server with apache and mod_h264
You can see final result of this tutorial @ wovi.es
Mod_h264
This mod runs the show. It enable your viewers to immediately jump to any part of the video regardless of the length of the video or whether it has all been downloaded yet. It also gives you possibility to only playback a part of the video or create download links to specific parts of the video. This opens door for an easy way for making previews available, say for example when you want to differentiate between registered and unregistered users, but more on this when we deal with mod_auth_token which will protect full length movies from being watched by unregistered users.
Instructions for CentOS
1 2 3 4 5 6 7 8 9 |
yum install httpd mod_ssl yum install gcc wget http://h264.code-shop.com/download/apache_mod_h264_streaming-2.2.7.tar.gz tar -zxvf apache_mod_h264_streaming-2.2.7.tar.gz cd mod_h264_streaming-2.2.7 ./configure make make install vi /etc/httpd/conf/httpd.conf |
Find LoadModule and at the end add:
1 2 |
LoadModule h264_streaming_module modules/mod_h264_streaming.so AddHandler h264-streaming.extensions .mp4 |
Mod_auth_token
mod_auth_token is a cool apache module that allow you to generate URIS for a determined time window, you can also limit them by IP (IP limit available only in 1.0.6 beta, if you install 1.0.5. stable this function will not work). This is very useful to handle file downloads, generated URIS can’t be hot-linked (after it expires), you can also use this mod to lock down directory for unregistered users.
Instructions for CentOS
1 2 3 4 5 6 7 8 9 10 11 |
yum install automake wget http://mod-auth-token.googlecode.com/files/mod_auth_token-1.0.6-beta.tar.gz tar xvzf mod_auth_token-1.0.6-beta.tar.gz cd mod_auth_token ln -s /usr/share/automake-1.11/missing missing ln -s /usr/share/automake-1.11/config.guess config.guess ln -s /usr/share/automake-1.11/config.sub config.sub ln -s /usr/share/automake-1.11/COPYING COPYING ln -s /usr/share/automake-1.11/install-sh install-sh automake vi /etc/httpd/conf/httpd.conf |
Find and uncomment following line in httpd.conf:
1 |
//LoadModule auth_token_module /usr/lib64/httpd/modules/mod_auth_token.so |
mod_bw
The httpd web server doesn’t really have a way to control how much resources a given virtual host can have/ a user can request, this mod helps with this. You don’t want 1 user to use up all available bandwidth on your server… Fortunately this one is easy, direct from repo.
Instructions for CentOS
1 |
yum install mod_bw |
VirtualHost
It is time now to configure virtual host and give our special “video” directory power of mod_h264, mod_auth_token and mod_bw.
1 |
mkdir /var/video |
Create virtualhost containing following directives. What happens here is pretty simple. We create alias so that domain.com/video points to video directory in the filesystem. Same for /preview. But there is a difference on how we handle /video and /preview. /video is protected using mod_auth_token – files cannot be directly accessed and correct token (based on IP and time) is required. /preview is not protected and you can directly view all files there, but any .mp4 file accessed in this directory is rewritten to show only 10 sec clips (?start=400&end=500).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<VirtualHost *:80> DocumentRoot /var/www/html Alias /video /var/video Alias /preview /var/video <Directory /var/video> AllowOverride None allow from all #Subtitles - this is not needed if backend and frontend are on the same domain. #<filesMatch "\.(txt|srt|vtt)$"> # <ifModule mod_headers.c> # Header set Access-Control-Allow-Origin "*" # Header set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept" # </ifModule> #</filesMatch> #Limit bandwidth BandwidthModule On ForceBandWidthModule On Bandwidth all 500000 </Directory> <LocationMatch /preview> RewriteEngine On RewriteRule ^(.+)\.mp4$ $1.mp4?start=400&end=500 [L] </Location> <Location /video/> AuthTokenSecret "secretstring" AuthTokenPrefix /video/ AuthTokenTimeout 60 AuthTokenLimitByIp on </Location> </VirtualHost> |
PHP FrontEnd
Lets me explain this briefly. It should be self explanatory to any programmer though.
- We have two functions. First function (generate_token) given filename generates token for mod_auth_token, without this we will not be able to access full length videos inside /var/video directory. Second one is just a simple example for managing sessions, not important for the scope of this article.
- I used video.js in this example, good but pricey alternative is jwplayer (pricey if you want to remove watermark, otherwise they also have free version).
- We have user sandro, he can be either level 0 or 1. 0 level can view only short clip, and 1 level can see full video.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php // function generate_token($fileName) { // Settings to generate the URI $secret = "secretstring"; // Same as AuthTokenSecret $protectedPath = "/video/"; // Same as AuthTokenPrefix $ipLimitation = true; // Same as AuthTokenLimitByIp $hexTime = dechex(time()); // Time in Hexadecimal //$hexTime = dechex(time()+120); // Link available after 2 minutes // Let's generate the token depending if we set AuthTokenLimitByIp if ($ipLimitation) {$token = md5($secret . $fileName . $hexTime . $_SERVER['REMOTE_ADDR']);} else {$token = md5($secret . $fileName. $hexTime);} // We build the url $url = $protectedPath . $token. "/" . $hexTime . $fileName; return $url; } // function current_user_level_is($level) { $current_user_level = $_SESSION['user_level']; if ($current_user_level == $level) { return true; } } session_start(); // $_SESSION['user_id'] = 1; $_SESSION['user_name'] = "sandro"; $_SESSION['user_level'] = 0; // 0 = free user, 1 = VIP user. // //print_r($_SESSION); ?> <!DOCTYPE html> <html> <head> <title></title> <link href="video-js.min.css" rel="stylesheet"> <link href="style.css" rel="stylesheet"> <script src="video.js"></script> <script>videojs.options.flash.swf = "video-js.swf"</script> </head> <body> <video id="" class="video-js vjs-default-skin" controls preload="none" width="100%" height="500" poster="" data-setup="{}"> <source src="<?php if (current_user_level_is(1)){ ?>http://yourdomain.com<?php echo generate_token("/file.mp4");} else {echo "http://yourdomain.com/preview/file.mp4";} ?>" type='video/mp4' /> </video> </body> </html> |
Encoding
You will need to encode all your files into .mp4 container with h264 codec, but pay attention, I noticed handbreak encoded files don’t play on IOS. So use ffmpeg or avconv, whichever you like.
I prefer ffmpeg, as I have a simple way to increase volume using -vol
flag, I couldn’t find same for avconv. You can also use non-experimental aac code libfaac.
1 |
ffmpeg -i file.avi -vcodec libx264 -crf 18 -vol 640 -strict experimental -acodec aac -threads 2 file.mp4 |
If you have huge library of videos you can go through all your *.avi files and encode them into *.mp4, using this command (nohup makes sure you can close terminal window and continue encoding uninterrupted):
1 |
for i in *.avi; do nohup avconv -i "$i" -vcodec libx264 -acodec libvo_aacenc -r 30000/1001 "$i".mp4; done |
Leave a Reply