ref: 298977f391e79902b3bd3d3b9f1f6d9c8a3d3347
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; }; 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 Player *curplayer; static Image *curframe; /* 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 int dav1d_loadimage(Rectangle r, Image **oim, Dav1dPicture *p) { Image *im; uchar *rgb; int w, h; 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; yuv420_rgb24(w, h, p->data[0], p->data[1], p->data[2], p->stride[0], p->stride[1], rgb, w*3); /* uchar *out; if((out = malloc(Dx(r)*Dy(r)*3)) == nil){ free(rgb); return -1; } 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; */ w = Dx(r); h = Dy(r); loadimage(im, Rect(0,0,w,h), rgb, w*h*3); free(rgb); return 0; } 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 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; start = nanosec(); dt = 0; lockdisplay(display); 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; 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; } 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{ sendp(p->frames, 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->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); }