サーバ サイド ライブ ストリーミング アプリ
server-side live streaming application を試作。ソース一式(newvieLive-20091025.lzh)
VOD と Live は似てますが、相違点が多々あります。作成に当たっての最大の違いは、VOD は Red5 に埋め込まれてますが、 Live はサーバサイドアプリのコード書きが必要なことです。それでも、VOD 用のストリーミング環境が Live にも使えるので、かなり楽にライブストリーミングが書けます。
Red5 が起動すると、サーバサイドアプリが起動し(appStart)、動画ファイルをライブストリーミング方式で配信します(垂れ流し状態で、繰り返し配信)。Flex4(AIR1.5)製クライアントアプリが3個、接続して動画を再生します。クライアントアプリを終了・再起動を行っても、同じタイミングで動画が再生されます。
参考にしたのは、
- oflaDemo の Application.java
- 移行ガイド
- Red5 API リファレンス org.red5.server.adapter.MultiThreadedApplicationAdapter
環境は、これまでと同一なので省略(例えばここ参照)。
VOD vs. Live (revise)
以前も書きましたが、ここでは視聴者(クライアントサイドアプリ)へのサービスの違いでまとめ直します。どのようなサービス方式にするかによってコードの書き方が変わることになります。今回作成したアプリは、次の 2.TV型・録画済み動画のライブストリーミングになります。
1. VOD型サービス
視聴者が接続してくると、「冒頭から動画を再生」して提供します。別の視聴者が接続して来ると、やはり「冒頭から動画を再生」して提供します。視聴者は常に冒頭から動画を見ることができます。この為、VODサービスでは、録画済み(recorded video)の動画が使われます。サーバサイドの動画ストリーム(serverStream)は、視聴者毎に必要になります。
2. TV型サービス
ライブストリーミングには違いないですが、TV と同様のサービスです。視聴者には「再生途中の動画」が提供されます。サーバ側では、サービス期間中、常に動画が再生されており、視聴者は各々が接続したタイミングに応じて、動画の途中から視聴をすることになります。動画は録画済みでも、生中継でも構いません。視聴者がいなくても動画は再生を続けているので、サーバは無駄なサービスを提供していますが、生中継の場合は止むを得ません(視聴率が低いTV番組と同じです)。ただ、serverStream はひとつで賄えます。
3. ハイブリッド(VOD+TV)型サービス
最初の視聴者が接続して来たタイミングで、動画の再生が始まります。先客がいる場合には「再生途中の動画」を見ることになります。接続している視聴者がゼロになったら再生を終了し、次の接続を待ちます。このサービスのメリットは動画を常に再生しなくて良いので、サーバの負担や無駄が減るということです。しかも、serverStream はひとつで済みます。この方式では、録画済みの動画が使われます。
Red5 の oflaDemo は VOD 型です。VOD 型も大きく分類すれば、クライアント毎のライブストリーミングになると考えられます。違いは、Flex クライアントでの接続時には live="false" が指定されることです。このフラグに依存して、Red5の内部組み込みのストリーミングをするか、サーバサイドアプリ(開発者)に任せるかの判断をしているように思えます(完全に想像です。実験も調査もしていません)。
さて、上記の真偽の程は計りかねます。ただ、プログラムは期待通りに動いていますので、それ程、間違っているとは思いません。
接続のタイミング
移行ガイドに接続のタイミングについてまとめた記述があります。この記事に関係するものだけをまとめます。
boolean appStart( IScope app) | 起動時に一度だけ呼ばれる。 サービス全体の初期化。 |
boolean appConnect( IConnection conn, Object[] params ) | クライアント接続毎。 最初のクライアントに対する特別な処理もここで。 |
void appDisconnect( IConnection conn ) | クライアント切断毎。 最後のクライアントに対する特別な処理もここで。 |
void appStop( IScope app ) | 終了時に一度だけ呼ばれる。 サービス全体の後始末。 |
サーバサイド
サーバサイドアプリ
(後付でコメント入れました。)
package org.eggtoothcroc; import java.io.*; // System.out.println などを使うため import org.slf4j.Logger; // 汎用ロギングライブラリ import org.red5.logging.Red5LoggerFactory; // Red5用ロギングライブラリ import org.red5.server.adapter.ApplicationAdapter; // デフォルトのサーバサイドアプリ import org.red5.server.api.IScope; // Red5 が渡してくれるアプリの識別子のようなもの import org.red5.server.api.stream.IServerStream; // 動画のストリーム import org.red5.server.api.stream.support.StreamUtils; // ストリームの作成、取得を代行してくれる import org.red5.server.api.stream.support.SimplePlayItem; // ストリームに渡す動画の識別子のようなもの public class Application extends ApplicationAdapter { private static Logger log =Red5LoggerFactory.getLogger(Application.class, "newbieLive"); private IServerStream serverStream; // Startで作成し、Stopで破棄する @Override public boolean appStart( IScope app ) { boolean b =super.appStart(app); System.out.println("newvieLive appStart"); log.info( "newvieLive appStart" ); // "MaxFactor-live"というパブリッシュ名でストリームを作成 serverStream =StreamUtils.createServerStream( app, "MaxFactor-live" ); SimplePlayItem item = new SimplePlayItem(); item.setName("Max Factor (web limited edition)"); // streamsフォルダの動画名から.flvを除いたもの serverStream.addItem(item); serverStream.start(); serverStream.setRepeat(true); // 繰り返しを指定する(start後でないといけない?) return b; } @Override public void appStop( IScope app ) { System.out.println("newvieLive appStop"); log.info("newvieLive appStop"); if( serverStream!=null ){ // 絶対に null でないはずだけど・・・ serverStream.stop(); // ストリームを止めて、 serverStream.close(); // 破棄する } super.appStop( app ); } }
クライアントサイド
SuonoDolce.mxml
これまでと違うのは、live="true"フラグが付き、動画ファイル名ではなく、サーバサイドアプリで指定したパブリッシュ名「MaxFactor-live」を使用していることです。ここに至り、Adobe の広告が謳っているように、動画ファイルの名称を隠したことになる訳です。
<?xml version="1.0" encoding="utf-8"?> <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo"> <s:VideoPlayer autoPlay="true"> <s:StreamingVideoSource serverURI="rtmp://localhost/newbieLive" live="true"> <s:StreamItem streamName="MaxFactor-live"/> </s:StreamingVideoSource> </s:VideoPlayer> </s:WindowedApplication>
SuonoDolce.xml、SuonoDolce-Another.xml、SuonoDolce-OneMore.xml
ライブストリーミングの確認の為に、複数のクライアントアプリの起動を行います。その為に、3つのアプリ定義(.xml)を行いました(実行本体は同じもの.mxml)。いずれも、adl.exe で実行します。また、air化して実行もできます。各々の違いの本質は、アプリIDとアプリ名の相違です。それぞれの起動タイミングが違ったとしても、再生される動画のタイミングが同じことが確認できれば成功です。実際のソースコードは「ソース一式」に格納されてますので、ここでは日本語でコメントした疑似コードとします。
<?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://ns.adobe.com/air/application/1.5"> <id>egc.SuonoDolce.Another</id> ・・・これがアプリケーションID。実行時の名前。 <version>0.9</version> <filename>SuonoDolce-Another</filename> ・・・ これがインストール時のアプリケーション名 <description> 「c:/Program Files/SuonoDolce-Another/SuonoDolce-Another.exe」となります。 SuonoDolce Another </description> <copyright>@copyleft 2009 eggtoothcroc. All wrongs reserved.</copyright> <initialWindow> <content>SuonoDolce.swf</content> <systemChrome>standard</systemChrome> <transparent>false</transparent> <visible>true</visible> </initialWindow> <icon> <image16x16>icons/SuonoDolce16.png</image16x16> <image32x32>icons/SuonoDolce32.png</image32x32> <image48x48>icons/SuonoDolce48.png</image48x48> <image128x128>icons/SuonoDolce128.png</image128x128> </icon> </application>
ビルド・実行
ビルドは以前と同じなので省略。ただし、ant build.xml の daemon 起動は止めました(記事(2009.10.24)参照)。また、air をインストールしてみるのも一興ですが、わざわざアンインストールしなければならないので、adl.exe で実行する方をおすすめします。
実行手順
1. Red5.bat (あるいは、サービスの再開)で Red5 を起動させます。
2. client フォルダにて、
$ adl.exe SuonoDolce.xml & $ adl.exe SuonoDolce-Another.xml & $ adl.exe SuonoDolce-OneMore.xml &
などとしてください。動画が同じタイミングで再生されるはずです。クライアントアプリを終了・再起動させることで確認できます。
ライブストリーミングが出来たのだと思います。少なくとも、クライアントサイドのコード上では、インターネットラジオ Suono Dolche へのアクセスと同じになりました。これで、Suono Dolce にご迷惑をかけることなく、実験が出来るという訳です。あとは、CurrentPlayList 取得などの HTTPプロトコル 周りを実装すれば、自分専用のSuono Dolce Player が作れるはず・・・。