shithub: treason

Download patch

ref: 3f03592d6961c9f33b681b3269803739766b0c4b
parent: 6d7bf12a173f770c54c290c608a6f80bd0d0c83e
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Sep 9 12:23:23 EDT 2020

prepare for multiple streams support

--- a/README.md
+++ b/README.md
@@ -20,9 +20,10 @@
 	cd /tmp
 	git/clone https://git.sr.ht/~ft/dav1d
 	git/clone https://git.sr.ht/~ft/treason
+	git/clone https://git.sr.ht/~ft/mcfs
 	cd dav1d/src && mk
-	cd ../../treason
-	mk install
+	cd ../../treason && mk install
+	cd ../mcfs && mk install
 
 ## Gotchas
 
--- a/dec_av1.c
+++ /dev/null
@@ -1,148 +1,0 @@
-#include <dav1d.h>
-#include "frame.h"
-#include "stream.h"
-#include "decoder.h"
-#include "misc.h"
-
-#pragma lib "../dav1d/src/libdav1d.$M.a"
-
-typedef struct Aux Aux;
-
-struct Aux {
-	Dav1dContext *c;
-};
-
-struct Stream {
-	Streamhdr;
-};
-
-static char *layout[] = {
-	[DAV1D_PIXEL_LAYOUT_I400] = "i400",
-    [DAV1D_PIXEL_LAYOUT_I420] = "i420",
-    [DAV1D_PIXEL_LAYOUT_I422] = "i422",
-    [DAV1D_PIXEL_LAYOUT_I444] = "i444",
-};
-
-static u8int *
-allocdata(void *data, int sz)
-{
-	return dav1d_data_create(data, sz);
-}
-
-static int
-readframe(Stream *s, Dav1dData *data)
-{
-	Streamframe f;
-
-	s->ops.aux = data;
-	if(Sread(s, &f) != 0)
-		return -1;
-	data->m.offset = f.offset;
-	data->m.timestamp = f.timestamp;
-
-	return 0;
-}
-
-static void
-decode(void *x)
-{
-	uvlong lasttimestamp;
-	Dav1dPicture pic;
-	Dav1dData data;
-	Decoder *d;
-	Frame *f;
-	Aux *a;
-	int res;
-
-	d = x;
-	a = d->aux;
-	lasttimestamp = 0;
-	memset(&pic, 0, sizeof(pic));
-	for(res = 0, data.sz = 0; data.sz > 0 || (res = readframe(d->s, &data)) == 0;){
-		if(nbrecvp(d->stop) != 0){
-			print("received stop\n");
-			break;
-		}
-
-		res = dav1d_get_picture(a->c, &pic);
-		if(res < 0){
-			if(res != DAV1D_ERR(EAGAIN)){
-				werrstr("dav1d_get_picture: %d", res);
-				break;
-			}
-		}else{
-			if((f = malloc(sizeof(*f) + pic.p.w*pic.p.h*3)) != nil){
-				f->w = pic.p.w;
-				f->h = pic.p.h;
-				yuv420_rgb24(f->w, f->h, pic.data[0], pic.data[1], pic.data[2], pic.stride[0], pic.stride[1], f->rgb, f->w*3);
-				f->dt = (pic.m.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
-				lasttimestamp = pic.m.timestamp;
-				dav1d_picture_unref(&pic);
-
-				if(sendp(d->frames, f) < 0){
-					free(f);
-					break;
-				}
-			}
-		}
-
-		res = dav1d_send_data(a->c, &data);
-		if(res < 0){
-			if(res != DAV1D_ERR(EAGAIN)){
-				werrstr("dav1d_send_data: %d", res);
-				break;
-			}
-		}
-	}
-	if(res != 0)
-		fprint(2, "av1: %r\n");
-
-	if(data.sz > 0)
-		dav1d_data_unref(&data);
-
-	/* drain */
-	while(dav1d_get_picture(a->c, &pic) >= 0)
-		dav1d_picture_unref(&pic);
-
-	dav1d_close(&a->c);
-	sendp(d->finished, nil);
-	free(a);
-
-	threadexits(nil);
-}
-
-static int
-av1open(Decoder *d)
-{
-	Dav1dSettings av1s;
-	Aux *a;
-	int res;
-
-	a = calloc(1, sizeof(*a));
-
-	dav1d_default_settings(&av1s);
-	av1s.n_frame_threads = nproc;
-	av1s.n_tile_threads = nproc;
-
-	if((res = dav1d_open(&a->c, &av1s)) != 0){
-		werrstr("dav1d_open: %d", res);
-		free(a);
-		return -1;
-	}
-	d->aux = a;
-	d->s->ops.alloc = allocdata;
-	proccreate(decode, d, 16384);
-
-	return 0;
-}
-
-static void
-av1close(Decoder *d)
-{
-	USED(d);
-}
-
-Decoderops av1ops = {
-	.open = av1open,
-	.close = av1close,
-};
--- a/decoder.c
+++ b/decoder.c
@@ -16,7 +16,7 @@
 };
 
 Decoder *
