ref: 6dd7fd3086ffa8c87a35c955ddaed3e9fa0e292d
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();
thisframe = curplayer->lastframe + 1000000000ULL/curplayer->fps - delay;
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){
while(nanosec() < thisframe)
sleep(1000/curplayer->fps);
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*), 15); // 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);
}