Wednesday, February 04, 2009

Apache FLV streaming done right!

After 2 days of researching, I've just found how to do FLV streaming the right way!
Let's wrap up current (I.2009) situation:

Why?
  • FLV is still very common container format for videos. h264 is more efficient but is supported by Flash since version 9.115
  • FLV streaming means you can seek to any position during video, and browser (flash player) will buffer only from this position to the end.
  • Streaming allows people to skip boring parts or see video ending without loading the whole file (saves bandwidth).
  • Lighttpd has FLV streaming built in.
  • Apache doesn't.
  • There is number of PHP and Perl scripts that allow to implement streaming. These work by loading the given flv file, and pushing it's content from given position to the end.
  • It's bloated! PHP uses memory, Apache PHP module uses memory, every single streaming connection is a running php script (try empty php file with just <?=memory_get_usage(true)?> )

Behind the scenes
  1. Flash player requests the file, starts loading i.e.: http://host/video.flv
  2. When user clicks any position on the "scrubber" (seek bar) player stops buffering and starts buffering from url: http://host/video.flv?start=1503320
  3. The parameter is byte count from where buffering should start.
  4. Server fetches the file, truncates it and rewrites FLV header.

Solution: flv streaming in apache:
  1. Download apache mod_flv module by Paul Querna or my tuned version.
  2. Install apxs tool (also avaliable in RPMs for most systems as httpd-devel)
  3. Compile and install your module with the following command:

    apxs -c -i ./mod_flvx.c

  4. Add the following 2 lines to your httpd.conf or create a dedicated /etc/httpd/conf.d/mod_flvx.conf (path depends on your distribution):

    LoadModule flvx_module modules/mod_flvx.so
    AddHandler flv-stream .flv

  5. Restart Apache (i.e. with service httpd restart)

Tuning:

  1. I've modified the module to fix content-length and add last-modified headers. I've also added a fix when offset is set to wrong position (i.e. past video length).
  2. You can download the tuned module here.

Things to remember:
  • To use flv streaming on the web, you need to use a pseudo-streaming compliant Flash player. Flowplayer does the job quite well.
  • Streaming requires that your FLV has embedded keyframe markers (meta-data). You can inject any FLV with this data using flvtool2:

    flvtool2 -U video.flv

  • Generated streams will work briliantly in Flash player, but you won't be able to play them back in standalone player (VLC, WMP etc.). So don't be suprised when you "wget" a video with ?start=1000 and the file doesn't play. That is because they don't have starting meta-data and players will have hard time deciding which codec to use.

Have fuN!

