shithub: dav1d

Download patch

ref: 4a9ad7222f0a90576a0b8bcefcda73747c5c5a97
parent: 50e8d89a65b18a2371bfc76b63e49554923ff862
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Mon Sep 7 12:00:16 EDT 2020

simplify player's logic and fix timing

--- a/src/av19.c
+++ b/src/av19.c
@@ -16,8 +16,9 @@
 typedef struct Player Player;
 
 struct Frame {
-	Dav1dPicture pic;
-	uvlong n;
+	u8int *rgb;
+	int w, h;
+	uvlong dt;
 };
 
 struct Player {
@@ -24,22 +25,16 @@
 	Dav1dData data;
 	Dav1dContext *c;
 	DemuxerContext *dc;
-	Channel *done;
+	Channel *frames, *done;
+	double timebase;
 	uvlong fps;
 	uvlong lastframe;
-	uvlong n;
 };
 
-// FIXME why does it need this much?
-int mainstacksize = 512*1024;
+int mainstacksize = 128*1024;
 
 static Player *curplayer;
 static Image *curim;
-static Channel **converters;
-static QLock framelock;
-static uvlong todisplay;
-static int resize = 1;
-static int maxframes;
 
 /* yuv→rgb by Adrien Descamps */
 
@@ -143,7 +138,7 @@
 static void
 freeframe(Frame *f)
 {
-	dav1d_picture_unref(&f->pic);
+	free(f->rgb);
 	free(f);
 }
 
@@ -177,76 +172,120 @@
 }
 
 static void
