<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" 
   xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" 
   xmlns:html="http://www.w3.org/1999/html" 
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
   xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
<channel>
   <title>糸柳和法のオッパイぷるるん☆放送局</title>
   <link>http://blog.itoyanagi.name</link>
   <description>なんだか、はずかしいな…☆ミ</description>
   <language>ja</language>
   <copyright>Copyright 2007 ITOYANAGI Kazunori</copyright>
   <ttl>60</ttl>
   <pubDate>Tue, 18 Mar 2008 06:14 GMT</pubDate>
   <managingEditor>kazunori@itoyanagi.name</managingEditor>
   <generator>PyBlosxom http://pyblosxom.sourceforge.net/ 1.4.2 8/16/2007</generator>
<item>
   <title>RTMP によるライブストリーミングソフトウェア</title>
   <guid isPermaLink="false">entries/d20080318</guid>
   <link>http://blog.itoyanagi.name/entries/d20080318</link>
   <description><![CDATA[

<p>株式会社ドワンゴの糸柳です。業務に関わるエントリでは所属を名乗るよう言われているのでこう書きましたが、もちろん本職はグッドウィルで日雇いの肉体労働をやっていて、登録している営業局が営業停止命令を受けたため生活に困窮しています。100 万円ほどください。と言いたいのですが先日実際にもらったので本題に入ります。</p>

<p>RTMP によるライブストリーミングでは、Flash Player が対応しているコーデックでエンコードしたビデオ、及びオーディオのストリームを RTMP に乗せる必要がある。</p>

<p>現在最新の Flash Player が対応しているオーディオの圧縮形式は以下の通りである。</p>

<ul>
<li>MP3</li>
<li>PCM</li>
<li>ADPCM</li>
<li>AAC</li>
<li>HE-AAC</li>
</ul>

<p>ライブストリーミングを実現するときに技術的面からネックになるのは、配信側及び受信側の CPU 使用率と、帯域の占有である。そのほとんどはビデオストリームによるものであり、オーディオストリームはストリーム全体からすると比較的消費するリソースの割合がかなり小さいために致命的な問題になることが少ない。仮に圧縮形式が目的に不向きで音質に問題があったとしても、多少の CPU 使用率や帯域の増加を犠牲すればよいため改善しやすい。よって、この記事では RTMP ライブストリーミングにおけるオーディオについてこれ以上深く扱わない。</p>

<p>対してビデオの圧縮形式にはかなり切実な問題があり、形式そのものの特性や、様々なライセンスが複雑に絡んでいる。今回はこの点を掘り下げて扱う。</p>

<h4>RTMP でライブストリーミングに使用することのできるビデオの圧縮形式</h4>

<p>現在最新の Flash Player が対応しているビデオの圧縮形式は以下の 3 つのみである。</p>

<ul>
<li>Sorenson Spark</li>
<li>On2 VP6</li>
<li>H.264</li>
</ul>

<p>これらはそれぞれに強い個性がある。世界の中でもとりわけブロードバンドが普及している日本においても数百人から数千人以上といった規模のライブストリーミングを軽々扱えるほどの帯域を用意するのは難しい。用途と帯域に相談して適切なビデオの圧縮形式を選択する必要がある。</p>

<h5>Sorenson Spark</h5>

<p>別名を Sorenson H.263 という。国際電気通信連合電気通信標準化部門（以下、ITU-T）によって規格化された H.263 のサブセットで、<a href="http://www.sorensonmedia.com/">Sorenson Media</a> という企業によって作られた。元の H.263 と大きな差はないようだ。Sorenson Media によって実装された商品としては Sorenson Spark Professional があり、2002 年 3 月 14 日の<a href="http://www.sorensonmedia.com/news/read_full.php?pageID=18&amp;paraID=228">Sorenson Media Announces Sorenson Squeeze for Macromedia Flash MX</a> というプレスリリースにおいて Flash Player 6 に内蔵されるという旨が記載されている。</p>

<p>そもそも H.263 は低解像度や低帯域の環境に向いた圧縮形式でテレビ電話などに活用されていたのだが、最近では YouTube が Flash Video のビデオ圧縮形式に Sorenson Spark を使うようになったことで PC ユーザーにも知られるようになった。テレビ電話での解像度は CIF（352x288）や QCIF（176x144）などが使われているのだが、YouTube では QVGA（320x240）を使用している。</p>

<p>Flash Player にエンコーダーが内蔵された唯一の圧縮形式であり、モダンブラウザを使って手軽にライブストリーミングを実現することができるという最大の利点がある。但し、Ustream.tv などを使用している人はわかると思うが、常に誰もが満足できるような画質とは程遠いと言わざるを得ない。かなり高い画質で圧縮をかけない限りは細かな文字などを読ませるなどといった用途に使用することが難しいため、画質を求める場合には別の形式を使用する必要がある。</p>

<h5><a href="http://www.on2.com/index.php?358">On2 VP6 (On2 Technologies)</a></h5>

<p><a href="http://www.on2.com/">On2 Technologies</a> が開発したビデオ圧縮形式で、Sorenson Spark に比べると同じビットレートでかなりの画質を実現することができる。<a href="http://www.on2.com/index.php?358">On2 Technologies 公式サイトにおける VP6 のページ</a>では Flash Player 8 及び 9 で使用できるとある。</p>

<p>あまりの高ビットレートになると重くなるようだが、近年の PC であれば特に問題なく再生できる。古い環境の例として、Pentieum III 600MHz で 512x384 600kbps の動画を再生するとかなりのコマ落ちが発生した。これは現時点でニコニコ動画の無料ユーザーがアップロードすることのできる最大のビットレートだが、実際にライブストリーミングで使う場合には 1pass でリアルタイム圧縮をかける必要があるため画質はこれよりかなり落ちる。それでも Sorenson Spark よりはかなり改善されるため、環境が許すならば VP6 を利用するのが現時点で最も良い選択であると言える。</p>

<p>但し、Flash Player では再生することしかできず、エンコーダーは内蔵されていないため配信のためには別にソフトウェアを用意する必要がある。</p>

<h5>H.264</h5>

<p>ITU-T によって規格化されたビデオ圧縮形式である。ワンセグでの動画配信に利用されており、意識はされないがかなり身近な存在であると言える。QuickTime Player ではデフォルトで再生が可能になり、Blu-ray Disc と HD DVD ではサポートするコーデックのひとつとして採用されるなど利用できる場面は増えてきている。</p>

<p>Adobe Labs の <a href="http://labs.adobe.com/wiki/index.php/Flash_Player:9:Update:H.264">Flash Player:9:Update:H.264</a> というページにおいて Flash Player 9 Update 3（v 9.0.115.0）から H.264 がサポートされる旨が記載され、「<q>the next version of Flash Media Server will support streaming of H.264 content and HE-AAC content - both live and on demand.</q>」とライブストリーミングにおいても H.264 が使用できることが明らかになった。</p>

<p>地上デジタル放送や DVD-Video で採用されている MPEG-2 ビデオの 2 倍以上の圧縮効率を誇るとされている。しかし複雑な仕様によりエンコード・デコード処理に高い負荷がかかり、VP6 に比べかなり重くなっている。不特定多数のユーザーに対して高画質配信を実現しようとする場合には、使用する画面解像度、ビットレートなどにおいて低スペックマシンを使うユーザーに対する考慮をしておいた方がいいだろう。</p>

<h4>RTMP ライブストリーミングのパブリッシャー</h4>

<p>RTMP では、プロトコル内部でのストリーム送出命令が publish というものであるためか、ストリームを送出するソフトウェアのことをパブリッシャーと呼ぶことが多い。ブロードキャスターと呼ぶこともある。この項では、現在 Flash Player が再生することのできる圧縮形式のビデオストリームを RTMP で送出することが可能なパブリッシャーを紹介する。</p>

<h5>Flash Player 内蔵</h5>

<p>前述の通り、Flash Player に内蔵された機能では Sorenson Spark にしか対応することができず、あまり高い画質は期待できない。しかし Ustream.tv や <a href="http://www.orgoo.com/chat/">Orgoo Video Chat</a> などのような一般ユーザーが何らかの配信を行うことが前提となったサービスにおいては配信するためのハードルを可能な限り低くする必要があるため、ブラウザだけで配信環境が完結するというのはかなりの利点である。</p>

<p>Sorenson Spark のデコードはかなり軽いため、視聴側における問題も少ない。</p>

<p>また Sorenson Spark が Flash Player 6 から実装されているというのも多少のアドバンテージになる。例えば管理者権限を与えられていないために On2 TrueMotion VP6 が実装された Flash Player 8 よりも前のものしか使えないままになっているユーザーというのはいまだにある程度残っているため、そういったユーザーも視聴することが可能になる。Wii に搭載された標準の Flash Player は 7 相当なので、このような組み込み用途で機能の制限された Flash Player の一部からも視聴することが可能になる。</p>

<p>また、RTMP との通信部分が ActionScript によってプログラマブルになっているため、RTMP やよりセキュアな RTMPS（Real Time Messaging Protocol over SSL）による認証などを実現することが可能になっている。</p>

<h5><a href="http://www.adobe.com/products/flashmediaserver/flashmediaencoder/">Flash Media Encoder 2</a></h5>

<p>Adobe 公式のパブリッシャーであり、On2 TrueMotion VP6 による配信が可能になっている。残念ながら少々不安定であり、配信の開始や停止の時点で数秒から十数秒のフリーズが発生し、何度も配信と停止を繰り返すようなことをすると完全に復帰しなくなることがある。但し一度配信が始まれば動作は安定しており、長時間の連続配信なら問題は無い。ほぼ間違いなく最も運用実績のあるパブリッシャーである。</p>

<p>おまけ程度の機能として、数値を手入力することでビデオリソースの一部分だけを切り取って配信することが可能である。これは他のソフトウェアを使えば簡単に解決する問題なので、複雑な項目の設定が必要なこのソフトウェアを使うようなユーザーにとってはあまり大きな利点ではない。</p>

<p>Flash Media Encoder 2 において最も注意しなければならないのはライセンスの問題である。Flash Media Encoder 2 のライセンス条文 4.3  General Restrictions. には次のようにある。</p>

<blockquote><p>When used for streaming the Encoded Content to the Flash Media Server, you may only use the Software to stream Encoded Content to the Flash Media Server.  You may not use the Software in any way to create a server, executable, or other program that will steam or deliver data and/or content from a client to a server, from a server to another server, or from a server to a client.</p></blockquote>

<p>この縛りによって、Flash Media Encoder 2 が配信を行うネットワークは Flash Media Server シリーズのみで構成しなければならなくなっている。</p>

<p>特に有用であろう特徴として、公式のプラグインによって配信時の認証機能を追加することができるようになっている。認証においてどのような通信を行っているのか、パケットキャプチャーなどを行わない限り詳細まではわからない。Flash Media Server シリーズ縛りであるからこそ可能な囲い込みによるの機能と言える。</p>

<p>サーバー周りは先日値下げ合戦が行われ、新しく現れた Flash Media Server 3 においても大幅な価格やラインナップの変更があったものの、やはり RTMP によるライブストリーミングを実現可能なサーバーソフトウェア製品の中では Flash Media Server が最も高い。Flash Media Encoder 2 自体が無料であるとしても、やはりサーバー側にかける金額を考えれば、サポートが余程重要であると判断できない限りは他の選択肢を探った方がいいだろう。</p>

<h5><a href="http://www.on2.com/index.php?391">Flix Live (On2 Technologies)</a></h5>

<p>On2 VP6 を作った On2 Technologies 自身による、Flash Video 用ソリューションラインナップのライブ版である。動作は Flash Media Encoder 2 よりも安定しており、インターフェイスも簡潔かつ明瞭で、主なパブリッシャーの中では現時点で最も質が高いと言える。</p>

<p>価格は年間 999 ドルで、Flash Media Server シリーズのみといった構成を回避したい場合にはこれを選択するのが最も手早く安い。<a href="http://www.flix-j.com/flix/">flix-j.com という日本代理店サイトの Flix ラインナップ</a>の中に Flix Live は含まれていないので、On2 Technologies と直接交渉するか代理店に Flix Live を購入できるよう交渉する必要がある。</p>

<p>GUI 上からは一見、あらかじめ用意されたいくつかの設定でしか配信できないように見えるが、これはインストールディレクトリに存在する XML ファイルを直接編集することで詳細な配信設定が可能になる。Flash Media Encoder 2 の設定がところどころ簡略化されているのに比べれば詳細な設定が可能だし、一度書いてしまえばあとはプルダウンメニューから選択するだけで済む。</p>

<p>RTMP 上における最低限のやりとりだけで配信することしかできないという最大の問題があり、プログラマブルな部分が一切存在しない。つまり誰でも無条件に受け入れる状態の RTMP サーバーに対してしか配信を行うことができない。認証機能など極一部に関しては <a href="http://www.on2.com/index.php?401">Flix SDK for Live</a> で一部解決することができる。但しこれに実装されている認証機能は Flash Media Server 2 との通信でのみ使えるものであり、Flash Media Server シリーズを購入するのならば Flash Media Encoder 2 を無料で使うことができるため Flix を使う必要性は激減する。</p>

<p>ニコニコ生放送では、パブリッシュするネットワークとサーバーのあるネットワークを VPN で繋ぐことによって、Flix Live を使った認証無しのセキュアな配信を実現している。但しこれには、配信先において独立した固定 IP アドレスが確保できないなどといった問題が発生する可能性があり、ハードルは高い。</p>

<p>ちなみに、少なくとも Wowza Media Server で確認した限りでは、誰の配信でも受け入れる状態だと、一度配信が開始された後で他の誰かが同じストリーム名で配信を乗っ取ることができる。何らかの保護機構は必須である。</p>

<h5>H.264 独自実装</h5>

<p>Wowza Media Server は <a href="http://www.wowzamedia.com/h264.html">Build number 5368 で H.264 のライブストリーミングに対応した</a>ようだが、「<q>H.264/AAC live streaming is not supported in this Preview1. At this time there are no live H.264 encoders that support RTMP.</q>」とあるように現時点ではライブでストリーミングが可能なパブリッシャーが存在しない。</p>

<p>しかし前述のように Adobe Labs が既に Flash Player 側での対応を表明していること、Wowza Media Server でも「現時点ではサポートしていない」と言っているだけであることなどから、ライブストリーミングにおいて H.264 を使っての配信は可能であると思われる。</p>

<p>と書いたところでほんの数日前にドワンゴに技術デモをしにきてくれた<a href="http://www.sgra.co.jp/">株式会社エスグラ</a>の森さんが <a href="http://d.hatena.ne.jp/takuma104/20080317/1205783390">RTMP ライブストリーミングにおける H.264 のビデオストリーム送信に成功している</a>という記事が出た。RTMP で H.264 をライブストリーミングした実装コードをオープンにすることに躊躇しているようだが、せめて解析結果を <a href="http://osflash.org/documentation">Open Source Flash の技術文書</a>に書き足して欲しい。Flash Pleyer 9 Update 3 の英語圏における広まりや日本以外での一般家庭における帯域の細さなどを考えると、ライブストリーミングに関わる最新文書が整うまでには時間がかかる。</p>

<h4>総括</h4>

<p>手軽さを重視するならば Flash Player 内蔵の Sorenson Spark を使う。画質を重視するならば Flash Media Server に囲い込まれた Flash Media Encoder か、RTMP による認証機能を諦めて Flex Live による On2 VP6 を使う。どうしても H.264 によるビデオストリームを使用したかったり RTMP による配信以外の制御が行いたければ自前で実装するしかない。</p>

<p>現状は各ベンダーの思惑や実装レベルでの技術的背景の不備などといった問題があまりにも多いため、これといった決定打も無く、トレードオフの中で目的に合った選択をしなければならない。</p>

<p>そもそも配信をする際の帯域の問題により実運用レベルでは政治的な要素が伴うため、誰もが手軽に配信を行えるような環境を整えるための社会的圧力が発生しにくい。現時点における現実的な解決策を提案しながらも、Open Source Flash などのようなオープンな団体の活動を含めた今後の傾向を窺う必要がある。</p>


]]></description>
   <category domain="http://blog.itoyanagi.name">entries</category>
   <pubDate>Tue, 18 Mar 2008 06:14 GMT</pubDate>