56 comments:

  1. Is this for Apache 2.2.X or will it also work with 2.0? I ask because with Apache 2.0 I'm getting this when trying to run the module: "undefined symbol: apr_brigade_insert_file"

    -Gary

    ReplyDelete
  2. Haven't tried it yet. It might be missing some of the new api's functions, but the first version of module was created in 2006, so in the times of 2.0. Have you tried upgrading to higher version in 2.0.x branch ?

    ReplyDelete
  3. Anonymous5:41 PM

    I believe that you need to upgrade/patch apr-util which I am still having a problem in doing it because I think I will need to upgrade glibc 2.3.4 to 2.4 (my system is redhat AS 4). Does someone know how to do it without the need to upgrade glibc or another solution?

    ReplyDelete
  4. How about on IIS? Any ideas guys?

    ReplyDelete
  5. Anonymous7:15 AM

    To build on Mac OS X (10.5.6):

    sudo apxs -c -i -Wc,"-arch x86_64" -Wl,"-arch x86_64" ./mod_flvx.c

    ReplyDelete
  6. Anonymous12:00 PM

    If I use this module, I assume it affects all requests for FLV files. Is it a problem for progressive download FLV's? ie Could I use this in a mixed environment where some are pseudo-streamed and some are progressive. My progressive ones won't have any start defined. Thanks.

    ReplyDelete
  7. Yeah, you can. If there is no "start=" param or "start=0" then it doesn't make any difference - you will get the full binary flv file.

    ReplyDelete
  8. Anonymous10:07 AM

    This is driving me insane... I have compiled it using "sudo apxs -c -i -Wc,"-arch x86_64" -Wl,"-arch x86_64" ./mod_flvx.c" and I get this error:

    Syntax error on line 290 of /Applications/MAMP/conf/apache/httpd.conf:
    Cannot load /Applications/MAMP/Library/modules/mod_flvx.so into server: cannot create object file image or add library

    Does someone have the .so file that I can just download and use? That seems like it would be much easier.

    ReplyDelete
  9. Hi,

    I compiled into the server, however when i try to check a header i get this:

    HTTP/1.1 406 Not Acceptable
    Date: Wed, 22 Apr 2009 22:23:19 GMT
    Server: Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 PHP/5.2.9
    Content-Type: text/html; charset=iso-8859-1
    X-Pad: avoid browser bug


    I figure it has something to do with the AddHandler ?

    I have both the lines in the server, not sure why it doesnt work right - please could you advise ?

    thanks !

    ReplyDelete
  10. I don't think it's related to this module. It looks like something else (rewrites messing up url?)

    ReplyDelete
  11. Hi !
    I managed to fix up the problem with the 406 header error. It was a mod_security error.

    So streaming is good - quick question - if i wanted to start the stream at say 3 minutes into the file - how would i do it ? I tried with ?start=x but that doesnt work, it just results in a corrupted video file that wont play.

    Please could you advise how to do that ?

    ReplyDelete
  12. I've setup two files:

    http://nettrackers.net/flv/working.html

    and
    http://nettrackers.net/flv/not_working.html

    In the working file i dont have a querystring parameter. As soon as i add in a query string parameter the file goes into a wait and i dont get anything - any idea how i could get this working so that it starts say 1 minute into the file?

    Thanks !

    ReplyDelete
  13. FLV pseudostreaming relies on byte positions, so start=380000 would stream you to 380k position.

    If you want to stream from a point in time, you can extrapolate it from duration and filesize, ie: bytePos = (fileSize / durationInSeconds) * secondsPos

    ReplyDelete
  14. Ahh ok, thanks for the hint , ill figure the startpoint that way, however whenever im pass thing start parameter, i dont see the video working at all : (

    ReplyDelete
  15. It's still a problem in your apache config:

    -------------------------
    curl -I http://nettrackers.net/t/Extremists.flv?start=123
    HTTP/1.1 406 Not Acceptable
    Date: Mon, 27 Apr 2009 15:59:33 GMT
    Server: Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 PHP/5.2.8
    Content-Type: text/html; charset=iso-8859-1
    X-Pad: avoid browser bug
    -------------------------

    ReplyDelete
  16. thanks for the replies!

    It's not working right because of my mod security rules.

    If you try curl -I -A "MSIE" you will see it works fine.

    The mod security blocks off curl.

    ReplyDelete
  17. curl -I -A "MSIE" nettrackers.net/t/Extremists.flv?start=20259653
    HTTP/1.1 200 OK
    Date: Mon, 27 Apr 2009 17:11:25 GMT
    Server: Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 PHP/5.2.8
    Content-Length: 2661984
    Last-Modified: Wed, 22 Apr 2009 22:16:38 GMT
    Content-Type: video/x-flv

    ReplyDelete
  18. Hi !Was wondering if you had any other info i could use on the start point problem i was having - please do let me know - really stuck on this point : (

    ReplyDelete
  19. Anonymous9:50 PM

    Hi, how to build this module without apxs, OR rather how to install apxs on the server? I want to add this on my Godaddy VPS
    thanks

    ReplyDelete
  20. Anonymous9:16 AM

    Hi
    I have videos which are provided to logged in users. How should I use that along with this? Is it possible.
    Does this module allow users to play a flv which is somewhere in the /www/ or /public_html/ dir?
    thanks

    ReplyDelete
  21. I does not interfere with access restrictions, aliases, redirects or other features of apache.

    Once enabled (according to my instructions), the module works site-wide.

    ReplyDelete
  22. "how to build this module without apxs"

    If you have access to you server's shell, just follow the above instructions. APXS is part of apache - you can get it from here: http://httpd.apache.org/docs/2.0/programs/apxs.html

    If you don't have access to shell, but can throw in modules to apache (.so files) you might try sniffing what type of server and apache Godaddy uses. Then try to compile the module on linux under the same (or closest) version of apache/system. After compilation by apxs you'll have "mod_flvx.so".

    ReplyDelete
  23. Check out yamdi (yet Another MetaData Injector for FLV) instead of flvtool2. flvmdi/flvtool2 read the whole file into memory.

    otoh, yamdi uses less memory and is faster.

    ReplyDelete
  24. [b]406 Not acceptable:[/b]

    the client (browser) has requested a document in a format that the client does not accept (Accept-Header).
    So it looks not like a server problem (except you have configured a wrong type, but thats not most likely,

    ReplyDelete
  25. Thanks evuraan for the yamdi program - flvtool2 kept crashing with EOFERROR on CS4 encoded videos.

    ReplyDelete
  26. Anonymous6:06 AM

    Thanks So much this works. It really works.

    ReplyDelete
  27. Hi!
    This module can work with Apache 1.3.x?

    ReplyDelete
  28. Apache 1.3.x does not have the same module API, so the module should be rewritten. It's way of working is so simple, I'm sure it's possible to fix one for 1.3.x, but I cannot assist you with it.

    If someone contributed - please write us how to modify source code to make it 1.3.x compatible?

    ReplyDelete
  29. Anonymous4:22 AM

    Very interesting post.

    QQ. Are above steps applicable on Windows XP as well? I have apache running on Windows XP and doesn't have PHP installed.

    ReplyDelete
  30. Hello, I'm using JW Player with jquery media plugin with swfobject and i can't make it work. I installed the module, fixed some videos with flvtool2, and it doesn't work. Tips and tricks anyone?

    ReplyDelete
  31. Have you tried checking http headers?. In linux you could use curl, i.e. curl -I http://server/file.flv?start=1000000. It will show you incoming headers. Try with different start parameter and it should result in different content-length.

    ReplyDelete
  32. Anonymous3:01 AM

    Hi!

    Sounds great! But why does the FLV needs embedded keyframe markes when the "start" parameter is a byte stamp ? If I want that the Video has three different points in time where i can jump in - can i jump to the byte stamp with ?start= without cue points embedded or not ?

    ReplyDelete
  33. Keyframes are not "cue points". The latter are used for events, marking chapters etc.

    Keyframes are needed for FLV video decoder to be able to seek properly. Keyframes are created automatically when encoding video. Flvtool2 scans the file for keyframe positions and then writes meta-data header with a list of all "keyframe->byteposition" markers. The header is put at the very beginning of the .FLV file.

    It's a limitation from flash video decoder and flash video streaming technology.

    ReplyDelete
  34. your tuned version unavailable

    ReplyDelete
  35. your tuned version is unavailable for downloading

    ReplyDelete
  36. pd_voodoo12:44 AM

    Is this method working fully? I've tried streaming with Lighttpd, but in vain. I need a solution quickly. Please let me know. Thanks.

    voodoo

    ReplyDelete
  37. Any place to get the tuned flvx module? Thinkscape.biz is down :(
    Thanks

    ReplyDelete
  38. Currently it's available at:
    http://github.com/osantana/mod_flvx

    ReplyDelete
  39. or you can reach me if you read that,
    img23 [dot] imageshack [dot] us [dash] img23 [dash] 2464 [dash] labeloverflowfont.gif

    ReplyDelete
  40. hi,we talks together about watermark patch...
    p.s. could you mail me? there my mail...
    img23.imageshack.us/img23/2464/labeloverflowfont.gif

    ReplyDelete
  41. Nice to have a maintained version of mod_flvx but I have a hard time compiling it. Must be my knowledge of compiling though.

    I have Apache 2.2 (for Windows) within my Adobe FMS. I like to add beside the current rtmp streams, http streams for HTML5 and iPhone etc.

    How to compile using MinGW? I have no Apache apsx (http://httpd.apache.org/docs/2.2/programs/apxs.html) to do the job. I've read a wiki (http://www.mosalov.com/wiki/Flash_streaming_with_mod_flvx) where I can get mod_flvx.so but I like to have a maintained version like yours (http://github.com/osantana/mod_flvx)

    Can you help me step by step how to compile mod_flvx.c using mingw? I have mingw installed an in the path directive.

    ReplyDelete
  42. Thank you for this. I have working in all my servers and working very nice.

    ReplyDelete
  43. Anonymous3:31 PM

    Hi, has someone managed to successful resolved the following error:

    Syntax error on line 34 of /usr/local/apache/conf/httpd.conf:
    Cannot load /usr/local/apache/modules/mod_flvx.so into server: /usr/local/apache/modules/mod_flvx.so: undefined symbol: apr_strtoff

    ReplyDelete
  44. To build on Mac OS X as a universal file, use this:

    sudo apxs -c -i -Wc,"-arch ppc -arch ppc64 -arch x86_64 -arch i386" -Wl,"-arch ppc -arch ppc64 -arch x86_64 -arch i386" ./mod_flvx.c

    This will compile for all architectures.

    ReplyDelete
  45. Anonymous10:07 AM

    Can u hid the source file so that people can not just download the file ?

    ReplyDelete
  46. Anonymous2:06 AM

    can you playback those streams with flash using netstream?

    ReplyDelete
  47. Thank you for the tutorial, but I still have a problem: the header is ok, apache is ok, module loads without errors but jwplayer can't seek.

    Any suggestions?

    ReplyDelete
  48. Anonymous8:46 AM

    @Tomi: HTTP pseudostreaming is enabled by setting the option provider=http in your player.

    http://www.longtailvideo.com/support/jw-player/jw-player-for-flash-v5/12534/video-delivery-http-pseudo-streaming

    ReplyDelete
  49. I have done all as you said in tut, but still i have probs with loading video(something like crash halfway , i cant rewind movie). Do I need to adress path of htm/l files(flowplayer with pseudostreaming plugin-embed code) with iframe I load that htm/l in joomla (articles-iframe code).

    Can this work without lighttp , can you explain me how?
    I need to stream flvz.com links.

    Can this script download & load external links on my VPS ?

    Please help .

    @thinkscape plz respond on this thru e-mail .

    ReplyDelete
  50. Anonymous2:16 PM

    Here the ubuntu way - http://translate.google.com/translate?js=n&prev=_t&hl=en&ie=UTF-8&layout=2&eotf=1&sl=pt&tl=en&u=http%3A%2F%2Fbarrasbin.wordpress.com%2F2011%2F05%2F08%2Fstreaming-de-flv-no-apacheubuntu%2F

    ReplyDelete
  51. How would you use this method in combination with say a PHP script? Assuming you wanted to authenticate the requester of the video before serving it. Just generally want to protect the videos from direct download / direct streaming, any ideas would be most appreciated!

    ReplyDelete
  52. How about tokens?
    http://flowplayer.org/plugins/streaming/secure.html

    ReplyDelete
  53. hey great article

    I am a web developer and my client requested that the users should not be able to download the media files on the site. Now this took a lot of time, so I researched a lot on the web for an answer.

    Once it was accomplished I decided to write a blog for it.
    http://goldprogramming.blogspot.com/2011/11/secure-streaming-of-media-on-your-site.html

    ReplyDelete
  54. Thank YOU!!!!!!

    Lifesaver.

    ReplyDelete
  55. for anyone working with large large flv files, in order for pseudo streaming to work, you need to make sure the meta data is inserted.. I was using flvtool2 to do this, but it craps out on large files because flvtool2 loads the whole file into memory during metadata injection. I switched to using yamdi, and it managed to inject the meta data correctly

    ReplyDelete