shithub: dav1d

Download patch

ref: 80719550be738385d1deddf193c6321a1e80f3a1
parent: d5d560ff367f111e0fd62664e9473643d2afbaff
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Sep 6 08:30:16 EDT 2020

av19: a base for future video player

--- a/src/av19.c
+++ b/src/av19.c
@@ -2,90 +2,334 @@
 #include "tools/input/input.h"
 #include <draw.h>
 #include <memdraw.h>
+#include <mouse.h>
 #include <keyboard.h>
+#include <yuv_rgb.h> // FIXME this is most likely slow
+#include <tos.h>
+/* FIXME this one is slow as hell
+#define STB_IMAGE_RESIZE_IMPLEMENTATION
+#define STBIR_MALLOC(x,u) malloc(x)
+#define STBIR_FREE(x,u) free(x)
+#define STBIR_ASSERT(x) assert(x)
+typedef uintptr size_t;
+#include <stb_image_resize.h>
+*/
+typedef struct Frame Frame;
+typedef struct Player Player;
 
+struct Frame {
+	Dav1dPicture pic;
+};
+
+struct Player {
+	Dav1dData data;
+	Dav1dContext *c;
+	DemuxerContext *dc;
+	Channel *frames;
+	Channel *done;
+	uvlong fps;
+	uvlong lastframe;
+};
+
+// FIXME why does it need this much?
 int mainstacksize = 512*1024;
 
-static uchar *buf;
-static int nfr;
+static Player *curplayer;
+static Image *curframe;
 
+static char *layout[] = {
+	[DAV1D_PIXEL_LAYOUT_I400] = "i400",
+    [DAV1D_PIXEL_LAYOUT_I420] = "i420",
+    [DAV1D_PIXEL_LAYOUT_I422] = "i422",
+    [DAV1D_PIXEL_LAYOUT_I444] = "i444",
+};
+
 static int
