H.264の動画ファイルをCGI経由で出力してiOSで再生させる方法。

| コメント(0) | トラックバック(0)
このエントリーをはてなブックマークに追加

さてさて、今回もまたiOSネタです^^(もしかしたらAndroidも)
CGI(PHP, Perl, Ruby, Python・・・諸々)を介してH.264を出力すると、
名称未設定 2.png
このように、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;


いかがでしたか?

これで動画は正しく再生できるようになったと思います。多分、大丈夫だと思いますが、もし不具合があったら下のコメント欄にぜひ書き込んでくださいねぇ〜。


トラックバック(0)

トラックバックURL: http://blog.taporu.net/system/mt-tb.cgi/17

コメントする

PR

プロフィール

ユーザ名:とっとこ。

このブログ記事について

このページは、とっとこが2014年6月 1日 02:46に書いたブログ記事です。

ひとつ前のブログ記事は「Mac OS X Mavericks Server のメールサーバで正しくPush通知が送られない問題について。」です。

次のブログ記事は「iOS 8 Beta 1ではMVNOテザリング不可」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

OpenID対応しています OpenIDについて

PR