FLVビデオの擬似ストリーミング最適化

当社のプロジェクトの1つは、youtubeに似たオンラインビデオサービスです。 ngx_http_flv_moduleモジュールを備えた素晴らしいnginx Webサーバーは、ストリーミング機能のブロードキャストと実装に使用されます。



サーバーのネットワークチャネルが過負荷になるだけでなく、サーバーのディスクサブシステムが読み取り要求に対処しなくなるレベルまでビューの数が達するまで、すべてが正常でした。



ディスクサブシステムの問題は、10番目のRAIDをインストールするだけで解決されました。 しかし、ネットワークには何か関係があり、単一のストリームではなく、同じサイズの断片で個別に表示するファイルをアップロードするというアイデアが浮上しました。 幸いなことに、すべてがフラッシュで必要でした。 ドキュメントによると、NetStreamオブジェクトには必要な機能を持つappendBytes()メソッドがあります。 公式文書からの抜粋は次のとおりです。



「再生のためにByteArrayをNetStreamインスタンスに渡します。 データ作成モードのNetStreamオブジェクトでこのメソッドを呼び出します。 NetStreamオブジェクトをデータ作成モードにするには、nullに接続されているNetConnectionオブジェクト用に作成されたNetStreamインスタンスでNetStream.play(null)メソッドを呼び出します。 データ作成モードではないNetStreamオブジェクトに対してappendBytes()メソッドを呼び出すことはできません。そうしないと、例外がスローされます。



Loaderクラスを取り、Rangeヘッダーを規定しているように見えますが、ここではAdobe開発者の鉱山が待っています。 HTTPで動作する標準のアクションスクリプトクラスは、範囲ヘッダーを使用しません。 ここでも、セキュリティ上の理由から、HTTPバージョンで実行されているクラスのコードでRangeヘッダーがブロックされていることが公式ソースから判明します。プレーヤーバージョン9までさかのぼります。 Socketソケットの基本クラスを使用して、HTTPクライアントを作成できます。 Google ビンの利点は、既製のクラスHTTPClientLibであることがわかりました。 以下のコードは、ビデオファイルの最初のメガバイトをダウンロードして再生します。



private var ns:NetStream; private var video:Video; private var meta:Object; private var client:HttpClient; private var filesize:Number = 0; private var loadedBytes:Number = 0; private var data:ByteArray = new ByteArray(); private var datadelta:Number = 1024*1024; //    - private var file:String = %   %; private function init():void{ // ,      var nsClient: Object = {}; //       nsClient.onMetaData = metaDataHandler; //   ;          null       - var nc:NetConnection = new NetConnection(); nc.connect(null); // ,          ns = new NetStream(nc); ns.client = nsClient; //     ns.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler); //   ns.addEventListener(IOErrorEvent.IO_ERROR,nsIOErrorHandler); //      video = new Video(); video.attachNetStream(ns); //  .  ,       video.smoothing = true; uic.addChild(video); // ,      NetStream client = new HttpClient(); //     loadData(); //     NetStream ns.play(null); } private function loadData():void{ //     var uri:URI = new URI(file); //,      var request:HttpRequest = new Get(); //maxdata      //loadedBytes -     var maxdata:Number = loadedBytes+datadelta; //    ,           if (maxdata>=filesize and filesize>0){ request.addHeader('Range','bytes='+loadedBytes+'-'); } else { request.addHeader('Range','bytes='+loadedBytes+'-'+maxdata); } //    ,       data //        //         NetStream  //     client.listener.onData = function(e:HttpDataEvent):void { var bytes:ByteArray = new ByteArray(); bytes = e.bytes; bytes.position = 0; data.writeBytes(bytes); }; //      client.listener.onComplete = function(e:HttpResponseEvent ):void{ //       loadedBytes+=data.length;       filesize = Number(e.response.header.getValue('Content-Length'))/1024; //     ns.appendBytes(data); //  data.clear(); //  .      inLoaded = false; }; //    client.request(uri,request); }
      
      







