Private
Public Access
36 lines
1.7 KiB
Python
36 lines
1.7 KiB
Python
"""Inspect MP4 track metadata without external tools."""
|
|
import json, struct, sys
|
|
from pathlib import Path
|
|
|
|
def boxes(data):
|
|
offset=0
|
|
while offset+8<=len(data):
|
|
size,kind=struct.unpack_from('>I4s',data,offset);header=8
|
|
if size==1:size=struct.unpack_from('>Q',data,offset+8)[0];header=16
|
|
if size==0:size=len(data)-offset
|
|
if size<header or offset+size>len(data):raise ValueError('Invalid MP4 box')
|
|
yield kind.decode('ascii'), data[offset+header:offset+size]
|
|
offset+=size
|
|
|
|
def child(data,name):return next(body for kind,body in boxes(data) if kind==name)
|
|
|
|
def inspect(path, expected_seconds=20):
|
|
moov=child(Path(path).read_bytes(),'moov');result=[]
|
|
for kind,trak in boxes(moov):
|
|
if kind!='trak':continue
|
|
mdia=child(trak,'mdia');hdlr=child(mdia,'hdlr');kind=hdlr[8:12].decode()
|
|
mdhd=child(mdia,'mdhd')
|
|
if mdhd[0]==0:scale,duration=struct.unpack_from('>II',mdhd,12)
|
|
else:scale=struct.unpack_from('>I',mdhd,20)[0];duration=struct.unpack_from('>Q',mdhd,24)[0]
|
|
stbl=child(child(mdia,'minf'),'stbl');stsz=child(stbl,'stsz');count=struct.unpack_from('>I',stsz,8)[0]
|
|
desc=child(stbl,'stsd');entry=next(boxes(desc[8:]));seconds=duration/scale
|
|
item={'type':kind,'codec':entry[0],'duration_seconds':seconds,'samples':count}
|
|
if kind=='vide':
|
|
width,height=struct.unpack_from('>HH',entry[1],24);item.update(width=width,height=height,fps=count/seconds)
|
|
assert (width,height,count)==(1280,720,round(expected_seconds*24)),item
|
|
assert abs(seconds-expected_seconds)<.01,item
|
|
result.append(item)
|
|
assert any(t['type']=='soun' for t in result),'Missing audio track'
|
|
return result
|
|
if __name__=='__main__':print(json.dumps(inspect(sys.argv[1],float(sys.argv[2]) if len(sys.argv)>2 else 20),indent=2))
|