</item>
<item>
   <title>﻿各種 RTMP サーバーでのライブストリーミングの実現</title>
   <guid isPermaLink="false">entries/d20071109</guid>
   <link>http://blog.itoyanagi.name/entries/d20071109</link>
   <description><![CDATA[

<p>こんにちは、株式会社ドワンゴの糸柳です。何故突然名乗っているのかというと、業務での成果を外部に出す場合には所属を明らかにせよと言われたからです。実際の僕はというと、グッドウィルに登録し日雇いの派遣で肉体労働をしているワーキングプアということになっているのですから、本当にインターネットというのは信用できませんね。このようなブログも読まない方が良いと思いますよ。</p>

<p>本題に移る。Flash を使ってライブストリーミング（生放送）を実現するためには、Flash Media Server と Flash Player 上で動いている .swf ファイルのクライアントとの間で通信を行うために作られた RTMP（Real Time Messaging Protocol）という独自のプロトコルを使用する。RTMP はサーバーや別クライアントへの通知及び呼び出しの送受信（Notify 及び Invoke）、複数の Flash クライアント上でのオブジェクトの共有（Shared Object）、ストリーミングデータの送受信（Video Data 及び Audio Data）という 3 つの機能を持っている。ライブストリーミングではこのうち、ストリーミングデータと一部の通知及び呼び出しを使用する。</p>

<p>RTMP のサーバー側が最初に実装された Flash Media Server（旧 Flash Communication Server）シリーズでは、大規模なストリーミングデータを配信する際、サーバーの処理能力や帯域を分散するために配置するサーバーのうち、配信する元データを受け取り下位構造にデータを流すものをオリジンサーバー、オリジンの下に複数ぶら下がりクライアントからの接続を受けてオリジンからのデータをクライアントへブロードキャストするものをエッジサーバーと呼んでいる。</p>

<p><img src="/data/d20071109-unko.png" width="295" height="295" alt="Flash Media Server におけるライブストリームのクラスタリング" /></p>

<p>エッジ一台で捌くことのできるクライアントの台数には限度があるため、このようにしてスケーラビリティを確保する。</p>

<p>現在 RTMP による通信が可能なサーバーソフトウェアは自分が知る限り Flash Media Server、Wowza Media Server、Red5 の 3 種類があり、それぞれに大きく特性が違う。これらについてオリジンサーバー、エッジサーバーによるクラスタリングを実現する方法を紹介する。</p>

<p>この記事を書いている時点において、Windows Media Video を使った大規模配信があちこちで実現しているので、わざわざ Flash でやるということで、ニコニコ動画の最高画質の 500kbps から 600kbps あたりを勝手に目標に定めて検証する。</p>

<h4>Flash Media Server 2 を使ってのライブストリーミング</h4>

<p>Flash Media Server 2 は Adobe 純正の RTMP サーバーの最新版であり、これを使うと確実ではあるのだが、非常にコストパフォーマンスが悪い。これで環境を構築しようにも、莫大なユーザーがアクセスできる状態にするのは金銭的な面で少々大変だ。</p>

<p><a href="http://www.adobe.com/jp/products/flashmediaserver/productinfo/pricing/">Flash Media Server 2 の価格について書かれているページ</a>を見てみると「<q>Edge Edition はハードウェアに大きな負担をかける、メディア配信の大部分を処理することから、Edge Edition の同時接続ユーザー数は 600 人以下に制限することが推奨され</q>」ている。<a href="http://www.adobe.com/products/flashmediaserver/productinfo/pricing/fms_whitepaper_bandwidth.pdf">帯域とパフォーマンスについての文書（PDF）</a>の中の「Max Total Server Bandwidth」という表を見ると、600 人の場合は全体で 60 Mbps 弱の帯域を処理することができる。</p>

<blockquote>
<p><img src="/data/d20071109-bandwidth.png" width="594" height="315" alt="Flash Media Server の帯域幅とコネクション数のトレードオフ" /></p>
</blockquote>

<p>青い破線がライブストリーミングでのパフォーマンス</p>

<p>しかし、60Mbps では 1 クライアントの帯域が 500kbps でも 120 人しか捌くことができない。余裕を持たせて考えれば無理をしても 100 人が限度だろう。1 クライアント 500kbps を前提にしてグラフと照らし合わせると、80Mbps で 160 人を処理するのが良さそうだ。実運用では余裕を持たせて 130 人ぐらいにするなりすれば良い。</p>

<p>この設定だとエッジを 1 台追加して配信可能人数を 130 人増やすごとに 175 万円が必要だ。もちろん別途オリジンの代金 520 万円もかかる。ちなみに先ほどのページでフェイルオーバーと書かれているオリジンは、障害時に置き換えて動作するためのサーバーなので、それ単体では使えない。</p>

<p>ちなみに Professional Edition を購入する場合は、3 種類の Profile から、最大接続数と最大帯域幅のトレードオフで 1 つを選んで 1 ライセンス 156,000 円で購入することになる。自分が把握している限りでは、このライセンスではオリジンやエッジとして動作させることはできない。</p>

<blockquote>
<table summary="Flash Media Server Professional Edition ライセンス体系">
<tr><td></td><td>最大接続数</td><td>最大帯域幅</td></tr>
<tr><td>Profile 1</td><td>150</td><td>無制限</td></tr>
<tr><td>Profile 2</td><td>1000</td><td>40Mbps</td></tr>
<tr><td>Profile 3</td><td>2500</td><td>25Mbps</td></tr>
</table>
</blockquote>

<p>複数のライセンスを一つのマシンの中に束ねることもできるとあるので、Profile 1 を 2 つ、312,000 円で買えば、ライセンス上は、500kbps の動画を 300 人に配信することができる。但し、処理能力のグラフから考えれば、それだけの処理は全く期待できない。300 人ギリギリまで配信するならば 200kbps 強までビットレートを下げる必要があるだろう。</p>


<h4>Wowza Media Server を使ってのライブストリーミング</h4>

<p>元 Adobe の社員がスピンアウトして立ち上げた Wowza Media Systems という会社の製品で、Flash Media Server とほぼ同等の機能を持っている。<a href="http://www.wowzamedia.com/resources/WowzaMediaServerPro_UsersGuide.pdf">User's Guide</a> によれば、クラスタリングは、クライアントがエッジに接続したあと、再生するストリーム名としてオリジンへのアドレスを含めた URI を記述し、エッジがプロキシのように動作することで実現しているようだ。これはちょっと微妙な実装のような気もするが確かに手軽ではある。</p>

<p>また、<a href="http://www.wowzamedia.com/faq.html#f2">FAQ の「Have you conducted any performance tests?」という項</a>において、ライブストリーミングで 512kbps まで出しても 2000 の接続に対応できると書かれている。いくらカタログスペックとは言え Flash Media Server を極端に引き離している。</p>

<p>更には<a href="http://www.wowzamedia.com/pricing.html">接続数無制限ライセンスでも $5,000（2007/11/09 現在で約 57 万円）</a>でしかない。極めて安い。</p>

<p>自分は Flash Media Server と Wowza Media Server に関して、評価として双方のサーバー側アプリケーションの開発や通信実験などをある程度やったが、Wowza Media Server に機能的な遜色は無いと言っていい。カタログスペック及び価格の面で大幅に Flash Media Server を引き離し、またサーバー側アプリケーションの実装方法もかなり似せてあるので、現在 Flash Media Server を使っている人間は、可能であれば Wowza Media Server に乗り換えることを検討するのも悪くはない。但し現時点で日本に代理店は存在しない。</p>


<h4>Red5 を使ってのライブストリーミング</h4>

<p>Red5 は <a href="http://osflash.org/">Open Source Flash という団体</a>が作っているその名の通りオープンソースの RTMP を喋るサーバーで、やはり Flash Media Server をかなり真似て作られている。単体のサーバーならば、おおよそ同等の機能を有している。</p>

<p><a href="http://live.sohosai.com/nyafu/">にゃふにゃふ動画</a>が既に Red5 を使った一般向けのライブストリーミング配信を実現している。とりあえず動く。但し、ざっと使ってみた限りだとどうしても再生クライアント側での動画がカクついてしまって、それを改善することができなかった。接続しているクライアント数を増やしても減らしても目視で把握できるような差が無い。つまり配信が遅れているというわけでもないようで、多分パケットの扱いが Flash Media Server や Wowza Media Server とは異なっている、というか要するに実装を間違えているのだと思う。</p>

<p>また、クラスタリング構成に全く対応していない。つい数ヶ月前に <a href="http://orionl.blogspot.com/2007/06/clustering-red5.html">Terracotta を使ってクラスタリングを実現した人が出た</a>ようではあるが、Terracotta というのは JavaVM 上の Java オブジェクトのうち一部を複数のサーバーで同期させるようなものなのであって、あまりスマートな方法とは言えない。同期のための処理速度低下は、ライブストリーミングのような大量のデータを扱う場合に大きくなりやすい。</p>

<p>比較的まともな方法として、<a href="http://www.nixspot.com/red5/archive/index.php/t-1222.html">RTMPClient</a> を使うことが考えられる。これは Red5 開発者の Joachim Bauch が勝手に Red5 の中に突っ込んだクラスで、名前の通り RTMP のクライアントとして通信することが可能なものだ。検索すればいくつかサンプルは転がっている。エッジがオリジンに RTMPClient で接続することで、クラスタリングを実現することは充分に可能だ。但し、「<q>The RTMPClient currently doesn't support streams, only method calls and
shared objects.</q>」とあるようにライブを含めたストリームの配送には全く対応していない。</p>

<p>RTMPClient の実装は粗方把握しているが、正直言って Red5 そのものと癒着しすぎている。Red5 はあくまでもサーバーなのであって RTMP のクライアントとして通信することを目的としていない。つまり RTMPClient は、サーバーとして動作するはずの様々なクラスを騙しながらクライアントとして動作している。かなりの力技で更にもう一段階あちこちを騙して回ることでストリーミングデータの受信には成功しているが、Red5 本体の設計にも多少難があるためその辺りの修正にまで手を回さなければならなかった。ストリーミングデータのクラスタリングの実現を本家に contribute するためには Red5 本体の設計にまで口を挟まなければならない。</p>

<p>そしてそこまでやっても Red5 のみで構成している限りでは動画のカクりが生じる。試しにオリジンを Flash Media Server に、エッジを Red5 + RTMPClient にしたのだが、こうするとカクりが突然消滅する。これならば上手くいくかも知れない。仮にこれでエッジの Red5 の処理能力が物凄く低くてその分だけハードウェアにやたらと投資しなければならず、Wowza Media Server にコストパフォーマンスで負けてしまうため Red5 をエッジにすることにあまり意味が無いとしても、コマンドラインから外部の RTMP を喋るサーバーに接続して通信や負荷の実験を行う用途に転用できる。上手く使えば無駄にはならないだろう。</p>

<p>ところで何故上記の実験でオリジンを Flash Media Server にしたかというと、Wowza Media Server に RTMPClient で接続できなかったからだ。ハンドシェイクのどこかでコケている。現在パケットを解析して問題を追っているが本来の仕事が忙しくてあまり時間を割けていない。Wowza Media Server にさえ接続できれば、動画のカクりなく、かつ台数が必要なエッジのソフトウェアへの現金でのコストをタダにすることができるようになる。業務でライブストリーミングの配信に使うかどうかはともかくとして何らかの使い道はある。</p>

<p>ところで Red5 でのパフォーマンスだが、500kbps で RTMPClient を接続させ続ける負荷テストを行ったところ、データセンターのマシンと自席の机で数百本のコネクションを張った時点で近くにあった 100Mbps のハブが死に掛けて情シスの人間が飛んできて痛烈なお叱りを受け、データセンター内に研究用のマシンを三台借りることができたので一台をサーバーに、2 台を RTMPClient にしたのだけれど、その 2 台の RTMPClient からそれぞれ 300 コネクションぐらい張った時点で、クライアント側二台が負荷に耐え切れずエラーを吐きまくってコネクションを切断しだしたり、600 コネクションぐらいから Out of Memory で落ちたりした。誰かデータセンター内に十台ぐらいマシンを出してくれ。ドワンゴはニコニコ動画にハードウェア取られてるからあんまり回してもらえないんだよ。</p>

<p>その改造版 Red5 は後日詳説する。ブログを書く意欲が全く無いので、実際に書くのは十年後ぐらいとかになるんじゃないですかね。</p>


<h4>総括</h4>

<p>ライブストリーミングをクラスタリング構成で大規模に配信する際、ハードウェアの台数も含めて安くあげるならば Wowza Media Server を使うと良い。信頼性やサポートを得る必要があって、かつソフトウェアやハードウェアに多くの投資が可能な状況ならば Flash Media Server を選択することになるだろう。ハードウェアだけが何故か潤沢にあり、ソフトウェアに全く金をかけたくなくて、更に高い技術力でパケット配送の様々な問題を自分で解決したいぜ、という知能に障害があるとしか思えない人間は Red5 を選択するといいが、多分お前は報われない。</p>

<p>また、実際に配信するとなると他にも問題がある。最も重大なのは配信側のソフトウェアのライセンスだ。まともな画質を実現しようとするとかなり面倒くさい縛りがある。これについてはまた今度まとめて報告する。</p>

]]></description>
   <category domain="http://blog.itoyanagi.name">entries</category>
   <pubDate>Thu, 08 Nov 2007 20:40 GMT</pubDate>
