shithub: mcfs

ref: 7aee9d30601748c0affb1fe73331db5e5d9f069e
dir: /iso.c/

View raw version
#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;
}