shithub: treason

ref: 388fe8b594357a85c94803d1c73b233672c1ecc0
dir: treason/decoder_h264.c

View raw version
#include <decoder/core/inc/decoder.h>
#include <thread.h>
#include "frame.h"
#include "stream.h"
#include "decoder.h"
#include "misc.h"

#pragma lib "../openh264/codec/libopenh264.a"

typedef struct Aux Aux;

struct Aux {
	SWelsDecoderContext ctx;
	SLogContext logctx;
	SBufferInfo info;
	SWelsLastDecPicInfo pic;
	SVlcTable vlctbl;
	SDecoderStatistics stat;
	uint8_t *data[3];

	/* reordering */
	SPictInfo pics[16];
	int npics;
	int lastgopremain;
	int lastbuffered;
	int lastwritten;
	int lbufpicind;
	int minpoc;
	int picind;
};

static void
logfun(void *ctx, const int32_t level, const char *fmt, va_list argv)
{
	USED(ctx);

	if(debug >= level){
		fprint(2, "h264: ");
		vfprint(2, fmt, argv);
		fprint(2, "\n");
	}
}

static uvlong
reorder(Aux *a, uvlong ts)
{
	int i, firstvalid;

	if(a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb && a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->bNewSeqBegin){
		a->lastgopremain = a->npics;

		for(i = 0; i <= a->lbufpicind; i++)
			a->pics[i].bLastGOP = a->pics[i].iPOC > IMinInt32;
	}else{
		for(i = 0; i <= a->lbufpicind; i++){
			if(a->pics[i].iPOC == a->ctx.pSliceHeader->iPicOrderCntLsb){
				a->lastgopremain = a->npics;
				for(i = 0; i <= a->lbufpicind; i++)
					a->pics[i].bLastGOP = a->pics[i].iPOC > IMinInt32;
				break;
			}
		}
	}

	for(i = 0; i < nelem(a->pics); i++){
		if(a->pics[i].iPOC == IMinInt32){
			memmove(&a->pics[i].sBufferInfo, &a->info, sizeof(a->info));
			a->pics[i].uiDecodingTimeStamp = ts;
			a->pics[i].iPOC = a->ctx.pSliceHeader->iPicOrderCntLsb;
			a->pics[i].iPicBuffIdx = a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx;
			a->pics[i].uiDecodingTimeStamp = ts;
			a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->iRefCount++;
			a->pics[i].bLastGOP = false;
			a->lastbuffered = i;
			a->info.iBufferStatus = 0;
			a->npics++;
			if(i > a->lbufpicind)
				a->lbufpicind = i;
			break;
		}
	}

	PPicBuff picbuf = a->ctx.pPicBuff;
	if(a->lastgopremain > 0){
		a->minpoc = IMinInt32;
		firstvalid = -1;
		for(i = 0; i <= a->lbufpicind; i++){
			if(a->minpoc == IMinInt32 && a->pics[i].iPOC > IMinInt32 && a->pics[i].bLastGOP){
				a->minpoc = a->pics[i].iPOC;
				a->picind = i;
				firstvalid = i;
				break;
			}
		}
		for(i = 0; i <= a->lbufpicind; i++){
			if(i == firstvalid)
				continue;
			if(a->pics[i].iPOC > IMinInt32 && a->pics[i].iPOC < a->minpoc && a->pics[i].bLastGOP){
				a->minpoc = a->pics[i].iPOC;
				a->picind = i;
			}
		}
		a->lastwritten = a->minpoc;
		memmove(&a->info, &a->pics[a->picind].sBufferInfo, sizeof(a->info));
		a->data[0] = a->info.pDst[0];
		a->data[1] = a->info.pDst[1];
		a->data[2] = a->info.pDst[2];
		a->pics[a->picind].iPOC = IMinInt32;
		ts = a->pics[a->picind].uiDecodingTimeStamp;
		picbuf->ppPic[a->pics[a->picind].iPicBuffIdx]->iRefCount--;
		a->pics[a->picind].bLastGOP = false;
		a->minpoc = IMinInt32;
		a->npics--;
		a->lastgopremain--;
		if(a->lastgopremain == 0)
			a->lastwritten = IMinInt32;
		return ts;
	}

	if(a->npics > 0){
		a->minpoc = IMinInt32;
		firstvalid = -1;
		for(i = 0; i <= a->lbufpicind; i++){
			if(a->minpoc == IMinInt32 && a->pics[i].iPOC > IMinInt32){
				a->minpoc = a->pics[i].iPOC;
				a->picind = i;
				firstvalid = i;
				break;
			}
		}
		for(i = 0; i <= a->lbufpicind; i++){
			if(i == firstvalid)
				continue;
			if(a->pics[i].iPOC > IMinInt32 && a->pics[i].iPOC < a->minpoc){
				a->minpoc = a->pics[i].iPOC;
				a->picind = i;
			}
		}
	}
	if(a->minpoc > IMinInt32 && ((a->lastwritten > IMinInt32 && a->minpoc - a->lastwritten <= 1) || a->minpoc < a->ctx.pSliceHeader->iPicOrderCntLsb)){
		a->lastwritten = a->minpoc;
		memmove(&a->info, &a->pics[a->picind].sBufferInfo, sizeof (SBufferInfo));
		a->data[0] = a->info.pDst[0];
		a->data[1] = a->info.pDst[1];
		a->data[2] = a->info.pDst[2];
		ts = a->pics[a->picind].uiDecodingTimeStamp;
		a->pics[a->picind].iPOC = IMinInt32;
		picbuf->ppPic[a->pics[a->picind].iPicBuffIdx]->iRefCount--;
		a->pics[a->picind].bLastGOP = false;
		a->minpoc = IMinInt32;
		a->npics--;
	}

	return ts;
}