</item>
<item>
   <title>﻿「自由なディレクトリでコンテンツをストリームする方法」の和訳</title>
   <guid isPermaLink="false">entries/d20070925b</guid>
   <link>http://blog.itoyanagi.name/entries/d20070925b</link>
   <description><![CDATA[

<p><a href="http://mirror1.cvsdude.com/trac/osflash/red5/browser/doc/trunk/HOWTO-StreamCustomDirectories.txt">HOWTO stream content to/from custom directories</a> の 2007/05/13 版。ライブストリームの話も出てくるかと思ったが、完全にオンデマンド配信に関することしか書いていない。本当に「翻訳したからなんなの？」という文書。</p>
<blockquote>
<h4>自由なディレクトリでコンテンツをストリームする方法</h4>
<p>（訳注: ライセンスは他の Open Source Flash のドキュメント群から類推するに Creative Commons だと思うのだが、明示されていない。Red5 同梱であるせいでこのままだと GPL が適用されてしまうのだがどうなっているのだろう）</p>
<dl>
<dt>Author</dt><dd>Joachim Bauch</dd>
<dt>Contact</dt><dd>jojo@struktur.de</dd>
<dt>Date</dt><dd>$Date$</dd>
<dt>Revision</dt><dd>$Revision$</dd>
<dt>Id</dt><dd>$Id$</dd>
</dl>
<p>... コンテンツ::</p>
<h5>前書き</h5>
<p>このドキュメントはアプリケーションがどうやってウェブアプリケーションの中にあるデフォルトの `streams` フォルダ以外の自由なディレクトリにオンデマンドビデオ（VOD）を保存、配信していくのかについて記述しています。</p>
<h5>ファイル名生成サービス</h5>
<p>Red5 は特定のスコープに提供される機能を持った「スコープサービス」と呼ばれる概念を使用します。これらのスコープサービスのうちの一つが、保存または再生されるべき VOD ストリームのファイル名を生成する IStreamFilenameGenerator_ です。</p>
<h5>カスタムジェネレーター</h5>
<p>異なるフォルダのファイル名を生成するには、新しいファイル名ジェネレーターが実装されていなければなりません::</p>
<pre><code>import org.red5.server.api.IScope;
import org.red5.server.api.stream.IStreamFilenameGenerator;

public class CustomFilenameGenerator implements IStreamFilenameGenerator {

  /** 保存されたビデオを保存するパス */
  public String recordPath = &quot;recordedStreams/&quot;;
  /** VOD ストリームが入っているパス */
  public String playbackPath = &quot;videoStreams/&quot;;
  
  public String generateFilename(IScope scope, String name,
      GenerationType type) {
    // 拡張子を除いたファイル名を生成
    return generateFilename(scope, name, null, type);
  }

  public String generateFilename(IScope scope, String name,
      String extension, GenerationType type) {
    String filename;
    if (type == GenerationType.RECORD)
      filename = recordPath + name;
    else
      filename = playbackPath + name;
    
    if (extension != null)
      // 拡張子を追加
      filename += extension;
    
    return filename;
  }
}</code></pre>
<p>上記のクラスは `recordedStreams/red5RecordDemo1234.flv` のようなストリームの保存用ファイル名を生成し、全ての VOD ストリームのソースに `videoStreams` ディレクトリを使います。</p>
<h5>カスタムジェネレーターの稼動</h5>
<p>次のステップとして、カスタムジェネレーターが任意のアプリケーションの設定ファイルの中で有効にされていなければなりません。</p>
<p>以下の定義を `yourApp/WEB-INF/red5-web.xml` に追加してください::</p>
<pre><code>&lt;bean id=&quot;streamFilenameGenerator&quot; 
      class=&quot;path.to.your.CustomFilenameGenerator&quot; /&gt;</code></pre>
<p>この設定により、ストリームのファイル名を生成するために前述のクラスを使うようになります。</p>
<h5>設定ファイルを通してパスを変更する</h5>
<p>ここで書いたクラスは期待通りに動きますが、書き換えるたびにクラスの再コンパイルが必要なコードの中のパスを変更するのは少々不便です。</p>
<p>そのため、設定ファイルの中で使うパスを指定する前回のステップの中で定義された bean に、パラメーターを渡すことができます。</p>
<p>設定ファイルがパースされる際に実行されたクラスに二つの関数を追加します::</p>
<pre><code>public void setRecordPath(String path) {
  recordPath = path;
}

public void setPlaybackPath(String path) {
  playbackPath = path;
}</code></pre>
<p>そしてこのように bean 定義の中でパスを設定することができます::</p>
<pre><code>&lt;bean id=&quot;streamFilenameGenerator&quot; 
      class=&quot;path.to.your.CustomFilenameGenerator&quot;&gt;
  &lt;property name=&quot;recordPath&quot; value=&quot;recordedStreams/&quot; /&gt;
  &lt;property name=&quot;playbackPath&quot; value=&quot;videoStreams/&quot; /&gt;
&lt;/bean&gt;</code></pre>
<p>`yourApp/WEB-INF/red5-web.properties` ファイルへパスを移動し、それらにアクセスするパラメーターを使うこともできます::</p>
<pre><code>&lt;bean id=&quot;streamFilenameGenerator&quot; 
      class=&quot;path.to.your.CustomFilenameGenerator&quot;&gt;
  &lt;property name=&quot;recordPath&quot; value=&quot;${recordPath}&quot; /&gt;
  &lt;property name=&quot;playbackPath&quot; value=&quot;${playbackPath}&quot; /&gt;
&lt;/bean&gt;</code></pre>
<p>その場合プロパティーファイルに以下の行を追加しなければなりません::</p>
<pre><code>    recordPath=recordedStreams/
    playbackPath=videoStreams/</code></pre>
</blockquote>

]]></description>
   <category domain="http://blog.itoyanagi.name">entries</category>
   <pubDate>Tue, 25 Sep 2007 14:00 GMT</pubDate>
