Streaming MP4 with Nginx

Annika Backstrom
in misc, on 2 September 2013. It is tagged #video and #term.

Ages ago, I set up a mini video server in Nginx using JW Player, testing out its pseudo-streaming support. Revisiting this, HTML5 video seems to have picked up the slack and seeking in large videos is now possible without depending on Flash. Even with client improvements, the video files themselves can be the limiting factor and a poorly-structured file will prevent streaming.

For my later reference, here's a guide to preparing MPEG-4 video for web streaming.

"Fast Start" and the moov Atom

To stream web video most effectively, the MPEG-4 "moov" atom must be located at the beginning of the file. This allows for seeking through the movie before the whole file is downloaded, requiring less processing and data transfer to the client. This method works for browsers that can play MPEG-4 natively (like Google Chrome) and the Nginx ngx_http_mp4_module pseudo-streaming module that works in conjunction with Flash players.

AtomicParsley can inspect the movie container and tell you where the moov atom is located inside the file.

sudo apt-get install atomicparsley

Use "test" mode (-T) to output the atom structure:

AtomicParsley input.mp4 -T | less

"Atom moov" should be located at the top, just after "Atom ftyp." If it comes after "Atom mdat", your moov atom is after the raw audio/video data, and your video is not optimized for streaming.

To relocate the moov atom, use ffmpeg or avconv to build a new .mp4 file without reencoding. Recent Ubuntu seems to have replaced ffmpeg with avconv, so here's how I did it:

avconv -i input.mp4 -movflags faststart -c:v copy -c:a copy output.mp4

This should work for ffmepg:

ffmpeg -i input.mp4 -movflags faststart -vcodec copy -acodec copy output.mp4

Other notes

You may need to reencode your video as h.264 in order to stream. (I don't have a link for this one, but my test video would not stream until I reencoded from XVID "Advanced Simple Profile" to h.264.) Use avprobe or ffprobe to figure out which codecs are in play. To reencode, first make sure you have the appropriate codecs installed:

sudo apt-get install libavcodec-extra-53

This combination of flags worked for me:

avconv -i input.mp4 -c:v libx264 -c:a libvo_aacenc -movflags faststart output.mp4