Raspberry Pi 2 が Azure Blob に保存した画像を Motion JPEG として配信してみたくなったので、まずはローカルに保存している画像を Motion JPEG として返す Web API を作って試してみました。
H.264 で圧縮しても良い気がしますが、エンコードしながら HTTP で出力する難易度がとても高そうだったので、今回はとても簡単な Motion JPEG です。
Web API に用意されている PushStreamContent を使うと、サーバーからのプッシュが簡単に実装できるので、これを利用して JPEG データを定期的に送信することで動画っぽく見せます。
http://blogs.msdn.com/b/henrikn/archive/2012/04/23/using-cookies-with-asp-net-web-api.aspx
中の人がブログで簡単な使い方を紹介してくれています。
要するに PushStreamContent を Content として返すと、レスポンスのストリームを明示的に閉じるか、HTTP 自体が閉じられるまで自由に書き込みできるという話です。動画をバッファリングして配信したり、Server-Sent Events の実装もこれを使えば簡単でしょう。
ちなみに Motion JPEG として配信するための HTTP レスポンスについては、以下の Wiki に書いてあったので参考に実装しました。
[Wiki] Motion JPEG | en.code-bude.net
まずは、ローカルに保存してある画像を出力するだけの API を作ってみました。
public class StreamController : ApiController { private readonly byte[] _newLine = Encoding.UTF8.GetBytes("\r\n"); private readonly string _boundary = "0123456789"; public HttpResponseMessage Get() { var response = Request.CreateResponse(); response.Content = new PushStreamContent(async (stream, content, context) => { foreach (var file in Directory.GetFiles(@"C:\Users\shibayan\Documents\snapshot", "*.jpg")) { var fileInfo = new FileInfo(file); // ヘッダー書き込み var header = string.Format("--{0}\r\nContent-Type: image/jpeg\r\nContent-Length: {1}\r\n\r\n", _boundary, fileInfo.Length); var headerData = Encoding.UTF8.GetBytes(header); await stream.WriteAsync(headerData, 0, headerData.Length); // JPEG データ書き込み await fileInfo.OpenRead().CopyToAsync(stream); // 最後の改行書き込み await stream.WriteAsync(_newLine, 0, _newLine.Length); // 30fps で頑張ってみるつもり await Task.Delay(1000 / 30); } }); // PushStreamContent の mediaType に指定すると検証エラーになったのでここで追加 response.Content.Headers.TryAddWithoutValidation("Content-Type", "multipart/x-mixed-replace;boundary=" + _boundary); return response; } }
いつも通り手抜きな感じですが、とりあえずこれで動作を確認してみます。
残念と言うか当然という感じではありますが、世間的には multipart/x-mixed-replace は全く流行らなかったらしく、ブラウザとしては Firefox しか対応していないようです。
問題なく再生出来ました。正確には素早く JPEG を差し替えているだけですが。
他には VLC Media Player を使って、ネットワークストリームとして開くことで再生出来ました。
本来の目的である、Azure Blob に保存している画像を Motion JPEG として配信することに関しては、JPEG を読み込む元を変更するぐらいで対応出来ると思います。