2011年5月14日土曜日

RtmpClientの使い方その1

liverepeaterでも利用したRed5のRtmpClient実装についてのメモ書き

Red5_phpのリポジトリにも登録してありますが、この実装はBroadcastStreamとIRtmpClientEx、RtmpClientExの3つがセットでできてます。
BroadcastStreamは別のサーバーから流されてきたメディアデータをサーバーにつながっているユーザーに視聴させるためのIBroadcastStreamのインターフェイスに乗っ取ったもの
RtmpClientExはRTMPClientを拡張したクラス、まぁどちらかというとBaseRTMPClientHandlerを元にしてあります。
IRtmpClientExはRtmpClientExで利用するイベントリスナー用のインターフェイスです。

今回はBroadcastStreamについてちょっとメモ書き書いておきます。
このクラスの元ネタはXuggleというJava上でffmpeg(動画変換ソフト)を利用するためのライブラリの一部です。XuggleではJNIというJavaからOS依存コードを呼び出す方法でffmpegのコマンドを実行するみたいなことをやっているようです。
Xuggleの一部にXuggle-Xuggler-Red5というRed5用の拡張が存在し、そこにRed5に流している映像を変換して別の映像にして、視聴できるようにするというものがあります。
[放送者]→[サーバー]→[ffmpeg]→[サーバー]→[視聴者]
こんな流れです。
単に変換するだけでなく、サーバーからffmpegに渡すとき、もしくは、ffmpegからサーバーにデータを戻すときの2カ所でフレームに手を加えることができるので、画面を上限左右反転したり、ピクセルの色をいじって白黒画像に変換したり、特定のものの認識をしたりということができます。
内部でffmpegによる変換を加えることになるので、サーバーにはかなり負荷がかかることになりますが、H.264変換や画像のサイズ変換等も可能です。
最近のffmpegはlibrtmpに対応していて(これもxuggleの開発者の仕事みたいですが)、ffmpegのプロセスに直接rtmpサーバーに接続させたり、rtmpサーバーにデータ流したりできるので、変換そのものはあまり恩恵がないかもしれませんね。
そういう意味では、liverepeaterの処理は別になくてもffmpegで事足ります。(ただしroomを使ったりクライアントの関数実行をしたり等するときちんと動作しなかったりしますから、その点はliverepeaterの実装の方が優れてますね。)

さて、話は戻りますが、このxuggle-xuggler-red5の中で、サーバー内では自分がうけとったメディア情報を別の名前のストリームとして視聴者がみることができるようにする実装があります。これがBroadcastStreamのクラスというわけです。

使い方は以下のとおりです。
1:BroadcastStreamのオブジェクトをつくる。
2:名前やスコープ情報をセットする。
3:ProviderServiceをbeanから取得してそこにBroadcastStreamを登録
4:BroadcastScoepをProviderServiceから取得してBroadcastStreamを登録
あとは、IEventの形のメディアデータをファイルからとったり、別のサーバーからRtmpClientExでもってきたり、自分のサーバーのstreamからとったりしたものをdispatchEventになげてやればOKという形になります。
  BroadcastStream outputStream;
  outputStream = new BroadcastStream(name);
  outputStream.setScope(scope);

  IProviderService service = (IProviderService)getContext().getBean(IProviderService.BEAN_NAME);
  if(service.registerBroadcastStream(scope, name, outputStream)) {
    IBroadcastScope bsScope = (BroadcastScope) service.getLiveProviderInput(scope, name, true);
    bsScope.setAttribute(IBroadcastScope.STREAM_ATTRIBUTE, outputStream);
  }
  else {
    throw new RuntimeException("Failed to make mirroring stream");
  }

このBroadcastStreamは、ほぼ問題ないのですが、Xuggle-xuggler-red5や僕のつくったプログラム上での扱い方が完璧ではないようです。
まず、ストリームをサービスに登録する上記の部分はいいのですが、逆に解除する方法はなにかがたりないようです。
BroadcastStreamの中に独自実装としてterminateGhostConnectionをいれてありますが、そこにのせているとおり登録したBroadcastStreamを解除しなければいけないです。
ただし、誰かがそのストリームにつながっている状態で(放送していなくてもつながっているならやってはいけないです。)登録解除を実行しようとすると内部のメモリー上に、放送中であるというデータが変な風にのこるらしく、Flashプレーヤーで放送を開始しようとしてもBadNameがでるようになり、もう放送できなくなってしまうというバグがあります。

また、start() stop()の関数に独自実装を追加してありますが、statusの放送はじめました、終了しましたというメッセージをクライアントにおくることは可能なんですが、内部動作的にストリームを停止することはできません。
ニュアンスが伝わりにくいと思いますが、普通につながっているサーバーで放送を停止するとサーバーが停止したことを理解して、あたらしく別のクライアントが接続してplayを開始してもパケットデータがいっさい送られてきません。
ところが、BroadcastStreamをベースにしているチャンネルの場合playを開始すると最後に流れたパケットデータが送られてきてしまい、静止画が表示されてしまいます。
一応mLivePipeのunsubscribeを送ると止めることができるのですが、今後は再開することができないという・・・
まぁ、バグではありますが、あるからといって運用に問題がでるところではないですけど。

このBroadcastStreamの元ネタはたぶんRed5のClientBroadcastStreamだと思います。
ClientBroadcastStreamはFlashからRed5に放送をしたときに内部で利用しているクラスでRed5チームによりメンテナンスされてるようです。こっちはしっかり動作しますが、publishするクライアントとの癒着がひどくて、そのまま切り離しで一部機能を使うということはできないようでした。

 BroadcastStreamに関してはこんなところですね。

追記:
そうそう大切なこと書き忘れていました。
BroadcastStreamのdispatchEvent関数のRTMPMessageをつくる部分ですが、Red5 1.0.0の開発中にコンストラクタがprivate化されて、かわりにbuildメソッドが追加されています。よって、古いバージョンのRed5 1.0.0を使う場合は一部変更して、再コンパイルする必要がありますので、ご注意ください。

0 件のコメント:

コメントを投稿