ref: 7c5bc841307a98bc95654b281bb442852ac210ee
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;
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,
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 \
)
#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 == 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);
memmove(&track.video, &b->stsd.video, sizeof(Video));
}else if(track.handlertype == HandlerAudio){
b->stsd.audio.format = fmt;
/* reserved+id, 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;
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 - sizeof(d);
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){
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 == 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, "entry_count");
b->stsd.entrycount = bu32(d);
for(u = 0; u < b->stsd.entrycount; u++){
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);
Bseek(f, n, 1);
}
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(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);
}