May 5, 2013

How to stream video to an iPhone

Sometimes you just want to watch a movie and you don’t want to load it into iTunes. Or maybe you have a low-capacity iPhone and you want to set up a video-on-demand service in your home. Or maybe… who cares? It’s not that painful to do with HTTP Live Streaming (HLS).

HLS is an Apple-proprietary protocol. The specification has been published, but a rival specification known as MPEG-DASH was being worked on at the same time by an industry consortium. MPEG-DASH has since been adopted as an ISO standard, but there doesn’t seem to be any good implementations out there in the wild. At this time, iOS does not support MPEG-DASH and it may never do so. In theory, MPEG-DASH can be used to produce HLS and vice-versa.

This procedure produces only a simple HLS movie suitable for streaming over a LAN. It does not provide for bandwidth degradation or other advanced features of HLS that require more sophisticated assembly. To do so requires Apple tools that are not generally available.

You’ll need a few things:

x264 and libav are available as part of most Linux distros, but they are usually configured for x264 to use libav rather than the other way around. Live streaming may not be supported in this configuration. If avconv -formats returns a line that reads:

E hls             Apple HTTP Live Streaming

then avconv can convert H.264/AAC videos to HLS without recompiling. If avconv -codecs returns lines that read:

DEV.LS h264       H.264 / AVC / MPEG-4 AVC …
DEA.L. aac        AAC (Advanced Audio Coding)

then avconv can convert any supported video to iPhone-friendly HLS. (The ‘D’ says that avconv can decode H.264, the ‘E’ says that it can encode it, ‘V’ is for a video codec, ‘A’ is for an audio codec, and ‘L’ and ‘S’ indicate lossy and lossless support respectively. Since we only plan on creating H.264/AAC videos, the ‘E’ is the most important column.)

Install x264

  1. Download x264. You have two choices:

    1. Get a nightly build:​pub/​videolan/​x264/​snapshots/​last_stable_x264.tar.bz2
    2. Use git: git clone git://
  2. Configure, compile, and install

Install libav

  1. Download libav. Again, two choices:

    1. Get the nightly build:​releases/​libav-snapshot.tar.bz2
    2. Use git: git clone git:// libav
  2. Configure, compile and install

Convert a movie

Here’s where you have to make a decision. HLS works by slicing up a movie into small chunks and creating an index file for those chunks stating which chunk corresponds to what offset in the overall movie. Obviously, smaller chunks mean more flexible playback at the cost of a few thousand files. I’ve found that 10 second chunks work pretty well — a typical 90-minute movie produces about six hundred files.

Copy the movie into an empty directory that is published by your web server. This is important because HLS conversion produces a lot of files.

Depending on the type of the movie, you may or may not need to do some codec conversions. iOS only does H.264 video with AAC audio; fortunately, most video you encounter on the web (including newer Flash videos!) will be using these codecs. You can find out what type of video you have with the avprobe command: avprobe movie.ext.

If the video stream is ‘h264’ and the audio stream is ‘aac’ then you only have to change the file format:

avconv -i movie.ext -codec copy -hls_time 10 -hls_list_size 32767 [-bsf h264_mp4toannexb] movie.m3u8

The -codec option tells avconv to copy the audio and video stream directly, with no conversion. -hls_time tells avconv how long each chunk should be (approximately — the actual length can be ±80% of this value); this defaults to 2 seconds if unspecified which comes to 3000 files for a typical movie. -hls_list_size tells avconv how many of the chunks need to go into the index. The last one is very important: HLS was designed for live video, where only a few segments would actually be kept and the stale files deleted. By default avconv will only put the last five chunks in the index, which means you would only see the last minute of the movie without this option. You may also need the -bsf option to do a little preprocessing of the stream to look for time markers in incorrectly coded videos — if it is needed, avconv will tell you.

Now, if the video stream is not ‘h264’ or the audio is not ‘aac’, then some conversion is in order. Converting video is only possible if avconv supports H.264 encoding.

avconv -i movie.ext -strict experimental -vcodec h264 -acodec aac -hls_time 10 -hls_list_size 32767 movie.m3u8

If the video stream is already ‘h264’, you can replace the video codec with ‘copy’. Same goes for the audio codec and ‘aac’. (It’s rare that someone uses one and not the other, but it does happen. The less translation we do, the less degeneration of quality will be seen.)

avconv can also be used to create uploadable movies. Simply remove all of the ‘hls’ options and change the output suffix to ‘m4v’. These movies can now be imported into iTunes and uploaded to the phone for offline viewing.

Watch the movie

The nice thing about avconv’s implementation of HLS is that you can start watching while the movie is converting. Simply navigate using your iPhone to the movie.m3u8 file and the iPhone browser will start the video streaming app automatically. It will jump to the current point of the conversion, but you can easily go back to the beginning of the video to start watching. Granted, if you are running avconv on a machine where the conversion process is slow then the playback will stall when it reaches the last segment.

Make an archive

Quick-n-dirty Perl script to make a web page of your movies:

print qq[\n];
print qq[\n];
print qq[\n\n];

find sub {
  if (/\.m3u8\z/) {
    my $file = $File::Find::name;
    print qq[


\n]; $file =~ s/([^\w\/])/sprintf '%%%02X', ord $1/ge; print qq[

\n]; print qq[
\n]; } }, <*>; print qq[\n\n];]]>

Applications that support HLS