-dav1d_loadimage(Image **oim, Dav1dPicture *p)
+dav1d_loadimage(Rectangle r, Image **oim, Dav1dPicture *p)
 {
 	Image *im;
-	int w, h;
+	uchar *rgb, t;
+	int w, h, i;
 
+	if(*oim == nil)
+		*oim = allocimage(display, r, RGB24, 0, DNofill);
+	im = *oim;
 	w = p->p.w;
 	h = p->p.h;
+	if((rgb = malloc(w*h*3)) == nil)
+		return -1;
 
-	if(*oim == nil){
-		*oim = allocimage(display, Rect(0,0,w,h), RGB24, 0, 0);
-		buf = malloc(w * h * 3);
+	yuv420_rgb24(w, h, p->data[0], p->data[1], p->data[2], p->stride[0], p->stride[1], rgb, w*3, YCBCR_JPEG);
+/*
+	uchar *out;
+	if((out = malloc(Dx(r)*Dy(r)*3)) == nil){
+		free(rgb);
+		return -1;
 	}
-	im = *oim;
+	stbir_resize_uint8_generic(
+		rgb, w, h, w*3,
+		out, Dx(r), Dy(r), Dx(r)*3,
+		3, -1, 0,
+		STBIR_EDGE_CLAMP, STBIR_FILTER_MITCHELL, STBIR_COLORSPACE_LINEAR,
+		NULL);
+	free(rgb);
+	rgb = out;
+*/
 
-	/* FIXME convert YUV → RGB24 */
-	USED(im);
+	w = Dx(r);
+	h = Dy(r);
+	for(i = 0; i < w*h; i++){
+		t = rgb[i*3+2];
+		rgb[i*3+2] = rgb[i*3+0];
+		rgb[i*3+0] = t;
+	}
+	loadimage(im, Rect(0,0,w,h), rgb, w*h*3);
+	free(rgb);
 
 	return 0;
 }
 
-static int
-info(void *, char *)
+static void
+freeframe(Frame *f)
 {
-	print("frames: %d\n", nfr);
-	return 0;
+	dav1d_picture_unref(&f->pic);
+	free(f);
 }
 
-void
-threadmain(int argc, char **argv)
+static uvlong
+nanosec(void)
 {
-	Dav1dSettings av1s;
-	Dav1dContext *c;
-	Dav1dPicture *p;
-	Dav1dData data;
-	DemuxerContext *dc;
-	unsigned fps[2], timebase[2], total;
+	static uvlong fasthz, xstart;
+	uvlong x, div;
+
+	if(fasthz == ~0ULL)
+		return nsec() - xstart;
+
+	if(fasthz == 0){
+		if((fasthz = _tos->cyclefreq) == 0){
+			fasthz = ~0ULL;
+			xstart = nsec();
+			fprint(2, "cyclefreq not available, falling back to nsec()\n");
+			fprint(2, "you might want to disable aux/timesync\n");
+			return 0;
+		}else{
+			cycles(&xstart);
+		}
+	}
+	cycles(&x);
+	x -= xstart;
+
+	/* this is ugly */
+	for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+	return x / (fasthz / div);
+}
+
+static void
+gotframe(Frame *f)
+{
 	int res;
+	Point size = subpt(screen->r.max, screen->r.min);
+	Rectangle r = (Rectangle){Pt(0, 0), size};
+	uvlong thisframe, start, dt;
+	static uvlong delay;
 
-	if(argc != 2)
-		sysfatal("usage");
+	start = nanosec();
+	dt = 0;
+	lockdisplay(display);
 
-	if(input_open(&dc, "ivf", argv[1], fps, &total, timebase) < 0)
-		sysfatal("input_open");
-	if(input_read(dc, &data) < 0)
-		sysfatal("input_read");
+	r.max.x = f->pic.p.w;
+	r.max.y = f->pic.p.h;
+	res = dav1d_loadimage(r, &curframe, &f->pic);
+	freeframe(f);
+	dt += nanosec()-start;
 
-	dav1d_default_settings(&av1s);
-	av1s.n_frame_threads = 1; // FIXME threads
-	av1s.n_tile_threads = 1; // FIXME threads
+	if(res == 0){
+		thisframe = curplayer->lastframe + 1000000000ULL/curplayer->fps - delay;
+		while(nanosec() < thisframe)
+			sleep(10);
+		dt += nanosec() - thisframe;
+		start = nanosec();
+		draw(screen, screen->r, curframe, nil, ZP);
+		flushimage(display, 1);
+		curplayer->lastframe = start;
+		dt += nanosec()-start;
+	}
+	unlockdisplay(display);
+	delay = dt;
+}
 
-	if(dav1d_open(&c, &av1s) != 0)
-		sysfatal("dav1d_open");
+static void
+playerproc(void *aux)
+{
+	Player *p;
+	Frame *f;
+	int res;
 
-	threadnotify(info, 1);
-	nfr = 0;
+	p = aux;
+
 	do{
-		res = dav1d_send_data(c, &data);
-		if(res < 0 && res != DAV1D_ERR(EAGAIN))
-			sysfatal("dav1d_send_data: %d", res);
-		else{
-			p = calloc(1, sizeof(*p));
-			if((res = dav1d_get_picture(c, p)) < 0){
-				if(res != DAV1D_ERR(EAGAIN))
-					sysfatal("dav1d_get_picture");
+		res = dav1d_send_data(p->c, &p->data);
+		if(res < 0 && res != DAV1D_ERR(EAGAIN)){
+			fprint(2, "dav1d_send_data: %d\n", res);
+			break;
+		}else{
+			f = calloc(1, sizeof(*f));
+			if((res = dav1d_get_picture(p->c, &f->pic)) < 0){
+				if(res != DAV1D_ERR(EAGAIN)){
+					fprint(2, "dav1d_get_picture: %d\n", res);
+					break;
+				}
 			}else{
-				dav1d_picture_unref(p);
-				free(p);
-				nfr++;
+				sendp(p->frames, f);
 			}
 		}
-	}while(data.sz > 0 || input_read(dc, &data) == 0);
+	}while(p->data.sz > 0 || input_read(p->dc, &p->data) == 0);
 
-	if(data.sz > 0)
-		dav1d_data_unref(&data);
+	if(p->data.sz > 0)
+		dav1d_data_unref(&p->data);
 
-	/* get more pics here? */
+	// FIXME there might be more here
+
+	sendul(p->done, 1);
+
+	threadexits(nil);
+}
+
+static void
+freeplayer(Player *p)
+{
+	// FIXME
+	chanfree(p->frames);
+	chanfree(p->done);
+	free(p);
+}
+
+static Player *
+newplayer(char *filename)
+{
+	Player *p;
+	unsigned fps[2], timebase[2], total;
+	Dav1dSettings av1s;
+
+	p = calloc(1, sizeof(*p));
+	if(input_open(&p->dc, "ivf", filename, fps, &total, timebase) < 0){
+		werrstr("input_open");
+		goto err;
+	}
+	p->fps = fps[0]/fps[1]; // FIXME that's not precise
+	if(input_read(p->dc, &p->data) < 0){
+		werrstr("input_read");
+		goto err;
+	}
+
+	dav1d_default_settings(&av1s);
+	av1s.n_frame_threads = 1; // FIXME threads
+	av1s.n_tile_threads = 1; // FIXME threads
+
+	if(dav1d_open(&p->c, &av1s) != 0){
+		werrstr("dav1d_open");
+		goto err;
+	}
+
+	p->frames = chancreate(sizeof(Frame*), 30); // FIXME prerender?
+	p->done = chancreate(sizeof(ulong), 0);
+	p->lastframe = 0;
+
+	proccreate(playerproc, p, mainstacksize);
+
+	return p;
+err:
+	werrstr("%s: %r", filename);
+	free(p);
+	return nil;
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	enum {
+		Cplayerframes,
+		Cplayerdone,
+		Cmouse,
+		Ckeyboard,
+		Cresize,
+		Cnum,
+	};
+	Mousectl *mctl;
+	Keyboardctl *kctl;
+	Frame *frame;
+	Rune key;
+	Mouse m;
+	int i, end, done;
+	Alt a[Cnum+1] =
+	{
+		[Cplayerframes] = { nil, &frame, CHANRCV },
+		[Cplayerdone] = { nil, nil, CHANRCV },
+		[Cmouse] = { nil, &m, CHANRCV },
+		[Ckeyboard] = { nil, &key, CHANRCV },
+		[Cresize] =  { nil, nil, CHANRCV },
+		[Cnum] = { nil, nil, CHANEND },
+	};
+
+	ARGBEGIN{
+	}ARGEND
+
+	if(argc < 1)
+		sysfatal("usage");
+
+	srand(nanosec());
+	if(initdraw(nil, nil, "treason") < 0)
+		sysfatal("initdraw: %r");
+	display->locking = 1;
+	unlockdisplay(display);
+	if((mctl = initmouse(nil, screen)) == nil)
+		sysfatal("initmouse: %r");
+	if((kctl = initkeyboard(nil)) == nil)
+		sysfatal("initkeyboard: %r");
+
+	a[Cmouse].c = mctl->c;
+	a[Cresize].c = mctl->resizec;
+	a[Ckeyboard].c = kctl->c;
+
+	for(end = i = 0; !end && i < argc; i++){
+		if((curplayer = newplayer(argv[0])) == nil)
+			sysfatal("%r");
+
+		a[Cplayerframes].c = curplayer->frames;
+		a[Cplayerdone].c = curplayer->done;
+
+		for(done = 0; !done && !end;){
+			switch(alt(a)){
+			case Cplayerframes:
+				gotframe(frame);
+				break;
+
+			case Cplayerdone:
+				done = 1;
+				break;
+
+			case Cmouse:
+				break;
+
+			case Ckeyboard:
+				if(key == 'q' || key == Kdel){
+					end = 1;
+					break;
+				}
+				break;
+
+			case Cresize:
+				if(getwindow(display, Refnone) < 0)
+					sysfatal("getwindow: %r");
+				freeimage(curframe);
+				curframe = nil;
+				break;
+			}
+		}
+
+		freeplayer(curplayer);
+	}
 
 	threadexitsall(nil);
 }