javascriptでMP4タグのパース
開発スタッフAHです。
先日ChromeにてMediarecorderをコーデックMP4指定で行ったところ、指定した数値と異なった秒数でメディアを切り出してきた事件がありました。特にエラーも無しだったのでこれが仕様となると今後きっちり指定した秒数で切り出してきてくれるchromeとそうでないchromeを分ける必要があるかも?ということで出力されたMP4の秒数を確認する必要があるためちょっとMP4のパースを書いてみようと思います。本来ならMP4内のtimescaleやdurationで判定するのですがライブソースなのでまったくわからない状態となっております。詳しく秒数を知りたい方はMP4の仕様をチェックしてください。ここではあくまでタグだけ抽出します。
ソースです。
fetch('./test.mp4').then(function(response){
return response.arrayBuffer();
}).then(function(buffer){
const decoder = new TextDecoder('utf-8');
const mp4data = new Uint8Array(buffer);
let pos = 0;
let tag_size = 0;
let depth = 0;
let depth_array = [];
while(pos <= mp4data.length){
const tag = decoder.decode(mp4data.slice(pos+4,pos+8));
if(/[a-z]{4}/.test(tag)){
let depth_str = '';
for(let i = depth; i > 0; i--){
depth_str += '-';
}
console.log(depth_str+tag);
tag_size = new DataView(mp4data.slice(pos,pos+4).buffer).getUint32(0,false);
depth_array.push(tag_size+pos);
pos += 8;
depth++;
}else{
pos += tag_size - 8;
for(let j = depth_array.length; j >= 0; j--){
if(depth_array[j]){
if(pos >= depth_array[j]){
depth = j;
depth_array.pop();
}
}
}
}
}
});
node.jsで元のソースを作りました。タグ名やタグのサイズを取りやすいのですがjavascriptの場合若干手順が違うっぽいのでchatgptに聞いて変更しました。
const decoder = new TextDecoder('utf-8');const tag = decoder.decode(mp4data.slice(pos+4,pos+8));
TextDecorderでdecodeすれば良いらしいです。(TextDecorderをこれで知りました)
tag_size = new DataView(mp4data.slice(pos,pos+4).buffer).getUint32(0,false);
DataviewはarrayBufferじゃないとダメらしいので
mp4data.slice(pos,pos+4).buffer
ここらへんの処理はUint8ArrayをarrayBufferにしてます。
getUint32(0,false);mp4のタグの数字はビッグエンディアンなので指定しています。これでスムーズにパース出来ました。
ちなみに出力するとこんな感じです。
ftypmoov-mvhd-trak--tkhd--mdia---mdhd---hdlr---minf----smhd----dinf-----dref----stbl-----stsd-----stts-----stsc-----stsz-----stco-mvex--trexmoof-mfhd-traf--tfhd--tfdt--trunmdat
不細工な作りですがとりあえず出力されるので良いのかなと。必要に応じて各タグで処理を入れていくのですがchatgptなどで調べてアレンジすると楽しいかもしれません。
文:開発スタッフAH
2025/05/8 18:55
#開発部