2013-10-24 14 views
11

私のローカルDBからビデオをストリーミングするアプリケーションがあります。私は昨日データを返すことを試みるのに多くの時間を費やしましたRangeFileContentResultまたはRangeFileStreamResult成功なし。RangeFileContentResultとRanged Requestsを使用したビデオストリーミング

要するに、これらの2つの結果のいずれかとしてファイルを返すと、正しくストリームする(または全く再生しない)ように見えることがありません。

ブラウザからの要求が以下のヘッダで送信されます:

Range: bytes=0- 

そして、応答が提供されていますが、例として、これらのヘッダを与える:

Accept-Ranges: bytes 
Content-Range: bytes 0-5103295/5103296 

ネットワークトラフィックの面では、私が手部分的な結果のための一連の206、そして最終的に正しいと思われる(フィドラーによると)200。 Chromeのネットワークタブはこれと一致せず、最初のリクエスト(常に13バイトと見なしてハンドシェイクしています)と、キャンセルまたは保留中のステータスを持つリクエストがもう2つあります。 私が理解する限り、これは多かれ少なかれ正しいです、206 - キャンセル、206 - キャンセルなど。しかし、ビデオは決して再生されません。

コントローラの結果をFileResultに切り替えると、ビデオが再生され、Chrome、IE10、Firefoxが再生され、ダウンロードが完了する前に再生されているように見えます(ストリーミングのような感じです!そうではありません)

しかし、結果の範囲では、私はchromeやIEには何も得ておらず、ビデオ全体がfirefoxの1滴でダウンロードされます。

私が理解している限り、はダウンロードするバイトの範囲でクライアントに応答する必要があります(これは私の言うことではありません。ファイル全体を取得するように指示しています))。そして、クライアントはそれに対応する必要があります。

誰もがこのエリアに考えを持っていますか?具体的には:

a)RangeFileContentResultは、クライアントにバイトの範囲を送り返すべきですか? b)クライアント側から要求されたバイトの範囲を明示的に制御できる方法はありますか? c)RangeFileContentResultをリクエストしているときに、ブラウザがビデオを全く読み込まない原因となる理由がありますか?

編集:私は見ているかを説明するための図を追加しました:

RangedRequestImage

EDIT2:[OK]を、プロットは厚くなるようにします。我々は別のシステムのテストバージョンをプッシュするために必要なRangedFileのgubbinsで遊んとしながら、私は以下のように私のコントローラのアクションに「RangeFileContentResultを」左:

private ActionResult RetrieveVideo(MediaItem media) 
    { 
     return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now);    
    } 

をむしろ奇妙な、これは今、私たちのAzure上で期待通りに動作するようです私のローカルマシンにはまだありません。 Azure IIS8ではうまく動作するが、私のローカル7.5インスタンスでは動作しないIISベースのものがあるのだろうか?あなたのファイルは標準を使用してプレイするはずです。

+0

こんにちは。私はLib.Web.Mvcの著者であり、私はこの特定の問題を掘り下げて喜んでいます。私たちはiPad上のSafariと同様のものを持っていましたが、これはファイルに関する情報を得るために上限を使わずに要求を出したためです。ブラウザは、データが送信されるのを待たずに要求をキャンセルしていました(一般に、プロトコルはブラウザによって違反されていました)。これは似たようなものかもしれません。あなたの環境の詳細をメールで送ってください。私は問題を再現することができます。また、Lib.Web.Mvcのソースコード全体とその他のものをhttp:\\ tpeczek.codeplex.comで見つけることもできます。 – tpeczek

+0

@tpeczekこんにちはトーマス、ありがとう、あなたに私にあなたに連絡するための最善のアドレスを教えてくれますか?私が見ている主な変わったことは、同じビルドからロードする間の不一致です。例えばユーザ1の場合は動作し、ユーザ2の場合は動作しません。その後、ユーザー1がリフレッシュされ動作しますが、ユーザー2はリフレッシュされますが、ユーザー2はリフレッシュしません。私に最高の住所を知らせてください、そして私はあなたにいくつかの詳細をポップします。 – dougajmcdonald

+0

私のプロフィールからのもの:[email protected]できるだけ詳しく(コード、ビデオ、関係するブラウザ、設定、例外の詳細、ログなど) – tpeczek

答えて

4

ここで説明する問題の理由は、RangeFileContentResultコンストラクタのmodificationDateパラメータに渡された値は次のとおりです。

  • ETag

    return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now); 
    

    この日は、2つのヘッダを作成するためにRangeFileResultで使用されています - このヘッダーは、ブラウザとサーバーが同じエンティティについて話していることを確認するために使用する識別子です。

  • Last-Modified - このヘッダーは、エンティティの最終更新日をブラウザに通知します。

DateTime.Nowは、ブラウザが全体のプロセスがより長くかかる場合は、クライアントは通常、(全体のエンティティを取得する前に、部分的な要求が変化するETagLast-Modifiedヘッダーの値の理由かもしれません作るたびに渡されているという事実一秒)。

上記の場合、ブラウザはリクエストと共にIf-Rangeヘッダーを送信しています。このヘッダーは、エンティティタグ(またはIf-Rangeがこれらの2つの値のいずれかを持つことができるので、変更日)があまりない場合、エンティティ全体が再送信されるべきであることをサーバに伝えています。この場合、これが起こります。

クライアントは、検証のために次のヘッダーのいずれかを使用することを決定した場合は変更日は、「ダイナミック」であるという事実は、さらなる問題を引き起こす可能性がありますIf-Modified-SinceIf-Unmodified-SinceIf-MatchIf-None-Matchを。

