ref: 3e359350719541544bc2f2c1138c006e85b8e5a6
parent: 564664d7fc2e3822f6d5ce04cb873d5a079b62e8
author: Sigrid Haflínudóttir <[email protected]>
date: Tue Sep 15 10:22:28 EDT 2020
remux AVC1 (h264) properly
--- a/iso.c
+++ b/iso.c
@@ -7,6 +7,7 @@
typedef struct Audio Audio;
typedef struct Box Box;
typedef struct Moof Moof;
+typedef struct ParamSet ParamSet;
typedef struct RunSample RunSample;
typedef struct SampleEntry SampleEntry;
typedef struct SampleToChunk SampleToChunk;
@@ -21,6 +22,11 @@
int samplerate;
};
+struct ParamSet {
+ int len;
+ u8int *data;
+};
+
struct Video {
u32int format;
int width;
@@ -28,7 +34,18 @@
u32int hres;
u32int vres;
int framecount;
- uchar compressor[32];
+ char compressor[32];
+
+ struct {
+ u8int profile;
+ u8int compat;
+ u8int level;
+ u8int nallen; /* in bytes (1, 2, 4), ie with 1 already added to it */
+ int nsps;
+ ParamSet *sps;
+ int npps;
+ ParamSet *pps;
+ }avc;
};
struct SampleEntry {
@@ -206,6 +223,7 @@
BoxStbl = 0x7374626cu,
BoxCtts = 0x63747473u,
BoxStsd = 0x73747364u,
+ BoxAvcc = 0x61766343u,
BoxEsds = 0x65736473u,
BoxStts = 0x73747473u,
BoxStsc = 0x73747363u,
@@ -427,7 +445,25 @@
Bprint(&stderr, "\t\t\t%.*shres\t%d\n", dind, ind, b->stsd.entry[u].video.hres);
Bprint(&stderr, "\t\t\t%.*svres\t%d\n", dind, ind, b->stsd.entry[u].video.vres);
Bprint(&stderr, "\t\t\t%.*sframecount\t%d\n", dind, ind, b->stsd.entry[u].video.framecount);
- Bprint(&stderr, "\t\t\t%.*scompressor\t%.*s\n", dind, ind, 32, (char*)b->stsd.entry[u].video.compressor);
+ Bprint(&stderr, "\t\t\t%.*scompressor\t%.*s\n", dind, ind, 32, b->stsd.entry[u].video.compressor);
+ if(b->stsd.entry[u].video.format == FmtAvc1){
+ Bprint(&stderr, "\t\t\t%.*sprofile\t%d\n", dind, ind, b->stsd.entry[u].video.avc.profile);
+ Bprint(&stderr, "\t\t\t%.*scompat\t%d\n", dind, ind, b->stsd.entry[u].video.avc.compat);
+ Bprint(&stderr, "\t\t\t%.*slevel\t%d\n", dind, ind, b->stsd.entry[u].video.avc.level);
+ Bprint(&stderr, "\t\t\t%.*snallen\t%d\n", dind, ind, b->stsd.entry[u].video.avc.nallen);
+ Bprint(&stderr, "\t\t\t%.*snsps\t%d\n", dind, ind, b->stsd.entry[u].video.avc.nsps);
+ for(i = 0; i < b->stsd.entry[u].video.avc.nsps; i++){
+ Bprint(&stderr, "\t\t\t\t%.*ssps[%d]\n", dind, ind, i);
+ Bprint(&stderr, "\t\t\t\t\t%.*slen\t%d\n", dind, ind, b->stsd.entry[u].video.avc.sps[i].len);
+ Bprint(&stderr, "\t\t\t\t\t%.*sdata\t%.*H\n", dind, ind, b->stsd.entry[u].video.avc.sps[i].len, b->stsd.entry[u].video.avc.sps[i].data);
+ }
+ Bprint(&stderr, "\t\t\t%.*snpps\t%d\n", dind, ind, b->stsd.entry[u].video.avc.npps);
+ for(i = 0; i < b->stsd.entry[u].video.avc.npps; i++){
+ Bprint(&stderr, "\t\t\t\t%.*spps[%d]\n", dind, ind, i);
+ Bprint(&stderr, "\t\t\t\t\t%.*slen\t%d\n", dind, ind, b->stsd.entry[u].video.avc.pps[i].len);
+ Bprint(&stderr, "\t\t\t\t\t%.*sdata\t%.*H\n", dind, ind, b->stsd.entry[u].video.avc.pps[i].len, b->stsd.entry[u].video.avc.pps[i].data);
+ }
+ }
}
if(b->stsd.entry[u].audio.format != 0){
Bprint(&stderr, "\t\t\t%.*schannels\t%d\n", dind, ind, b->stsd.entry[u].audio.channels);
@@ -533,7 +569,7 @@
dumpstc(Biobuf *f, Biobuf *out, Track *t, u8int *frame)
{
SampleToChunk *stc;
- u32int si, ch, nextch;
+ u32int si, ch, nextch, n, len;
u32int samplelast, sample, rawsz, samplesz;
u64int ts;
u8int *raw;
@@ -597,6 +633,15 @@
break;
}
}
+ if(t->video.format == FmtAvc1){
+ for(n = 0; n < samplesz; n += len+4){
+ len = raw[n+0]<<24 | raw[n+1]<<16 | raw[n+2]<<8 | raw[n+3];
+ raw[n+0] = 0;
+ raw[n+1] = 0;
+ raw[n+2] = 0;
+ raw[n+3] = 1;
+ }
+ }
if(Bwrite(out, raw, samplesz) != samplesz){ /* EOF? */
werrstr("eof");
break;
@@ -613,7 +658,7 @@
static int
dumptrun(Biobuf *f, Biobuf *out, Track *t, u8int *frame)
{
- int i, j;
+ int i, j, len, n;
u64int ts;
u8int *raw;
int maxsz, sz;
@@ -673,6 +718,15 @@
werrstr("couldn't read sample (%d bytes)", s->size);
return -1;
}
+ if(t->video.format == FmtAvc1){
+ for(n = 12; n < 12+s->size; n += len+4){
+ len = raw[n+0]<<24 | raw[n+1]<<16 | raw[n+2]<<8 | raw[n+3];
+ raw[n+0] = 0;
+ raw[n+1] = 0;
+ raw[n+2] = 0;
+ raw[n+3] = 1;
+ }
+ }
if(Bwrite(out, raw, 12 + s->size) != 12 + s->size) /* eof? */
break;
ts += s->duration; /* sample's "time offset" is ignored here */
@@ -688,7 +742,7 @@
static int
dumptrack(Biobuf *f, int id)
{
- int i, j, res;
+ int i, j, x, res;
Biobuf out;
u64int dur;
Track *t;
@@ -739,6 +793,10 @@
frame[7] = 0;
if(t->video.format == FmtAv01){
memmove(frame+8, "AV01", 4);
+ }else if(t->video.format == FmtAvc1){
+ memmove(frame+8, "AVC1", 4);
+ }else if(t->video.format == FmtVp09){
+ memmove(frame+8, "VP90", 4);
}else{
werrstr("video: unsupported video format %T", t->video.format);
return -1;
@@ -774,6 +832,29 @@
frame[31] = dur >> 56;
Bwrite(&out, frame, 0x20);
+
+ if(t->video.format == FmtAvc1){
+ memset(frame, 0, 4+8+4);
+ frame[4+8+3] = 1;
+ for(i = 0; i < t->video.avc.nsps; i++){
+ x = 4 + t->video.avc.sps[i].len;
+ frame[0] = x;
+ frame[1] = x >> 8;
+ frame[2] = x >> 16;
+ frame[3] = x >> 24;
+ Bwrite(&out, frame, 4+8+4);
+ Bwrite(&out, t->video.avc.sps[i].data, t->video.avc.sps[i].len);
+ }
+ for(i = 0; i < t->video.avc.npps; i++){
+ x = 4 + t->video.avc.pps[i].len;
+ frame[0] = x;
+ frame[1] = x >> 8;
+ frame[2] = x >> 16;
+ frame[3] = x >> 24;
+ Bwrite(&out, frame, 4+8+4);
+ Bwrite(&out, t->video.avc.pps[i].data, t->video.avc.pps[i].len);
+ }
+ }
}
res = -1;
@@ -798,13 +879,13 @@
if(track.handlertype == HandlerVideo){
e->video.format = e->format;
- /* predefined+reserved+predefined, width+height, hres+vres, reserved, framecount, compressor */
- sz = 2+2+4*3 + 2+2 + 4+4 + 4 + 2 + 32;
+ /* predefined+reserved+predefined, width+height, hres+vres, reserved, framecount, compressor, depth, predefined */
+ sz = 2+2+4*3 + 2+2 + 4+4 + 4 + 2 + 32 + 2 + 2;
eBread(sz, "SampleEntry: video");
e->video.width = bu16(d+16);
e->video.height = bu16(d+18);
- e->video.hres = bu32(d+20);
- e->video.vres = bu32(d+24);
+ e->video.hres = bu32(d+20) >> 16;
+ e->video.vres = bu32(d+24) >> 16;
e->video.framecount = bu16(d+32);
memmove(e->video.compressor, d+34, 32);
n -= sz;
@@ -857,6 +938,46 @@
}
static int
+parseavcc(Biobuf *f, SampleEntry *e, int n)
+{
+ int i;
+ u8int d[2];
+ u32int x;
+
+ USED(n); /* FIXME we don't really validate anything here */
+ if((x = Bgetc(f)) != 1){
+ werrstr("unsupported config version %d", x);
+ goto err;
+ }
+ e->video.avc.profile = Bgetc(f);
+ e->video.avc.compat = Bgetc(f);
+ e->video.avc.level = Bgetc(f);
+ e->video.avc.nallen = (Bgetc(f) & 3) + 1;
+ e->video.avc.nsps = Bgetc(f) & 0x1f;
+ e->video.avc.sps = calloc(e->video.avc.nsps, sizeof(ParamSet));
+ for(i = 0; i < e->video.avc.nsps; i++){
+ eBread(2, "sps len");
+ x = bu16(d);
+ e->video.avc.sps[i].len = x;
+ e->video.avc.sps[i].data = malloc(x);
+ Bread(f, e->video.avc.sps[i].data, x);
+ }
+ e->video.avc.npps = Bgetc(f) & 0x1f;
+ e->video.avc.pps = calloc(e->video.avc.npps, sizeof(ParamSet));
+ for(i = 0; i < e->video.avc.npps; i++){
+ eBread(2, "pps len");
+ x = bu16(d);
+ e->video.avc.pps[i].len = x;
+ e->video.avc.pps[i].data = malloc(x);
+ Bread(f, e->video.avc.pps[i].data, x);
+ }
+
+ return 0;
+err:
+ return -1;
+}
+
+static int
descrlen(Biobuf *f, int *len)
{
int i, c;
@@ -1179,10 +1300,16 @@
b->stsd.entry[u].datarefid = bu16(d);
n -= 2;
- n = parsesampleentry(f, &b->stsd.entry[u], n);
+ if((n = parsesampleentry(f, &b->stsd.entry[u], n)) < 0)
+ goto err;
Bseek(f, n, 1);
}
printbox(b);
+ }else if(b->type == BoxAvcc){
+ assert(sampleentry != nil);
+ if(parseavcc(f, sampleentry, b->dsz) != 0)
+ goto err;
+ /* print it with the sample entry */
}else if(b->type == BoxEsds){
assert(sampleentry != nil);
if(parseesds(f, sampleentry, b->dsz) != 0)
@@ -1401,6 +1528,7 @@
usage();
fmtinstall('T', typefmt);
+ fmtinstall('H', encodefmt);
Binit(&stderr, 2, OWRITE);