shithub: treason

ref: b6f90701508babfab5fec9c71f6f43a72c933737
dir: treason/decoder_av1.c

View raw version
#include <dav1d.h>
#include "frame.h"
#include "stream.h"
#include "decoder.h"
#include "misc.h"

#pragma lib "../dav1d/src/libdav1d.$M.a"

typedef struct Aux Aux;

struct Aux {
	Dav1dContext *c;
};

static char *layouts[] = {
	[DAV1D_PIXEL_LAYOUT_I400] = "i400",
    [DAV1D_PIXEL_LAYOUT_I420] = "i420",
    [DAV1D_PIXEL_LAYOUT_I422] = "i422",
    [DAV1D_PIXEL_LAYOUT_I444] = "i444",
};

static u8int *
allocdata(void *data, int sz)
{
	return dav1d_data_create(data, sz);
}

static int
readframe(Stream *s, Dav1dData *data)
{
	Streamframe f;

	s->ops.aux = data;
	if(Sread(s, &f) != 0)
		return -1;
	data->m.timestamp = f.timestamp;

	return 0;
}

static void
decode(void *x)
{
	uvlong lasttimestamp;
	Dav1dPicture pic;
	Dav1dData data;
	uvlong start;
	Decoder *d;
	Channel *c;
	Frame *f;
	Aux *a;
	int res;

	threadsetname("decoder/av1");
	d = x;
	a = d->aux;
	lasttimestamp = 0;
	memset(&pic, 0, sizeof(pic));
	for(res = 0, data.sz = 0; data.sz > 0 || (res = readframe(d->s, &data)) == 0;){
		if(data.sz == 0)
			break;

		start = nanosec();
		res = dav1d_get_picture(a->c, &pic);
		d->decodetime = nanosec() - start;
		if(res < 0){
			if(res != DAV1D_ERR(EAGAIN)){
				werrstr("dav1d_get_picture: %d", res);
				break;
			}
		}else if(pic.p.layout != DAV1D_PIXEL_LAYOUT_I420){
			if(pic.p.layout >= 0 && pic.p.layout < nelem(layouts))
				werrstr("%s", layouts[pic.p.layout]);
			else
				werrstr("??? (%d)", pic.p.layout);
			werrstr("unsupported pixel layout: %r");
			res = -1;
			break;
		}else if(pic.p.bpc != 8){
			werrstr("unsupported bits per component: %d", pic.p.bpc);
			res = -1;
			break;
		}else if((f = newframe(pic.p.w, pic.p.h, pic.data, pic.stride[0], pic.stride[1])) != nil){
			memset(&f->crop, 0, sizeof(f->crop));
			f->dt = (pic.m.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
			lasttimestamp = pic.m.timestamp;
			dav1d_picture_unref(&pic);

			if(sendp(d->frames, f) < 0){
				free(f);
				break;
			}
		}

		res = dav1d_send_data(a->c, &data);
		if(res < 0){
			if(res != DAV1D_ERR(EAGAIN)){
				werrstr("dav1d_send_data: %d", res);
				break;
			}
		}
	}
	if(res != 0)
		fprint(2, "av1: %r\n");

	if(data.sz > 0)
		dav1d_data_unref(&data);

	/* drain */
	while(dav1d_get_picture(a->c, &pic) >= 0)
		dav1d_picture_unref(&pic);

	dav1d_close(&a->c);
	free(a);
	c = d->finished;
	sendp(c, res == 0 ? nil : "error");
	chanclose(c);

	threadexits(nil);
}

static int
av1open(Decoder *d)
{
	Dav1dSettings av1s;
	Aux *a;
	int res;

	a = calloc(1, sizeof(*a));

	dav1d_default_settings(&av1s);
	av1s.n_frame_threads = nproc;
	av1s.n_tile_threads = nproc;

	if((res = dav1d_open(&a->c, &av1s)) != 0){
		werrstr("dav1d_open: %d", res);
		free(a);
		return -1;
	}
	d->aux = a;
	d->s->ops.alloc = allocdata;
	proccreate(decode, d, 16384);

	return 0;
}

static void
av1close(Decoder *d)
{
	USED(d);
}

Decoderops av1ops = {
	.open = av1open,
	.close = av1close,
};