ref: def4aa492686c2babedc50d0f65e06b1eff14b21
dir: /iso.c/
#include <u.h> #include <libc.h> #include <bio.h> typedef struct Box Box; typedef struct RunSample RunSample; struct Box { vlong dsz; vlong dstart; char extended[16]; u32int type; /* full box */ u8int version; u32int flags; union { struct { u32int brand; u32int version; u32int *compat; int ncompat; }ftyp; struct { u64int creation; u64int modification; u64int duration; u32int timescale; u32int rate; u32int matrix[9]; u32int nexttrack; u16int volume; }mvhd; struct { u32int trackid; struct { u32int descrindex; u32int duration; u32int size; u32int flags; }defsample; }trex; struct { u32int seqnumber; }mfhd; struct { u64int decodetime; }tfdt; struct { u32int trackid; u64int baseoffset; struct { u32int descrindex; u32int duration; u32int size; u32int flags; }defsample; }tfhd; struct { u32int samplecount; s32int dataoffset; u32int firstsampleflags; RunSample *samples; }trun; }; }; struct RunSample { u32int duration; u32int size; u32int flags; vlong timeoffset; }; enum { BoxUuid = 0x75756964u, BoxFtyp = 0x66747970u, BoxMoov = 0x6d6f6f76u, BoxMvhd = 0x6d766864u, BoxMvex = 0x6d766578u, BoxTrex = 0x74726578u, BoxTrak = 0x7472616bu, BoxTkhd = 0x746b6864u, BoxMdia = 0x6d646961u, BoxMdhd = 0x6d646864u, BoxHdlr = 0x68646c72u, BoxMinf = 0x6d696e66u, BoxDinf = 0x64696e66u, BoxStbl = 0x7374626cu, BoxStsd = 0x73747364u, BoxStts = 0x73747473u, BoxStsc = 0x73747363u, BoxStco = 0x7374636fu, BoxStsz = 0x7374737au, BoxStss = 0x73747373u, BoxVmhd = 0x766d6864u, BoxSidx = 0x73696478u, BoxMoof = 0x6d6f6f66u, BoxMfhd = 0x6d666864u, BoxTraf = 0x74726166u, BoxTfdt = 0x74666474u, BoxTfhd = 0x74666864u, BoxTrun = 0x7472756eu, BoxMdat = 0x6d646174u, }; #define bu16(x) ((x)[0]<<8 | (x)[1]<<16) #define bu32(x) ((x)[0]<<24 | (x)[1]<<16 | (x)[2]<<8 | (x)[3]) #define bu64(x) ((u64int)(x)[0]<<56 | (u64int)(x)[1]<<48 | (u64int)(x)[2]<<40 | (u64int)(x)[3]<<32 | (x)[4]<<24 | (x)[5]<<16 | (x)[6]<<8 | (x)[7]) #define isfullbox(b) ( \ b->type == BoxMvhd || b->type == BoxTrex || b->type == BoxMdhd || b->type == BoxHdlr || \ b->type == BoxMfhd || b->type == BoxTfhd || b->type == BoxTfdt || b->type == BoxTrun \ ) #define eBread(sz, e) if(Bread(f, d, sz) != sz){ werrstr(e); goto err; } static int dflag; static int dind; static u32int defsamplesize; static int parsebox(Biobuf *f, Box *b, int *eof); #pragma varargck type "T" u32int static int typefmt(Fmt *f) { char t[5]; int x; x = va_arg(f->args, int); t[0] = x>>24; t[1] = x>>16; t[2] = x>>8; t[3] = x; t[4] = 0; return fmtstrcpy(f, t); } static void printbox(Box *b) { int i; char ind[16] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; uvlong u; if(dflag == 0) return; print("%.*s%T\n", dind, ind, b->type); /* full box */ if(isfullbox(b)){ print("\t%.*sversion\t%d\n", dind, ind, b->version); print("\t%.*sflags\t0x%ux\n", dind, ind, b->flags); } if(b->type == BoxFtyp){ print("\t%.*sbrand\t%T\n", dind, ind, b->ftyp.brand); print("\t%.*sversion\t%d\n", dind, ind, b->ftyp.version); print("\t%.*scompat", dind, ind); for(i = 0; i < b->ftyp.ncompat; i++) print("\t%.*s%T", dind, ind, b->ftyp.compat[i]); print("\n"); }else if(b->type == BoxMvhd){ print("\t%.*screation\t%zd\n", dind, ind, b->mvhd.creation); print("\t%.*smodification\t%zd\n", dind, ind, b->mvhd.modification); print("\t%.*stimescale\t%ud\n", dind, ind, b->mvhd.timescale); print("\t%.*sduration\t%zd\n", dind, ind, b->mvhd.duration); print("\t%.*srate\t0x%ux\n", dind, ind, b->mvhd.rate); print("\t%.*svolume\t0x%ux\n", dind, ind, b->mvhd.volume); print("\t%.*snexttrack\t0x%ux\n", dind, ind, b->mvhd.nexttrack); print("\t%.*smatrix\t0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux 0x%ux\n", dind, ind, b->mvhd.matrix[0], b->mvhd.matrix[1], b->mvhd.matrix[2], b->mvhd.matrix[3], b->mvhd.matrix[4], b->mvhd.matrix[5], b->mvhd.matrix[6], b->mvhd.matrix[7], b->mvhd.matrix[8] ); }else if(b->type == BoxTrex){ print("\t%.*strackid\t0x%ux\n", dind, ind, b->trex.trackid); print("\t%.*sdefsample.\n", dind, ind); print("\t\t%.*s.descrindex\t0x%ux\n", dind, ind, b->trex.defsample.descrindex); print("\t\t%.*s.duration\t%ud\n", dind, ind, b->trex.defsample.duration); print("\t\t%.*s.size\t0x%ux\n", dind, ind, b->trex.defsample.size); print("\t\t%.*s.flags\t0x%ux\n", dind, ind, b->trex.defsample.flags); }else if(b->type == BoxMfhd){ print("\t%.*sseqnumber\t%ud\n", dind, ind, b->mfhd.seqnumber); }else if(b->type == BoxTfhd){ print("\t%.*strackid\t0x%ux\n", dind, ind, b->tfhd.trackid); if(b->flags & 1) print("\t%.*sbaseoffset\t%zd\n", dind, ind, b->tfhd.baseoffset); print("\t%.*sdefsample.\n", dind, ind); if(b->flags & 2) print("\t\t%.*s.descrindex\t0x%ux\n", dind, ind, b->tfhd.defsample.descrindex); if(b->flags & 8) print("\t\t%.*s.duration\t%ud\n", dind, ind, b->tfhd.defsample.duration); if(b->flags & 16) print("\t\t%.*s.size\t0x%ux\n", dind, ind, b->tfhd.defsample.size); if(b->flags & 32) print("\t\t%.*s.flags\t0x%ux\n", dind, ind, b->tfhd.defsample.flags); if(b->flags & 0x10000) print("\t%.*sduration is empty\n", dind, ind); if(b->flags & 0x20000) print("\t%.*sdefault base is moof\n", dind, ind); }else if(b->type == BoxTfdt){ print("\t%.*sdecodetime\t%zd\n", dind, ind, b->tfdt.decodetime); }else if(b->type == BoxTrun){ print("\t%.*ssamplecount\t%ud\n", dind, ind, b->trun.samplecount); if(b->flags & 1) print("\t%.*sdataoffset\t%d\n", dind, ind, b->trun.dataoffset); if(b->flags & 2) print("\t%.*sfirstsampleflags\t0x%ux\n", dind, ind, b->trun.firstsampleflags); for(u = 0; u < b->trun.samplecount; u++){ print("\t%.*ssamples[%zd]\n", dind, ind, u); if(b->flags & 0x100) print("\t\t%.*s.duration\t%ud\n", dind, ind, b->trun.samples[u].duration); if(b->flags & 0x200) print("\t\t%.*s.size\t%ud\n", dind, ind, b->trun.samples[u].size); if(b->flags & 0x400) print("\t\t%.*s.flags\t0x%ux\n", dind, ind, b->trun.samples[u].flags); if(b->flags & 0x800) print("\t\t%.*s.timeoffset\t%zd\n", dind, ind, b->trun.samples[u].timeoffset); } }else{ print("\t%.*sstart\t%zd\n", dind, ind, b->dstart); print("\t%.*ssize\t%zd\n", dind, ind, b->dsz); } } static int parseboxdata(Biobuf *f, Box *b) { u8int d[128], *p; Box inner; int i, n, eof; s32int s32i; u64int u; u32int sz; if(b->type == BoxFtyp){ eBread(8, "brand and version"); b->ftyp.brand = bu32(d); b->ftyp.version = bu32(d+4); if(b->dsz % 4 != 0){ werrstr("compatible_brands size"); goto err; } b->ftyp.ncompat = (b->dsz - 8) / 4; b->ftyp.compat = calloc(b->ftyp.ncompat, 4); for(i = 0; i < b->ftyp.ncompat; i++){ eBread(4, "compatible_brands"); b->ftyp.compat[i] = bu32(d); } printbox(b); }else if(b->type == BoxMoov || b->type == BoxMvex || b->type == BoxTrak || b->type == BoxMdia || b->type == BoxMinf || b->type == BoxStbl || b->type == BoxMoof || b->type == BoxTraf){ printbox(b); dind++; for(;;){ memset(&inner, 0, sizeof(inner)); if(parsebox(f, &inner, &eof) != 0) goto err; Bseek(f, inner.dstart+inner.dsz, 0); if(inner.dstart+inner.dsz >= b->dstart+b->dsz) break; } dind--; }else if(b->type == BoxMvhd){ n = b->version == 0 ? 96 : 108; eBread(n, "short read"); p = d; b->mvhd.creation = bu32(p); p += 4; if(b->version == 1){ b->mvhd.creation = b->mvhd.creation<<32 | bu32(p); p += 4; } b->mvhd.modification = bu32(p); p += 4; if(b->version == 1){ b->mvhd.modification = b->mvhd.modification<<32 | bu32(p); p += 4; } b->mvhd.timescale = bu32(p); p += 4; b->mvhd.duration = bu32(p); p += 4; if(b->version == 1){ b->mvhd.duration = b->mvhd.duration<<32 | bu32(p); p += 4; } b->mvhd.rate = bu32(p); p += 4; b->mvhd.volume = bu16(p); p += 2; p += 2; p += 4*2; for(i = 0; i < 9; i++){ b->mvhd.matrix[i] = bu32(p); p += 4; } p += 4*6; b->mvhd.nexttrack = bu32(p); printbox(b); }else if(b->type == BoxTrex){ eBread(20, "short read"); b->trex.trackid = bu32(d); b->trex.defsample.descrindex = bu32(d+4); b->trex.defsample.duration = bu32(d+8); b->trex.defsample.size = bu32(d+12); b->trex.defsample.flags = bu32(d+16); defsamplesize = b->trex.defsample.size; printbox(b); }else if(b->type == BoxMfhd){ eBread(4, "short read"); b->mfhd.seqnumber = bu32(d); printbox(b); }else if(b->type == BoxTfhd){ eBread(4, "track_id"); b->tfhd.trackid = bu32(d); if(b->flags & 1){ eBread(8, "base_data_offset"); b->tfhd.baseoffset = bu64(d); } if(b->flags & 2){ eBread(4, "sample_description_index"); b->tfhd.defsample.descrindex = bu32(d); } if(b->flags & 8){ eBread(4, "default_sample_duration"); b->tfhd.defsample.duration = bu32(d); } if(b->flags & 16){ eBread(4, "default_sample_size"); b->tfhd.defsample.size = bu32(d); defsamplesize = b->tfhd.defsample.size; } if(b->flags & 32){ eBread(4, "default_sample_flags"); b->tfhd.defsample.flags = bu32(d); } printbox(b); }else if(b->type == BoxTfdt){ if(b->version == 1){ eBread(8, "base_media_decode_time"); b->tfdt.decodetime = bu64(d); }else{ eBread(4, "base_media_decode_time"); b->tfdt.decodetime = bu32(d); } printbox(b); }else if(b->type == BoxTrun){ eBread(4, "sample_count"); b->trun.samplecount = bu32(d); if(b->flags & 1){ eBread(4, "data_offset"); b->trun.dataoffset = bu32(d); } if(b->flags & 4){ eBread(4, "first_sample_flags"); b->trun.firstsampleflags = bu32(d); } /* FIXME free those after */ b->trun.samples = calloc(b->trun.samplecount, sizeof(RunSample)); for(u = 0; u < b->trun.samplecount; u++){ fprint(2, "sample %zd\n", u); sz = defsamplesize; if(b->flags & 0x100){ eBread(4, "sample_duration"); b->trun.samples[u].duration = bu32(d); sz -= 4; } if(b->flags & 0x200){ eBread(4, "sample_size"); b->trun.samples[u].size = bu32(d); sz = b->trun.samples[u].size - 4; if(b->flags & 0x100) sz -= 4; } if(b->flags & 0x400){ eBread(4, "sample_flags"); b->trun.samples[u].flags = bu32(d); sz -= 4; } if(b->flags & 0x800){ eBread(4, "sample_composition_time_offset"); s32i = bu32(d); if(b->version == 0) b->trun.samples[u].timeoffset = bu32(d); else b->trun.samples[u].timeoffset = s32i; sz -= 4; } fprint(2, "left %ud\n", sz); if(Bseek(f, sz, 1) < 0){ werrstr("seek"); goto err; } } printbox(b); }else{ printbox(b); } return 0; err: werrstr("%T: %r", b->type); return -1; } static int parsebox(Biobuf *f, Box *b, int *eof) { vlong sz, start; u8int d[8]; int r; *eof = 0; b->dstart = start = Boffset(f); if((r = Bread(f, d, 8)) != 8){ if(r == 0) *eof = 1; else werrstr("size and type"); goto err; } b->dstart += 8; b->dsz = sz = d[0]<<24 | d[1]<<16 | d[2]<<8 | d[3]; b->type = d[4]<<24 | d[5]<<16 | d[6]<<8 | d[7]; if(sz == 1){ if(Bread(f, d, 8) != 8){ werrstr("largesize"); goto err; } b->dstart += 8; b->dsz = (vlong)d[0]<<56 | (vlong)d[1]<<48 | (vlong)d[2]<<40 | (vlong)d[3]<<32 | d[4]<<24 | d[5]<<16 | d[6]<<8 | d[7]; }else if(sz == 0){ b->dsz = (vlong)1<<63 - 1; } if(b->type == BoxUuid){ if(Bread(f, b->extended, 16) != 16){ werrstr("extended_type"); goto err; } b->dstart += 16; }else if(isfullbox(b)){ if(Bread(f, d, 4) != 4){ werrstr("full box"); goto err; } b->version = d[0]; b->flags = d[1]<<16 | d[2]<<8 | d[3]; b->dstart += 4; } b->dsz -= b->dstart - start; if(parseboxdata(f, b) == 0) return 0; err: werrstr("parsebox: %r"); return -1; } int main(int argc, char **argv) { Biobuf *f; Box b; int eof; dflag = 0; ARGBEGIN{ case 'd': dflag = 1; break; }ARGEND fmtinstall('T', typefmt); for(; *argv; argv++){ if((f = Bopen(*argv, OREAD)) == nil) sysfatal("%s: %r", *argv); for(;;){ dind = 0; if(parsebox(f, &b, &eof) != 0){ if(eof) break; sysfatal("%s: %r", *argv); } Bseek(f, b.dstart+b.dsz, 0); } Bterm(f); } return 0; }