Nginxの HTTP Pseudo-Streaming (HTTPによる疑似ストリーミング)機能を使ってMP4(H.264/AAC)ファイルのストリーミング配信を試してみました。
* 環境
CentOS 5.8 (x86_64), Ubuntu 11.10 (x86_64)
Nginx 1.3.1
gcc 4.4.5
* HttpMp4Moduleモジュールの組み込み
Note that this module is only supported in nginx version 1.1.3 and higher in development branch and 1.0.7 in stable branch.
ということなのでNginxのバージョンには注意しておきます。HttpMp4Moduleを組み込むにはconfigureオプションを追加してコンパイルする必要があります。
$ ./configure --with-http_mp4_module $ make $ sudo make install $ /usr/local/nginx/sbin/nginx -V ## 確認 nginx version: nginx/1.3.1 built by gcc 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5.1) configure arguments: --with-http_mp4_module
* 設定ファイル
## /video 以下にアクセスされたらMP4 Pseudo-Streaming機能を有効にする location /video { mp4; mp4_buffer_size 1m; ## バッファサイズ(デフォルト512KB) mp4_max_buffer_size 5m; ## 最大バッファサイズ(デフォルト10MB) }
拡張子でマッチさせる設定を入れなくても .mp4/.m4v/.m4a は判別してくれるようです。atomを解析するためのバッファが足りなくなった場合はHTTP 500エラーが返るので適宜調整のこと。
* 利用方法
HTTPでMP4ファイルのあるパスにアクセスするだけです。その際にクエリパラメータ(start)として再生開始位置(秒)を指定します。動画の尺を超える値を入れるとHTTP 500エラーが返ります。
## /video/sample.mp4 ファイルの123秒目から HTTP Pseudo-Streaming 配信開始 http://example.com/video/sample.mp4?start=123
試しに小さなMP4ファイル(H.264/AAC, 90秒尺, 6.46MB)にFlash PlayerからHTTPアクセスしてみました。ストリーミングできているかをNginx側のログを見て確認します(以下のログはNginxのデフォルトフォーマットのログをIPなど一部情報を省いて載せています)。
"GET /video/sample.mp4 HTTP/1.1" 200 6781629 "-" "Shockwave Flash" "-" "GET /video/sample.mp4?start=45 HTTP/1.1" 200 3504815 "-" "Shockwave Flash" "-" "GET /video/sample.mp4?start=80 HTTP/1.1" 200 510770 "-" "Shockwave Flash" "-" "GET /video/sample.mp4?start=100 HTTP/1.1" 500 186 "-" "Shockwave Flash" "-"
送信バイトの値が意図した通りに変化しています。HTTPなので複数回リクエストしてみてキャッシュされているか確認してみてください。ちなみにどうでもいいんですけど、Flashはユーザーエージェントにプレイヤーのバージョン番号は入れないんですかね。今時珍しくないです? あと、NetConnectionとかNetStreamとかあの辺のAPIが雑に散らかってるのでそろそろ何とかしてあげればいいのにと思います。
* モジュールの実装 (ngx_http_mp4_module.c)
実装を読んでみると、Nginxモジュール開発のお作法以外の部分を除けば、後はよく見る形のMP4解析処理のプログラムなので、コンテナ構造に興味ある人は読んでみると勉強になるかと思います。各atomデータを読み書きしている部分に ngx_log_error 関数を仕込んで動作させてみるとさらに構造が把握しやすいです。
## 例) ## mvhd atom解析処理部分でログ出力する ## durationの値が書き換えられていることが確認できる 6107#0: *1 mvhd timescale:600, duration:54042, time:90.070s, request: "GET /video/sample.mp4?start=50 HTTP/1.1", 6107#0: *1 mvhd new duration:24042, time:40.070s, request: "GET /video/sample.mp4?start=50 HTTP/1.1",
外部からだと任意の開始位置からのストリーミング配信指示しかできないようになっていますが、内部的に提供されている機能の粒度から考えると、後でいろいろ拡張しやすいように作られているように見えます。バイナリの読み書きには ngx_mp4_(get|set)_(32|64)value マクロが用意されているのでこれを使えばOKです。また、atomデータは以下のようにひとつずつ構造体として定義されているので、MP4フォーマット仕様書を手元に置かなくてもコードを読めば大体把握できる感じですね。ストリーミング配信に必要なatomデータ(構造体)しか定義されていないので、拡張時にもし必要であれば適宜追加することになります。
[cpp]
## 例) mvhd, tkhd atomデータを扱う構造体
## mvhd atom spec
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[4];
u_char modification_time[4];
u_char timescale[4];
u_char duration[4];
u_char rate[4];
u_char volume[2];
u_char reserved[10];
u_char matrix[36];
u_char preview_time[4];
u_char preview_duration[4];
u_char poster_time[4];
u_char selection_time[4];
u_char selection_duration[4];
u_char current_time[4];
u_char next_track_id[4];
} ngx_mp4_mvhd_atom_t;
## tkhd atom spec
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[4];
u_char modification_time[4];
u_char track_id[4];
u_char reserved1[4];
u_char duration[4];
u_char reserved2[8];
u_char layer[2];
u_char group[2];
u_char volume[2];
u_char reverved3[2];
u_char matrix[36];
u_char width[4];
u_char heigth[4];
} ngx_mp4_tkhd_atom_t;
[/cpp]
今回は動作確認程度でしたがNginxのHTTP Pseudo-Streaming機能を試してみました。この機能自体はApacheやLighttpdなど他のHTTPサーバでも追加できるのであまり驚きはないですが、Nginxでの配信手順も覚えておくといいかもしれません。また、個人wikiにNginxの項目を追加しました。まだほとんど書いてませんがこれから少しずつメモしていこうと思います。
* Nginx – Tech Note
余談
HTTPを利用した映像配信の現状なんですが、去年リリースされた最新版の Flash Media Server から HTTP Live Streaming (HLS) をサポートするようになりました。Adobeは HTTP Dynamic Streaming (HDS) という別のHTTPストリーミング技術を持っているにもかかわらず、Appleの開発したHLSをサポートしたことで「ついにAdobeがAppleに屈した!」という感じの煽り記事がたくさん飛び、一般のiPhone/iPadユーザーがよく分からずに勝ち誇り、一部のWeb制作者がなぜか顔を真っ赤にしていたりとなかなか愉快な騒ぎになったことが記憶に新しいです。
ビジネスとして映像配信サービスを作る場合、実際はマルチビットレート対応(帯域制御)やGeo Filtering(地域限定配信)などが必要ですし、配信部分の問題をクリアしても今度はコンテンツ保護がぁぁという話になります。HTTPSで暗号化する程度でアメリカの配給会社さんが納得するわけないやろ!ということで高価なDRM製品を結局買うことになったりするわけです。大勢の大人が難しい長話をしている間は僕のような末端の技術者は正直ヒマなので、こうやってNginxをいじってみたりしながら仕事するフリして過ごすんですね。