さてさて、今回もまたiOSネタです^^(もしかしたらAndroidも)
CGI(PHP, Perl, Ruby, Python・・・諸々)を介してH.264を出力すると、
このように、iPhone、iPadなどのiOSデバイスでは再生できません(´;ω;`)
普通に動画ファイルを保存して、それをiOSデバイスで開くと何事もなく再生されるのになぜCGI経由だと再生されないのか。
それはレンジ(電子レンジじゃないよ)という機能が関係しています。
通常、普通のファイルとしておいた動画ファイルは、Webサーバ(Apacheなど)が自動的にレンジを受け取り、それにあわせた情報を自動的に出力します。
・・・ところが!!
CGIを介して出力する場合、Webサーバはレンジ機能を実行してくれません。
つまり、自分でCGI側で実装しなければならないということです。
(続きへ・・・)
では、レンジというのはいったい何者なのか?
みなさんYouTubeなどの動画サイトを利用したことがあると思います。
そこで、まだダウンロードが完了していない部分から途中再生させたこともあると思います。
はい(`・ω・´)。
ここでピンっときた皆さん、正解です(`・ω・´)。
ずばり、このレンジというのはファイルの途中からのダウンロードを可能にする機能です。
このレンジは主にヘッダーの部分でやりとりします。
では、その動画ファイルに関しての簡単なやりとりやらを簡単に解説しましょう。
1.まずクライアント(iOS)が動画ファイルを普通に要求します。
↓
2.次にサーバが「このファイルはレンジ対応だよ〜、最終更新日はいつだよ〜、データサイズは○○バイトだよ〜、Eタグは○○だよ〜」とヘッダーで返して、普通に動画ファイルを出力します。
↓
3.その後クライアントが「じゃあ、データの○○バイトから○○バイトまでちょうだーい」と要求します
↓
4.最後にサーバが「OK〜。じゃあデータの○○バイトから○○バイトまで出力するよ〜、最終更新日はいつだよ〜、データサイズは○○バイトだよ〜、Eタグは○○だよ〜、さっきのファイルと一緒か確認してね♡」とヘッダーで返して、要求された出力分を出力します。
だいたいがこんな流れです。
実際には、「2.」で
Accept-Ranges: bytes Content-Length: (出力ファイルサイズ) Last-Modified: (GMTタイム) Etag: "(リクエストURLをMD5ハッシュしたものに、全体のファイルサイズを付け加えたもの)"
「3.」では、
HTTP_RANGE = (開始バイト)-(終了バイト)
「4.」では、
HTTP/1.1 206 Partial Content Accept-Ranges: bytes Content-Length: (出力ファイルサイズ) Last-Modified: (GMTタイム) Etag: "(リクエストURLをMD5ハッシュしたものに、全体のファイルサイズを付け加えたもの)" Content-Range: bytes (最初のバイト数)-(最後のバイト数)/(実際のファイルサイズ)
というやりとりを行っています。
それを実際にPerlで記述したのがこちら↓
このスクリプトでは、$ImageDataに動画のバイナリデータ、$Dateに動画の最終更新日「2010/04/03 (Sat) 02:03:19」のようなデータが入っています。
use Digest::MD5 qw/md5_hex/; use Time::Local; $REQ = $ENV{"REQUEST_URI"}; $REN = $ENV{'HTTP_RANGE'}; $FileSize = length($ImageData); $Date =~ /^(\d{4,4})\/(\d{2,2})\/(\d{2,2})\s(?:.*?)\s(\d{2,2})\:(\d{2,2})\:(\d{2,2})$/; $RealDate = timelocal ($6,$5,$4,$3,$2,$1); @GMTime = gmtime($RealDate); @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); @Day = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); $Modi = sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", $Day[$GMTime[6]], $GMTime[3], $Month[$GMTime[4]], $GMTime[5]+1900, $GMTime[2], $GMTime[1], $GMTime[0]); print "Last-Modified: $Modi\n"; print "Accept-Ranges: bytes\n"; print "Content-type: video/mp4\n"; print "Etag: \"" . md5_hex( $REQ ) . "$FileSize\"\n" ; if($REN){ $REN =~ /(\d+)\-(\d+)/; $range_start = $1; $range_end = $2; if($range_end > $range_start){ $length = $range_end - $range_start +1; }else{ $range_end = $FileSize - 1; $length = $FileSize - $range_start; } $range2 = "$range_start-$range_end"; print "Status: 206 Partial Content\n"; print "Content-Length: $length\n"; print "Content-Range: bytes $range2/$FileSize\n"; $ImageData = substr($ImageData, $range_start, $range_end + 1); }else{ $content_length = $FileSize ; print "Content-Length: $FileSize\n"; } print "\n"; print $ImageData; exit;
いかがでしたか?
これで動画は正しく再生できるようになったと思います。多分、大丈夫だと思いますが、もし不具合があったら下のコメント欄にぜひ書き込んでくださいねぇ〜。
コメントする