Raspberry Pi 2 が Azure Blob に保存した画像を Motion JPEG として配信してみたくなったので、まずはローカルに保存している画像を Motion JPEG として返す Web API を作って試してみました。
H.264 で圧縮しても良い気がしますが、エンコードしながら HTTP で出力する難易度がとても高そうだったので、今回はとても簡単な Motion JPEG です。
Web API に用意されている PushStreamContent を使うと、サーバーからのプッシュが簡単に実装できるので、これを利用して JPEG データを定期的に送信することで動画っぽく見せます。
Recent ASP.NET Web API Updates – April 24 - Henrik's Blog - Site Home - MSDN Blogs
中の人がブログで簡単な使い方を紹介してくれています。
要するに PushStreamContent を Content として返すと、レスポンスのストリームを明示的に閉じるか、HTTP 自体が閉じられるまで自由に書き込みできるという話です。動画をバッファリングして配信したり、Server-Sent Events の実装もこれを使えば簡単でしょう。
ちなみに Motion JPEG として配信するための HTTP レスポンスについては、以下の Wiki に書いてあったので参考に実装しました。
まずは、ローカルに保存してある画像を出力するだけの 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 を読み込む元を変更するぐらいで対応出来ると思います。