-drawframe(void)
+drawframe(Frame *f)
 {
-	uvlong thisframe, start;
+	uvlong x, end, left;
 	static uvlong delay;
+	Rectangle r;
 
-	thisframe = curplayer->lastframe + 1000000000ULL/curplayer->fps - delay;
-	while(nanosec() < thisframe)
-		sleep(10);
-
 	lockdisplay(display);
-	start = nanosec();
+
+	if(curim != nil && (Dx(curim->r) != f->w || Dy(curim->r) != f->h)){
+		freeimage(curim);
+		curim = nil;
+	}
+	r = Rect(0,0,f->w,f->h);
+	if(curim == nil)
+		curim = allocimage(display, r, RGB24, 0, DNofill);
+	loadimage(curim, r, f->rgb, f->w*f->h*3);
+
+	if(curplayer->lastframe == 0)
+		curplayer->lastframe = nanosec();
+
+	end = curplayer->lastframe + f->dt - delay;
+	while(1){
+		x = nanosec();
+		if(x >= end)
+			break;
+		left = end - x;
+		if(left > 750000000ULL)
+			sleep(70);
+		else if(left > 250000000ULL)
+			sleep(20);
+		else if(left > 100000000ULL)
+			sleep(1);
+	}
+
+	x = nanosec();
 	draw(screen, screen->r, curim, nil, ZP);
 	flushimage(display, 1);
 	unlockdisplay(display);
-	delay = nanosec() - start;
-	curplayer->lastframe = start;
+
+	delay = nanosec() - x;
+	curplayer->lastframe += f->dt;
+
+	freeframe(f);
 }
 
 static void
 playerproc(void *aux)
 {
+	Dav1dPicture pic;
+	uvlong lasttimestamp;
 	Player *p;
 	Frame *f;
 	int res;
 
 	p = aux;
-
+	lasttimestamp = 0;
+	memset(&pic, 0, sizeof(pic));
 	do{
-		res = dav1d_send_data(p->c, &p->data);
-		if(res < 0 && res != DAV1D_ERR(EAGAIN)){
-			fprint(2, "dav1d_send_data: %d\n", res);
-			break;
+		res = dav1d_get_picture(p->c, &pic);
+		if(res < 0){
+			if(res != DAV1D_ERR(EAGAIN)){
+				fprint(2, "dav1d_get_picture: %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);
+			f->w = pic.p.w;
+			f->h = pic.p.h;
+			if((f->rgb = malloc(f->w*f->h*3)) != nil){
+				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) * p->timebase * 1000000000ULL;
+				lasttimestamp = pic.m.timestamp;
+				dav1d_picture_unref(&pic);
+
+				if(sendp(p->frames, f) < 0){
+					freeframe(f);
 					break;
 				}
 			}else{
-				f->n = p->n++;
-				sendp(converters[f->n % maxframes], f);
+				freeframe(f);
 			}
 		}
+
+		res = dav1d_send_data(p->c, &p->data);
+		if(res < 0){
+			if(res != DAV1D_ERR(EAGAIN)){
+				fprint(2, "dav1d_send_data: %d\n", res);
+				break;
+			}
+		}
 	}while(p->data.sz > 0 || input_read(p->dc, &p->data) == 0);
 
 	if(p->data.sz > 0)
 		dav1d_data_unref(&p->data);
 
-	// FIXME there might be more here
+	/* drain */
+	while(dav1d_get_picture(p->c, &pic) >= 0)
+		dav1d_picture_unref(&pic);
 
+	input_close(p->dc);
+	dav1d_close(&p->c);
+
 	sendul(p->done, 1);
 
 	threadexits(nil);
 }
 
-static void
-freeplayer(Player *p)
-{
-	// FIXME
-	chanfree(p->done);
-	free(p);
-}
-
 static Player *
 newplayer(char *filename)
 {
 	Player *p;
-	unsigned fps[2], timebase[2], total;
+	unsigned fps[2], timebase[2], total, threads;
 	Dav1dSettings av1s;
+	char *s;
 
 	p = calloc(1, sizeof(*p));
 	if(input_open(&p->dc, "ivf", filename, fps, &total, timebase) < 0){
@@ -253,15 +292,16 @@
 		werrstr("input_open");
 		goto err;
 	}
-	p->fps = fps[0]/fps[1]; // FIXME that's not precise
+	p->timebase = (double)timebase[1]/(double)timebase[0];
 	if(input_read(p->dc, &p->data) < 0){
 		werrstr("input_read");
 		goto err;
 	}
 
+	threads = atoi((s = getenv("NPROC")) != nil ? s : "1");
 	dav1d_default_settings(&av1s);
-	av1s.n_frame_threads = maxframes;
-	av1s.n_tile_threads = maxframes;
+	av1s.n_frame_threads = threads;
+	av1s.n_tile_threads = threads;
 
 	if(dav1d_open(&p->c, &av1s) != 0){
 		werrstr("dav1d_open");
@@ -268,10 +308,10 @@
 		goto err;
 	}
 
+	p->frames = chancreate(sizeof(Frame*), 0);
 	p->done = chancreate(sizeof(ulong), 0);
-	p->lastframe = 0;
 
-	proccreate(playerproc, p, mainstacksize);
+	proccreate(playerproc, p, 16384);
 
 	return p;
 err:
@@ -281,76 +321,17 @@
 }
 
 static void
-rgbready(uchar *rgb, int w, int h, uvlong n)
+closeplayer(Player *p)
 {
-	Rectangle r;
-
-	r = Rect(0,0,w,h);
-	for(;;){
-		qlock(&framelock);
-		if(todisplay == n){
-			if(curim != nil && Dx(curim->r) != w){
-				freeimage(curim);
-				curim = nil;
-			}
-			if(curim == nil)
-				curim = allocimage(display, r, RGB24, 0, DNofill);
-			loadimage(curim, r, rgb, w*h*3);
-			free(rgb);
-			drawframe();
-			todisplay++;
-			qunlock(&framelock);
-			break;
-		}
-		qunlock(&framelock);
-		sleep(10);
-	}
+	chanclose(p->frames);
+	chanclose(p->done);
 }
 
-static void
-converterproc(void *c)
-{
-	Frame *f;
-	uchar *rgb;
-	Dav1dPicture *p;
-	uchar *out;
-	int w, h;
-
-	for(; (f = recvp(c)) != nil;){
-		p = &f->pic;
-		if((rgb = malloc(p->p.w * p->p.h * 3)) != nil){
-			yuv420_rgb24(p->p.w, p->p.h, p->data[0], p->data[1], p->data[2], p->stride[0], p->stride[1], rgb, p->p.w*3);
-			w = p->p.w;
-			h = p->p.h;
-
-			if(resize){
-				w = Dx(screen->r);
-				h = Dy(screen->r);
-				if((out = malloc(w*h*3)) != nil){
-					stbir_resize_uint8_generic(
-						rgb, p->p.w, p->p.h, p->p.w*3,
-						out, w, h, w*3,
-						3, -1, 0,
-						STBIR_EDGE_CLAMP, STBIR_FILTER_MITCHELL, STBIR_COLORSPACE_LINEAR,
-						NULL);
-				}
-				free(rgb);
-				rgb = out;
-			}
-			if(rgb != nil)
-				rgbready(rgb, w, h, f->n);
-		}
-
-		freeframe(f);
-	}
-
-	threadexits(nil);
-}
-
 void
 threadmain(int argc, char **argv)
 {
 	enum {
+		Cframe,
 		Cplayerdone,
 		Cmouse,
 		Ckeyboard,
@@ -357,14 +338,15 @@
 		Cresize,
 		Cnum,
 	};
+	Frame *frame;
 	Mousectl *mctl;
 	Keyboardctl *kctl;
-	char *s;
 	Rune key;
 	Mouse m;
-	int i, end, done;
+	int i, end, done, res;
 	Alt a[Cnum+1] =
 	{
+		[Cframe] = { nil, &frame, CHANRCV },
 		[Cplayerdone] = { nil, nil, CHANRCV },
 		[Cmouse] = { nil, &m, CHANRCV },
 		[Ckeyboard] = { nil, &key, CHANRCV },
@@ -392,24 +374,20 @@
 	a[Cresize].c = mctl->resizec;
 	a[Ckeyboard].c = kctl->c;
 
-	maxframes = atoi((s = getenv("NPROC")) != nil ? s : "1");
-	if(maxframes < 1)
-		maxframes = 1;
-	converters = calloc(maxframes, sizeof(*converters));
-	for(i = 0; i < maxframes; i++){
-		converters[i] = chancreate(sizeof(Frame*), 0);
-		proccreate(converterproc, converters[i], 4096);
-	}
-
 	for(end = i = 0; !end && i < argc; i++){
-		todisplay = 0;
 		if((curplayer = newplayer(argv[0])) == nil)
 			sysfatal("%r");
 
+		a[Cframe].c = curplayer->frames;
 		a[Cplayerdone].c = curplayer->done;
 
 		for(done = 0; !done && !end;){
-			switch(alt(a)){
+			res = alt(a);
+			switch(res){
+			case Cframe:
+				drawframe(frame);
+				break;
+
 			case Cplayerdone:
 				done = 1;
 				break;
@@ -422,28 +400,21 @@
 					end = 1;
 					break;
 				}
-				if(key == 'r')
-					resize = !resize;
 				break;
 
 			case Cresize:
-				qlock(&framelock);
+				lockdisplay(display);
 				if(getwindow(display, Refnone) < 0)
 					sysfatal("getwindow: %r");
-				lockdisplay(display);
 				freeimage(curim);
 				curim = nil;
 				unlockdisplay(display);
-				qunlock(&framelock);
 				break;
 			}
 		}
 
-		freeplayer(curplayer);
+		closeplayer(curplayer);
 	}
-
-	for(i = 0; i < nelem(converters); i++)
-		chanclose(converters[i]);
 
 	threadexitsall(nil);
 }
--- a/src/plan9_thread.c
+++ b/src/plan9_thread.c
@@ -3,6 +3,10 @@
 #include </sys/include/thread.h>
 #include "thread.h"
 
+enum {
+	Magic = 1325465,
+};
+
 void
 dav1d_set_thread_name(const char *const name)
 {
@@ -49,9 +53,9 @@
 pthread_once(pthread_once_t *once, void (*init_routine)(void))
 {
 	qlock(once);
-	if (once->done != 12345) {
+	if (once->done != Magic) {
 		init_routine();
-		once->done = 12345;
+		once->done = Magic;
 	}
 	qunlock(once);
 	return 0;
@@ -89,6 +93,7 @@
 int
 pthread_cond_init(pthread_cond_t *const cond, const void *const attr)
 {
+	USED(attr);
     memset(cond, 0, sizeof(*cond));
     cond->l = &cond->lock;
     return 0;
@@ -97,6 +102,7 @@
 int
 pthread_cond_destroy(pthread_cond_t *const cond)
 {
+	USED(cond);
     return 0;
 }
 
--- a/tools/input/input.c
+++ b/tools/input/input.c
@@ -72,6 +72,7 @@
     DemuxerContext *c;
     int res, i;
 
+	impl = NULL;
     if (name) {
         for (i = 0; demuxers[i]; i++) {
             if (!strcmp(demuxers[i]->name, name)) {