shithub: mcfs

Download patch

ref: 03f6379de5cbf44e8d5cec74e4e3d259309af562
parent: 24a65311b538504923aaee04393fd363c2a63356
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Mon Sep 21 01:01:04 EDT 2020

ebml: more complicated packet handling; fix opus pre-skip in granule pos

--- a/common.h
+++ b/common.h
@@ -15,10 +15,16 @@
 };
 
 typedef struct Ebml Ebml;
-typedef struct Framectx Framectx;
-typedef int (*packet_f)(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts);
+typedef struct Packet Packet;
+typedef struct Packetctx Packetctx;
+typedef int (*packet_f)(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
 
-struct Framectx {
+struct Packet {
+	uchar *data;
+	int sz;
+};
+
+struct Packetctx {
 	uvlong frid;
 	uvlong duration;
 	struct {
@@ -51,7 +57,7 @@
 };
 
 struct Ebml {
-	Framectx;
+	Packetctx;
 
 	packet_f fpacket;
 
@@ -74,8 +80,8 @@
 int ebmlrun(Biobuf *f);
 
 u32int crc32(u32int init, u8int *d, ulong len);
-int ivfpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts);
-int oggpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts);
+int ivfpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+int oggpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
 int ebmlint(Biobuf *f, vlong *out, int isid);
 vlong ebmlel(Biobuf *f, vlong sz, vlong *id, vlong *esz);
 vlong ebmlrawint(Biobuf *f, vlong sz, vlong *dst);
--- a/ebml.c
+++ b/ebml.c
@@ -4,7 +4,7 @@
 #include <ctype.h>
 #include "common.h"
 
-static vlong frszs[256];
+static Packet packets[256];
 
 int
 ebmluintb(u8int *b, int sz, vlong *out)
@@ -240,8 +240,8 @@
 int
 ebmlrun(Biobuf *f)
 {
-	int isebml, nframes, i;
-	vlong left, id, n, sz, bufsz, track, off, frsz, x;
+	int isebml, npackets, i;
+	vlong left, id, n, sz, bufsz, track, off, packetsz, x;
 	uvlong ts;
 	uchar *buf;
 	vlong timestamp, endtracks, timestampscale;
@@ -331,76 +331,77 @@
 				}
 				left -= sz;
 				sz -= 3;
-				/* ns timestamp */
-				ts = (timestamp + (s16int)(buf[0]<<8 | buf[1])) * timestampscale + te.codec.delay;
-				nframes = buf[3]+1;
+				npackets = buf[3]+1;
 				switch((buf[2] >> 1) & 3){ /* lacing */
 				case 0: /* none */
-					if(te.fpacket(&out, &te, buf+3, sz, ts) != 0)
-						goto err;
+					packets[0].data = buf+3;
+					packets[0].sz = sz;
+					npackets = 1;
 					break;
 				case 1: /* xiph */
 					sz--;
 					off = 4;
-					for(i = 0; i < nframes-1; i++){
-						frszs[i] = 0;
+					for(i = 0; i < npackets-1; i++){
+						packets[i].sz = 0;
 						do{
-							frszs[i] += buf[off];
+							packets[i].sz += buf[off];
 						}while(buf[off++] == 0xff);
 					}
-					for(i = 0; i < nframes-1; i++){
-						if(te.fpacket(&out, &te, buf+off, frszs[i], ts) != 0)
-							goto err;
-						off += frszs[i];
-						sz -= frszs[i];
+					for(i = 0; i < npackets-1; i++){
+						packets[i].data = buf+off;
+						off += packets[i].sz;
+						sz -= packets[i].sz;
 					}
-					if(sz > 0 && te.fpacket(&out, &te, buf+off, sz, ts) != 0)
-						goto err;
+					packets[i].data = buf+off;
+					packets[i].sz = sz;
 					break;
 				case 2: /* fixed-size */
 					sz--;
-					if((sz % nframes) != 0){
+					if((sz % npackets) != 0){
 						werrstr("invalid number of frames with fixed-size lacing");
 						goto err;
 					}
-					frsz = sz / nframes;
-					for(i = 0; i < nframes; i++){
-						if(te.fpacket(&out, &te, buf+4 + i*frsz, frsz, ts) != 0)
-							goto err;
-						sz -= frsz;
+					packets[0].sz = sz / npackets;
+					for(i = 0; i < npackets; i++){
+						packets[i].data = buf+4 + i*packets[0].sz;
+						packets[i].sz = packets[0].sz;
+						sz -= packets[0].sz;
 					}
 					break;
 				case 3: /* ebml */
 					sz--;
-					frsz = 0;
+					packetsz = 0;
 					off = 4;
-					for(i = 0; i < nframes-1; i++){
+					for(i = 0; i < npackets-1; i++){
 						if((n = (i == 0 ? ebmluintb : ebmlsintb)(buf+off, sz, &x)) < 0)
 							goto err;
-						frsz += x;
-						if(frsz < 0){
-							werrstr("invalid frame size %zd", frsz);
+						packetsz += x;
+						if(packetsz < 0){
+							werrstr("invalid frame size %zd", packetsz);
 							goto err;
 						}
-						frszs[i] = frsz;
+						packets[i].sz = packetsz;
 						off += n;
 						sz -= n;
 					}
-					for(i = 0; i < nframes-1; i++){
-						frsz = frszs[i];
-						if(frsz > sz){
-							werrstr("frame %d/%d out of bounds: %zd > %zd", i, nframes-1, frsz, sz);
+					for(i = 0; i < npackets-1; i++){
+						if(packets[i].sz > sz){
+							werrstr("frame %d/%d out of bounds: %d > %zd", i, npackets-1, packets[i].sz, sz);
 							goto err;
 						}
-						if(frsz > 0 && te.fpacket(&out, &te, buf+off, frsz, ts) != 0)
-							goto err;
-						off += frsz;
-						sz -= frsz;
+						packets[i].data = buf+off;
+						off += packets[i].sz;
+						sz -= packets[i].sz;
 					}
-					if(sz > 0 && te.fpacket(&out, &te, buf+off, sz, ts) != 0)
-						goto err;
+					packets[i].data = buf+off;
+					packets[i].sz = sz;
 					break;
 				}
+
+				/* ns timestamp */
+				ts = (timestamp + (s16int)(buf[0]<<8 | buf[1])) * timestampscale;
+				if(te.fpacket(&out, &te, packets, npackets, ts) != 0)
+					goto err;
 				continue;
 			}
 		}else{
@@ -461,8 +462,10 @@
 	}
 
 	if(isebml == 2 && left == 0){
-		if(e.tracknum > 0 && trackdump == Nodump)
-			trackinfo(&out, &e);
+		if(e.tracknum > 0){
+			if(trackdump == Nodump)
+				trackinfo(&out, &e);
+		}
 		return 0;
 	}
 
--- a/ivf.c
+++ b/ivf.c
@@ -8,7 +8,7 @@
 };
 
 int
-ivfpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts)
+ivfpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts)
 {
 	u8int d[0x20];
 	uvlong dur;
@@ -17,78 +17,80 @@
 	dur = ctx->duration / 1000000ULL;
 	ts = ts / 1000000ULL;
 
-	if(ctx->frid == 0){
-		memmove(d, "DKIF", 4);
-		d[4] = 0;
-		d[5] = 0;
-		d[6] = 0x20;
-		d[7] = 0;
-		if(ctx->fmt == FmtAv01)
-			memmove(d+8, "AV01", 4);
-		else if(ctx->fmt == FmtAvc1)
-			memmove(d+8, "AVC1", 4);
-		else if(ctx->fmt == FmtVp08)
-			memmove(d+8, "VP80", 4);
-		else if(ctx->fmt == FmtVp09)
-			memmove(d+8, "VP90", 4);
-		else{
-			werrstr("unsupported video format %T", ctx->fmt);
-			goto err;
+	for(; np > 0; np--, p++){
+		if(ctx->frid == 0){
+			memmove(d, "DKIF", 4);
+			d[4] = 0;
+			d[5] = 0;
+			d[6] = 0x20;
+			d[7] = 0;
+			if(ctx->fmt == FmtAv01)
+				memmove(d+8, "AV01", 4);
+			else if(ctx->fmt == FmtAvc1)
+				memmove(d+8, "AVC1", 4);
+			else if(ctx->fmt == FmtVp08)
+				memmove(d+8, "VP80", 4);
+			else if(ctx->fmt == FmtVp09)
+				memmove(d+8, "VP90", 4);
+			else{
+				werrstr("unsupported video format %T", ctx->fmt);
+				goto err;
+			}
+			d[12] = ctx->video.width;
+			d[13] = ctx->video.width >> 8;
+			d[14] = ctx->video.height;
+			d[15] = ctx->video.height >> 8;
+			/* timebase denum */
+			d[16] = Timedenum;
+			d[17] = Timedenum >> 8;
+			d[18] = Timedenum >> 16;
+			d[19] = Timedenum >> 24;
+			/* timebase num */
+			d[20] = 1;
+			d[21] = 0;
+			d[22] = 0;
+			d[23] = 0;
+			/* FIXME is it a "number of frames" or "total duration?" */
+			d[24] = dur;
+			d[25] = dur >> 8;
+			d[26] = dur >> 16;
+			d[27] = dur >> 24;
+			d[28] = dur >> 32; /* FIXME is it 64 bits? */
+			d[29] = dur >> 40;
+			d[30] = dur >> 48;
+			d[31] = dur >> 56;
+
+			if(Bwrite(out, d, 0x20) != 0x20)
+				goto err;
 		}
-		d[12] = ctx->video.width;
-		d[13] = ctx->video.width >> 8;
-		d[14] = ctx->video.height;
-		d[15] = ctx->video.height >> 8;
-		/* timebase denum */
-		d[16] = Timedenum;
-		d[17] = Timedenum >> 8;
-		d[18] = Timedenum >> 16;
-		d[19] = Timedenum >> 24;
-		/* timebase num */
-		d[20] = 1;
-		d[21] = 0;
-		d[22] = 0;
-		d[23] = 0;
-		/* FIXME is it a "number of frames" or "total duration?" */
-		d[24] = dur;
-		d[25] = dur >> 8;
-		d[26] = dur >> 16;
-		d[27] = dur >> 24;
-		d[28] = dur >> 32; /* FIXME is it 64 bits? */
-		d[29] = dur >> 40;
-		d[30] = dur >> 48;
-		d[31] = dur >> 56;
 
-		if(Bwrite(out, d, 0x20) != 0x20)
+		d[0] = p[0].sz;
+		d[1] = p[0].sz >> 8;
+		d[2] = p[0].sz >> 16;
+		d[3] = p[0].sz >> 24;
+		d[4] = ts;
+		d[5] = ts >> 8;
+		d[6] = ts >> 16;
+		d[7] = ts >> 24;
+		d[8] = ts >> 32;
+		d[9] = ts >> 40;
+		d[10] = ts >> 48;
+		d[11] = ts >> 56;
+		if(Bwrite(out, d, 12) != 12)
 			goto err;
-	}
-
-	d[0] = sz;
-	d[1] = sz >> 8;
-	d[2] = sz >> 16;
-	d[3] = sz >> 24;
-	d[4] = ts;
-	d[5] = ts >> 8;
-	d[6] = ts >> 16;
-	d[7] = ts >> 24;
-	d[8] = ts >> 32;
-	d[9] = ts >> 40;
-	d[10] = ts >> 48;
-	d[11] = ts >> 56;
-	if(Bwrite(out, d, 12) != 12)
-		goto err;
-	if(ctx->fmt == FmtAvc1){
-		for(n = 0; n < sz; n += len+4){
-			len = buf[n+0]<<24 | buf[n+1]<<16 | buf[n+2]<<8 | buf[n+3];
-			buf[n+0] = 0;
-			buf[n+1] = 0;
-			buf[n+2] = 0;
-			buf[n+3] = 1;
+		if(ctx->fmt == FmtAvc1){
+			for(n = 0; n < p[0].sz; n += len+4){
+				len = p[0].data[n+0]<<24 | p[0].data[n+1]<<16 | p[0].data[n+2]<<8 | p[0].data[n+3];
+				p[0].data[n+0] = 0;
+				p[0].data[n+1] = 0;
+				p[0].data[n+2] = 0;
+				p[0].data[n+3] = 1;
+			}
 		}
+		if(Bwrite(out, p[0].data, p[0].sz) != p[0].sz)
+			goto err;
+		ctx->frid++;
 	}
-	if(Bwrite(out, buf, sz) != sz)
-		goto err;
-	ctx->frid++;
 
 	return 0;
 err:
--- a/ogg.c
+++ b/ogg.c
@@ -3,16 +3,14 @@
 #include <bio.h>
 #include "common.h"
 
-static int sgszs[256];
-
 static int
-packet(Biobuf *out, Framectx *ctx, int htype, u8int *buf, int sz, uvlong granule)
+packet(Biobuf *out, Packetctx *ctx, int htype, Packet *p, int np, uvlong granule)
 {
 	/*                 magic            ver  htype  granule          serial   seq      checksum  page segments  segsz */
 	/*         segsz   0                4    5      6                14       18       22        26             27 */
 	u8int h[28+255] = {'O','g','g','S', 0,   0,     0,0,0,0,0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,  0,             0};
 	u32int crc;
-	int psz, i, n;
+	int psz, i, n, ntot, ip;
 
 	h[5] = htype;
 	h[6] = granule;
@@ -36,32 +34,43 @@
 	h[23] = 0;
 	h[24] = 0;
 	h[25] = 0;
-	n = 1 + (sz > 0 ? (sz-1)/255 : 0);
-	h[26] = n;
-	if(n+27 >= nelem(h)){
-		werrstr("frame is too large");
-		return -1;
-	}
-	for(i = 0, psz = sz; i < n && i < nelem(h)-27; i++){
-		h[27+i] = min(255, psz);
-		psz -= h[27+i];
-		if(psz == 0)
-			break;
+
+	for(ntot = ip = 0; ip < np; ip++){
+		n = 1 + (p[ip].sz > 0 ? (p[ip].sz-1)/255 : 0);
+		if(27+ntot+n >= nelem(h)){
+			werrstr("frame is too large");
+			return -1;
+		}
+		for(i = 0, psz = p[ip].sz; i < n; i++){
+			h[27+ntot+i] = min(255, psz);
+			psz -= h[27+ntot+i];
+			if(psz == 0)
+				break;
+		}
+		ntot += n;
 	}
-	n += 27;
-	crc = crc32(crc32(0, h, n), buf, sz);
+	h[26] = ntot;
+	ntot += 27;
+
+	for(ip = 0, crc = crc32(0, h, ntot); ip < np; ip++)
+		crc = crc32(crc, p[ip].data, p[ip].sz);
+
 	h[22] = crc >> 24;
 	h[23] = crc >> 16;
 	h[24] = crc >> 8;
 	h[25] = crc;
-	if(Bwrite(out, h, n) != n || Bwrite(out, buf, sz) != sz)
+	if(Bwrite(out, h, ntot) != ntot)
 		return -1;
+	for(ip = 0; ip < np; ip++){
+		if(Bwrite(out, p[ip].data, p[ip].sz) != p[ip].sz)
+			return -1;
+	}
 
 	return 0;
 }
 
 int
-oggpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts)
+oggpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts)
 {
 	/*                     magic                            vendor len   list len */
 	u8int opuscomment[] = {'O','p','u','s','T','a','g','s', 0,0,0,0,     0,0,0,0};
@@ -68,12 +77,15 @@
 	int r, i, nsg, dsz, total;
 	u8int *d;
 	u64int gr;
+	int sgszs[4];
+	Packet onep;
 
-	gr = ts * 48 / 1000000;
+	if(ctx->fmt == FmtOpus)
+		ts += 80000000; /* pre-skip */
+	gr = (ts * 48) / 1000000;
+
 	r = 0;
 	if(ctx->frid == 0){ /* first packet? */
-		ctx->frid;
-
 		d = ctx->codec.priv.data;
 		dsz = ctx->codec.priv.sz;
 
@@ -80,13 +92,14 @@
 		/* id/codec setup header always goes first */
 		if(ctx->codec.priv.sz > 0){ /* if we have codec setup data, write it */
 			if(ctx->fmt == FmtVorbis){
-				nsg = *d++;
+				i = *d++;
 				dsz--;
-				for(i = 0, total = 0; i < nsg && dsz > 0; i++, total += sgszs[i]){
-					sgszs[i] = 0;
+				for(nsg = 0, total = 0; i > 0 && dsz > 0; total += sgszs[nsg], nsg++){
+					sgszs[nsg] = 0;
 					do{
-						sgszs[i] += *d;
+						sgszs[nsg] += *d;
 						dsz--;
+						i--;
 					}while(*d++ == 0xff);
 				}
 				if(total > dsz){
@@ -94,29 +107,37 @@
 					goto err;
 				}
 				for(i = 0; i < nsg && dsz > 0; d += sgszs[i], dsz -= sgszs[i], i++){
-					if(packet(out, ctx, i == 0 ? 2 : 0, d, sgszs[i], 0) != 0)
+					onep.data = d;
+					onep.sz = sgszs[i];
+					if(packet(out, ctx, i == 0 ? 2 : 0, &onep, 1, 0) != 0)
 						goto err;
 				}
-				if(dsz > 0)
-					r = packet(out, ctx, 0, d, dsz, 0);
+				if(dsz > 0){
+					onep.data = d;
+					onep.sz = dsz;
+					r = packet(out, ctx, 0, &onep, 1, 0);
+				}
 			}else{
-				r = packet(out, ctx, 2, d, dsz, 0);
+				onep.data = d;
+				onep.sz = dsz;
+				r = packet(out, ctx, 2, &onep, 1, 0);
 			}
-			sz = 0; /* don't duplicate it */
-		}else{ /* otherwise let's hope the first packet has that data itself */
-			r = packet(out, ctx, 2, buf, sz, 0);
 		}
+		/* otherwise let's hope the first packet has that data itself */
 		if(r != 0)
 			goto err;
 
 		/* comment */
-		if(ctx->fmt == FmtOpus)
-			r = packet(out, ctx, 0, opuscomment, sizeof(opuscomment), 0);
+		if(ctx->fmt == FmtOpus){
+			onep.data = opuscomment;
+			onep.sz = sizeof(opuscomment);
+			r = packet(out, ctx, 0, &onep, 1, 0);
+		}
 		if(r != 0)
 			goto err;
 	}
 
-	if(sz > 0 && packet(out, ctx, 0, buf, sz, gr) != 0)
+	if(np > 0 && packet(out, ctx, 0, p, np, gr) != 0)
 		goto err;
 
 	return 0;