ref: 7aee9d30601748c0affb1fe73331db5e5d9f069e
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 { u32int handlertype; u32int entrycount; /* FIXME entries? */ }stsd; struct { u32int entrycount; /* FIXME entries */ }stts; struct { u32int entrycount; /* FIXME entries */ }stss; struct { u32int entrycount; struct { u32int firstchunk; u32int samplesperchunk; u32int sdt; }*entry; }stsc; struct { u32int entrycount; u64int *chunkoffset; }stco_co64; struct { u64int creattime; u64int modtime; u32int trackid; u64int duration; u32int width; u32int height; }tkhd; struct { u32int handlertype; char *name; }hdlr; }; }; 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, BoxEdts = 0x65647473u, BoxElst = 0x656c7374u, BoxTkhd = 0x746b6864u, BoxMdia = 0x6d646961u, BoxMdhd = 0x6d646864u, BoxHdlr = 0x68646c72u, BoxMinf = 0x6d696e66u, BoxDinf = 0x64696e66u, BoxStbl = 0x7374626cu, BoxStsd = 0x73747364u, BoxStts = 0x73747473u, BoxStsc = 0x73747363u, BoxStco = 0x7374636fu, BoxCo64 = 0x636f3634u, 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 || \ b->type == BoxStsd || b->type == BoxStts || b->type == BoxStss || b->type == BoxTkhd || \ b->type == BoxElst || b->type == BoxStsc || b->type == BoxStco || b->type == BoxCo64 \ ) #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 if(b->type == BoxStsd){ print("\t%.*shandler_type\t%08x\n", dind, ind, b->stsd.handlertype); print("\t%.*sentry_count\t%ud\n", dind, ind, b->stsd.entrycount); }else if(b->type == BoxStts){ print("\t%.*sentry_count\t%ud\n", dind, ind, b->stts.entrycount); }else if(b->type == BoxStss){ print("\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); }else if(b->type == BoxStsc){ print("\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); for(u = 0; u < b->stsc.entrycount; u++){ print("\t%.*sentry[%zd]\n", dind, ind, u); print("\t\t%.*sfirst_chunk\t%ud\n", dind, ind, b->stsc.entry[u].firstchunk); print("\t\t%.*ssamples_per_chunk\t%ud\n", dind, ind, b->stsc.entry[u].samplesperchunk); print("\t\t%.*ssample_description_table\t%ud\n", dind, ind, b->stsc.entry[u].sdt); } }else if(b->type == BoxTkhd){ print("\t%.*screation_time\t%zd\n", dind, ind, b->tkhd.creattime); print("\t%.*smodification_timetime\t%zd\n", dind, ind, b->tkhd.modtime); print("\t%.*strack_id\t%ud\n", dind, ind, b->tkhd.trackid); print("\t%.*sduration\t%zd\n", dind, ind, b->tkhd.duration); print("\t%.*swidth\t%ud\n", dind, ind, b->tkhd.width); print("\t%.*sheight\t%ud\n", dind, ind, b->tkhd.height); }else if(b->type == BoxHdlr){ print("\t%.*shandler_type\t%c%c%c%c\n", dind, ind, b->hdlr.handlertype>>24, b->hdlr.handlertype>>16&0xff, b->hdlr.handlertype>>8&0xff, b->hdlr.handlertype&0xff); print("\t%.*sname\t%s\n", dind, ind, b->hdlr.name); }else if(b->type == BoxStco || b->type == BoxCo64){ print("\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); for(u = 0; u < b->stco_co64.entrycount; u++) print("\t%.*schunkoffset[%zd]\t%zd\n", dind, ind, u, b->stco_co64.chunkoffset[u]); }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; 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 || b->type == BoxEdts){ 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 */ b->trun.samples = calloc(b->trun.samplecount, sizeof(RunSample)); for(u = 0; u < b->trun.samplecount; u++){ if(b->flags & 0x100){ eBread(4, "sample_duration"); b->trun.samples[u].duration = bu32(d); } if(b->flags & 0x200){ eBread(4, "sample_size"); b->trun.samples[u].size = bu32(d); } if(b->flags & 0x400){ eBread(4, "sample_flags"); b->trun.samples[u].flags = bu32(d); } 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; } } printbox(b); }else if(b->type == BoxStsd){ eBread(4, "handler_type"); b->stsd.handlertype = bu32(d); eBread(4, "entry_count"); b->stsd.entrycount = bu32(d); /* FIXME not reading actual entries here */ printbox(b); }else if(b->type == BoxStts){ eBread(4, "entry_count"); b->stts.entrycount = bu32(d); /* FIXME not reading actual entries here */ printbox(b); }else if(b->type == BoxStss){ eBread(4, "entry_count"); b->stss.entrycount = bu32(d); /* FIXME not reading actual entries here */ printbox(b); }else if(b->type == BoxStsc){ eBread(4, "entry_count"); b->stsc.entrycount = bu32(d); b->stsc.entry = calloc(b->stsc.entrycount, sizeof(*b->stsc.entry)); for(u = 0; u < b->stsc.entrycount; u++){ eBread(4, "first_chunk"); b->stsc.entry[u].firstchunk = bu32(d); eBread(4, "samples_per_chunk"); b->stsc.entry[u].samplesperchunk = bu32(d); eBread(4, "sample_description_table"); b->stsc.entry[u].sdt = bu32(d); } printbox(b); }else if(b->type == BoxStco || b->type == BoxCo64){ eBread(4, "entry_count"); b->stco_co64.entrycount = bu32(d); b->stco_co64.chunkoffset = calloc(b->stco_co64.entrycount, sizeof(*b->stco_co64.chunkoffset)); for(u = 0; u < b->stco_co64.entrycount; u++){ eBread(b->type == BoxStco ? 4 : 8, "chunk_offset"); b->stco_co64.chunkoffset[u] = b->type == BoxStco ? bu32(d) : bu64(d); } printbox(b); }else if(b->type == BoxTkhd){ if(b->version == 1){ eBread(8, "creation_time"); b->tkhd.creattime = bu64(d); eBread(8, "modification_time"); b->tkhd.modtime = bu64(d); eBread(8, "track_id"); /* skipping 4 reserved as well */ b->tkhd.trackid = bu32(d); eBread(8, "duration"); b->tkhd.duration = bu64(d); }else if(b->version == 0){ eBread(4, "creation_time"); b->tkhd.creattime = bu32(d); eBread(4, "modification_time"); b->tkhd.modtime = bu32(d); eBread(8, "track_id"); /* skipping 4 reserved as well */ b->tkhd.trackid = bu32(d); eBread(4, "duration"); b->tkhd.duration = bu32(d); }else{ werrstr("uknown version %d", b->version); goto err; } eBread(8+2+2+2+2, "reserved, layer, alternate_group, volume, reserved"); eBread(9*4, "matrix"); eBread(4, "width"); b->tkhd.width = bu32(d)>>16; /* FIXME fixed-point 16.16 */ eBread(4, "height"); b->tkhd.height = bu32(d)>>16; /* FIXME fixed-point 16.16 */ printbox(b); }else if(b->type == BoxHdlr){ eBread(4, "pre_defined"); eBread(4, "handler_type"); b->hdlr.handlertype = bu32(d); eBread(3*4, "reserved"); for(u = 0; u < sizeof(d)-1; u++){ if(Bread(f, d+u, 1) != 1){ werrstr("name"); goto err; } if(d[u] == 0) break; } d[u] = 0; b->hdlr.name = strdup((char*)d); 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; memset(&b, 0, sizeof(b)); if(parsebox(f, &b, &eof) != 0){ if(eof) break; sysfatal("%s: %r", *argv); } Bseek(f, b.dstart+b.dsz, 0); } Bterm(f); } return 0; }