2012-10-24 6 views
6

HTML5ビデオで動作するようにカスタムWebサーバーアプリケーションを変更しようとしています。HTML5ビデオと部分的な範囲のHTTPリクエスト

これは基本的な<video>タグを持つHTML5ページを提供し、実際のコンテンツに対するリクエストを処理する必要があります。

私がこれまでに動作させる唯一の方法は、ビデオファイル全体をメモリにロードし、それを1回の応答で送り返すことです。実用的な選択肢ではありません。私はそれを一枚ずつ提供したい:100kbと言って返信し、ブラウザがもっとリクエストするのを待つ。

私は次のヘッダーを持つリクエストを参照してください。

http_version = 1.1 
request_method = GET 

Host = ###.###.###.###:## 
User-Agent = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0 
Accept = video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5 
Accept-Language = en-US,en;q=0.5 
Connection = keep-alive 
Range = bytes=0- 

私は部分コンテンツレスポンスを返送してみました:

HTTP/1.1 206 Partial content 
Content-Type: video/mp4 
Content-Range: bytes 0-99999/232725251 
Content-Length: 100000 

を次のように私は、さらにいくつかのGETリクエストを取得
Cache-Control = no-cache 
Connection = Keep-Alive 
Pragma = getIfoFileURI.dlna.org 
Accept = */* 
User-Agent = NSPlayer/12.00.7601.17514 WMFSDK/12.00.7601.17514 
GetContentFeatures.DLNA.ORG = 1 
Host = ###.###.###.###:## 

(ブラウザがファイルの特定の部分を望んでいることを示すものではありません) kをブラウザに送ると、ビデオは再生されません。

同じHTTPパケットで230 MBファイル全体を一度に送信しようとすると、同じビデオが正しく再生されます。

部分的なコンテンツリクエストによってうまく動作する方法はありますか?テスト目的でFirefoxを使用していますが、最終的にはすべてのブラウザで動作する必要があります。

+0

私はそれは奇妙あなたがテストのためのFirefoxを使用してFirefoxがサポートされていないMP4ファイルを送っていることがわかり、それは動作しません。 –

+0

これまではFirefox(H264プラグイン付き)とIE9をブラウザ側で、MP4とWebMをサーバ側で試してみました。私は何かが足りないと思う。 – user434507

+0

なぜクライアントから部分的な範囲要求を取得したいのですか?彼らは彼らが望むものを尋ねて、最も便利な方法でそれらに送ることができます。私は、(Chromeからリクエストされた)ストリーミングオーディオとカスタムサーバーを使ってリアルタイムで音を送りました。私はこれがデフォルトで容認できる方法だと思う。ネットワークの負荷が気になる場合は、スムーズなビデオ再生に十分なスピードでスロットルを使用してください。 – Stan

答えて

4

これは古い質問ですが、役立つ場合は、コードベースで使用する次の「モデル」を試すことができます。

class Model_DownloadableFile { 
private $full_path; 

function __construct($full_path) { 
    $this->full_path = $full_path; 
} 

public function get_full_path() { 
    return $this->full_path; 
} 

// Function borrowed from (been cleaned up and modified slightly): http://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file/4451376#4451376 
// Allows for resuming paused downloads etc 
public function download_file_in_browser() { 
    // Avoid sending unexpected errors to the client - we should be serving a file, 
    // we don't want to corrupt the data we send 
    @error_reporting(0); 

    // Make sure the files exists, otherwise we are wasting our time 
    if (!file_exists($this->full_path)) { 
     header('HTTP/1.1 404 Not Found'); 
     exit; 
    } 

    // Get the 'Range' header if one was sent 
    if (isset($_SERVER['HTTP_RANGE'])) { 
     $range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions 
    } else if ($apache = apache_request_headers()) { // Try Apache again 
     $headers = array(); 
     foreach ($apache as $header => $val) { 
      $headers[strtolower($header)] = $val; 
     } 
     if (isset($headers['range'])) { 
      $range = $headers['range']; 
     } else { 
      $range = false; // We can't get the header/there isn't one set 
     } 
    } else { 
     $range = false; // We can't get the header/there isn't one set 
    } 

    // Get the data range requested (if any) 
    $filesize = filesize($this->full_path); 
    $length = $filesize; 
    if ($range) { 
     $partial = true; 
     list($param, $range) = explode('=', $range); 
     if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes' 
      header("HTTP/1.1 400 Invalid Request"); 
      exit; 
     } 
     $range = explode(',', $range); 
     $range = explode('-', $range[0]); // We only deal with the first requested range 
     if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid 
      header("HTTP/1.1 400 Invalid Request"); 
      exit; 
     } 
     if ($range[0] === '') { // First number missing, return last $range[1] bytes 
      $end = $filesize - 1; 
      $start = $end - intval($range[0]); 
     } else if ($range[1] === '') { // Second number missing, return from byte $range[0] to end 
      $start = intval($range[0]); 
      $end = $filesize - 1; 
     } else { // Both numbers present, return specific range 
      $start = intval($range[0]); 
      $end = intval($range[1]); 
      if ($end >= $filesize || (!$start && (!$end || $end == ($filesize - 1)))) { 
       $partial = false; 
      } // Invalid range/whole file specified, return whole file 
     } 
     $length = $end - $start + 1; 
    } else { 
     $partial = false; // No range requested 
    } 

    // Determine the content type 
    $finfo = finfo_open(FILEINFO_MIME_TYPE); 
    $contenttype = finfo_file($finfo, $this->full_path); 
    finfo_close($finfo); 

    // Send standard headers 
    header("Content-Type: $contenttype"); 
    header("Content-Length: $length"); 
    header('Content-Disposition: attachment; filename="' . basename($this->full_path) . '"'); 
    header('Accept-Ranges: bytes'); 

    // if requested, send extra headers and part of file... 
    if ($partial) { 
     header('HTTP/1.1 206 Partial Content'); 
     header("Content-Range: bytes $start-$end/$filesize"); 
     if (!$fp = fopen($this->full_path, 'r')) { // Error out if we can't read the file 
      header("HTTP/1.1 500 Internal Server Error"); 
      exit; 
     } 
     if ($start) { 
      fseek($fp, $start); 
     } 
     while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server 
      $read = ($length > 8192) ? 8192 : $length; 
      $length -= $read; 
      print(fread($fp, $read)); 
     } 
     fclose($fp); 
    } else { 
     readfile($this->full_path); // ...otherwise just send the whole file 
    } 

    // Exit here to avoid accidentally sending extra content on the end of the file 
    exit; 
} 
} 

あなたは、このようにそれを使用します。

(new Model_DownloadableFile('FULL/PATH/TO/FILE'))->download_file_in_browser(); 

これは、ファイルまたは完全なファイルなどの一部を送信することに対処し、これと他の状況の多くに私たちのためにうまく機能します。それが役に立てば幸い。

+1

ヘッダー( "Content-Length:$ filesize")を読み取る行。 は実際には でなければなりません。header( "Content-Length:$ length"); ファイルのサイズではなく、配信される合計バイト数です。 Chromeのビデオプレーヤーは正しく設定されていない場合はこれを禁止します。 – frankieandshadow

+0

良い点、私は元の投稿を修正しました。 $ lengthは、$ rangeがtrueの場合にのみ設定されるので、$ filesizeに最初に設定されていることを確認し、$ rangeがtrueなら上書きします。 –

+0

私の問題は、PHPがContent-Type:text/htmlを配信することです。たとえheader_remove( 'Conten-Type'); –

1

私はリアルタイムのトランスコードを行っているので部分的な範囲リクエストが必要なので、ファイルを完全にトランスコードして、リクエストに応じて使用することはできません。あなたはまだフルボディの内容を知らない応答については

(おContent-Length、ライブエンコーディングを推測不可)、チャンクエンコーディングを使用します。

HTTP/1.1 200 OK 
Content-Type: video/mp4 
Transfer-Encoding: chunked 
Trailer: Expires 

1E; 1st chunk 
...binary....data...chunk1..my 
24; 2nd chunk 
video..binary....data....chunk2..con 
22; 3rd chunk 
tent...binary....data....chunk3..a 
2A; 4th chunk 
nd...binary......data......chunk4...etc... 
0 
Expires: Wed, 21 Oct 2015 07:28:00 GMT 

各チャンクは、それが利用可能なときに送信されます:とき22がヘキサ(ただし0x22 = 34バイト)におけるチャンクバイト長を与える

22; 3rd chunk 
tent...binary....data....chunk3..a 

、等、いくつかのフレームが符号化されるか、または出力バッファが満杯である場合、100KBが生成されます; 3rd chunkは余分なチャンク情報(オプション)で、tent...binary....data....chunk3..aはチャンクの内容です。

0がゼロ以上トレーラ(許可ヘッダフィールド)、続いて複数のチャンクは、ヘッダ内に存在定義されないことを意味
0 
Expires: Wed, 21 Oct 2015 07:28:00 GMT 

:符号化が終了し、すべてのチャンクが送信されている、ことによって終了そして

、 、チェックサムまたはデジタル署名を提供するために、(Trailer: ExpiresExpires: Wed, 21 Oct 2015 07:28:00 GMTが必要とされていない)など。ここ

あるファイルがすでに生成された場合、サーバーの応答の同等(無ライブエンコーディング):

詳細については

Chunked transfer encoding — WikipediaTrailer - HTTP | MDN

+0

ビデオタグが(少なくともChromeでは)チャンクエンコーディングをサポートしているとは思えません。 – themihai

+0

HTTP/1.1をサポートするには、チャンク転送エンコーディングが必要です。また、ChromeとFirefoxは、チャンクされたトランスポートエンコーディングを使用してうまく動画を再生しました。 Safari(デスクトップとiOS)では、ビデオを再生するために[ranges](https://en.wikipedia.org/wiki/Byte_serving)サポートが必要です(オーディオには必要ありません)。トランスポートのエンコーディングと範囲は互換性がありませんが、バイト範囲には事前にバイトサイズを知っておく必要があります。 そのためには、HLSやMSEのようなライブストリーミングプロトコルを使用することができます(ビデオをエンコードする際は特別な処理が必要です)。 [hls.js](https://github.com/video-dev/hls.js)を参照してください。 – mems

+0

Chromeはサーバーがサポートしているかどうかにかかわらず部分的なリクエストを行います。 html5動画を配信するには十分ではありません。サーバーがコンテンツの全長を宣伝していない場合(つまり、ワイルドカード*を提供している場合)、最初の部分的なリクエストが完了した後にプレーヤーが停止し、次の部分をリクエストするようになります。 – themihai

関連する問題