static void
decode(void *x)
{
	uvlong lasttimestamp;
	Decoder *d;
	Channel *c;
	Frame *f;
	Streamframe sf;
	Aux *a;
	int res, w, h, *stride;

	d = x;
	a = d->aux;
	lasttimestamp = 0;
	for(res = 0; res >= 0 && (res = Sread(d->s, &sf)) == 0 && sf.sz > 0;){
		if((res = WelsDecodeBs(&a->ctx, sf.buf, sf.sz, a->data, &a->info, nil)) != 0){
			werrstr("WelsDecodeBs: error %#x", res);
			break;
		}
		if(a->info.iBufferStatus == 0)
			continue;

		if(a->ctx.pSps->uiProfileIdc != 66 && a->ctx.pSps->uiProfileIdc != 83){
			/* non-baseline needs reordering */
			memset(a->data, 0, sizeof(a->data));
			sf.timestamp = reorder(a, sf.timestamp);
			if(a->data[0] == nil)
				continue;
		}

		if(sf.timestamp < lasttimestamp) /* this is not supposed to happen unless mcfs is wrong */
			continue;
		w = a->info.UsrData.sSystemBuffer.iWidth;
		h = a->info.UsrData.sSystemBuffer.iHeight;
		if((f = malloc(sizeof(*f) + w*h*3)) == nil)
			continue;

		stride = a->info.UsrData.sSystemBuffer.iStride;
		f->w = w;
		f->h = h;
		f->crop.left = a->ctx.sFrameCrop.iLeftOffset;
		f->crop.top = a->ctx.sFrameCrop.iTopOffset;
		f->crop.right = a->ctx.sFrameCrop.iRightOffset;
		f->crop.bottom = a->ctx.sFrameCrop.iBottomOffset;
		yuv420_rgb24(w, h, a->data[0], a->data[1], a->data[2], stride[0], stride[1], f->rgb, w*3);
		f->dt = (sf.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
		lasttimestamp = sf.timestamp;

		if(sendp(d->frames, f) < 0){
			free(f);
			goto done;
		}
	}
	if(res != 0)
		fprint(2, "h264: %r\n");

	/* FIXME the frames are finished but there might still be left-overs */

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

	threadexits(nil);
}

static int
h264open(Decoder *d)
{
	Aux *a;
	int res, i;

	a = calloc(1, sizeof(*a));
	a->ctx.pLastDecPicInfo = &a->pic;
	a->ctx.pVlcTable = &a->vlctbl;
	a->ctx.pDecoderStatistics = &a->stat;
	a->ctx.pMemAlign = &cMemoryAlign;
	a->logctx.pfLog = logfun;
	a->ctx.sLogCtx = a->logctx;

	a->ctx.pParam = calloc(1, sizeof(SDecodingParam));
	a->ctx.pParam->sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;

    a->minpoc = IMinInt32;
    a->lastwritten = IMinInt32;
    for(i = 0; i < nelem(a->pics); i++)
      a->pics[i].iPOC = IMinInt32;

	if((res = WelsInitDecoder(&a->ctx, &a->logctx)) != 0){
		werrstr("WelsInitDecoder: %d", res);
		free(a);
		return -1;
	}
	d->aux = a;
	proccreate(decode, d, 65536);

	return res;
}

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

Decoderops h264ops = {
	.open = h264open,
	.close = h264close,
};