この状況の解決策は、ファイルとの一貫性を確認するために、データベースに変更日を保存することです。

最適化のための場所もあります。部分的な要求が行われるたびに、DBからビデオ全体を取得する代わりに、アプリケーションをキャッシュしたり、関連する部分のみを取得したりすることができます(アプリケーションが使用しているデータベースエンジンがそのような操作を許可している場合)。このようなメカニズムは、RangeFileResultから配信し、WriteEntireEntityWriteEntityRangeのメソッドを上書きすることによって特殊なアクション結果を作成するために使用できます。

+0

これは正解です。私はパラメータの重要性を理解していないので、私は修正日としてDateTime.Nowを使用していました。 静的ファイルをストリーミングしていないかのように、このパラメータの周りのいくつかのドキュメントは有用であるかもしれません(私たちがそうでなかったように)、あなたはそれぞれのファイルで「修正日」を保存しない可能性があります。 – dougajmcdonald

+0

@dougajmcdonaldあなたが提案したように、ドキュメントは延長されます – tpeczek

+0

こんにちはトーマス、もう一度お付き合いいただき、ありがとうございました。 – dougajmcdonald

3

mofiPleaseはちょうど
RangeFileResult
RangeFileStreamResult

public ActionResult Movie() 
{ 
    var path = new FileStream(@"C:\temp\01.avi", FileMode.Open); 
    return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 
} 

http://youraddress.com:45454/Main/Movieを例えば)プロジェクトを実行し、Chromeで開いているあなたのMVCプロジェクトでこれらの2つのファイルをコピーしますクロムビデオプレーヤー。それはストリーミングだと、あなたが

return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now); 

にブレークポイントを置く場合は、再度ソースがストリーミングに使用されるバッファのサイズを変更するために変更するのは簡単です、それを見ることができます!

+0

こんにちはアントン、返信いただきありがとうございます。私はコードを少し修正する前に簡単な質問。 私たちのビデオはdbに格納され、media.Contentをbyte []とmedia.MimeTypeという文字列で返すエンティティを返すので、おそらくこの値を受け入れる 'RangeFileContentResult'にパラメータとしてパディングしていますそれはうまくいくはずですか?または、特にファイルストリームを開き、RangeFileStreamResultを使用する必要がありますか? – dougajmcdonald

3

[OK]をので、私は詳細にRangeFileResultを見てするのに十分な時間を持っていなかったが、私はちょうど RangeFileContentResult

からファイル(RangeFileContentResult)をダウンロードし、それが

public ActionResult Movie() 
{ 

    byte[] file = System.IO.File.ReadAllBytes(@"C:\HOME\asp\Java\Java EE. Programming Spring 3.0\01.avi"); 

    return new RangeFileContentResult(file, "video/x-msvideo", "01.avi", DateTime.Now); 

} 
のように見えるので、私のコードを変更しました

そしてもう一度動作します。しかし、私は、ビデオを停止したとき、私は例外があり、あなたがより良いユーザーはすでに切断時にit.In条件を処理するためにコードを変更RangeFileResult

if (context.HttpContext.Response.IsClientConnected) 
{ 
    WriteEntityRange(context.HttpContext.Response, RangesStartIndexes[i], RangesEndIndexes[i]); 
    if (MultipartRequest) 
       context.HttpContext.Response.Write("\r\n"); 
    context.HttpContext.Response.Flush(); 
} 

に起きていますが、まだ送信しようとしていることに気づきましたそれらの応答。

あなたはストリームを渡す場合でも、それに

using (FileStream) 
      { 
       FileStream.Seek(rangeStartIndex, SeekOrigin.Begin); 

       int bytesRemaining = Convert.ToInt32(rangeEndIndex - rangeStartIndex) + 1; 
       byte[] buffer = new byte[_bufferSize]; 

       while (bytesRemaining > 0) 
       { 
        int bytesRead = FileStream.Read(buffer, 0, _bufferSize < bytesRemaining ? _bufferSize : bytesRemaining); 
        response.OutputStream.Write(buffer, 0, bytesRead); 
        bytesRemaining -= bytesRead; 
       } 
      } 

作業コードが再びデータを読み込み、バイトにそれらを置くため再度、技術的にそれは[、あなたが[]またはストリームバイトを渡すかどうかは大きな違いはありません]配列!....それはあなた次第です!

ただし、あなたが提供するコンテンツタイプに注意することをお勧めします。 ポイントは、お使いのブラウザがそれを処理できなければならないということです!だからあなたが未知の何かを提供する場合、間違いなく、あなたのコンテンツタイプの文字列を検索problems.To持つことになります再び mime-types-by-content-type

を確認してください私は簡単に見を与え、あなたが持っている場合問題家に帰ると後でお手伝いします。

+0

もう一度アントン、私は後で細かい歯の櫛でこれを通過し、結果を知らせる。 Nugetの最新のLib.Web.MVCを入手しましたので、ファイルの必要性をカバーすることを期待しています(ただし、コンテンツを2回確認して、古いコピー)。 また、私のコンテンツタイプは、HttpPostedFileBaseのコンテンツタイプを使用してビデオがアップロードされたときに保存されます。すべてのブラウザがすべての拡張機能を処理するとは限りませんが、シンプルなFileResultを返すと、私は使用しているテストビデオを使って正しくレンダリングしますが、シンクを持たない1つのチャンクにのみ受け入れます。 – dougajmcdonald

関連する問題