コードが機能するための1つの重要なポイント:NetStramオブジェクトは、NetStream.play(null)としてオンになっているデータ作成モードである必要があります。 これは、最初のビデオが到着する前に行う必要があります。



さらに、必要に応じて、ファイルの残りの部分に1メガバイトの同じ部分をロードする必要があります。 システムの多数のテスト中に、1 mb(ビデオの約15秒に相当)のサイズが実験的に取得されました。 負荷は、イベントで次のコードを実行するタイマーによって制御されます。



 if ((!inLoaded) && (ns.bufferLength <= Math.ceil(loadtime+timeDelta)) && (loadedBytes < filesize)){ inLoaded = true; loadData(); }
      
      







次の条件に従って、ダウンロードを開始します。

inLoaded = false-ステータスインジケータ、フローtrue-データがロードされている、false-いいえ。

ns.bufferLength <= Math.ceil(loadtime + timeDelta)-前回のロード(loadtime)にストックのデルタ(timeDelta)を加えた時間以下の再生バッファーに残っている時間が少なくなります。

loadedBytes <filesize-再生ファイルの終わりに達していません。



データ作成モードの重要なポイントは、NetStream.Play.Startイベントです。NetStream.Play.Stopイベントは生成されません。 したがって、ダウンロードしたデータの量またはビデオクリップで失われた時間を監視する必要があります。



timeDelta変数を使用すると、ユーザーのチャンネルの品質を調整できます。再生バッファーが空の場合、次のデータが読み込まれる前にデルタが増加します。



巻き戻しについて少し


データ作成モードでは、NetStreamクラスの一部のメソッドは通常の機能を変更します。 これは、seek()メソッドにも適用されます。指定されたポイントに最も近いキーフレーム(いわゆるIフレーム)を検索します。 データ作成モードでは、seekメソッドを呼び出した後、appendBytesAction()メソッドを呼び出す前に、クラスはappendBytes()メソッドによって送信されたすべてのデータを無視し始めます。 メソッドの引数には、NetStreamAppendBytesAction.RESET_BEGINまたはNetStreamAppendBytesAction.RESET_SEEKの値を指定できます。最初の値は、新しいデータに新しいflvファイルヘッダーが存在することを示し、クラスの再生カウンターをリセットします。 サンプルの巻き戻しビデオコードを次に示します。



 //    ,   - -,               for (var i:Number=0;i<positions.length;i++){ if ((positions[i]<=seekpositions)&&(positions[i+1]>=seektpositions)){ //                loadedBytes = positions[i]; break; } } // seek       ns.seek(seektime); //C  NetStream         ,      ns.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK); //    loadData();
      
      







times-ナビゲーションポイントの時間オフセットの配列。

位置-ナビゲーションポイントのオフセットバイトの配列。



いくつかのコメント


HTTPClientLibライブラリの機能の1つは、80番目から取得できる標準オブジェクトとは異なり、ドメイン間の作業中にソケットセキュリティポリシー(crossdomain.xml)を持つファイルがサーバーポート843から要求されることです。 したがって、nginxサーバー構成に次のエントリが追加されました。



 server { listen 843; server_name localhost; location / { rewrite ^(.*)$ /crossdomain.xml; } error_page 400 /crossdomain.xml; location = /crossdomain.xml { root /home/www-root; default_type text/x-cross-domain-policy; } }
      
      







まとめ


この手法を使用してビデオをダウンロードするプレーヤーを使用すると、「過剰-スプリアス」トラフィックの量が大幅に削減され、ストリーミングサーバーの負荷が軽減されます。 平均200,000ビューで、トラフィックは30%減少しました。 ただし、上記の手法には「マイナス」の側面があります。つまり、ユーザーは少なくとも10.1バージョンのFlash Playerを持っています。



完全なコード例



All Articles