ref: de18a5f5d2f4f03b78663d203f72e6099ea59d0e
dir: /src/av19.c/
#include "dav1d.h" #include "tools/input/input.h" #include <draw.h> #include <memdraw.h> #include <mouse.h> #include <keyboard.h> #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; uvlong n; }; struct Player { Dav1dData data; Dav1dContext *c; DemuxerContext *dc; Channel *done; uvlong fps; uvlong lastframe; uvlong n; }; // FIXME why does it need this much? int mainstacksize = 512*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 */ #define clamp(v) ((v)<0?0 : ((v)>255?255:v)) #define FIXED_POINT_VALUE(value, precision) ((int)(((value)*(1<<precision))+0.5)) typedef struct { u8int cb_factor; // [(255*CbNorm)/CbRange] u8int cr_factor; // [(255*CrNorm)/CrRange] u8int g_cb_factor; // [Bf/Gf*(255*CbNorm)/CbRange] u8int g_cr_factor; // [Rf/Gf*(255*CrNorm)/CrRange] u8int y_factor; // [(YMax-YMin)/255] u8int y_offset; // YMin } YUV2RGBParam; static char *layout[] = { [DAV1D_PIXEL_LAYOUT_I400] = "i400", [DAV1D_PIXEL_LAYOUT_I420] = "i420", [DAV1D_PIXEL_LAYOUT_I422] = "i422", [DAV1D_PIXEL_LAYOUT_I444] = "i444", }; #define YUV2RGB_PARAM(Rf, Bf, YMin, YMax, CbCrRange) \ {.cb_factor=FIXED_POINT_VALUE(255.0*(2.0*(1-Bf))/CbCrRange, 6), \ .cr_factor=FIXED_POINT_VALUE(255.0*(2.0*(1-Rf))/CbCrRange, 6), \ .g_cb_factor=FIXED_POINT_VALUE(Bf/(1.0-Bf-Rf)*255.0*(2.0*(1-Bf))/CbCrRange, 7), \ .g_cr_factor=FIXED_POINT_VALUE(Rf/(1.0-Bf-Rf)*255.0*(2.0*(1-Rf))/CbCrRange, 7), \ .y_factor=FIXED_POINT_VALUE(255.0/(YMax-YMin), 7), \ .y_offset=YMin} static const YUV2RGBParam YUV2RGB[3] = { // ITU-T T.871 (JPEG) YUV2RGB_PARAM(0.299, 0.114, 0.0, 255.0, 255.0), // ITU-R BT.601-7 YUV2RGB_PARAM(0.299, 0.114, 16.0, 235.0, 224.0), // ITU-R BT.709-6 YUV2RGB_PARAM(0.2126, 0.0722, 16.0, 235.0, 224.0) }; static void yuv420_rgb24( u32int width, u32int height, const u8int *Y, const u8int *U, const u8int *V, u32int Y_stride, u32int UV_stride, u8int *RGB, u32int RGB_stride) { const YUV2RGBParam *const param = &(YUV2RGB[0]); u32int x, y; for(y=0; y<(height-1); y+=2) { const u8int *y_ptr1=Y+y*Y_stride, *y_ptr2=Y+(y+1)*Y_stride, *u_ptr=U+(y/2)*UV_stride, *v_ptr=V+(y/2)*UV_stride; u8int *rgb_ptr1=RGB+y*RGB_stride, *rgb_ptr2=RGB+(y+1)*RGB_stride; for(x=0; x<(width-1); x+=2) { s8int u_tmp, v_tmp; u_tmp = u_ptr[0]-128; v_tmp = v_ptr[0]-128; //compute Cb Cr color offsets, common to four pixels s16int b_cb_offset, r_cr_offset, g_cbcr_offset; b_cb_offset = (param->cb_factor*u_tmp)>>6; r_cr_offset = (param->cr_factor*v_tmp)>>6; g_cbcr_offset = (param->g_cb_factor*u_tmp + param->g_cr_factor*v_tmp)>>7; s16int y_tmp; y_tmp = (param->y_factor*(y_ptr1[0]-param->y_offset))>>7; rgb_ptr1[2] = clamp(y_tmp + r_cr_offset); rgb_ptr1[1] = clamp(y_tmp - g_cbcr_offset); rgb_ptr1[0] = clamp(y_tmp + b_cb_offset); y_tmp = (param->y_factor*(y_ptr1[1]-param->y_offset))>>7; rgb_ptr1[5] = clamp(y_tmp + r_cr_offset); rgb_ptr1[4] = clamp(y_tmp - g_cbcr_offset); rgb_ptr1[3] = clamp(y_tmp + b_cb_offset); y_tmp = (param->y_factor*(y_ptr2[0]-param->y_offset))>>7; rgb_ptr2[2] = clamp(y_tmp + r_cr_offset); rgb_ptr2[1] = clamp(y_tmp - g_cbcr_offset); rgb_ptr2[0] = clamp(y_tmp + b_cb_offset); y_tmp = (param->y_factor*(y_ptr2[1]-param->y_offset))>>7; rgb_ptr2[5] = clamp(y_tmp + r_cr_offset); rgb_ptr2[4] = clamp(y_tmp - g_cbcr_offset); rgb_ptr2[3] = clamp(y_tmp + b_cb_offset); rgb_ptr1 += 6; rgb_ptr2 += 6; y_ptr1 += 2; y_ptr2 += 2; u_ptr += 1; v_ptr += 1; } } } static void freeframe(Frame *f) { dav1d_picture_unref(&f->pic); free(f); } static uvlong nanosec(void) { 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 drawframe(void) { uvlong thisframe, start; static uvlong delay; thisframe = curplayer->lastframe + 1000000000ULL/curplayer->fps - delay; while(nanosec() < thisframe) sleep(10); lockdisplay(display); start = nanosec(); draw(screen, screen->r, curim, nil, ZP); flushimage(display, 1); unlockdisplay(display); delay = nanosec() - start; curplayer->lastframe = start; } static void playerproc(void *aux) { Player *p; Frame *f; int res; p = aux; 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; }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{ f->n = p->n++; sendp(converters[f->n % maxframes], f); } } }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 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; 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 = maxframes; av1s.n_tile_threads = maxframes; if(dav1d_open(&p->c, &av1s) != 0){ werrstr("dav1d_open"); goto err; } p->done = chancreate(sizeof(ulong), 0); p->lastframe = 0; proccreate(playerproc, p, mainstacksize); return p; err: werrstr("%s: %r", filename); free(p); return nil; } static void rgbready(uchar *rgb, int w, int h, uvlong n) { 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); } } 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 { Cplayerdone, Cmouse, Ckeyboard, Cresize, Cnum, }; Mousectl *mctl; Keyboardctl *kctl; char *s; Rune key; Mouse m; int i, end, done; Alt a[Cnum+1] = { [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; 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[Cplayerdone].c = curplayer->done; for(done = 0; !done && !end;){ switch(alt(a)){ case Cplayerdone: done = 1; break; case Cmouse: break; case Ckeyboard: if(key == 'q' || key == Kdel){ end = 1; break; } if(key == 'r') resize = !resize; break; case Cresize: qlock(&framelock); if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); lockdisplay(display); freeimage(curim); curim = nil; unlockdisplay(display); qunlock(&framelock); break; } } freeplayer(curplayer); } for(i = 0; i < nelem(converters); i++) chanclose(converters[i]); threadexitsall(nil); }