perlでMP4やMOVの縦横サイズを取得する

タイトルのまんまのエントリ。
動機としては、ウチのおひとり様ActivityPubサーバーに動画をアップロードしてみたい。
んで、ActivityPubサーバーは、フォロワーさんのところに投稿(ActivityのJSON)を配送するんだけど、この時に縦横のピクセルサイズが必要っぽい。
動画を扱う時に、鉄板の定番のffmpegでサイズ取得も当然、ほかに解像度変更やファイルサイズの圧縮とかいろいろできるんだけど、レンタルサーバーでffmpegを使えるという話を見たことがなく、当然わたしが利用しているロリポップも使えない。
となると、縦横のピクセルサイズは自力でなんとかする必要がある。
ぐーぐる先生に聞きまくってなんとかなったのでメモ。
「MP4のファイル構造を解説」
https://qiita.com/satken2/items/d14b4113fe3fb5f5597b
↑こちらのサイトで一発解答(多謝!)
MP3とMOVファイルは基本3つのブロックの塊らしい。
サイズ | タイプ名 | データ |
4バイト | 4バイト | 可変 |
最初の4バイトに入ってるサイズでブロック全体の長さがわかる。
BOXの長さの情報が2^32バイト(=4294967296)を超える場合、最初の4バイトには0x00000001が格納され、その変わりに9バイト~16バイトに実際のBOXの長さが書かれます
ということだけど、今回わたしの場合は10秒もないようなSNSに上げるためだけの動画なのでスルーする。
そのブロックにもろもろの情報が入っているので、欲しいところを取得する。
このブロックのタイプはいったいなんなの?というのは
「QuickTime File Format」
https://developer.apple.com/documentation/quicktime-file-format#//apple_ref/doc/uid/TP40000939-CH204-SW1
↑こちらに全部ある。
ここまでわかればやることはわりと単純。
1)最初の4バイトをみてブロックサイズを取得
2)次の4バイトをみてブロックのタイプを取得
3)必要なタイプだったらそこで情報を取得して終了
必要なタイプじゃなかったら次のブロックをみにいく
ちょっとハマったのが
各ブロックはデータを持たないものと持ってるものがあるということ。
しょうがないんで、次のブロックを先読みしてタイプ名があるかないかで判定。
以下で欲しい情報が取れた。
わたしが欲しいのはタイプ「tkhd」(たぶん、トラックヘッダーの略)のブロックのデータ部に入ってる幅と高さ。
my @parent = (
'cmov', 'ctts', 'edts', 'esds',
'free', 'ftyp', 'iods', 'junk',
'mdia', 'minf', 'moov', 'mvhd',
'pict', 'pnot', 'rmda', 'rmra',
'skip', 'stbl', 'trak', 'uuid',
'wide');
my @child = (
'fiel', 'mdat', 'rdrf', 'rmcd',
'rmcs', 'rmdr', 'rmqu', 'rmvc',
'wfex', 'cmvd', 'co64', 'dcom',
'elst', 'gmhd', 'hdlr', 'mdhd',
'smhd', 'stco', 'stsc', 'stsd',
'stss', 'stsz', 'stts', 'tkhd',
'vmhd');
open(IMG, $file) || die;
binmode IMG;
my $len; my $type; my $data;
my $width; my $height;
while(1){
my $buf;
read(IMG, $buf, 4);
$len = unpack("N", $buf);
read(IMG, $type, 4);
last if ! $type;
# check next type
my $has_data;
seek(IMG,4, 1) || last;
read(IMG,$buf,4);
last if ! $buf;
$buf = quotemeta $buf;
unless( grep(m!$buf!, @parent) || grep(m!$buf!, @child) ){
$has_data = 1;
}
seek(IMG, -8, 1);
if( $has_data ){
$len -= 8;
last if $len < 0;
my $begin = tell(IMG);
read(IMG, $data, $len);
}
if($type =~ m!tkhd!){
my @check = unpack("x76 n x2 n", $data);
if( $check[0] && $check[1] ){
$width = $check[0]; $height = $check[1];
last;
}
}
}
perlはやっぱり凄くて、バイナリファイルも簡単に扱うことができる。
とはいえ、それを扱うスキルがない人間のせいでずいぶん時間がかかってしまった。
これでうちのActivityPubサーバーに動画をあげるための下準備はできた!
…と思ったんだけど、動画ファイルって5秒ぐらいのものでも1M〜2Mもあって慌てた(今さら
わたしはロリポップのビギナーコース。そんなでかいファイルを気楽に上げてたらあっという間に利用できるディスクスペースを食い尽くしてしまう。
なもんで、ここまで作ったけど諦め。きっとそのうち何かの約に立つ、こともある、かな。
今の時期、まじでタケノコ美味くて悶絶する。
刺身で美味いのはもちろん、焼きタケノコがもう絶品。
水分を飛ばすイメージでじっくり弱火のフライパン。水分が飛んできたかなと思ったらオリーブオイルを回しかけてタケノコに焼きめをつけて、塩をふたふり。
これだけでいくらでもご飯が食えるし酒が飲める。
マジでオススメ!!!
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
【電子書籍発売中】