ref: f8e5a16a06c337bafb54921804a90fe4229ea3e9
dir: /iso.c/
#include <u.h> #include <libc.h> #include <bio.h> typedef struct Audio Audio; typedef struct Box Box; typedef struct RunSample RunSample; typedef struct SampleToChunk SampleToChunk; typedef struct Track Track; typedef struct Video Video; struct Audio { u32int format; int channels; int samplerate; }; struct Video { u32int format; int width; int height; }; struct Box { vlong dsz; vlong offset; 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; }mdhd; 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 entrycount; Video video; Audio audio; }stsd; struct { u32int entrycount; /* FIXME entries */ }stts; struct { u32int entrycount; /* FIXME entries */ }stss; struct { u32int entrycount; SampleToChunk *entry; }stsc; struct { u32int entrycount; u64int *chunkoffset; }stco_co64; struct { u32int samplesizeeach; u32int samplecount; u32int *samplesize; }stsz; /* FIXME need stz2 as well */ 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; }; struct SampleToChunk { u32int firstchunk; u32int samplesperchunk; u32int sdt; }; struct Track { u32int handlertype; u64int *chunkoffset; u32int numchunks; u32int samplesizeeach; u32int *samplesize; u32int numsamples; SampleToChunk *stc; u32int numstc; Audio audio; Video video; int id; }; enum { HandlerVideo = 0x76696465u, HandlerAudio = 0x736f756eu, FmtMp4a = 0x6d703461u, 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, BoxCtts = 0x63747473u, BoxStsd = 0x73747364u, BoxEsds = 0x65736473u, 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]) #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 || \ b->type == BoxStsz || b->type == BoxCtts || b->type == BoxEsds \ ) #define eBread(sz, e) if(Bread(f, d, (sz)) != (sz)){ werrstr(e); goto err; } static int dflag; static int dind; static u32int defsamplesize; static Track track; static Track *tracks; static int ntracks; 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; fprint(2, "%.*s%T\n", dind, ind, b->type); /* full box */ if(isfullbox(b)){ fprint(2, "\t%.*sversion\t%d\n", dind, ind, b->version); fprint(2, "\t%.*sflags\t0x%ux\n", dind, ind, b->flags); } if(dflag > 1){ fprint(2, "\t%.*soffset\t%zd\n", dind, ind, b->offset); fprint(2, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); fprint(2, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); } if(b->type == BoxFtyp){ fprint(2, "\t%.*sbrand\t%T\n", dind, ind, b->ftyp.brand); fprint(2, "\t%.*sversion\t%d\n", dind, ind, b->ftyp.version); fprint(2, "\t%.*scompat", dind, ind); for(i = 0; i < b->ftyp.ncompat; i++) fprint(2, "\t%.*s%T", dind, ind, b->ftyp.compat[i]); fprint(2, "\n"); }else if(b->type == BoxMdhd){ fprint(2, "\t%.*screation\t%zd\n", dind, ind, b->mdhd.creation); fprint(2, "\t%.*smodification\t%zd\n", dind, ind, b->mdhd.modification); fprint(2, "\t%.*stimescale\t%ud\n", dind, ind, b->mdhd.timescale); fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->mdhd.duration); }else if(b->type == BoxMvhd){ fprint(2, "\t%.*screation\t%zd\n", dind, ind, b->mvhd.creation); fprint(2, "\t%.*smodification\t%zd\n", dind, ind, b->mvhd.modification); fprint(2, "\t%.*stimescale\t%ud\n", dind, ind, b->mvhd.timescale); fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->mvhd.duration); fprint(2, "\t%.*srate\t0x%ux\n", dind, ind, b->mvhd.rate); fprint(2, "\t%.*svolume\t0x%ux\n", dind, ind, b->mvhd.volume); fprint(2, "\t%.*snexttrack\t0x%ux\n", dind, ind, b->mvhd.nexttrack); fprint(2, "\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){ fprint(2, "\t%.*strackid\t0x%ux\n", dind, ind, b->trex.trackid); fprint(2, "\t%.*sdefsample\n", dind, ind); fprint(2, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->trex.defsample.descrindex); fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->trex.defsample.duration); fprint(2, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->trex.defsample.size); fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trex.defsample.flags); }else if(b->type == BoxMfhd){ fprint(2, "\t%.*sseqnumber\t%ud\n", dind, ind, b->mfhd.seqnumber); }else if(b->type == BoxTfhd){ fprint(2, "\t%.*strackid\t0x%ux\n", dind, ind, b->tfhd.trackid); if(b->flags & 1) fprint(2, "\t%.*sbaseoffset\t%zd\n", dind, ind, b->tfhd.baseoffset); fprint(2, "\t%.*sdefsample\n", dind, ind); if(b->flags & 2) fprint(2, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->tfhd.defsample.descrindex); if(b->flags & 8) fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->tfhd.defsample.duration); if(b->flags & 16) fprint(2, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->tfhd.defsample.size); if(b->flags & 32) fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->tfhd.defsample.flags); if(b->flags & 0x10000) fprint(2, "\t%.*sduration is empty\n", dind, ind); if(b->flags & 0x20000) fprint(2, "\t%.*sdefault base is moof\n", dind, ind); }else if(b->type == BoxTfdt){ fprint(2, "\t%.*sdecodetime\t%zd\n", dind, ind, b->tfdt.decodetime); }else if(b->type == BoxTrun){ fprint(2, "\t%.*ssamplecount\t%ud\n", dind, ind, b->trun.samplecount); if(b->flags & 1) fprint(2, "\t%.*sdataoffset\t%d\n", dind, ind, b->trun.dataoffset); if(b->flags & 2) fprint(2, "\t%.*sfirstsampleflags\t0x%ux\n", dind, ind, b->trun.firstsampleflags); for(u = 0; dflag > 1 && u < b->trun.samplecount; u++){ fprint(2, "\t%.*ssamples[%zd]\n", dind, ind, u); if(b->flags & 0x100) fprint(2, "\t\t%.*sduration\t%ud\n", dind, ind, b->trun.samples[u].duration); if(b->flags & 0x200) fprint(2, "\t\t%.*ssize\t%ud\n", dind, ind, b->trun.samples[u].size); if(b->flags & 0x400) fprint(2, "\t\t%.*sflags\t0x%ux\n", dind, ind, b->trun.samples[u].flags); if(b->flags & 0x800) fprint(2, "\t\t%.*stimeoffset\t%zd\n", dind, ind, b->trun.samples[u].timeoffset); } }else if(b->type == BoxStsd){ fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stsd.entrycount); if(b->stsd.video.format != 0){ fprint(2, "\t\t%.*svideo\t%T\n", dind, ind, b->stsd.video.format); fprint(2, "\t\t\t%.*swidth\t%d\n", dind, ind, b->stsd.video.width); fprint(2, "\t\t\t%.*sheight\t%d\n", dind, ind, b->stsd.video.height); } if(b->stsd.audio.format != 0){ fprint(2, "\t\t%.*saudio\t%T\n", dind, ind, b->stsd.audio.format); fprint(2, "\t\t\t%.*schannels\t%d\n", dind, ind, b->stsd.audio.channels); fprint(2, "\t\t\t%.*ssample_rate\t%d\n", dind, ind, b->stsd.audio.samplerate); } }else if(b->type == BoxStts){ fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stts.entrycount); }else if(b->type == BoxStss){ fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); }else if(b->type == BoxStsc){ fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); for(u = 0; dflag > 1 && u < b->stsc.entrycount; u++){ fprint(2, "\t%.*sentry[%zd]\n", dind, ind, u); fprint(2, "\t\t%.*sfirst_chunk\t%ud\n", dind, ind, b->stsc.entry[u].firstchunk); fprint(2, "\t\t%.*ssamples_per_chunk\t%ud\n", dind, ind, b->stsc.entry[u].samplesperchunk); fprint(2, "\t\t%.*ssample_description_table\t%ud\n", dind, ind, b->stsc.entry[u].sdt); } }else if(b->type == BoxStsz){ fprint(2, "\t%.*ssample_size\t%ud\n", dind, ind, b->stsz.samplesizeeach); fprint(2, "\t%.*ssample_count\t%ud\n", dind, ind, b->stsz.samplecount); if(dflag > 1 && b->stsz.samplesizeeach == 0){ for(u = 0; u < b->stsz.samplecount; u++) fprint(2, "\t%.*ssamplesize[%zd]\t%ud\n", dind, ind, u, b->stsz.samplesize[u]); } }else if(b->type == BoxTkhd){ fprint(2, "\t%.*screation_time\t%zd\n", dind, ind, b->tkhd.creattime); fprint(2, "\t%.*smodification_timetime\t%zd\n", dind, ind, b->tkhd.modtime); fprint(2, "\t%.*strack_id\t%ud\n", dind, ind, b->tkhd.trackid); fprint(2, "\t%.*sduration\t%zd\n", dind, ind, b->tkhd.duration); fprint(2, "\t%.*swidth\t%ud\n", dind, ind, b->tkhd.width); fprint(2, "\t%.*sheight\t%ud\n", dind, ind, b->tkhd.height); }else if(b->type == BoxHdlr){ fprint(2, "\t%.*shandler_type\t%T\n", dind, ind, b->hdlr.handlertype); fprint(2, "\t%.*sname\t%s\n", dind, ind, b->hdlr.name); }else if(b->type == BoxStco || b->type == BoxCo64){ fprint(2, "\t%.*sentry_count\t%ud\n", dind, ind, b->stss.entrycount); for(u = 0; dflag > 1 && u < b->stco_co64.entrycount; u++) fprint(2, "\t%.*schunkoffset[%zd]\t%zd\n", dind, ind, u, b->stco_co64.chunkoffset[u]); }else if(dflag < 2){ fprint(2, "\t%.*soffset\t%zd\n", dind, ind, b->offset); fprint(2, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart); fprint(2, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz); } } static void addtrack(void) { tracks = realloc(tracks, (ntracks+1)*sizeof(*tracks)); memmove(&tracks[ntracks++], &track, sizeof(track)); } static int srate2mpeg4fi[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, }; static int dumptrack(Biobuf *f, int id) { SampleToChunk *stc; u32int si, ch, nextch; u32int samplelast, sample, rawsz, samplesz; u64int o, wo; int i; u8int *raw; Biobuf out; Track *t; u8int frame[7]; for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++); if(i >= ntracks){ werrstr("no track %d", id); return -1; } fprint( 2, "track %d: handler=%T numstc=%ud chunks=%ud samples=%ud\n", t->id, t->handlertype, t->numstc, t->numchunks, t->numsamples ); if(t->audio.format != 0){ fprint(2, "audio: format=%T channels=%d samplerate=%d\n", t->audio.format, t->audio.channels, t->audio.samplerate); if(t->audio.format == FmtMp4a){ for(i = 0; i < nelem(srate2mpeg4fi) && srate2mpeg4fi[i] != t->audio.samplerate; i++); if(i >= nelem(srate2mpeg4fi)){ werrstr("audio: mpeg4: invalid sample rate %d", t->audio.samplerate); return -1; } frame[0] = 0xff; /* syncword */ frame[1] = 0xf1; /* syncword, mpeg4, no crc */ frame[2] = 1<<6 | i<<2 | t->audio.channels>>2; /* AAC(LC)??? FIXME, frequency index, channels */ }else{ werrstr("audio: unknown format %T\n", t->audio.format); return -1; } } if(t->video.format != 0){ fprint(2, "video: format=%T resolution=%dx%d\n", t->video.format, t->video.width, t->video.height); //werrstr("video: unknown format %T", t->video.format); //return -1; } Binit(&out, 1, OWRITE); raw = nil; rawsz = 0; sample = samplelast = 0; stc = t->stc; wo = 0; ch = 0; for(si = 0; si < t->numstc; si++, stc++){ nextch = t->numchunks; if(si+1 < t->numstc) nextch = stc[1].firstchunk - 1; for(; ch < nextch; ch++){ o = t->chunkoffset[ch]; if(Bseek(f, o, 0) != o){ werrstr("chunk %ud: %r", ch); return -1; } for(; sample < samplelast+stc->samplesperchunk && sample < t->numsamples; sample++){ if((samplesz = t->samplesizeeach) == 0) samplesz = t->samplesize[sample]; if(samplesz == 0) break; if(rawsz < samplesz){ rawsz = samplesz * 2; raw = realloc(raw, rawsz); } if(Bread(f, raw, samplesz) != samplesz){ werrstr("chunk %ud sample %ud size %ud: %r", ch, sample, samplesz); return -1; } if(t->audio.format == FmtMp4a){ samplesz += 7; frame[3] = (t->audio.channels&3)<<6 | (samplesz>>11)&3; /* channels, frame length */ frame[4] = (samplesz>>3)&0xff; /* frame length */ frame[5] = (samplesz&7)<<5 | 0x1f; /* frame length, fullness */ frame[6] = 0xfc; /* fullness, number of frames */ samplesz -= 7; if(Bwrite(&out, frame, 7) != 7){ /* EOF */ werrstr("eof"); break; } wo += 7; o += 7; } if(Bwrite(&out, raw, samplesz) != samplesz){ /* EOF? */ werrstr("eof"); break; } wo += samplesz; o += samplesz; } samplelast = sample; } } Bterm(&out); free(raw); return 0; }; int sampleentry(Biobuf *f, Box *b, u32int fmt, int n) { u8int d[96]; if(track.handlertype == HandlerVideo){ b->stsd.video.format = fmt; /* predefined+reserved+predefined, width+height, hres+vres, reserved, framecount, compressor */ eBread(2+2+4*3 + 2+2 + 4+4 + 4 + 2 + 32, "SampleEntry: video"); b->stsd.video.width = bu16(d+16); b->stsd.video.height = bu16(d+18); n -= 2+2+4*3 + 2+2 + 4+4 + 4 + 2 + 32; memmove(&track.video, &b->stsd.video, sizeof(Video)); }else if(track.handlertype == HandlerAudio){ b->stsd.audio.format = fmt; /* ver+rev+vendor, channels+bps, ?+?, sample rate */ eBread(2+4+2 + 2+2 + 2+2 + 4, "SampleEntry: audio"); b->stsd.audio.channels = bu16(d+8); b->stsd.audio.samplerate = bu32(d+16)>>16; n -= 2+4+2 + 2+2 + 2+2 + 4; memmove(&track.audio, &b->stsd.audio, sizeof(Audio)); /* FIXME do we care about the rest? */ }else{ fprint(2, "SampleEntry: unknown handler type %T\n", track.handlertype); } return n; err: return -1; } 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 || b->type == BoxDinf){ 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; } if(b->type == BoxTrak){ addtrack(); memset(&track, 0, sizeof(track)); } dind--; }else if(b->type == BoxMdhd){ n = b->version == 0 ? 16 : 28; eBread(n, "short read"); p = d; b->mdhd.creation = bu32(p); p += 4; if(b->version == 1){ b->mdhd.creation = b->mdhd.creation<<32 | bu32(p); p += 4; } b->mdhd.modification = bu32(p); p += 4; if(b->version == 1){ b->mdhd.modification = b->mdhd.modification<<32 | bu32(p); p += 4; } b->mdhd.timescale = bu32(p); p += 4; b->mdhd.duration = bu32(p); p += 4; if(b->version == 1){ b->mdhd.duration = b->mdhd.duration<<32 | bu32(p); } printbox(b); }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 == BoxEsds){ eBread(3, "es id"); fprint(2, "# es id: %ud\n# es flags: 0x%02x\n", bu16(d+1), d[3]); printbox(b); }else if(b->type == BoxStsd){ eBread(4, "entry_count"); b->stsd.entrycount = bu32(d); eBread(4, "size"); n = bu32(d); eBread(4, "format"); Bseek(f, 6+2, 1); /* skip reserved+id */ n -= 8 + 6+2; n = sampleentry(f, b, bu32(d), n); printbox(b); dind += 2; for(; n > 0;){ 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 -= 2; }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(SampleToChunk)); 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); } track.numstc = b->stsc.entrycount; track.stc = b->stsc.entry; 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(u64int)); 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); } track.numchunks = b->stco_co64.entrycount; track.chunkoffset = b->stco_co64.chunkoffset; printbox(b); }else if(b->type == BoxStsz){ eBread(4, "sample_size"); b->stsz.samplesizeeach = bu32(d); track.samplesizeeach = b->stsz.samplesizeeach; eBread(4, "sample_count"); b->stsz.samplecount = bu32(d); if(b->stsz.samplesizeeach == 0){ b->stsz.samplesize = calloc(b->stsz.samplecount, sizeof(u32int)); for(u = 0; u < b->stsz.samplecount; u++){ eBread(4, "chunk_offset"); b->stsz.samplesize[u] = bu32(d); } track.numsamples = b->stsz.samplecount; track.samplesize = b->stsz.samplesize; } 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); track.id = b->tkhd.trackid; 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); track.id = b->tkhd.trackid; 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); track.handlertype = b->hdlr.handlertype; 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 = b->offset = 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; } static void usage(void) { fprint(2, "usage: iso [-d] [-t TRACK] FILE\n"); exits("usage"); } void main(int argc, char **argv) { Box b; Biobuf *f; char *status; int eof, trackdump; dflag = 0; trackdump = -1; ARGBEGIN{ case 'd': dflag++; break; case 't': trackdump = atoi(EARGF(usage())); break; }ARGEND fmtinstall('T', typefmt); status = nil; for(; *argv && status == nil; 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); } if(trackdump >= 0 && dumptrack(f, trackdump) != 0){ fprint(2, "%s: %r\n", *argv); status = "dump"; } Bterm(f); } exits(status); }