ref: 24a65311b538504923aaee04393fd363c2a63356
parent: c10d17a9f0c84746e55f939a2a2724c47090c96e
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Sep 20 17:15:15 EDT 2020
ebml support
--- /dev/null
+++ b/common.h
@@ -1,0 +1,144 @@
+enum {
+ Nodump = -1,
+ Dumpaudio = -2,
+ Dumpvideo = -3,
+
+ FmtAv01 = 0x61763031u,
+ FmtAvc1 = 0x61766331u,
+ FmtMp4a = 0x6d703461u,
+ FmtOpus = 0x6f707573u,
+ FmtVp08 = 0x76703038u,
+ FmtVp09 = 0x76703039u,
+
+ /* a fake one, not supposed to show up in a mp4 */
+ FmtVorbis = 0x766f7262u,
+};
+
+typedef struct Ebml Ebml;
+typedef struct Framectx Framectx;
+typedef int (*packet_f)(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts);
+
+struct Framectx {
+ uvlong frid;
+ uvlong duration;
+ struct {
+ char name[16];
+ vlong delay;
+ struct {
+ uchar *data;
+ int sz;
+ }priv;
+ }codec;
+ u32int trackuid;
+ u32int fmt;
+
+ struct {
+ int width, height;
+ struct {
+ int left, right, top, bottom;
+ }crop;
+ struct {
+ int width, height;
+ int unit;
+ int aspectmode;
+ }display;
+ }video;
+ struct {
+ float samplerate;
+ int channels;
+ int bps;
+ }audio;
+};
+
+struct Ebml {
+ Framectx;
+
+ packet_f fpacket;
+
+ double ebmldur;
+ vlong timestampscale;
+ vlong seekpreroll;
+ vlong perframe;
+ int tracknum;
+ int tracktype;
+ vlong codecdelay;
+};
+
+extern Biobuf stderr, out;
+extern int dflag, trackdump;
+
+#pragma varargck type "T" u32int
+int isotypefmt(Fmt *f);
+
+int isorun(Biobuf *f);
+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 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);
+
+#define ebmlgetnumber(expid, dest) \
+ if(id == expid){ \
+ vlong x; \
+ if(ebmlrawint(f, sz, &x) < 0) \
+ return -1; \
+ dest = x; \
+ left -= sz; \
+ continue; \
+ }
+
+#define ebmlgetstring(expid, dest) \
+ if(id == expid){ \
+ n = min(sizeof(dest)-1, sz); \
+ if(Bread(f, dest, n) != n) \
+ return -1; \
+ dest[n] = 0; \
+ if(n != sz) \
+ Bseek(f, sz-n, 1); \
+ left -= sz; \
+ continue; \
+ }
+
+#define ebmlgetbytes(expid, dest) \
+ if(id == expid){ \
+ dest.data = malloc(sz); \
+ if(Bread(f, dest.data, sz) != sz) \
+ return -1; \
+ dest.sz = sz; \
+ left -= sz; \
+ continue; \
+ }
+
+#define ebmlgetfloat(expid, dest) \
+ if(id == expid){ \
+ u32int u; \
+ union { \
+ uchar b[8]; \
+ u32int u[2]; \
+ float f; \
+ double d; \
+ }x; \
+ if(sz == 4){ \
+ if(Bread(f, x.b, 4) != 4) \
+ return -1; \
+ x.u[0] = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24; \
+ dest = x.f; \
+ }else if(sz == 8){ \
+ if(Bread(f, x.b, 8) != 8) \
+ return -1; \
+ u = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24; \
+ x.u[0] = (x.u[1]&0xff000000)>>24 | (x.u[1]&0x00ff0000)>>8 | (x.u[1]&0x0000ff00)<<8 | (x.u[1]&0x000000ff)<<24; \
+ x.u[1] = u; \
+ dest = x.d; \
+ }else{ \
+ werrstr("invalid float size"); \
+ break; \
+ } \
+ left -= sz; \
+ continue; \
+ }
+
+#define min(a,b) ((a)<=(b)?(a):(b))
--- /dev/null
+++ b/crc32.c
@@ -1,0 +1,61 @@
+#include <u.h>
+#include <libc.h>
+
+static u32int table[] = {
+ 0x00000000, 0xb71dc104, 0x6e3b8209, 0xd926430d, 0xdc760413, 0x6b6bc517,
+ 0xb24d861a, 0x0550471e, 0xb8ed0826, 0x0ff0c922, 0xd6d68a2f, 0x61cb4b2b,
+ 0x649b0c35, 0xd386cd31, 0x0aa08e3c, 0xbdbd4f38, 0x70db114c, 0xc7c6d048,
+ 0x1ee09345, 0xa9fd5241, 0xacad155f, 0x1bb0d45b, 0xc2969756, 0x758b5652,
+ 0xc836196a, 0x7f2bd86e, 0xa60d9b63, 0x11105a67, 0x14401d79, 0xa35ddc7d,
+ 0x7a7b9f70, 0xcd665e74, 0xe0b62398, 0x57abe29c, 0x8e8da191, 0x39906095,
+ 0x3cc0278b, 0x8bdde68f, 0x52fba582, 0xe5e66486, 0x585b2bbe, 0xef46eaba,
+ 0x3660a9b7, 0x817d68b3, 0x842d2fad, 0x3330eea9, 0xea16ada4, 0x5d0b6ca0,
+ 0x906d32d4, 0x2770f3d0, 0xfe56b0dd, 0x494b71d9, 0x4c1b36c7, 0xfb06f7c3,
+ 0x2220b4ce, 0x953d75ca, 0x28803af2, 0x9f9dfbf6, 0x46bbb8fb, 0xf1a679ff,
+ 0xf4f63ee1, 0x43ebffe5, 0x9acdbce8, 0x2dd07dec, 0x77708634, 0xc06d4730,
+ 0x194b043d, 0xae56c539, 0xab068227, 0x1c1b4323, 0xc53d002e, 0x7220c12a,
+ 0xcf9d8e12, 0x78804f16, 0xa1a60c1b, 0x16bbcd1f, 0x13eb8a01, 0xa4f64b05,
+ 0x7dd00808, 0xcacdc90c, 0x07ab9778, 0xb0b6567c, 0x69901571, 0xde8dd475,
+ 0xdbdd936b, 0x6cc0526f, 0xb5e61162, 0x02fbd066, 0xbf469f5e, 0x085b5e5a,
+ 0xd17d1d57, 0x6660dc53, 0x63309b4d, 0xd42d5a49, 0x0d0b1944, 0xba16d840,
+ 0x97c6a5ac, 0x20db64a8, 0xf9fd27a5, 0x4ee0e6a1, 0x4bb0a1bf, 0xfcad60bb,
+ 0x258b23b6, 0x9296e2b2, 0x2f2bad8a, 0x98366c8e, 0x41102f83, 0xf60dee87,
+ 0xf35da999, 0x4440689d, 0x9d662b90, 0x2a7bea94, 0xe71db4e0, 0x500075e4,
+ 0x892636e9, 0x3e3bf7ed, 0x3b6bb0f3, 0x8c7671f7, 0x555032fa, 0xe24df3fe,
+ 0x5ff0bcc6, 0xe8ed7dc2, 0x31cb3ecf, 0x86d6ffcb, 0x8386b8d5, 0x349b79d1,
+ 0xedbd3adc, 0x5aa0fbd8, 0xeee00c69, 0x59fdcd6d, 0x80db8e60, 0x37c64f64,
+ 0x3296087a, 0x858bc97e, 0x5cad8a73, 0xebb04b77, 0x560d044f, 0xe110c54b,
+ 0x38368646, 0x8f2b4742, 0x8a7b005c, 0x3d66c158, 0xe4408255, 0x535d4351,
+ 0x9e3b1d25, 0x2926dc21, 0xf0009f2c, 0x471d5e28, 0x424d1936, 0xf550d832,
+ 0x2c769b3f, 0x9b6b5a3b, 0x26d61503, 0x91cbd407, 0x48ed970a, 0xfff0560e,
+ 0xfaa01110, 0x4dbdd014, 0x949b9319, 0x2386521d, 0x0e562ff1, 0xb94beef5,
+ 0x606dadf8, 0xd7706cfc, 0xd2202be2, 0x653deae6, 0xbc1ba9eb, 0x0b0668ef,
+ 0xb6bb27d7, 0x01a6e6d3, 0xd880a5de, 0x6f9d64da, 0x6acd23c4, 0xddd0e2c0,
+ 0x04f6a1cd, 0xb3eb60c9, 0x7e8d3ebd, 0xc990ffb9, 0x10b6bcb4, 0xa7ab7db0,
+ 0xa2fb3aae, 0x15e6fbaa, 0xccc0b8a7, 0x7bdd79a3, 0xc660369b, 0x717df79f,
+ 0xa85bb492, 0x1f467596, 0x1a163288, 0xad0bf38c, 0x742db081, 0xc3307185,
+ 0x99908a5d, 0x2e8d4b59, 0xf7ab0854, 0x40b6c950, 0x45e68e4e, 0xf2fb4f4a,
+ 0x2bdd0c47, 0x9cc0cd43, 0x217d827b, 0x9660437f, 0x4f460072, 0xf85bc176,
+ 0xfd0b8668, 0x4a16476c, 0x93300461, 0x242dc565, 0xe94b9b11, 0x5e565a15,
+ 0x87701918, 0x306dd81c, 0x353d9f02, 0x82205e06, 0x5b061d0b, 0xec1bdc0f,
+ 0x51a69337, 0xe6bb5233, 0x3f9d113e, 0x8880d03a, 0x8dd09724, 0x3acd5620,
+ 0xe3eb152d, 0x54f6d429, 0x7926a9c5, 0xce3b68c1, 0x171d2bcc, 0xa000eac8,
+ 0xa550add6, 0x124d6cd2, 0xcb6b2fdf, 0x7c76eedb, 0xc1cba1e3, 0x76d660e7,
+ 0xaff023ea, 0x18ede2ee, 0x1dbda5f0, 0xaaa064f4, 0x738627f9, 0xc49be6fd,
+ 0x09fdb889, 0xbee0798d, 0x67c63a80, 0xd0dbfb84, 0xd58bbc9a, 0x62967d9e,
+ 0xbbb03e93, 0x0cadff97, 0xb110b0af, 0x060d71ab, 0xdf2b32a6, 0x6836f3a2,
+ 0x6d66b4bc, 0xda7b75b8, 0x035d36b5, 0xb440f7b1, 0x00000001
+};
+static u32int poly = 0x04c11db7;
+
+u32int
+crc32(u32int init, u8int *d, ulong len)
+{
+ u32int c;
+ ulong i;
+
+ c = init;
+ for(i = 0; i < len; i++)
+ c = table[(c & 0xff) ^ d[i]] ^ (c >> 8);
+ return c;
+}
--- /dev/null
+++ b/ebml.c
@@ -1,0 +1,471 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "common.h"
+
+static vlong frszs[256];
+
+int
+ebmluintb(u8int *b, int sz, vlong *out)
+{
+ uvlong v, m;
+ int c, n;
+
+ *out = 0;
+ for(n = 1, m = 0x80, v = 0; n <= 8; v <<= 8, m <<= 7, n++){
+ if(n-1 >= sz){
+ werrstr("eof");
+ return -1;
+ }
+ c = b[n-1];
+ if(n == 1 && c == 0){
+ werrstr("invalid number: %02x", c);
+ return -1;
+ }
+ v |= c;
+ if(v & m){
+ *out = v & ~m;
+ return n;
+ }
+ v &= ~m;
+ }
+ werrstr("number overflow");
+
+ return -1;
+}
+
+int
+ebmlsintb(u8int *b, int sz, vlong *out)
+{
+ int n;
+
+ if((n = ebmluintb(b, sz, out)) < 0)
+ return -1;
+ *out -= (1 << n*7-1) - 1;
+
+ return n;
+}
+
+int
+ebmluint(Biobuf *f, vlong *out, int isid)
+{
+ uvlong v, m;
+ int c, n;
+
+ *out = 0;
+ for(n = 1, m = 0x80, v = 0; n <= 8; v <<= 8, m <<= 7, n++){
+ if((c = Bgetc(f)) < 0){
+ werrstr("eof");
+ return -1;
+ }
+ if(n == 1 && c == 0){
+ werrstr("invalid number: %02x at 0x%zx", c, Boffset(f));
+ return -1;
+ }
+ v |= c;
+ if(v & m){
+ *out = isid ? v : (v & ~m);
+ return n;
+ }
+ if(!isid)
+ v &= ~m;
+ }
+ werrstr("number overflow");
+
+ return -1;
+}
+
+int
+ebmlsint(Biobuf *f, vlong *out)
+{
+ int n;
+
+ if((n = ebmluint(f, out, 0)) < 0)
+ return -1;
+ *out -= (1 << n*7-1) - 1;
+
+ return n;
+}
+
+vlong
+ebmlel(Biobuf *f, vlong sz, vlong *id, vlong *esz)
+{
+ vlong x, n, r;
+
+ if(sz < 2)
+ return -1;
+ n = 0;
+ if((n += (r = ebmluint(f, &x, 1))) >= sz || r < 0){
+ werrstr("id: %r");
+ return -1;
+ }
+ *id = x;
+ if((n += (r = ebmluint(f, &x, 0))) >= sz || r < 0 || sz-n < x){
+ werrstr("sz: (sz=%zd n=%zd r=%zd x=%zd): %r", sz, n, r, x);
+ return -1;
+ }
+ *esz = x;
+
+ return n;
+}
+
+vlong
+ebmlrawint(Biobuf *f, vlong sz, vlong *dst)
+{
+ vlong i;
+ int c;
+
+ *dst = 0;
+ for(i = 0; i < sz; i++){
+ *dst <<= 8;
+ if((c = Bgetc(f)) < 0){
+ werrstr("eof");
+ return -1;
+ }
+ *dst |= c;
+ }
+
+ return 0;
+}
+
+static char *
+codec(char *s)
+{
+ static char t[16];
+ int n;
+
+ n = snprint(t, sizeof(t), "%s", s+2);
+ for(n -= 1; n >= 0; n--)
+ t[n] = tolower(t[n]);
+ if((s = strchr(t, '/')) != nil)
+ *s = 0;
+
+ return t;
+}
+
+static int
+initctx(Ebml *e)
+{
+ char *c;
+
+ c = codec(e->codec.name);
+ if(e->tracktype == 1){
+ if(strcmp(c, "vp9") == 0)
+ e->fmt = FmtVp09;
+ else if(strcmp(c, "vp8") == 0)
+ e->fmt = FmtVp08;
+ else
+ goto err;
+ e->fpacket = ivfpacket;
+ return 0;
+ }
+ if(e->tracktype == 2){
+ if(strcmp(c, "vorbis") == 0){
+ e->fmt = FmtVorbis;
+ e->fpacket = oggpacket;
+ return 0;
+ }
+ if(strcmp(c, "opus") == 0){
+ e->fmt = FmtOpus;
+ e->fpacket = oggpacket;
+ return 0;
+ }
+ }
+ e->duration = e->ebmldur;
+ e->duration *= e->timestampscale;
+
+err:
+ werrstr("don't know how to remux %s", c);
+
+ return -1;
+}
+
+static char *
+tracktype(Ebml *e)
+{
+ static char *types[] = {
+ [1] = "video",
+ [2] = "audio",
+ [3] = "complex",
+ [16] = "logo",
+ [17] = "subtitle",
+ [18] = "buttons",
+ [32] = "control",
+ [33] = "metadata",
+ };
+ char *t;
+
+ t = e->tracktype < nelem(types) ? types[e->tracktype] : "???";
+ if(t == nil)
+ t = "???";
+
+ return t;
+}
+
+static void
+trackinfo(Biobuf *o, Ebml *e)
+{
+ char *t;
+
+ if(dflag){
+ Bprint(o, "per frame: %zd\n", e->perframe);
+ Bprint(o, "track number: %d\n", e->tracknum);
+ Bprint(o, "track type: %d\n", e->tracktype);
+ Bprint(o, "codec name: %s\n", e->codec.name);
+ Bprint(o, "codec priv data: %d bytes [%.*H]\n", e->codec.priv.sz, e->codec.priv.sz, e->codec.priv.data);
+ Bprint(o, "codec delay: %zdns\n", e->codec.delay);
+ if(e->tracktype == 1){
+ Bprint(o, "video: %dx%d [%d,%d,%d,%d]\n", e->video.width, e->video.height, e->video.crop.left, e->video.crop.top, e->video.crop.right, e->video.crop.bottom);
+ Bprint(o, "display: %dx%d unit=%d aspectmode=%d\n", e->video.display.width, e->video.display.height, e->video.display.unit, e->video.display.aspectmode);
+ }else if(e->tracktype == 2){
+ Bprint(o, "audio: samplerate=%g, channels=%d, bps=%d\n", e->audio.samplerate, e->audio.channels, e->audio.bps);
+ }
+ Bprint(o, "seek pre-roll: %zd\n", e->seekpreroll);
+ }
+
+ t = tracktype(e);
+ Bprint(o, "%d\t%s\t", e->tracknum, t);
+ if(e->tracktype == 1)
+ Bprint(o, "%s\t%d\t%d", codec(e->codec.name), e->video.width, e->video.height);
+ else if(e->tracktype == 2)
+ Bprint(o, "%s\t%d\t%d", codec(e->codec.name), e->audio.channels, (int)e->audio.samplerate);
+ else if(e->tracktype == 17)
+ Bprint(o, ""); /* FIXME */
+ else
+ Bprint(o, "???");
+ Bprint(o, "\n");
+}
+
+int
+ebmlrun(Biobuf *f)
+{
+ int isebml, nframes, i;
+ vlong left, id, n, sz, bufsz, track, off, frsz, x;
+ uvlong ts;
+ uchar *buf;
+ vlong timestamp, endtracks, timestampscale;
+ Ebml e, te;
+
+ buf = nil;
+ bufsz = 0;
+ track = -1;
+ timestamp = 0;
+ left = (1ULL<<63)-1;
+ endtracks = -1;
+ te.tracknum = -1;
+ timestampscale = 1000000;
+ e.tracknum = -1;
+ for(isebml = 0; left != 0;){
+ if(endtracks > 0 && left < endtracks && trackdump == Nodump){
+ /* early exit */
+ left = 0;
+ break;
+ }
+
+ off = Boffset(f);
+ if((n = ebmlel(f, left, &id, &sz)) < 0){
+ werrstr("invalid ebml: %r at 0x%zx", off);
+ goto err;
+ }
+ if(dflag > 1)
+ Bprint(&stderr, "0x%zux\t%zux\t%zd\t%zd\n", off, id, sz, left);
+ left -= n;
+
+ if(id == 0x1a45dfa3){ /* EBML comes first */
+ if(isebml != 0){
+ werrstr("double EBML?");
+ goto err;
+ }
+ isebml++;
+ }else if(id == 0x18538067){ /* segment */
+ left = sz;
+ if(isebml != 1){
+ werrstr("invalid ebml");
+ goto err;
+ }
+ isebml++; /* make sure we don't see more segments */
+ continue; /* go in */
+ }else if(id == 0x1549a966){ /* segment info */
+ continue;
+ }else if(id == 0x1654ae6b){ /* tracks */
+ endtracks = left - sz; /* to skip early in case track dump not required */
+ continue;
+ }else if(id == 0x1f43b675){ /* cluster */
+ if(trackdump != Nodump) /* skip it entirely if no dump required */
+ continue;
+ }else if(id == 0xae){ /* track entry */
+ if(e.tracknum > 0 && trackdump == Nodump)
+ trackinfo(&out, &e);
+ if(e.tracknum == trackdump)
+ memmove(&te, &e, sizeof(e));
+ memset(&e, 0, sizeof(e));
+ e.timestampscale = timestampscale;
+ continue;
+ }else if(id == 0xe0 || id == 0xe1){ /* video/audio settings */
+ continue;
+ }else if(id == 0xa0 && trackdump != Nodump){ /* block group */
+ continue;
+ }else if((id == 0xa3 || id == 0xa1) && trackdump != Nodump){ /* (simple) block */
+ if(te.tracknum == -1)
+ memmove(&te, &e, sizeof(e));
+ if((n = ebmluint(f, &track, 0)) < 0){
+ werrstr("block: %r");
+ goto err;
+ }
+ left -= n;
+ sz -= n;
+ if(track == trackdump && track == te.tracknum){
+ if(te.fpacket == nil && initctx(&te) != 0){
+ werrstr("packet: %r");
+ goto err;
+ }
+
+ if(bufsz < sz){
+ buf = realloc(buf, sz);
+ bufsz = sz;
+ }
+ if(Bread(f, buf, sz) != sz){
+ werrstr("short read");
+ goto err;
+ }
+ left -= sz;
+ sz -= 3;
+ /* ns timestamp */
+ ts = (timestamp + (s16int)(buf[0]<<8 | buf[1])) * timestampscale + te.codec.delay;
+ nframes = buf[3]+1;
+ switch((buf[2] >> 1) & 3){ /* lacing */
+ case 0: /* none */
+ if(te.fpacket(&out, &te, buf+3, sz, ts) != 0)
+ goto err;
+ break;
+ case 1: /* xiph */
+ sz--;
+ off = 4;
+ for(i = 0; i < nframes-1; i++){
+ frszs[i] = 0;
+ do{
+ frszs[i] += 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];
+ }
+ if(sz > 0 && te.fpacket(&out, &te, buf+off, sz, ts) != 0)
+ goto err;
+ break;
+ case 2: /* fixed-size */
+ sz--;
+ if((sz % nframes) != 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;
+ }
+ break;
+ case 3: /* ebml */
+ sz--;
+ frsz = 0;
+ off = 4;
+ for(i = 0; i < nframes-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);
+ goto err;
+ }
+ frszs[i] = frsz;
+ 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);
+ goto err;
+ }
+ if(frsz > 0 && te.fpacket(&out, &te, buf+off, frsz, ts) != 0)
+ goto err;
+ off += frsz;
+ sz -= frsz;
+ }
+ if(sz > 0 && te.fpacket(&out, &te, buf+off, sz, ts) != 0)
+ goto err;
+ break;
+ }
+ continue;
+ }
+ }else{
+ ebmlgetnumber(0x2ad7b1, timestampscale)
+ else
+ ebmlgetfloat(0xb5, e.audio.samplerate)
+ else
+ ebmlgetnumber(0x9f, e.audio.channels)
+ else
+ ebmlgetnumber(0x6264, e.audio.bps)
+ else
+ ebmlgetnumber(0xd7, e.tracknum)
+ else
+ ebmlgetnumber(0x83, e.tracktype)
+ else
+ ebmlgetstring(0x86, e.codec.name)
+ else
+ ebmlgetbytes(0x63a2, e.codec.priv)
+ else
+ ebmlgetnumber(0x56aa, e.codec.delay)
+ else
+ ebmlgetnumber(0xb0, e.video.width)
+ else
+ ebmlgetnumber(0xba, e.video.height)
+ else
+ ebmlgetnumber(0x54aa, e.video.crop.bottom)
+ else
+ ebmlgetnumber(0x54bb, e.video.crop.top)
+ else
+ ebmlgetnumber(0x54cc, e.video.crop.left)
+ else
+ ebmlgetnumber(0x54dd, e.video.crop.right)
+ else
+ ebmlgetnumber(0x54b0, e.video.display.width)
+ else
+ ebmlgetnumber(0x54ba, e.video.display.height)
+ else
+ ebmlgetnumber(0x54b2, e.video.display.unit)
+ else
+ ebmlgetnumber(0x54b3, e.video.display.aspectmode)
+ else
+ ebmlgetnumber(0xe7, timestamp)
+ else
+ ebmlgetnumber(0x23e383, e.perframe)
+ else
+ ebmlgetnumber(0x56bb, e.seekpreroll)
+ else
+ ebmlgetfloat(0x4489, e.duration)
+ else
+ ebmlgetnumber(0x73c5, e.trackuid)
+ //else
+ // ebmlgetnumber(0x9b, blockdur)
+ }
+
+ if(Bseek(f, sz, 1) < 0)
+ return -1;
+ left -= sz;
+ }
+
+ if(isebml == 2 && left == 0){
+ if(e.tracknum > 0 && trackdump == Nodump)
+ trackinfo(&out, &e);
+ return 0;
+ }
+
+err:
+ return -1;
+}
--- a/iso.c
+++ b/iso.c
@@ -2,9 +2,8 @@
#include <libc.h>
#include <bio.h>
#include <ctype.h>
+#include "common.h"
-#define MIN(a,b) ((a)<=(b)?(a):(b))
-
typedef struct Audio Audio;
typedef struct Box Box;
typedef struct Moof Moof;
@@ -200,11 +199,6 @@
HandlerVideo = 0x76696465u,
HandlerAudio = 0x736f756eu,
- FmtMp4a = 0x6d703461u,
- FmtAv01 = 0x61763031u,
- FmtAvc1 = 0x61766331u,
- FmtVp09 = 0x76703039u,
-
BoxUuid = 0x75756964u,
BoxFtyp = 0x66747970u,
BoxMoov = 0x6d6f6f76u,
@@ -290,7 +284,6 @@
int id;
};
-static int dflag;
static int dind;
static u32int defsamplesize;
@@ -297,7 +290,6 @@
static Track track;
static Track *tracks;
static int ntracks;
-static Biobuf stderr;
static Moof moof;
static SampleEntry *sampleentry;
@@ -306,9 +298,8 @@
static int parsebox(Biobuf *f, Box *b, int *eof);
-#pragma varargck type "T" u32int
-static int
-typefmt(Fmt *f)
+int
+isotypefmt(Fmt *f)
{
char t[5];
int x;
@@ -330,7 +321,7 @@
dt = 0;
for(e = 0; e < t->numtts && s > 0; e++){
- n = MIN(t->tts[e].samplecount, s);
+ n = min(t->tts[e].samplecount, s);
dt += t->tts[e].sampledelta * n;
s -= n;
}
@@ -526,7 +517,12 @@
Track *t;
int i;
- for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++);
+ for(t = tracks, i = 0; i < ntracks && t->id != id; i++, t++){
+ if(id == Dumpaudio && t->audio.format != 0)
+ break;
+ if(id == Dumpvideo && t->video.format != 0)
+ break;
+ }
if(i >= ntracks){
werrstr("no track %d", id);
t = nil;
@@ -1499,50 +1495,34 @@
return -1;
}
-static void
-usage(void)
+static char *
+codec(u32int fmt)
{
- fprint(2, "usage: %s [-i | -t TRACK] FILE\n", argv0);
- exits("usage");
+ static char t[16];
+
+ if(fmt == FmtMp4a)
+ return "aac";
+ if(fmt == FmtAv01)
+ return "av1";
+ if(fmt == FmtAvc1)
+ return "h264";
+ if(fmt == FmtVp08)
+ return "vp8";
+ if(fmt == FmtVp09)
+ return "vp9";
+
+ snprint(t, sizeof(t), "%T", fmt);
+
+ return t;
}
-void
-main(int argc, char **argv)
+int
+isorun(Biobuf *f)
{
Box b;
- Biobuf *f;
- char *status;
Track *t;
- int eof, trackdump, iflag, i;
+ int eof, i;
- dflag = 0;
- iflag = 0;
- trackdump = -1;
- ARGBEGIN{
- case 'i':
- iflag++;
- break;
- case 'd':
- dflag++;
- break;
- case 't':
- trackdump = atoi(EARGF(usage()));
- break;
- default:
- usage();
- }ARGEND
-
- if(argc != 1 || (iflag && trackdump >= 0))
- usage();
-
- fmtinstall('T', typefmt);
- fmtinstall('H', encodefmt);
-
- Binit(&stderr, 2, OWRITE);
-
- if((f = Bopen(*argv, OREAD)) == nil)
- sysfatal("%s: %r", *argv);
-
for(;;){
dind = 0;
memset(&b, 0, sizeof(b));
@@ -1549,31 +1529,26 @@
if(parsebox(f, &b, &eof) != 0){
if(eof)
break;
- sysfatal("%s: %r", *argv);
+ return -1;
}
Bseek(f, b.dstart+b.dsz, 0);
}
- status = nil;
- if(iflag){
+ if(trackdump != Nodump && dumptrack(f, trackdump) != 0){
+ return -1;
+ }else{
for(i = 0; i < ntracks; i++){
t = &tracks[i];
- print("%d ", t->id);
+ Bprint(&out, "%d\t", t->id);
if(t->handlertype == HandlerVideo)
- print("video %T %d %d", t->video.format, t->video.width, t->video.height);
+ Bprint(&out, "video\t%s\t%d\t%d", codec(t->video.format), t->video.width, t->video.height);
else if(t->handlertype == HandlerAudio)
- print("audio %T %d %d", t->audio.format, t->audio.channels, t->audio.samplerate);
+ Bprint(&out, "audio\t%s\t%d\t%d", codec(t->audio.format), t->audio.channels, t->audio.samplerate);
else
- print("%T ???", t->handlertype);
- print("\n");
+ Bprint(&out, "%T\t???", t->handlertype);
+ Bprint(&out, "\n");
}
- }else if(trackdump >= 0 && dumptrack(f, trackdump) != 0){
- Bprint(&stderr, "%r\n");
- status = "dump";
}
- Bterm(f);
- Bterm(&stderr);
-
- exits(status);
+ return 0;
}
--- /dev/null
+++ b/ivf.c
@@ -1,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+
+enum {
+ Timedenum = 1000ULL,
+};
+
+int
+ivfpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts)
+{
+ u8int d[0x20];
+ uvlong dur;
+ int n, len;
+
+ 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;
+ }
+ 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[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(Bwrite(out, buf, sz) != sz)
+ goto err;
+ ctx->frid++;
+
+ return 0;
+err:
+ werrstr("ivfpacket: %r");
+ return -1;
+}
--- /dev/null
+++ b/main.c
@@ -1,0 +1,80 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+
+Biobuf stderr, out;
+int dflag, trackdump;
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-t INDEX|audio|video] [FILE]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ Biobuf *f;
+ char *s, *status;
+ u8int d[4];
+
+ dflag = 0;
+ trackdump = Nodump;
+ ARGBEGIN{
+ case 'd':
+ dflag++;
+ break;
+ case 't':
+ s = EARGF(usage());
+ if(strcmp(s, "audio") == 0)
+ trackdump = Dumpaudio;
+ else if(strcmp(s, "video") == 0)
+ trackdump = Dumpvideo;
+ else
+ trackdump = atoi(s);
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ f = nil;
+ if(argc == 1)
+ f = Bopen(*argv, OREAD);
+ else if(argc == 0)
+ f = Bfdopen(0, OREAD);
+ else
+ usage();
+
+ if(f == nil)
+ sysfatal("%r");
+
+ fmtinstall('H', encodefmt);
+ fmtinstall('T', isotypefmt);
+ Binit(&out, 1, OWRITE);
+ Binit(&stderr, 2, OWRITE);
+
+ status = nil;
+ if(Bread(f, d, 4) != 4){
+ Bprint(&stderr, "unknown format\n");
+ status = "unknown format";
+ }else if(Bungetc(f) < 0 || Bungetc(f) < 0 || Bungetc(f) < 0 || Bungetc(f) < 0){
+ Bprint(&stderr, "seek back failed: %r\n");
+ status = "seek failed";
+ }else if(memcmp(d, "\x1a\x45\xdf\xa3", 4) == 0){
+ if(ebmlrun(f) != 0){
+ Bprint(&stderr, "embl: %r\n");
+ status = "ebml failed";
+ }
+ }else if(isorun(f) != 0){
+ Bprint(&stderr, "iso: %r\n");
+ status = "iso failed";
+ }
+
+ Bterm(f);
+ Bterm(&out);
+ Bterm(&stderr);
+
+ exits(status);
+}
--- a/mkfile
+++ b/mkfile
@@ -6,9 +6,15 @@
BIN=/$objtype/bin
OFILES=\
+ crc32.$O\
+ ebml.$O\
iso.$O\
+ ivf.$O\
+ main.$O\
+ ogg.$O\
HFILES=\
+ common.h\
default:V: all
--- /dev/null
+++ b/ogg.c
@@ -1,0 +1,126 @@
+#include <u.h>
+#include <libc.h>
+#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)
+{
+ /* 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;
+
+ h[5] = htype;
+ h[6] = granule;
+ h[7] = granule >> 8;
+ h[8] = granule >> 16;
+ h[9] = granule >> 24;
+ h[10] = granule >> 32;
+ h[11] = granule >> 40;
+ h[12] = granule >> 48;
+ h[13] = granule >> 56;
+ h[14] = ctx->trackuid;
+ h[15] = ctx->trackuid >> 8;
+ h[16] = ctx->trackuid >> 16;
+ h[17] = ctx->trackuid >> 24;
+ h[18] = ctx->frid;
+ h[19] = ctx->frid >> 8;
+ h[20] = ctx->frid >> 16;
+ h[21] = ctx->frid >> 24;
+ ctx->frid++;
+ h[22] = 0;
+ 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;
+ }
+ n += 27;
+ crc = crc32(crc32(0, h, n), buf, 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)
+ return -1;
+
+ return 0;
+}
+
+int
+oggpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, 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};
+ int r, i, nsg, dsz, total;
+ u8int *d;
+ u64int gr;
+
+ gr = ts * 48 / 1000000;
+ r = 0;
+ if(ctx->frid == 0){ /* first packet? */
+ ctx->frid;
+
+ d = ctx->codec.priv.data;
+ dsz = ctx->codec.priv.sz;
+
+ /* 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++;
+ dsz--;
+ for(i = 0, total = 0; i < nsg && dsz > 0; i++, total += sgszs[i]){
+ sgszs[i] = 0;
+ do{
+ sgszs[i] += *d;
+ dsz--;
+ }while(*d++ == 0xff);
+ }
+ if(total > dsz){
+ werrstr("vorbis setup data out of bounds");
+ 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)
+ goto err;
+ }
+ if(dsz > 0)
+ r = packet(out, ctx, 0, d, dsz, 0);
+ }else{
+ r = packet(out, ctx, 2, d, dsz, 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);
+ }
+ if(r != 0)
+ goto err;
+
+ /* comment */
+ if(ctx->fmt == FmtOpus)
+ r = packet(out, ctx, 0, opuscomment, sizeof(opuscomment), 0);
+ if(r != 0)
+ goto err;
+ }
+
+ if(sz > 0 && packet(out, ctx, 0, buf, sz, gr) != 0)
+ goto err;
+
+ return 0;
+err:
+ werrstr("oggpacket: %r");
+ return -1;
+}