ref: 0a34d5dfc0c5f80509c205e50d79d09393638984
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> typedef struct Frame Frame; typedef struct Player Player; struct Frame { u8int *rgb; int w, h; uvlong dt; }; struct Player { Dav1dData data; Dav1dContext *c; DemuxerContext *dc; Channel *frames, *done; double timebase; uvlong fps; uvlong lastframe; }; int mainstacksize = 128*1024; static Player *curplayer; static Image *curim; /* 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) { free(f->rgb); 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(Frame *f) { uvlong x, end, left; static uvlong delay; Rectangle r; lockdisplay(display); 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() - 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_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)); 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{ 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); /* 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 Player * newplayer(char *filename) { Player *p; 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){ werrstr("input_open"); goto err; } 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 = threads; av1s.n_tile_threads = threads; if(dav1d_open(&p->c, &av1s) != 0){ werrstr("dav1d_open"); goto err; } p->frames = chancreate(sizeof(Frame*), 0); p->done = chancreate(sizeof(ulong), 0); proccreate(playerproc, p, 16384); return p; err: werrstr("%s: %r", filename); free(p); return nil; } static void closeplayer(Player *p) { chanclose(p->frames); chanclose(p->done); } void threadmain(int argc, char **argv) { enum { Cframe, Cplayerdone, Cmouse, Ckeyboard, Cresize, Cnum, }; Frame *frame; Mousectl *mctl; Keyboardctl *kctl; Rune key; Mouse m; 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 }, [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[Cframe].c = curplayer->frames; a[Cplayerdone].c = curplayer->done; for(done = 0; !done && !end;){ res = alt(a); switch(res){ case Cframe: drawframe(frame); break; case Cplayerdone: done = 1; break; case Cmouse: break; case Ckeyboard: if(key == 'q' || key == Kdel){ end = 1; break; } break; case Cresize: lockdisplay(display); if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); freeimage(curim); curim = nil; unlockdisplay(display); break; } } closeplayer(curplayer); } threadexitsall(nil); }