Best approach to real time http streaming to HTML5 video client
EDIT 3: As of IOS 10, HLS will support fragmented mp4 files. The answer now, is to create fragmented mp4 assets, with a DASH and HLS manifest. > Pretend flash, iOS9 and below and IE 10 and below don't exist.
Everything below this line is out of date. Keeping it here for posterity.
EDIT 2: As people in the comments are pointing out, things change. Almost all browsers will support AVC/AAC codecs. iOS still requires HLS. But via adaptors like hls.js you can play HLS in MSE. The new answer is HLS+hls.js if you need iOS. or just Fragmented MP4 (i.e. DASH) if you don't
There are many reasons why video and, specifically, live video is very difficult. (Please note that the original question specified that HTML5 video is a requirement, but the asker stated Flash is possible in the comments. So immediately, this question is misleading)
First I will restate: THERE IS NO OFFICIAL SUPPORT FOR LIVE STREAMING OVER HTML5. There are hacks, but your mileage may vary.
EDIT: since I wrote this answer Media Source Extensions have matured, and are now very close to becoming a viable option. They are supported on most major browsers. IOS continues to be a hold out.
Next, you need to understand that Video on demand (VOD) and live video are very different. Yes, they are both video, but the problems are different, hence the formats are different. For example, if the clock in your computer runs 1% faster than it should, you will not notice on a VOD. With live video, you will be trying to play video before it happens. If you want to join a a live video stream in progress, you need the data necessary to initialize the decoder, so it must be repeated in the stream, or sent out of band. With VOD, you can read the beginning of the file them seek to whatever point you wish.
Now let's dig in a bit.
Platforms:
- iOS
- PC
- Mac
- Android
Codecs:
- vp8/9
- h.264
- thora (vp3)
Common Delivery methods for live video in browsers:
- DASH (HTTP)
- HLS (HTTP)
- flash (RTMP)
- flash (HDS)
Common Delivery methods for VOD in browsers:
- DASH (HTTP Streaming)
- HLS (HTTP Streaming)
- flash (RTMP)
- flash (HTTP Streaming)
- MP4 (HTTP pseudo streaming)
- I'm not going to talk about MKV and OOG because I do not know them very well.
html5 video tag:
- MP4
- webm
- ogg
Lets look at which browsers support what formats
Safari:
- HLS (iOS and mac only)
- h.264
- MP4
Firefox
- DASH (via MSE but no h.264)
- h.264 via Flash only!
- VP9
- MP4
- OGG
- Webm
IE
- Flash
- DASH (via MSE IE 11+ only)
- h.264
- MP4
Chrome
- Flash
- DASH (via MSE)
- h.264
- VP9
- MP4
- webm
- ogg
MP4 cannot be used for live video (NOTE: DASH is a superset of MP4, so don't get confused with that). MP4 is broken into two pieces: moov and mdat. mdat contains the raw audio video data. But it is not indexed, so without the moov, it is useless. The moov contains an index of all data in the mdat. But due to its format, it can not be 'flattened' until the timestamps and size of EVERY frame is known. It may be possible to construct an moov that 'fibs' the frame sizes, but is is very wasteful bandwidth wise.
So if you want to deliver everywhere, we need to find the least common denominator. You will see there is no LCD here without resorting to flash example:
- iOS only supports h.264 video. and it only supports HLS for live.
- Firefox does not support h.264 at all, unless you use flash
- Flash does not work in iOS
The closest thing to an LCD is using HLS to get your iOS users, and flash for everyone else. My personal favorite is to encode HLS, then use flash to play HLS for everyone else. You can play HLS in flash via JW player 6, (or write your own HLS to FLV in AS3 like I did)
Soon, the most common way to do this will be HLS on iOS/Mac and DASH via MSE everywhere else (This is what Netflix will be doing soon). But we are still waiting for everyone to upgrade their browsers. You will also likely need a separate DASH/VP9 for Firefox (I know about open264; it sucks. It can't do video in main or high profile. So it is currently useless).
Take a look at JSMPEG project. There is a great idea implemented there — to decode MPEG in the browser using JavaScript. Bytes from encoder (FFMPEG, for example) can be transfered to browser using WebSockets or Flash, for example. If community will catch up, I think, it will be the best HTML5 live video streaming solution for now.
Thanks everyone especially szatmary as this is a complex question and has many layers to it, all which have to be working before you can stream live video. To clarify my original question and HTML5 video use vs flash - my use case has a strong preference for HTML5 because it is generic, easy to implement on the client and the future. Flash is a distant second best so lets stick with HTML5 for this question.
I learnt a lot through this exercise and agree live streaming is much harder than VOD (which works well with HTML5 video). But I did get this to work satisfactorily for my use case and the solution worked out to be very simple, after chasing down more complex options like MSE, flash, elaborate buffering schemes in Node. The problem was that FFMPEG was corrupting the fragmented MP4 and I had to tune the FFMPEG parameters, and the standard node stream pipe redirection over http that I used originally was all that was needed.
In MP4 there is a 'fragmentation' option that breaks the mp4 into much smaller fragments which has its own index and makes the mp4 live streaming option viable. But not possible to seek back into the stream (OK for my use case), and later versions of FFMPEG support fragmentation.
Note timing can be a problem, and with my solution I have a lag of between 2 and 6 seconds caused by a combination of the remuxing (effectively FFMPEG has to receive the live stream, remux it then send it to node for serving over HTTP). Not much can be done about this, however in Chrome the video does try to catch up as much as it can which makes the video a bit jumpy but more current than IE11 (my preferred client).
Rather than explaining how the code works in this post, check out the GIST with comments (the client code isn't included, it is a standard HTML5 video tag with the node http server address). GIST is here: https://gist.github.com/deandob/9240090
I have not been able to find similar examples of this use case, so I hope the above explanation and code helps others, especially as I have learnt so much from this site and still consider myself a beginner!
Although this is the answer to my specific question, I have selected szatmary's answer as the accepted one as it is the most comprehensive.