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