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;