</item>
<item>
   <title>﻿「FCS/FMS から Red5 への移行ガイド」の和訳</title>
   <guid isPermaLink="false">entries/d20070925</guid>
   <link>http://blog.itoyanagi.name/entries/d20070925</link>
   <description><![CDATA[

<p>今度は Red5 の簡単な使い方ガイドみたいな感じの <a href="http://mirror1.cvsdude.com/trac/osflash/red5/browser/doc/trunk/MigrationGuide.txt">Migration guide from FCS/FMS to Red5</a> の 2006/11/14 版を和訳。他に<a href="http://mirror1.cvsdude.com/trac/osflash/red5/browser/doc/trunk/HOWTO-StreamCustomDirectories.txt">ストリーミング専用の文書</a>があるようなのでそっちも和訳した方がいいか。</p>
<blockquote>
<h4>FCS/FMS から Red5 への移行ガイド</h4>
<p>この文書のライセンスは、<a href="http://creativecommons.org/licenses/by-sa/2.5/deed.ja">Creative Commons の「表示・継承 2.5」</a>です。</p>
<dl>
<dt>author</dt><dd>Joachim Bauch</dd>
<dt>contact</dt><dd>jojo@struktur.de</dd>
<dt>Date</dt><dd>$Date: 2006-11-15 00:22:04 +0100 (Mi, 15 Nov 2006) $</dd>
<dt>Revision</dt><dd>$Revision: 1540 $</dd>
<dt>Id</dt><dd>$Id: MigrationGuide.txt 1540 2006-11-14 23:22:04Z jbauch $</dd>
</dl>
<p>... コンテンツ::</p>
<h5>前書き</h5>
<p>このドキュメントは Macromedia Flash Communication Server / Flash Media Server 2 と Red5 の間の差について記述しています。既存のアプリケーションから Red5 への移行の手助けをすることが目的としています。</p>
<p>もし Red5 をまだ持っていない場合は、`howto create new applications` を初めに読んでください。</p>
<h5>アプリケーションコールバック</h5>
<p>多くの機能のうちサーバーサイドアプリケーションを実装する際に最も重要なものの一つが、接続か切断したクライアントの通知を取得しアプリケーションの新しいインスタンスの生成に関する情報を受け取ることです。</p>
<h5>インターフェイス IScopeHandler</h5>
<p>Red5 は IScopeHandler インターフェイスでこれらのアクションを定義しています。詳細は API ドキュメントを見てください。</p>
<h5>クラス ApplicationAdapter</h5>
<p>いくつかの関数は一度のリクエストで複数回呼ばれます（たとえば、`connect` はクライアントが接続するツリーの全てのスコープで一度呼ばれます）。ApplicationAdapter_ クラスは更に関数を定義しています。</p>
<p>このクラスは通常新しいアプリケーションの基幹クラスとして利用されます。</p>
<p>FCS / FMS の `application` クラスの関数の概要と、それに該当する Red5 の AppilcationAdapter_ の関数は次の通りです:</p>
<table summary="FCS/FMS と Red5 の API 対応表">
<tr><td>FCS / FMS</td><td>Red5</td></tr>
<tr><td rowspan="2">onAppStart</td><td>appStart</td></tr>
<tr>                               <td>roomStart</td></tr>
<tr><td rowspan="2">onAppStop</td><td>appStop</td></tr>
<tr>                              <td>roomStop</td></tr>
<tr><td rowspan="4">onConnect</td><td>appConnect</td></tr>
<tr>                              <td>roomConnect</td></tr>
<tr>                              <td>appJoin</td></tr>
<tr>                              <td>roomJoin</td></tr>
<tr><td rowspan="4">onDisconnect</td><td>appDisconnect</td></tr>
<tr>                                 <td>roomDisconnect</td></tr>
<tr>                                 <td>appLeave</td></tr>
<tr>                                 <td>roomLeave</td></tr>
</table>
<p>`app*` 関数はメインアプリケーションで呼ばれ、`room*` 関数はアプリケーションのルーム（即ちインスタンス）で呼ばれます。</p>
<p>それと同様に、ストリームやシェアードオブジェクトのチェックや承認にも ApplicationAdapter_ を使うことができます。更に細かいことについては API ドキュメントを見てください。</p>
<h6>コネクション関数の実行</h6>
<p>`rtmp://server/app/room1/room2` へ接続すると仮定します。</p>
<p>まず初めにコネクションが確立され、そして `room2` に至る全てのスコープに対してユーザーが「接続」されます。</p>
<ol>
<li>`app`     (-&gt; appConnect)</li>
<li>`room1`   (-&gt; roomConnect)</li>
<li>`room2`   (-&gt; roomConnect)</li>
</ol>
<p>コネクション確立後、このクライアントオブジェクトは回収され、そのスコープにおいてこのクライアントが始めて接続する場合はクライアントがスコープに「加入」します。</p>
<ol>
<li>`app`     (-&gt; appJoin)</li>
<li>`room1`   (-&gt; roomJoin)</li>
<li>`room2`   (-&gt; roomJoin)</li>
</ol>
<p>同じクライアントが同じスコープに二度目のコネクションを確立する場合、単に `connect` 関数が呼ばれます。部分的に同じスコープへ接続する場合、単にいくつかの `join` 関数が呼ばれます。たとえば `rtmp://server/app/room1/room3` をトリガーとした場合は次のようになります。</p>
<ol>
<li>`appConnect`</li>
<li>`joinConnect(&quot;room1&quot;)`</li>
<li>`joinConnect(&quot;room3&quot;)`</li>
<li>`roomJoin(&quot;room3&quot;)`</li>
</ol>
<p>`appStart` 関数は今のところ Red5 の起動時に一度だけ呼ばれ、FCS/FMS のようにアプリケーションをロード/アンロードすることはできません。`roomStart` 関数は最初のクライアントがルームに接続した時に呼ばれます。</p>
<h5>クライアントの承認 / 拒否</h5>
<p>FCS / FMS は新しいクライアントの承認と拒否のために関数 `acceptConnection` と `rejectConnection` を提供しています。Red5 アプリケーションがクライアントの接続を許可する時には `*Connect` 関数が `true` を返す以外に特別な処理は必要ありません。</p>
<p>クライアントが接続を許可すべきでない時、ApplicationAdapter_ クラスで実装された関数 `rejectClient` が呼び出されます。`rejectClient` へ渡されたいくつかのパラメーターは、呼び出し元に返されたステータスオブジェクトの `application` プロパティで利用できます。</p>
<h5>現在のコネクションとクライアント</h5>
<p>Red5 は呼び出された関数から現在のコネクションにアクセスするために二つの異なる方法がサポートされています。コネクションは接続中のスコープとアクティブなクライアントを取得するために使うことができます。一つ目の方法は、「不思議な」Red5 オブジェクトを使います::</p>
<pre><code>import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;

public void whoami() {
    IConnection conn = Red5.getConnectionLocal();
    IClient client = conn.getClient();
    IScope scope = conn.getScope();
    // ...
}</code></pre>
<p>二つ目の方法では、クライアントが関数を呼んだ時に Red5 によって自動的に追加される暗黙の最初のパラメーターである IConnection_ タイプの引数で定義されている関数を使います::</p>
<pre><code>import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;

public void whoami(IConnection conn) {
    IClient client = conn.getClient();
    IScope scope = conn.getScope();
    // ...
}</code></pre>
<h5>追加のハンドラー</h5>
<p>多くのアプリケーションのために、再利用を義務付けられた Red5 に関連しない、アプリケーションロジックを含むクラスが存在します。RTMP 経由で接続するクライアントで利用できるようにそれらのクラスは Red5 のハンドラーとして登録されている必要があります。</p>
<p>現在ハンドラーに登録するには二つの方法があります:</p>
<ol>
<li>設定ファイルに追加する。</li>
<li>アプリケーションのコード内で手動で登録する。</li>
</ol>
<p>ハンドラーはこのようなコードでクライアントから実行されます::</p>
<pre><code>nc = new NetConnection();
nc.connect(&quot;rtmp://localhost/myapp&quot;);
nc.call(&quot;handler.method&quot;, nc, &quot;Hello world!&quot;);</code></pre>
<p>ハンドラーが要求された場合、Red5 は常に、設定ファイルでセットアップされたハンドラーをチェックするより先に、カスタムのスコープのハンドラー（訳注: アプリケーションコード上で登録したハンドラー、という意味だろうか）から探します。</p>
<h5>設定ファイル内のハンドラー</h5>
<p>このメソッドは、アプリケーションの中にある全てのスコープで共有され、かつアプリケーションの生存時間中に変化しないハンドラーに最も向いています。</p>
<p>ハンドラー `sample` としてクラス `com.fancycode.red5.HandlerSample` を登録する場合、以下の bean を `WEB-INF/red5-web.xml` に追加する必要があります。</p>
<pre><code>&lt;bean id=&quot;sample.service&quot; 
      class=&quot;com.fancycode.red5.HandlerSample&quot; 
      singleton=&quot;true&quot; /&gt;</code></pre>
<p>この bean の ID はハンドラー名（ここでは `sample`）とキーワード `service` から構成されていることに注意してください。</p>
<h5>アプリケーションコードからのハンドラー</h5>
<p>ハンドラーの変更を行いたい場合や様々なスコープで異なるハンドラーを使いたい場合、全てのアプリケーションはサービス内部のコードでハンドラーを登録する必要があります。これらのハンドラーは常に `red5-web.xml` で設定したハンドラーをオーバーライドします。登録に必要な関数は、ApplicationAdapter_ で実装されているインターフェイス IServiceHandlerProvider_ に記載されています。</p>
<p>前述のものと同じクラスはこのコードで登録することができます::</p>
<pre><code>public boolean appStart(IScope app) {
    if (!super.appStart(scope))
        return false;
    
    Object handler = new com.fancycode.red5.HandlerSample();
    app.registerServiceHandler(&quot;sample&quot;, handler);
    return true;
}</code></pre>
<p>この例では、このアプリケーションスコープは `sample` ハンドラーを持っていますが、そのサブスコープには適用されないことに注意してください。ハンドラーをルームでも同様に有効にしたい場合は、ルームのスコープの `roomStart` に登録されている必要があります。</p>
<h5>クライアント関数の呼び出し</h5>
<p>クライアント上の Red5 アプリケーションから関数を呼ぶには、まず現在のコネクションオブジェクトへの参照が必要です。</p>
<pre><code>import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IServiceCapableConnection;
...
IConnection conn = Red5.getConnectionLocal();</code></pre>
<p>コネクションが IServiceCapableConnection_ インターフェイスを実装している場合、他の端末上での関数呼び出しをサポートします。</p>
<pre><code>if (conn instanceof IServiceCapableConnection) {
    IServiceCapableConnection sc = (IServiceCapableConnection) conn;
    sc.invoke(&quot;the_method&quot;, new Object[]{&quot;One&quot;, 1});
}</code></pre>
<p>関数呼び出しの結果が必要な場合は、IPendingServiceCallback_ インターフェイスを実装しているクラスを準備しなければなりません。</p>
<pre><code>import org.red5.server.api.service.IPendingService;
import org.red5.server.api.service.IPendingServiceCallback;

class MyCallback implements IPendingServiceCallback {

    public void resultReceived(IPendingServiceCall call) { 
        // &quot;call.getResult()&quot; でなんかする
    }
}</code></pre>
<p>関数呼び出しは丁度このようになります::</p>
<pre><code>if (conn instanceof IServiceCapableConnection) {
    IServiceCapableConnection sc = (IServiceCapableConnection) conn;
    sc.invoke(&quot;the_method&quot;, new Object[]{&quot;One&quot;, 1}, new MyCallback());
}</code></pre>
<p>もちろんアプリケーションにこのインターフェイスを実装してアプリケーションのインスタンスへの参照を渡すことができます。</p>
<h5>シェアードオブジェクト</h5>
<p>アプリケーションにおけるシェアードオブジェクトへのアクセス関数はインターフェイス ISharedObjectService_ で定義されています。</p>
<p>サーバー側のスクリプトでシェアードオブジェクトを分配する時、生成されたスコープについて特に注意する必要があります。</p>
<p>ルームの作成時に新しいシェアードオブジェクトを作るために、アプリケーションの関数 `roomStart` をオーバーライドすることができます。</p>
<pre><code>import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IScope;
import org.red5.server.api.so.ISharedObject;

public class SampleApplication extends ApplicationAdapter {

  public boolean roomStart(IScope room) {
      if (!super.roomStart(room))
          return false;
      
      createSharedObject(room, &quot;sampleSO&quot;, true);
      ISharedObject so = getSharedObject(room, &quot;sampleSO&quot;);
      
      // ここでシェアードオブジェクトを使ってなんかする...
      
      return true;
  }
  
}</code></pre>
<p>この場合、例えば `rtmp://server/application/room1` のようなアプリケーションのルームへ最初のユーザーが接続するたび、シェアードオブジェクト `sampleSO` がサーバーで生成されます。</p>
<p>たとえば `rtmp://server/application` のようなメインアプリケーションへ接続した時にシェアードオブジェクトが生成されなければならない場合、関数 `appStart` 内で同じことをする必要があります。</p>
<p>シェアードオブジェクトが提供する利用可能な関数に関するこれ以上の情報は、インターフェイス ISharedObject_ の API ドキュメントを参照してください。</p>
<h5>サーバー側の更新の受信</h5>
<p>FCS / FMS における `onSync` のようにしてシェアードオブジェクトの変更通知を取得するには、受信側がインターフェイス ISharedObjectListener_ を実装している必要があります。</p>
<pre><code>import org.red5.server.api.so.ISharedObject;
import org.red5.server.api.so.ISharedObjectListener;

public class SampleSharedObjectListener
       implements ISharedObjectListener {

  public void onSharedObjectUpdate(ISharedObject so,
                                   String key, Object value) {
      // シェアードオブジェクト &lt;so&gt; の &lt;key&gt; 属性が
      // &lt;value&gt; で変更された
  }

  public void onSharedObjectDelete(ISharedObject so, String key) {
      // シェアードオブジェクト &lt;so&gt; の &lt;key&gt; 属性が削除された
  }

  public void onSharedObjectSend(ISharedObject so,
                                 String method, List params) {
      // シェアードオブジェクト &lt;so&gt; のハンドラー &lt;method&gt; が
      // パラメーター &lt;params&gt; で呼ばれた
  }
  
  // インターフェイスに記載された他の関数群...
}</code></pre>
<p>更に、受信側はシェアードオブジェクトの登録も行わなければなりません::</p>
<pre><code>ISharedObject so = getSharedObject(scope, &quot;sampleSO&quot;);
so.addSharedObjectListener(new SampleSharedObjectListener())</code></pre>
<h5>アプリケーションコードにおける変更</h5>
<p>更に、シェアードオブジェクトはサーバー側で変更することもできます::</p>
<pre><code>ISharedObject so = getSharedObject(scope, &quot;sampleSO&quot;);
so.setAttribute(&quot;fullname&quot;, &quot;Sample user&quot;);</code></pre>
<p>この場合、登録されたハンドラーだけでなく登録されたクライアントも属性の追加や更新の通知を受けることができます。</p>
<p>シェアードオブジェクトに対する複数の動作が、登録クライアントへの一度の更新イベント内で一度に処理される場合、関数 `beginUpdate` と `endUpdate` を使わなければなりません::</p>
<pre><code>ISharedObject so = getSharedObject(scope, &quot;sampleSO&quot;);
so.beginUpdate();
so.setAttribute(&quot;One&quot;, &quot;1&quot;);
so.setAttribute(&quot;Two&quot;, &quot;2&quot;);
so.removeAttribute(&quot;Three&quot;);
so.endUpdate();</code></pre>
<p>サーバー側では `beginUpdate` と `endUpdate` を除いて関数呼び出しごとに分割した上で更新通知を受け取ります。</p>
<h5>シェアードオブジェクトイベントのハンドラー</h5>
<p>Flash クライアントから `remote_so.send(&lt;handler&gt;, &lt;args&gt;)` を通じたシェアードオブジェクトの呼び出しを行う場合、もしくはサーバー側の呼び出しを行う場合、Red5 の関数にマッピングすることができます。そのため、ハンドラーはアプリケーションハンドラーのように ISharedObjectHandlerProvider_ インターフェイスの関数を通じて登録を行わなければなりません::</p>
<pre><code>package com.fancycode.red5;

class MySharedObjectHandler {

    public void myMethod(String arg1) {
        // ここでなんかする
    }
    
}

...
ISharedObject so = getSharedObject(scope, &quot;sampleSO&quot;);
so.registerServiceHandler(new MySharedObjectHandler());</code></pre>
<p>更に、ハンドラーに名前を付けて登録することもできます::</p>
<pre><code>ISharedObject so = getSharedObject(scope, &quot;sampleSO&quot;);
so.registerServiceHandler(&quot;one.two&quot;, new MySharedObjectHandler());</code></pre>
<p>この場合、関数は `one.two.myMethod` として呼び出すことができます。</p>
<p>シェアードオブジェクトのイベントハンドラーを定義する他の方法は、ファイルを使ったアプリケーションハンドラーのように `red5-web.xml` へハンドラーを追加することです。`&lt;SharedObjectName&gt;.&lt;DottedServiceName&gt;.soservice` といった命名規則でなければならないため、前述のサンプルを同じように定義するには次のようにします::</p>
<pre><code>&lt;bean id=&quot;sampleSO.one.two.soservice&quot; 
      class=&quot;com.fancycode.red5.MySharedObjectHandler&quot; 
      singleton=&quot;true&quot; /&gt;</code></pre>
<h5>パーシステンス</h5>
<p>パーシステンスを使うとサーバーを再起動してもそのままオブジェクトのプロパティが使用できるようになります。FCS / FMS では通常、サーバーサイドのローカルシェアードオブジェクトを使用してこれを実現しています。</p>
<p>Red5 はオブジェクトを自由にパーシステントにすることができ、そのために必要なものは全てインターフェイス IPersistable_ に実装されています。基本的にこれらのオブジェクトは `type`、`path`、`name` （全て文字列）を持ち、自分自身をシリアライズ・デシリアライズする方法を知っています。</p>
<p>シリアライズ・デシリアライズするサンプルを示します::</p>
<pre><code>import java.io.IOException;
import org.red5.io.object.Input;
import org.red5.io.object.Output;
import org.red5.server.api.persistence.IPersistable;

class MyPersistentObject implements IPersistable {

  // パーシステンスな感じに生成された属性
  private String data = &quot;My persistent value&quot;;

  void serialize(Output output) throws IOException {
      // オブジェクトの情報をセーブ。
      output.writeString(data);
  }
  
  void deserialize(Input input) throws IOException {
      // オブジェクトの情報をロード。
      data = input.readString();
  }
  
  // インターフェイスに記載された関数群...
}</code></pre>
<p>以下のコードでこのオブジェクトに対するセーブやロードを行うことができます::</p>
<pre><code>import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.persistence.IPersistenceStore;

class MyApplication extends ApplicationAdapter {

  private void saveObject(MyPersistentObject object) {
      // 現在のスコープを取得。
      IScope scope = Red5.getConnectionLocal().getScope();
      // 現在のスコープのオブジェクトを保存。
      // Save object in current scope.
      scope.getStore().save(object);
  }

  private void loadObject(MyPersistentObject object) {
      // 現在のスコープを取得。
      IScope scope = Red5.getConnectionLocal().getScope();
      // 現在のスコープからオブジェクトをロード。
      scope.getStore().load(object);
  }
  
}</code></pre>
<p>アプリケーションにカスタムオブジェクト（訳注: 動的に登録されたオブジェクト、という意味だろうか）が必要ではなかったがデータを後で再利用するために保存しなければならない場合、インターフェイス IAttributeStore_ を通じて IScope_ に追加することができます。スコープ内では、`IPersistable.TRANSIENT_PREFIX` で始まらない全ての属性はパーシステンスです。</p>
<p>オブジェクトを保存するために使われる処理は各種設定が可能です。デフォルトのパーシステンスではメモリとファイルシステムが使用可能です。</p>
<p>全てのオブジェクトでファイルシステムのパーシステンスを使う場合、ファイルは &quot;webapps/&lt;app&gt;/persistence/&lt;type&gt;/&lt;path&gt;/&lt;name&gt;.red5&quot; に生成されます。例えば、&quot;rtmp://server/myApp/room1&quot; に接続しシェアードオブジェクト &quot;theSO&quot; を保存する場合は&quot;webapps/myApp/persistence/SharedObject/room1/theSO.red5&quot; にファイルが生成されます。</p>
<h5>定期イベント</h5>
<p>定期的にタスクを実行しなければならないアプリケーションは、FCS/ FMS で定期的に関数を実行するスケジュールを決定するために実装されている `setInterval` を使うことができます。</p>
<p>Red5 は他の多くのサービスに似た形でスケジューリングサービス（ISchedulingService_）を ApplicationAdapter_ に実装しています。そのサービスは `execute` 関数が一定の間隔で呼び出されるオブジェクト（IScheduledJob_ インターフェイスを実装している必要があります）を登録することができます。</p>
<p>次のようなコードでオブジェクトを登録できます::</p>
<pre><code>import org.red5.server.api.IScope;
import org.red5.server.api.IScheduledJob;
import org.red5.server.api.ISchedulingService;
import org.red5.server.adapter.ApplicationAdapter;

class MyJob implements IScheduledJob {

  public void execute(ISchedulingService service) {
      // なんかする
  }
}

public class SampleApplication extends ApplicationAdapter {

  public boolean roomStart(IScope room) {
      if (!super.roomStart(room))
          return false;
      
      // 十秒ごとにスケジュールを実行するよう予定を入れる。
      String id = addScheduledJob(10000, new MyJob());
      room.setAttribute(&quot;MyJobId&quot;, id);
      return true;
  }
}</code></pre>
<p>`addScheduledJob` が返す ID は後で登録したジョブを停止するのに使うことができます::</p>
<pre><code>public void roomStop(IScope room) {
    String id = (String) room.getAttribute(&quot;MyJobId&quot;);
    removeScheduledJob(id);
    super.roomStop(room);
}</code></pre>
<h5>リモート</h5>
<p>リモートは RTMP ではないクライアントが Red5 の関数を起動するために使うことができます。他の可能性として、Red5 から遠隔サービスを提供する他のサーバーの関数を呼ぶことができます。</p>
<h5>リモートサーバー</h5>
<p>クライアントを利用できなければならないサービスはアプリケーションハンドラーの登録の追加と同じように登録できる必要があります。詳細は前述の情報を見てください。</p>
<p>アプリケーションのリモートサポートを有効にするには、`WEB-INF/web.xml` ファイルに次の項を追加しなければなりません。</p>
<pre><code>&lt;servlet&gt;
  &lt;servlet-name&gt;gateway&lt;/servlet-name&gt;
  &lt;servlet-class&gt;
      org.red5.server.net.servlet.AMFGatewayServlet
  &lt;/servlet-class&gt;
&lt;/servlet&gt;
  
&lt;servlet-mapping&gt;
  &lt;servlet-name&gt;gateway&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/gateway/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</code></pre>
<p>`&lt;url-pattern&gt;` タグで定義されているパス（この場合 `gateway`）は接続 URL としてリモートのクライアントから利用することができます。このサンプルがアプリケーション `myApp` で定義されていたなら、URL はこうなるでしょう::</p>
<pre><code>http://localhost:5080/myApp/gateway</code></pre>
<p>関数はアプリケーションのスコープの中で作成されたコネクションを通じて呼び出されます。関数をサブスコープで実行しなければならない場合、サブスコープのパスをこのように URL に追加しなければなりません::</p>
<pre><code>http://localhost:5080/myApp/gateway/room1/room2</code></pre>
<h5>リモートクライアント</h5>
<p>クラス RemotingClient_ はリモートプロトコルを通じて関数を呼び出す必要のある全ての関数を定義しています。</p>
<p>以下のコードはリモートクライアントの使い方の例です::</p>
<pre><code>import org.red5.server.net.remoting.RemotingClient;

String url = &quot;http://server/path/to/service&quot;;
RemotingClient client = new RemotingClient(url);
Object[] args = new Object[]{&quot;Hello world!&quot;};
Object result = client.invokeMethod(&quot;service.remotingMethod&quot;, args);
// ここで結果を返したりする</code></pre>
<p>デフォルトでは、呼び出しごとに 30秒でタイムアウトしますが、コンストラクタの第二引数にミリ秒で最大タイムアウト時間を渡して変更することができます。</p>
<p>リモートヘッダーの `AppendToGatewayUrl`、`ReplaceGatewayUrl`、`RequestPersistentHeader` は Red5 のリモートクライアントで自動的に処理されます。</p>
<p>いくつかの関数はサーバー呼び出し完了までにかなり長い時間を要すため、Red5 のスレッドの邪魔にならないように非同期呼び出しを実行した方が良いでしょう。そのため、インターフェイス IRemotingCallback_ を実装したオブジェクトには追加のパラメーターを渡さなければなりません::</p>
<pre><code>import org.red5.server.net.remoting.RemotingClient;
import org.red5.server.net.remoting.IRemotingCallback;

public class CallbackHandler implements IRemotingCallback {

  void errorReceived(RemotingClient client, String method,
                     Object[] params, Throwable error) {
      // リモート呼び出しを実行してる最中にエラー発生。
  }
  
  void resultReceived(RemotingClient client, String method,
                      Object[] params, Object result) {
      // サーバーから結果を受信。
  }
}

String url = &quot;http://server/path/to/service&quot;;
RemotingClient client = new RemotingClient(url);
Object[] args = new Object[]{&quot;Hello world!&quot;};
IRemotingCallback callback = new CallbackHandler();
client.invokeMethod(&quot;service.remotingMethod&quot;, args, callback);</code></pre>
<h5>ストリーミング</h5>
<p><strong>TODO</strong>: アプリケーションからストリームにアクセスするにはどうしたらいいのだろう</p>
</blockquote>
<p>現状、既に Red5 0.6.2 を改造し、Red5 内部に別サーバーのライブストリームを取得する機能を追加し終えている。その辺は長くなるのであとで書く。っていうかこの和訳文書群って今んとこかなり前に社内に書いたものの転載なのよ。</p>

]]></description>
   <category domain="http://blog.itoyanagi.name">entries</category>
   <pubDate>Tue, 25 Sep 2007 05:17 GMT</pubDate>
</item>
<item>
   <title>﻿RTMP と RTMPT に関する文書の和訳</title>
   <guid isPermaLink="false">entries/d20070924</guid>
   <link>http://blog.itoyanagi.name/entries/d20070924</link>
   <description><![CDATA[

<p><a href="http://osflash.org/documentation/">Open Source Flash の資料</a>のうち Red5 関連のものの一部を和訳した。露骨な typo などおかしなところは翻訳のついでに修正してある。</p>
<p>そもそもパケットキャプチャーやったりディスアセンブルしたりして構造を推測してるだけな上に、それを元にした実装も本家に比べれば完全に動いている訳ではないためこれらの資料には充分な信頼性が無く、また情報をまとめる作業もいい人員が揃っているとはお世辞にも言い難い状況なので、一応参考にならんではない、という程度の内容に過ぎないということを認識しておいていただきたい。</p>
<p>まず <a href="http://osflash.org/documentation/rtmp">RTMP Protocol [DRAFT] Open Source Flash</a> の 2007/06/16 版。今回最も重要な文書なのに、オランダ人が変な英語でぐっちゃぐちゃに書きちらかしていて大変なことになっていた。頻繁に更新されてはいるようだが少なくとも現時点では FIXME の大安売り。翻訳文からは削除しておいたが「地球外生命体から詳細情報を受け取ったら 5$ あげるよ」みたいな感じの馬鹿な記述もあった。地球外って Macromedia のことか。もうあとはソースに頼るしかない。</p>
<blockquote>
<h4>RTMP 仕様 [ドラフト]</h4>
<p>この文書のライセンスは、<a href="http://creativecommons.org/licenses/by-sa/2.5/deed.ja">Creative Commons の「表示・継承 2.5」</a>です。</p>
<h5>Introduction</h5>
<p>RTMP とは、バイナリの TCP コネクションもしくはポーリング HTTP トンネルを使って、リアルタイムでオブジェクト、ビデオ、オーディオを配送するために Flash で使われているプロトコルです。</p>
<p>このプロトコルは、flv で使われているような、amf や生のオーディオ・ビデオのデータパケットのコンテナです。</p>
<p>一つのコネクションで、異なるチャンネルを使って多くのネットストリーミングを多重送信することができます。これらのチャンネルのパケットはボディチャンクに収まるよう修正したサイズに分割されます。</p>
<h5>Connection</h5>
<p>ストリームに接続し再生するための ActionScript サンプルは次のようになります:</p>
<pre><code>var videoInstance:Video = your_video_instance;
var nc:NetConnection = new NetConnection();
var connected:Boolean = nc.connect(&quot;rtmp:/localhost/myapp&quot;);
var ns:NetStream = new NetStream(nc);
videoInstance.attachVideo(ns);
ns.play(&quot;flvName&quot;);</code></pre>
<p>デフォルトポートは 1935 です。</p>
<h5>Handshake</h5>
<p>Client → Server : ハンドシェイクリクエストを送信します。これはプロトコルパケットではありませんが、0x03 で始まり 1536 byte 続いています。この内容はプロトコルに必須のものに見えませんが、ランダムな内容でもありません。詳しくは <a id="return1" href="#see1">1)</a> を参照してください。</p>
<p>Server → Client : ハンドシェイクレスポンスを送信します。これは RTMP パケットではありませんが、0x03 で始まり 1536 byte のチャンクが二つ（つまり合計 3072 byte）続いています。バイト列のうち二つ目のチャンクは、クライアントからのハンドシェイクリクエストをそのまま返しています。一つ目のチャンクは何にでも成り得ます。null のバイト列で構わないようです。（訳注: 次の説明から考えて、一つ目と二つ目のチャンクは逆の可能性があります）</p>
<p>Client → Server : ハンドシェイクにおいてサーバーが生成した二つ目の 1536 byte チャンクをそのまま送信します。（訳注: 前の説明から考えて、二つ目ではなく一つ目の可能性があります）</p>
<p>この時点でハンドシェイクは完了し、更に RTMP のパケットが始まります。</p>
<p>Client → Server : RTMP 接続パケットを送ります。</p>
<p>Server → Client : サーバーレスポンスです。</p>
<p>...などなど...</p>
<h5>RTMP Datatypes</h5>
<table summary="RTMP データタイプの解説">
<tr><td>0x01</td><td>チャンクサイズ</td><td>パケットのチャンクサイズを変更します</td></tr>
<tr><td>0x02</td><td>Unknown</td><td>_</td></tr>
<tr><td>0x03</td><td>Bytes Read</td><td>両サイドで x byte 読み込むごとに送信</td></tr>
<tr><td>0x04</td><td>Ping</td><td>サブタイプで利用されるストリーム制御メッセージ</td></tr>
<tr><td>0x05</td><td>サーバー帯域幅</td><td>サーバーの下り帯域幅</td></tr>
<tr><td>0x06</td><td>クライアント帯域幅</td><td>クライアントの上り帯域幅</td></tr>
<tr><td>0x07</td><td>Unknown</td><td>_</td></tr>
<tr><td>0x08</td><td>オーディオデータ</td><td>オーディオを収納したパケット</td></tr>
<tr><td>0x09</td><td>ビデオデータ</td><td>ビデオを収納したパケット</td></tr>
<tr><td>0x0A - 0x11</td><td>Unknown</td><td>_</td></tr>
<tr><td>0x12</td><td>通知</td><td>応答を要求しない呼び出し</td></tr>
<tr><td>0x13</td><td>シェアードオブジェクト</td><td>サブタイプを持つ</td></tr>
<tr><td>0x14</td><td>呼び出し</td><td>リモートコールのように使うが、ストリームのアクションにも使われる</td></tr>
</table>
<p><strong>FIXME</strong> RTMP タイプごとの構造体を追加（Luke）</p>
<h5>Shared Object DataTypes</h5>
<table summary="シェアードオブジェクトのデータタイプの解説">
<tr><td>0x01</td><td>接続</td></tr>
<tr><td>0x02</td><td>切断</td></tr>
<tr><td>0x03</td><td>属性設定</td></tr>
<tr><td>0x04</td><td>データ更新</td></tr>
<tr><td>0x05</td><td>属性更新</td></tr>
<tr><td>0x06</td><td>メッセージ送信</td></tr>
<tr><td>0x07</td><td>ステータス</td></tr>
<tr><td>0x08</td><td>データのクリア</td></tr>
<tr><td>0x09</td><td>データの削除</td></tr>
<tr><td>0x0A</td><td>属性の削除</td></tr>
<tr><td>0x0B</td><td>データの初期化</td></tr>
</table>
<p><strong>FIXME</strong> シェアードオブジェクトのそれぞれのタイプについての構造体と詳細の追加（Joachim）</p>
<h5>RTMP Packet Structure</h5>
<p><strong>FIXME</strong> このドキュメントは書き始めたばかりでまだ完成していない</p>
<p>RTMP パケットは固定長ヘッダーと最大 128 byte までの可変長ボディから成ります。ヘッダーは四種類のサイズのうち一つが使用されます:<br />
12, 8, 4, 1 byte</p>
<p>パケットの最初の 1 byte（ヘッダーの最初の 1 byte でもある）の中にヘッダーの長さを決定する最も重要な二つのビットがあります。0xC0 と AND をとることで抜き出すことができます。ヘッダー長の取り得る値は以下のテーブルで定義されています:</p>
<table summary="ヘッダー長の解説">
<tr><td>Bits</td><td>ヘッダー長</td></tr>
<tr><td>00</td><td>12 bytes</td></tr>
<tr><td>01</td><td>8 bytes</td></tr>
<tr><td>10</td><td>4 bytes</td></tr>
<tr><td>11</td><td>1 byte</td></tr>
</table>
<p>同様に、以下のドキュメントも確認してください。本物の RTMP で AMF の通信を行う場合にはほとんど間違いはありません。AMF は RTMP 内部で使われていますが、以下のドキュメントではそのことについて扱っていません。</p>
<p>Mick's Breakdown of RTMP: <a href="http://osflash.org/_media/rtmp_spec.jpg">JPG</a> / <a href="http://www.acmewebworks.com/Downloads/openCS/TheAMF.pdf">PDF</a></p>
<h5>Streaming</h5>
<p><strong>FIXME</strong> 更に情報が必要</p>
<p>基本的なストリーミング配信サイクルで起こっていること :</p>
<p>Client → Server : CreateStream リクエストを送信（単体の RTMP パケットなのでしょうか）</p>
<p>Server → Client : streamIndex 番号のレスポンスを送信</p>
<p>Client → Server : ストリーミング配信（ここで何が行われているのでしょうか）</p>
<p>Client → Server : オーディオとビデオのパケットの送信（パケットはストリーミング配信リクエストにおける同一のチャンネルを通じて streamIndex が指し示すソースから送信されます）</p>
<p>（この項には更に詳細な記述が必要だと考えています。RTMP がどうやってストリームの同一性を保持したまま一つのコネクションを超えて交互にストリーミング通信を実現しているかについて概要の説明による導入部を追加する必要があると思います）</p>
<p><a id="see1" href="#return1">#1)</a> そう、ランダムに見えるのですがかなりのパターンが見受けられます。ろくなデータはないと考えていますが。バイト列をエディターで（20 byte ごとに折り返して）整理した時、バイト列を順に見ていくとカラムは減少していくのですが、それぞれのカラム間ではランダムな内容に見えます。私は一晩かけて二つの難問の解決に挑戦しましたが、結局ギブアップしました。あ、開始直後に僅かなデータを発見しました。上昇するカウンターがあるのですが、クライアントの稼働時間のようです。</p>
</blockquote>
次に <a href="http://mirror1.cvsdude.com/trac/osflash/red5/browser/doc/trunk/SPEC-RTMPT.txt">Specification of the RTMPT protocol</a> の 2006/04/20 版。内容が極めて簡素なためほとんど完全な情報が揃っている。
<blockquote>
<h4>Specification of the RTMPT</h4>
<dl>
<dt>author</dt><dd>Joachim Bauch</dd>
<dt>contact</dt><dd>mail@joachim-bauch.de</dd>
<dt>date</dt><dd>2006-03-23</dd>
<dt>copyright</dt><dd><a href="http://creativecommons.org/licenses/by-sa/2.5/deed.ja">`Creative Commons License (by-sa)`</a></dd>
</dl>
<h5>Overview</h5>
<p>このドキュメントはオープンソース Flash サーバー Red5 での RTMPT トンネリングプロトコル実装について記述しています。このドキュメントは Macromedia の公式仕様ではないことに注意してください。他の方々の RTMPT を活用したソフトウェア開発の助けになることを願っています。</p>
<p>RTMPT は基本的には、クライアントからサーバーに送られる POST リクエストを使った、HTTP による RTMP のラップです。HTTP コネクションの非永続的な特性のため、RTMPT は定期的にサーバーや他のクライアントから発生したイベントの通知を取得するために<a href="http://e-words.jp/w/E3839DE383BCE383AAE383B3E382B0.html">ポーリング</a>するクライアントが必要です。</p>
<p>RTMPT セッションの生存時間の間に送られてくる可能性のあるリクエストは、以下にある四種類です。</p>
<h5>URL</h5>
<p>URL は次のような形式になっています::</p>
<pre><code>
http://server/&lt;command&gt;/[&lt;client&gt;/]&lt;index&gt;
</code></pre>
<dl>
<dt>`&lt;command&gt;`</dt>
<dd>RTMPT リクエストタイプを示します（下記参照）</dd>
<dt>`&lt;client&gt;`</dt>
<dd>リクエストを実行するクライアントの id を指定します（確立したセッションが呼ばれた場合のみ）</dd>
<dt>`&lt;index&gt;`</dt>
<dd>喪失パッケージの発見に使われると思われる連番</dd>
</dl>
<h5>Request / Response</h5>
<p>全ての HTTP リクエストはいくつかの共通する特徴を持っています:</p>
<ul>
<li>HTTP 1.1 POST を利用している</li>
<li>Content-Type は `application/x-fcs`</li>
<li>コネクションはネットワークのオーバーヘッドを減らすために生かしておくべきである</li>
</ul>
<p>HTTP レスポンスはいくつかの特徴を持っています:</p>
<ul>
<li>Content-Type は `application/x-fcs`</li>
<li>確立した全セッションに対してレスポンスデータの最初のバイトでクライアントのポーリング間隔を制御しており、これが高い値の場合にはポーリングリクエストが少なくなる</li>
</ul>
<h5>ポーリング間隔</h5>
<p>サーバーはデータを返しした後は常に 0x01 の値から開始し、十回空の応答を返したらその値を増加させます。最大の遅延時間は 0x21 で、この場合は二つのリクエストの間におよそ 0.5 秒の遅延が発生します。</p>
<p>Red5 は現在次のようなステップで遅延時間を増加させています:<br />
0x01, 0x03, 0x05, 0x09, 0x11, 0x21</p>
<h5>接続開始（コマンド &quot;open&quot;）</h5>
<p>これはクライアントをサーバーに登録し新しいセッションを開始するために最初のリクエストでサーバーに送られるものです。サーバーは今後全てのリクエストでクライアントに使用されるユニーク id（通常は数字）を返します。</p>
<p>注意: 応答はポーリング間隔の値を含んでいません！ 接続に成功すると URL で使われている連番をリセットします。</p>
<h5>クライアント更新（コマンド &quot;send&quot;）</h5>
<p>クライアントが RTMP を使ってサーバーに送ろうとしたデータは単に HTTP ヘッダーなどを前に付けるだけで、そのまま送信しています。</p>
<p>サーバーは 1 byte のポーリング間隔制御と、あれば RTMP データを付けて応答します。</p>
<h5>ポーリングリクエスト（コマンド &quot;idle&quot;）</h5>
<p>クライアントがそれ以上サーバーに送るデータを持っていなかった場合、シェアードオブジェクトのようなストリーミングデータかイベントを受信し更新するためポーリングを行わなければなりません。</p>
<h5>セッションの切断（コマンド &quot;close&quot;）</h5>
<p>クライアントが接続を終了したい場合、&quot;close&quot; コマンドを送信し、サーバーから 0x00 が返されます。</p>
</blockquote>
<p>空返答によるポーリング間隔増加の説明がわかりづらいので簡単に書くと次のようになる。</p>
<ul>
<li>クライアント（以下、蔵）「ちょっと、データ出しなさいよ（ポ、ポーリングしちゃった/////）」</li>
<li>サーバー（以下、鯖）「ほらよ、次は 0x01 後に来てくれ（データを返したので現時点での空返答カウントゼロ）」</li>
<li>蔵「次のデータあるんじゃないの？ 早くしなさいよね」</li>
<li>鯖「ないから 0x01 後に来てくれ（空返答一回目）」</li>
<li>蔵「来いって言ってたから来てやったわよ。早く出しなさいよ」</li>
<li>鯖「まだないから 0x01 後に来てくれ（空返答二回目）」</li>
<li>（中略）</li>
<li>蔵「ポーリングしなきゃいけないから来ただけなんだから勘違いしないでよね！」</li>
<li>鯖「まだないな。0x01 後に来てくれ（空返答十回目、ポーリング間隔を 0x03 に増加、空返答カウントリセット）」</li>
<li>蔵「また来てやったわよ！」</li>
<li>鯖「ない。0x03 後に来てくれ（空返答一回目）」</li>
<li>蔵「ほ、ほら、来てやったんだから早くデータ出してよ（三倍も待たされるなんて……）」</li>
<li>鯖「ない。0x03 後に来てくれ（空返答二回目）」</li>
<li>（中略）</li>
<li>蔵「データは……」</li>
<li>鯖「だからないって。次は 0x21 後（空返答九回目）」</li>
<li>蔵「来たんだけど……」</li>
<li>鯖「ないって言ってんだろ。0x21 後（空返答十回目、既にポーリング間隔上限に達しているため増加なし）」</li>
<li>蔵「あの……」</li>
<li>鯖「別のクライアントからデータが来てるぞ（空返答カウントリセット、ポーリング間隔を 0x01 にリセット）。次は 0x01 後に来てくれ」</li>
<li>蔵「あんたが言うから 0x01 で取りに来てやったわよ！ データあるんでしょ！」</li>
<li>鯖「さっきのクライアントがまたデータの続きを送ってきてた。次は 0x01 後（空返答カウントゼロ）」</li>
</ul>
<p>データが続いている場合は続きが送信される可能性が高いために短時間のうちにポーリングさせ続けて、空返答が続いた場合はしばらくデータのやり取りが行われない可能性が高いためポーリング間隔を伸ばす、という設計はこの手のプロトコルの常套手段で、Apple の Bonjour なんかでも使われている。</p>
<p>ところで、RTMPT は RTMP の HTTP によるトンネリングだから RTMPT protocol って書いていいんだけど、<a href="http://www.google.co.jp/search?q=%22RTMP+protocol%22"><abbr title="Real Time Messaging Protocol">RTMP</abbr> protocol って書いてる人</a>が山程いて驚いた。頭痛が痛い。</p>

]]></description>
   <category domain="http://blog.itoyanagi.name">entries</category>
   <pubDate>Mon, 24 Sep 2007 14:00 GMT</pubDate>
</item>
</channel>
</rss>
