ref: ed803b2139a490df5d69f4d82320e8fecd2f7324
parent: f8e5a16a06c337bafb54921804a90fe4229ea3e9
author: Sigrid Haflínudóttir <[email protected]>
date: Wed Sep 9 10:21:44 EDT 2020
remux video streams as IVF
--- /dev/null
+++ b/.gitignore
@@ -1,0 +1,2 @@
+[a0125678vqki].out
+*.[o0125678vqki]
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# mp4
+# mcfs
+
+Multimedia container file system.
Someday this might end up as a 9p filesystem for various video
container formats so other programs (vidya players) can identify
--- a/iso.c
+++ b/iso.c
@@ -4,6 +4,7 @@
typedef struct Audio Audio;
typedef struct Box Box;
+typedef struct Moof Moof;
typedef struct RunSample RunSample;
typedef struct SampleToChunk SampleToChunk;
typedef struct Track Track;
@@ -139,6 +140,11 @@
u32int handlertype;
char *name;
}hdlr;
+
+ struct {
+ int id;
+ int flags;
+ }esds;
};
};
@@ -147,6 +153,7 @@
u32int size;
u32int flags;
vlong timeoffset;
+ vlong offset;
};
struct SampleToChunk {
@@ -155,30 +162,12 @@
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,
+ FmtAv01 = 0x61763031u,
BoxUuid = 0x75756964u,
BoxFtyp = 0x66747970u,
@@ -230,6 +219,36 @@
#define eBread(sz, e) if(Bread(f, d, (sz)) != (sz)){ werrstr(e); goto err; }
+struct Moof {
+ Box;
+ Box mfhd;
+ Box tfhd;
+ Box trun;
+};
+
+struct Track {
+ u32int handlertype;
+ u32int timescale;
+
+ u64int *chunkoffset;
+ u32int numchunks;
+
+ u32int samplesizeeach;
+ u32int *samplesize;
+ u32int numsamples;
+
+ SampleToChunk *stc;
+ u32int numstc;
+
+ Moof *moof;
+ int nmoof;
+
+ Audio audio;
+ Video video;
+
+ int id;
+};
+
static int dflag;
static int dind;
static u32int defsamplesize;
@@ -237,7 +256,10 @@
static Track track;
static Track *tracks;
static int ntracks;
+static Biobuf stderr;
+static Moof moof;
+
static int parsebox(Biobuf *f, Box *b, int *eof);
#pragma varargck type "T" u32int
@@ -267,40 +289,40 @@
if(dflag == 0)
return;
- fprint(2, "%.*s%T\n", dind, ind, b->type);
+ Bprint(&stderr, "%.*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);
+ Bprint(&stderr, "\t%.*sversion\t%d\n", dind, ind, b->version);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*soffset\t%zd\n", dind, ind, b->offset);
+ Bprint(&stderr, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*sbrand\t%T\n", dind, ind, b->ftyp.brand);
+ Bprint(&stderr, "\t%.*sversion\t%d\n", dind, ind, b->ftyp.version);
+ Bprint(&stderr, "\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");
+ Bprint(&stderr, "\t%.*s%T", dind, ind, b->ftyp.compat[i]);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*screation\t%zd\n", dind, ind, b->mdhd.creation);
+ Bprint(&stderr, "\t%.*smodification\t%zd\n", dind, ind, b->mdhd.modification);
+ Bprint(&stderr, "\t%.*stimescale\t%ud\n", dind, ind, b->mdhd.timescale);
+ Bprint(&stderr, "\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,
+ Bprint(&stderr, "\t%.*screation\t%zd\n", dind, ind, b->mvhd.creation);
+ Bprint(&stderr, "\t%.*smodification\t%zd\n", dind, ind, b->mvhd.modification);
+ Bprint(&stderr, "\t%.*stimescale\t%ud\n", dind, ind, b->mvhd.timescale);
+ Bprint(&stderr, "\t%.*sduration\t%zd\n", dind, ind, b->mvhd.duration);
+ Bprint(&stderr, "\t%.*srate\t0x%ux\n", dind, ind, b->mvhd.rate);
+ Bprint(&stderr, "\t%.*svolume\t0x%ux\n", dind, ind, b->mvhd.volume);
+ Bprint(&stderr, "\t%.*snexttrack\t0x%ux\n", dind, ind, b->mvhd.nexttrack);
+ Bprint(&stderr, "\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],
@@ -312,99 +334,99 @@
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);
+ Bprint(&stderr, "\t%.*strackid\t0x%ux\n", dind, ind, b->trex.trackid);
+ Bprint(&stderr, "\t%.*sdefsample\n", dind, ind);
+ Bprint(&stderr, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->trex.defsample.descrindex);
+ Bprint(&stderr, "\t\t%.*sduration\t%ud\n", dind, ind, b->trex.defsample.duration);
+ Bprint(&stderr, "\t\t%.*ssize\t0x%ux\n", dind, ind, b->trex.defsample.size);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*strackid\t%d\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);
+ Bprint(&stderr, "\t%.*sbaseoffset\t%zd\n", dind, ind, b->tfhd.baseoffset);
+ Bprint(&stderr, "\t%.*sdefsample\n", dind, ind);
if(b->flags & 2)
- fprint(2, "\t\t%.*sdescrindex\t0x%ux\n", dind, ind, b->tfhd.defsample.descrindex);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*sduration is empty\n", dind, ind);
if(b->flags & 0x20000)
- fprint(2, "\t%.*sdefault base is moof\n", dind, ind);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*ssamples[%zd]\n", dind, ind, u);
+ Bprint(&stderr, "\t\t%.*sduration\t%ud\n", dind, ind, b->trun.samples[u].duration);
+ Bprint(&stderr, "\t\t%.*ssize\t%ud\n", dind, ind, b->trun.samples[u].size);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t\t%.*svideo\t%T\n", dind, ind, b->stsd.video.format);
+ Bprint(&stderr, "\t\t\t%.*swidth\t%d\n", dind, ind, b->stsd.video.width);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t\t%.*saudio\t%T\n", dind, ind, b->stsd.audio.format);
+ Bprint(&stderr, "\t\t\t%.*schannels\t%d\n", dind, ind, b->stsd.audio.channels);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*sentry[%zd]\n", dind, ind, u);
+ Bprint(&stderr, "\t\t%.*sfirst_chunk\t%ud\n", dind, ind, b->stsc.entry[u].firstchunk);
+ Bprint(&stderr, "\t\t%.*ssamples_per_chunk\t%ud\n", dind, ind, b->stsc.entry[u].samplesperchunk);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*ssample_size\t%ud\n", dind, ind, b->stsz.samplesizeeach);
+ Bprint(&stderr, "\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]);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*screation_time\t%zd\n", dind, ind, b->tkhd.creattime);
+ Bprint(&stderr, "\t%.*smodification_time\t%zd\n", dind, ind, b->tkhd.modtime);
+ Bprint(&stderr, "\t%.*strack_id\t%ud\n", dind, ind, b->tkhd.trackid);
+ Bprint(&stderr, "\t%.*sduration\t%zd\n", dind, ind, b->tkhd.duration);
+ Bprint(&stderr, "\t%.*swidth\t%ud\n", dind, ind, b->tkhd.width);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\t%.*shandler_type\t%T\n", dind, ind, b->hdlr.handlertype);
+ Bprint(&stderr, "\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);
+ Bprint(&stderr, "\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]);
+ Bprint(&stderr, "\t%.*schunkoffset[%zd]\t%zd\n", dind, ind, u, b->stco_co64.chunkoffset[u]);
+ }else if(b->type == BoxEsds){
+ Bprint(&stderr, "\t%.*sid\t%d\n", dind, ind, b->esds.id);
+ Bprint(&stderr, "\t%.*sflags\t0x%x\n", dind, ind, b->esds.flags);
}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);
+ Bprint(&stderr, "\t%.*soffset\t%zd\n", dind, ind, b->offset);
+ Bprint(&stderr, "\t%.*sdstart\t%zd\n", dind, ind, b->dstart);
+ Bprint(&stderr, "\t%.*sdsize\t%zd\n", dind, ind, b->dsz);
}
}
@@ -415,6 +437,38 @@
memmove(&tracks[ntracks++], &track, sizeof(track));
}
+static Track *
+gettrack(int id)
+{
+ Track *t;
+ int i;
+
+ for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++){
+ fprint(2, "%d != %d\n", t->id, id);
+ }
+ if(i >= ntracks){
+ werrstr("no track %d", id);
+ t = nil;
+ }
+
+ return t;
+}
+
+static void
+addtrun(Box *trun)
+{
+ Track *t;
+ Moof *m;
+
+ if((t = gettrack(moof.tfhd.tfhd.trackid)) == nil)
+ sysfatal("trun without a track");
+
+ t->moof = realloc(t->moof, (t->nmoof+1)*sizeof(Moof));
+ m = &t->moof[t->nmoof++];
+ memmove(&moof.trun, trun, sizeof(moof.trun));
+ memmove(m, &moof, sizeof(moof));
+}
+
static int srate2mpeg4fi[] = {
96000,
88200,
@@ -432,51 +486,14 @@
};
static int
-dumptrack(Biobuf *f, int id)
+dumpstc(Biobuf *f, Biobuf *out, Track *t, u8int *frame)
{
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;
@@ -515,7 +532,7 @@
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 */
+ if(Bwrite(out, frame, 7) != 7){ /* EOF */
werrstr("eof");
break;
}
@@ -522,7 +539,7 @@
wo += 7;
o += 7;
}
- if(Bwrite(&out, raw, samplesz) != samplesz){ /* EOF? */
+ if(Bwrite(out, raw, samplesz) != samplesz){ /* EOF? */
werrstr("eof");
break;
}
@@ -532,10 +549,167 @@
samplelast = sample;
}
}
- Bterm(&out);
free(raw);
return 0;
+}
+
+static int
+dumptrun(Biobuf *f, Biobuf *out, Track *t, u8int *frame)
+{
+ int i, j;
+ u64int ts;
+ u8int *raw;
+ int maxsz;
+ RunSample *s;
+ Moof *m;
+
+ USED(frame);
+
+ if(t->video.format == 0)
+ sysfatal("trun dump only works with IVF atm"); /* FIXME */
+
+ ts = 0;
+ maxsz = 0;
+ raw = nil;
+ for(i = 0, m = t->moof; i < t->nmoof; i++, m++){
+ for(j = 0; j < m->trun.trun.samplecount; j++){
+ s = &m->trun.trun.samples[j];
+ if(12 + s->size > maxsz){
+ maxsz = 12 + s->size;
+ raw = realloc(raw, maxsz);
+ }
+ raw[0] = s->size;
+ raw[1] = s->size >> 8;
+ raw[2] = s->size >> 16;
+ raw[3] = s->size >> 24;
+ raw[4] = ts;
+ raw[5] = ts >> 8;
+ raw[6] = ts >> 16;
+ raw[7] = ts >> 24;
+ raw[8] = ts >> 32;
+ raw[9] = ts >> 40;
+ raw[10] = ts >> 48;
+ raw[11] = ts >> 56;
+ if(Bseek(f, s->offset, 0) != s->offset){
+ werrstr("couldn't seek to offset %zd", s->offset);
+ return -1;
+ }
+ if(Bread(f, raw+12, s->size) != s->size){
+ werrstr("couldn't read sample (%d bytes)", s->size);
+ return -1;
+ }
+ if(Bwrite(out, raw, 12 + s->size) != 12 + s->size) /* eof? */
+ break;
+ ts += s->duration; /* sample's "time offset" is ignored here */
+ }
+ }
+
+ return 0;
+}
+
+static int
+dumptrack(Biobuf *f, int id)
+{
+ int i, j, res;
+ Biobuf out;
+ u64int dur;
+ Track *t;
+ Moof *m;
+ u8int frame[0x20];
+
+ if((t = gettrack(id)) == nil)
+ return -1;
+
+ if(dflag)
+ Bprint(&stderr, "track %d: handler=%T ", t->id, t->handlertype);
+ if(t->numstc > 0){
+ if(dflag)
+ Bprint(&stderr, "numstc=%ud chunks=%ud samples=%ud\n", t->numstc, t->numchunks, t->numsamples);
+ }else if(t->nmoof > 0){
+ if(dflag)
+ Bprint(&stderr, "numtrun: %d\n", t->nmoof);
+ }else{
+ if(dflag)
+ Bprint(&stderr, "no samples\n");
+ werrstr("track %d has no samples", t->id);
+ return -1;
+ }
+
+ Binit(&out, 1, OWRITE);
+
+ if(t->audio.format != 0){
+ Bprint(&stderr, "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;
+ }
+ }else if(t->video.format != 0){
+ Bprint(&stderr, "video: format=%T resolution=%dx%d\n", t->video.format, t->video.width, t->video.height);
+ /* video is remuxed into IVF */
+ memset(frame, 0, 0x20);
+ memmove(frame, "DKIF", 4);
+ frame[4] = 0;
+ frame[5] = 0;
+ frame[6] = 0x20;
+ frame[7] = 0;
+ if(t->video.format == FmtAv01){
+ memmove(frame+8, "AV01", 4);
+ frame[12] = t->video.width;
+ frame[13] = t->video.width >> 8;
+ frame[14] = t->video.height;
+ frame[15] = t->video.height >> 8;
+ /* timebase denum */
+ frame[16] = t->timescale;
+ frame[17] = t->timescale >> 8;
+ frame[18] = t->timescale >> 16;
+ frame[19] = t->timescale >> 24;
+ /* timebase num */
+ frame[20] = 1;
+ frame[21] = 0;
+ frame[22] = 0;
+ frame[23] = 0;
+ /* FIXME is it a "number of frames" or "total duration?" */
+ dur = 0;
+ for(i = 0, m = t->moof; i < t->nmoof; i++, m++){
+ for(j = 0; j < m->trun.trun.samplecount; j++){
+ dur += m->trun.trun.samples[j].duration;
+ }
+ }
+ frame[24] = dur;
+ frame[25] = dur >> 8;
+ frame[26] = dur >> 16;
+ frame[27] = dur >> 24;
+ frame[28] = dur >> 32; /* FIXME is it 64 bits? */
+ frame[29] = dur >> 40;
+ frame[30] = dur >> 48;
+ frame[31] = dur >> 56;
+
+ Bwrite(&out, frame, 0x20);
+ }else{
+ werrstr("video: unknown format %T", t->video.format);
+ return -1;
+ }
+ }
+
+ res = -1;
+ if(t->numstc > 0)
+ res = dumpstc(f, &out, t, frame);
+ else if(t->nmoof > 0)
+ res = dumptrun(f, &out, t, frame);
+
+ Bterm(&out);
+
+ return res;
};
int
@@ -565,7 +739,7 @@
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);
+ Bprint(&stderr, "SampleEntry: unknown handler type %T\n", track.handlertype);
}
return n;
@@ -579,8 +753,8 @@
u8int d[128], *p;
Box inner;
int i, n, eof;
- s32int s32i;
- u64int u;
+ u64int u, off;
+ RunSample *s;
if(b->type == BoxFtyp){
eBread(8, "brand and version");
@@ -601,6 +775,10 @@
b->type == BoxMdia || b->type == BoxMinf || b->type == BoxStbl ||
b->type == BoxMoof || b->type == BoxTraf || b->type == BoxEdts ||
b->type == BoxDinf){
+ if(b->type == BoxMoof){
+ memset(&moof, 0, sizeof(moof));
+ memmove(&moof, b, sizeof(Box));
+ }
printbox(b);
dind++;
for(;;){
@@ -616,6 +794,8 @@
memset(&track, 0, sizeof(track));
}
dind--;
+ if(b->type == BoxMoof)
+ memset(&moof, 0, sizeof(moof));
}else if(b->type == BoxMdhd){
n = b->version == 0 ? 16 : 28;
eBread(n, "short read");
@@ -632,6 +812,7 @@
}
b->mdhd.timescale = bu32(p); p += 4;
+ track.timescale = b->mdhd.timescale; /* it's used for IVF timebase */
b->mdhd.duration = bu32(p); p += 4;
if(b->version == 1){
@@ -683,6 +864,7 @@
}else if(b->type == BoxMfhd){
eBread(4, "short read");
b->mfhd.seqnumber = bu32(d);
+ memmove(&moof.mfhd, b, sizeof(*b));
printbox(b);
}else if(b->type == BoxTfhd){
eBread(4, "track_id");
@@ -708,6 +890,7 @@
eBread(4, "default_sample_flags");
b->tfhd.defsample.flags = bu32(d);
}
+ memmove(&moof.tfhd, b, sizeof(*b));
printbox(b);
}else if(b->type == BoxTfdt){
if(b->version == 1){
@@ -731,32 +914,53 @@
}
/* FIXME free those */
b->trun.samples = calloc(b->trun.samplecount, sizeof(RunSample));
+ off = 0;
for(u = 0; u < b->trun.samplecount; u++){
+ s = &b->trun.samples[u];
if(b->flags & 0x100){
eBread(4, "sample_duration");
- b->trun.samples[u].duration = bu32(d);
- }
+ s->duration = bu32(d);
+ }else if(moof.tfhd.flags & 8)
+ s->duration = moof.tfhd.tfhd.defsample.duration;
+
if(b->flags & 0x200){
eBread(4, "sample_size");
- b->trun.samples[u].size = bu32(d);
- }
+ s->size = bu32(d);
+ }else if(moof.tfhd.flags & 16)
+ s->size = moof.tfhd.tfhd.defsample.size;
+
if(b->flags & 0x400){
eBread(4, "sample_flags");
- b->trun.samples[u].flags = bu32(d);
- }
+ s->flags = bu32(d);
+ }else if((b->flags & 4) && u == 0){
+ // FIXME default flags???
+ s->flags = b->trun.firstsampleflags;
+ }else if(moof.tfhd.flags & 32)
+ s->flags = moof.tfhd.tfhd.defsample.flags;
+
if(b->flags & 0x800){
eBread(4, "sample_composition_time_offset");
- s32i = bu32(d);
if(b->version == 0)
- b->trun.samples[u].timeoffset = bu32(d);
+ s->timeoffset = bu32(d);
else
- b->trun.samples[u].timeoffset = s32i;
+ s->timeoffset = (s32int)bu32(d);
}
+
+ s->offset = b->trun.dataoffset + off;
+ if(moof.tfhd.flags & 1)
+ Bprint(&stderr, "tfhd has base-data-offset-present set, this isn't supported atm\n");
+ else if(moof.tfhd.flags & 0x20000)
+ s->offset += moof.offset + moof.tfhd.tfhd.baseoffset;
+ else
+ Bprint(&stderr, "couldn't calculate sample offset\n");
+ off += s->size;
}
+ addtrun(b);
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]);
+ b->esds.id = bu16(d+1);
+ b->esds.flags = d[3];
printbox(b);
}else if(b->type == BoxStsd){
eBread(4, "entry_count");
@@ -950,7 +1154,7 @@
static void
usage(void)
{
- fprint(2, "usage: iso [-d] [-t TRACK] FILE\n");
+ Bprint(&stderr, "usage: iso [-d] [-t TRACK] FILE\n");
exits("usage");
}
@@ -975,6 +1179,8 @@
fmtinstall('T', typefmt);
+ Binit(&stderr, 2, OWRITE);
+
status = nil;
for(; *argv && status == nil; argv++){
if((f = Bopen(*argv, OREAD)) == nil)
@@ -992,12 +1198,14 @@
}
if(trackdump >= 0 && dumptrack(f, trackdump) != 0){
- fprint(2, "%s: %r\n", *argv);
+ Bprint(&stderr, "%s: %r\n", *argv);
status = "dump";
}
Bterm(f);
}
+
+ Bterm(&stderr);
exits(status);
}
--- a/mkfile
+++ b/mkfile
@@ -1,8 +1,7 @@
</$objtype/mkfile
MAN=/sys/man/1
-TARG=\
- iso\
+TARG=mcfs
BIN=/$objtype/bin
@@ -13,6 +12,4 @@
default:V: all
-</sys/src/cmd/mkmany
-
-install:
+</sys/src/cmd/mkone