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);
}
--
⑨