-Dopen(Stream *s, Streaminfo *info)
+Dopen(Stream *s)
 {
 	Decoder *d;
 	int i;
@@ -24,15 +24,14 @@
 	if((d = calloc(1, sizeof(*d))) == nil)
 		return nil;
 
-	d->timebase = (double)info->timebase.num/(double)info->timebase.denum;
+	d->timebase = (double)s->timebase.num/(double)s->timebase.denum;
 	d->s = s;
 	d->frames = chancreate(sizeof(Frame*), 4);
 	d->finished = chancreate(sizeof(void*), 1);
 	d->stop = chancreate(sizeof(void*), 1);
-	memmove(&d->info, info, sizeof(info));
 
 	for(i = 0; i < nelem(ops); i++){
-		if(ops[i].fmt == info->fmt){
+		if(ops[i].fmt == s->fmt){
 			if(ops[i].o->open(d) < 0){
 				werrstr("%s: %r", ops[i].name);
 				goto err;
--- a/decoder.h
+++ b/decoder.h
@@ -6,7 +6,6 @@
 	Channel *frames;
 	Channel *finished;
 	Channel *stop;
-	Streaminfo info;
 	Decoderops *ops;
 	double timebase;
 
@@ -18,5 +17,5 @@
 	void (*close)(Decoder *d);
 };
 
-Decoder *Dopen(Stream *s, Streaminfo *info);
+Decoder *Dopen(Stream *s);
 void Dclose(Decoder *d);
--- /dev/null
+++ b/decoder_av1.c
@@ -1,0 +1,152 @@
+#include <dav1d.h>
+#include "frame.h"
+#include "stream.h"
+#include "decoder.h"
+#include "misc.h"
+
+#pragma lib "../dav1d/src/libdav1d.$M.a"
+
+typedef struct Aux Aux;
+
+struct Aux {
+	Dav1dContext *c;
+};
+
+static char *layouts[] = {
+	[DAV1D_PIXEL_LAYOUT_I400] = "i400",
+    [DAV1D_PIXEL_LAYOUT_I420] = "i420",
+    [DAV1D_PIXEL_LAYOUT_I422] = "i422",
+    [DAV1D_PIXEL_LAYOUT_I444] = "i444",
+};
+
+static u8int *
+allocdata(void *data, int sz)
+{
+	return dav1d_data_create(data, sz);
+}
+
+static int
+readframe(Stream *s, Dav1dData *data)
+{
+	Streamframe f;
+
+	s->ops.aux = data;
+	if(Sread(s, &f) != 0)
+		return -1;
+	data->m.offset = f.offset;
+	data->m.timestamp = f.timestamp;
+
+	return 0;
+}
+
+static void
+decode(void *x)
+{
+	uvlong lasttimestamp;
+	Dav1dPicture pic;
+	Dav1dData data;
+	Decoder *d;
+	Frame *f;
+	Aux *a;
+	int res;
+
+	d = x;
+	a = d->aux;
+	lasttimestamp = 0;
+	memset(&pic, 0, sizeof(pic));
+	for(res = 0, data.sz = 0; data.sz > 0 || (res = readframe(d->s, &data)) == 0;){
+		if(nbrecvp(d->stop) != 0)
+			break;
+
+		res = dav1d_get_picture(a->c, &pic);
+		if(res < 0){
+			if(res != DAV1D_ERR(EAGAIN)){
+				werrstr("dav1d_get_picture: %d", res);
+				break;
+			}
+		}else if(pic.p.layout != DAV1D_PIXEL_LAYOUT_I420){
+			if(pic.p.layout >= 0 && pic.p.layout < nelem(layouts))
+				werrstr("%s", layouts[pic.p.layout]);
+			else
+				werrstr("??? (%d)", pic.p.layout);
+			werrstr("unsupported pixel layout: %r");
+			res = -1;
+			break;
+		}else if(pic.p.bpc != 8){
+			werrstr("unsupported bits per component: %d", pic.p.bpc);
+			res = -1;
+			break;
+		}else if((f = malloc(sizeof(*f) + pic.p.w*pic.p.h*3)) != nil){
+			f->w = pic.p.w;
+			f->h = pic.p.h;
+			yuv420_rgb24(f->w, f->h, pic.data[0], pic.data[1], pic.data[2], pic.stride[0], pic.stride[1], f->rgb, f->w*3);
+			f->dt = (pic.m.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
+			lasttimestamp = pic.m.timestamp;
+			dav1d_picture_unref(&pic);
+
+			if(sendp(d->frames, f) < 0){
+				free(f);
+				break;
+			}
+		}
+
+		res = dav1d_send_data(a->c, &data);
+		if(res < 0){
+			if(res != DAV1D_ERR(EAGAIN)){
+				werrstr("dav1d_send_data: %d", res);
+				break;
+			}
+		}
+	}
+	if(res != 0)
+		fprint(2, "av1: %r\n");
+
+	if(data.sz > 0)
+		dav1d_data_unref(&data);
+
+	/* drain */
+	while(dav1d_get_picture(a->c, &pic) >= 0)
+		dav1d_picture_unref(&pic);
+
+	dav1d_close(&a->c);
+	sendp(d->finished, nil);
+	free(a);
+
+	threadexits(nil);
+}
+
+static int
+av1open(Decoder *d)
+{
+	Dav1dSettings av1s;
+	Aux *a;
+	int res;
+
+	a = calloc(1, sizeof(*a));
+
+	dav1d_default_settings(&av1s);
+	av1s.n_frame_threads = nproc;
+	av1s.n_tile_threads = nproc;
+
+	if((res = dav1d_open(&a->c, &av1s)) != 0){
+		werrstr("dav1d_open: %d", res);
+		free(a);
+		return -1;
+	}
+	d->aux = a;
+	d->s->ops.alloc = allocdata;
+	proccreate(decode, d, 16384);
+
+	return 0;
+}
+
+static void
+av1close(Decoder *d)
+{
+	USED(d);
+}
+
+Decoderops av1ops = {
+	.open = av1open,
+	.close = av1close,
+};
--- a/ivf.c
+++ /dev/null
@@ -1,157 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include "stream.h"
-
-struct Stream {
-	Streamhdr;
-	Biobuf *b;
-	u8int *buf;
-	int bufsz;
-};
-
-static int
-Bu16le(Biobuf *b, u16int *o)
-{
-	int x;
-
-	x = Bgetc(b);
-	x |= Bgetc(b)<<8;
-	*o = x;
-	if(x < 0)
-		werrstr("failed to read 2 bytes");
-
-	return x < 0 ? -1 : 0;
-}
-
-static int
-Bu32le(Biobuf *b, u32int *o)
-{
-	int x, i;
-
-	*o = 0;
-	for(i = 0; i < 4; *o |= x<<(i*8), i++){
-		if((x = Bgetc(b)) < 0){
-			werrstr("failed to read 4 bytes");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static int
-Bu64le(Biobuf *b, u64int *o)
-{
-	int x, i;
-
-	*o = 0;
-	for(i = 0; i < 8; *o |= x<<(i*8), i++){
-		if((x = Bgetc(b)) < 0){
-			werrstr("failed to read 8 bytes");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static Stream *
-ivfopen(char *filename, Streaminfo *info, int *failed)
-{
-	Biobuf *b;
-	Stream *s;
-	u16int hlen, w, h;
-	u32int tbdenum, tbnum;
-	char tmp[6];
-
-	if((b = Bopen(filename, OREAD)) == nil)
-		return nil;
-	if(Bread(b, tmp, 6) != 6 || memcmp(tmp, "DKIF", 4) != 0 || Bu16le(b, &hlen) < 0){
-		Bterm(b);
-		return nil;
-	}
-
-	if((s = calloc(1, sizeof(*s))) == nil)
-		goto err;
-	if(hlen < 0x20 || Bread(b, tmp, 4) != 4){
-		werrstr("invalid header: hlen=%d", hlen);
-		goto err;
-	}
-	if(memcmp(tmp, "AV01", 4) != 0){ /* nothing else is supported yet */
-		werrstr("unsupported format %.*s", 4, tmp);
-		goto err;
-	}
-	info->fmt = FmtAV1;
-	if(Bu16le(b, &w) < 0 || Bu16le(b, &h) < 0 || Bu32le(b, &tbdenum) < 0 || Bu32le(b, &tbnum) < 0){
-		werrstr("invalid header: %r");
-		goto err;
-	}
-	info->w = w;
-	info->h = h;
-	info->timebase.denum = tbdenum;
-	info->timebase.num = tbnum;
-	if(Bseek(b, hlen, 0) != hlen){
-		werrstr("invalid IVF stream");
-		goto err;
-	}
-	s->b = b;
-
-	return s;
-err:
-	*failed = 1;
-	Bterm(b);
-	free(s);
-	return nil;
-}
-
-static void
-ivfclose(Stream *s)
-{
-	Bterm(s->b);
-	free(s->buf);
-	free(s);
-}
-
-static int
-ivfread(Stream *s, Streamframe *f)
-{
-	u64int timestamp;
-	u32int sz;
-	u8int *buf;
-
-	f->offset = Boffset(s->b);
-	if(Bu32le(s->b, &sz) < 0 || Bu64le(s->b, &timestamp) || (int)sz < 0)
-		return -1;
-	buf = s->buf;
-	if(s->ops.alloc != nil)
-		buf = s->ops.alloc(s->ops.aux, sz);
-	else if(sz > s->bufsz){
-		if((buf = realloc(s->buf, sz)) == nil){
-			werrstr("frame is too big: %d bytes", sz);
-			return -1;
-		}
-		s->buf = buf;
-	}
-	if(Bread(s->b, buf, sz) != sz){
-		werrstr("short read");
-		return -1;
-	}
-	f->buf = buf;
-	f->sz = sz;
-	f->timestamp = timestamp;
-
-	return 0;
-}
-
-static vlong
-ivfoffset(Stream *s)
-{
-	return Boffset(s->b);
-}
-
-Streamops ivfops = {
-	.open = ivfopen,
-	.close = ivfclose,
-	.read = ivfread,
-};
--- a/main.c
+++ b/main.c
@@ -83,9 +83,8 @@
 	Mouse m;
 	char *s;
 	Stream *stream;
-	Streaminfo info;
 	Decoder *d;
-	int i, end, done, res;
+	int i, end, done, res, nstreams;
 	Alt a[Cnum+1] =
 	{
 		[Cframe] = { nil, &frame, CHANRCV },
@@ -121,9 +120,9 @@
 
 	for(end = i = 0; !end && i < argc; i++){
 		draw(screen, screen->r, display->black, nil, ZP);
-		if((stream = Sopen(argv[i], &info)) == nil)
+		if((stream = Sopen(argv[i], &nstreams)) == nil)
 			sysfatal("%r");
-		if((d = Dopen(stream, &info)) == nil)
+		if((d = Dopen(stream)) == nil)
 			sysfatal("%r");
 
 		a[Cframe].c = d->frames;
@@ -163,7 +162,8 @@
 		}
 
 		Dclose(d);
-		Sclose(stream);
+		for(i = 0; i < nstreams; i++)
+			Sclose(stream+i);
 	}
 
 	threadexitsall(nil);
--- a/mkfile
+++ b/mkfile
@@ -11,12 +11,13 @@
 	stream.h\
 
 OFILES=\
-	dec_av1.$O\
 	decoder.$O\
-	ivf.$O\
+	decoder_av1.$O\
 	main.$O\
 	misc.$O\
 	stream.$O\
+	stream_ivf.$O\
+	stream_mp4.$O\
 	yuv.$O\
 
 default:V:	all
--- a/stream.c
+++ b/stream.c
@@ -2,11 +2,8 @@
 #include <libc.h>
 #include "stream.h"
 
-struct Stream {
-	Streamops ops;
-};
-
 extern Streamops ivfops;
+extern Streamops mp4ops;
 
 static struct {
 	char *name;
@@ -13,10 +10,11 @@
 	Streamops *o;
 }ops[] = {
 	{"ivf", &ivfops},
+	{"mp4", &mp4ops},
 };
 
 Stream *
-Sopen(char *filename, Streaminfo *info)
+Sopen(char *filename, int *num)
 {
 	int i, failed;
 	Stream *s;
@@ -23,7 +21,7 @@
 
 	for(i = 0; i < nelem(ops); i++){
 		failed = 0;
-		if((s = ops[i].o->open(filename, info, &failed)) != nil){
+		if((s = ops[i].o->open(filename, &failed, num)) != nil){
 			memmove(&s->ops, ops[i].o, sizeof(Streamops));
 			return s;
 		}
--- a/stream.h
+++ b/stream.h
@@ -6,7 +6,16 @@
 #pragma incomplete Stream
 
 enum {
-	FmtAV1,
+	Svideo,
+	Saudio,
+
+	/* video */
+	FmtAV1 = 0,
+	FmtVP9,
+	FmtVP8,
+	/* audio */
+	FmtAAC,
+	FmtOpus,
 };
 
 struct Streamframe {
@@ -17,7 +26,7 @@
 };
 
 struct Streamops {
-	Stream *(*open)(char *filename, Streaminfo *info, int *failed);
+	Stream *(*open)(char *filename, int *num, int *failed);
 	u8int *(*alloc)(void *aux, int sz);
 	void (*close)(Stream *s);
 	vlong (*offset)(Stream *s);
@@ -26,20 +35,37 @@
 	void *aux;
 };
 
-struct Streamhdr {
-	Streamops ops;
-};
-
 struct Streaminfo {
+	int type;
+	int fmt;
+
 	struct {
 		uvlong denum, num;
 	}timebase;
-	int fmt;
-	int w;
-	int h;
+
+	union {
+		struct {
+			int w, h;
+		}video;
+
+		struct {
+			int nchan;
+			int srate;
+		}audio;
+	};
 };
 
-Stream *Sopen(char *filename, Streaminfo *info);
+struct Stream {
+	Streaminfo;
+	Streamops ops;
+
+	/* private stuff */
+	void *b;
+	u8int *buf;
+	int bufsz;
+};
+
+Stream *Sopen(char *filename, int *num);
 vlong Soffset(Stream *s);
 void Sclose(Stream *s);
 int Sread(Stream *s, Streamframe *f);
--- /dev/null
+++ b/stream_ivf.c
@@ -1,0 +1,162 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "stream.h"
+
+static int
+Bu16le(Biobuf *b, u16int *o)
+{
+	int x;
+
+	x = Bgetc(b);
+	x |= Bgetc(b)<<8;
+	*o = x;
+	if(x < 0)
+		werrstr("failed to read 2 bytes");
+
+	return x < 0 ? -1 : 0;
+}
+
+static int
+Bu32le(Biobuf *b, u32int *o)
+{
+	int x, i;
+
+	*o = 0;
+	for(i = 0; i < 4; *o |= x<<(i*8), i++){
+		if((x = Bgetc(b)) < 0){
+			werrstr("failed to read 4 bytes");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+Bu64le(Biobuf *b, u64int *o)
+{
+	int x, i;
+
+	*o = 0;
+	for(i = 0; i < 8; *o |= x<<(i*8), i++){
+		if((x = Bgetc(b)) < 0){
+			werrstr("failed to read 8 bytes");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static Stream *
+ivfopen(char *filename, int *num, int *failed)
+{
+	Biobuf *b;
+	Stream *s;
+	u16int hlen, w, h, fmt;
+	u32int tbdenum, tbnum;
+	char tmp[6];
+
+	s = nil;
+	if((b = Bopen(filename, OREAD)) == nil)
+		return nil;
+	if(Bread(b, tmp, 6) != 6 || memcmp(tmp, "DKIF", 4) != 0 || Bu16le(b, &hlen) < 0){
+		Bterm(b);
+		return nil;
+	}
+	if(hlen < 0x20 || Bread(b, tmp, 4) != 4){
+		werrstr("invalid header: hlen=%d", hlen);
+		goto err;
+	}
+
+	if(memcmp(tmp, "AV01", 4) == 0)
+		fmt = FmtAV1;
+	else if(memcmp(tmp, "VP90", 4) == 0)
+		fmt = FmtVP9;
+	else if(memcmp(tmp, "VP80", 4) == 0)
+		fmt = FmtVP8;
+	else{
+		werrstr("unsupported format %.*s", 4, tmp);
+		goto err;
+	}
+
+	if(Bu16le(b, &w) < 0 || Bu16le(b, &h) < 0 || Bu32le(b, &tbdenum) < 0 || Bu32le(b, &tbnum) < 0){
+		werrstr("invalid header: %r");
+		goto err;
+	}
+	if(Bseek(b, hlen, 0) != hlen){
+		werrstr("invalid IVF stream");
+		goto err;
+	}
+
+	if((s = calloc(1, sizeof(*s))) == nil)
+		goto err;
+	*num = 1;
+	s->type = Svideo;
+	s->fmt = fmt;
+	s->video.w = w;
+	s->video.h = h;
+	s->timebase.denum = tbdenum;
+	s->timebase.num = tbnum;
+	s->b = b;
+
+	return s;
+err:
+	*failed = 1;
+	Bterm(b);
+	free(s);
+	return nil;
+}
+
+static void
+ivfclose(Stream *s)
+{
+	Bterm(s->b);
+	free(s->buf);
+	free(s);
+}
+
+static int
+ivfread(Stream *s, Streamframe *f)
+{
+	u64int timestamp;
+	u32int sz;
+	u8int *buf;
+
+	f->offset = Boffset(s->b);
+	if(Bu32le(s->b, &sz) < 0 || Bu64le(s->b, &timestamp) || (int)sz < 0)
+		return -1;
+	buf = s->buf;
+	if(s->ops.alloc != nil)
+		buf = s->ops.alloc(s->ops.aux, sz);
+	else if(sz > s->bufsz){
+		if((buf = realloc(s->buf, sz)) == nil){
+			werrstr("frame is too big: %d bytes", sz);
+			return -1;
+		}
+		s->buf = buf;
+	}
+	if(Bread(s->b, buf, sz) != sz){
+		werrstr("short read");
+		return -1;
+	}
+	f->buf = buf;
+	f->sz = sz;
+	f->timestamp = timestamp;
+
+	return 0;
+}
+
+static vlong
+ivfoffset(Stream *s)
+{
+	return Boffset(s->b);
+}
+
+Streamops ivfops = {
+	.open = ivfopen,
+	.close = ivfclose,
+	.read = ivfread,
+	.offset = ivfoffset,
+};
--- /dev/null
+++ b/stream_mp4.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "stream.h"
+
+static Stream *
+mp4open(char *filename, int *num, int *failed)
+{
+	USED(filename); USED(num); USED(failed);
+	return nil;
+}
+
+static void
+mp4close(Stream *s)
+{
+	free(s);
+}
+
+static int
+mp4read(Stream *s, Streamframe *f)
+{
+	USED(s); USED(f);
+	return -1;
+}
+
+static vlong
+mp4offset(Stream *s)
+{
+	USED(s);
+	return -1;
+}
+
+Streamops mp4ops = {
+	.open = mp4open,
+	.close = mp4close,
+	.read = mp4read,
+	.offset = mp4offset,
+};