shithub: treason

ref: b6f90701508babfab5fec9c71f6f43a72c933737
dir: treason/decoder_h264.c

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

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

typedef struct Aux Aux;

struct Aux {
	SPictReoderingStatus;
	SPictInfo pics[16];

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

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 void
reorder(Aux *a)
{
	int i, firstvalid;

	if(a->iNumOfPicts > 0){
		if(a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb && a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->bNewSeqBegin){
			a->iLastGOPRemainPicts = a->iNumOfPicts;

			for(i = 0; i <= a->iLargestBufferedPicIndex; i++)
				a->pics[i].bLastGOP = a->pics[i].iPOC > IMinInt32;
		}else{
			for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
				if(a->pics[i].iPOC == a->ctx.pSliceHeader->iPicOrderCntLsb){
					a->iLastGOPRemainPicts = a->iNumOfPicts;
					for(i = 0; i <= a->iLargestBufferedPicIndex; 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].iPOC = a->ctx.pSliceHeader->iPicOrderCntLsb;
			a->pics[i].iPicBuffIdx = a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx;
			a->pics[i].uiDecodingTimeStamp = a->ctx.uiDecodingTimeStamp;
			a->ctx.pLastDecPicInfo->pPreviousDecodedPictureInDpb->iRefCount++;
			a->pics[i].bLastGOP = false;
			a->info.iBufferStatus = 0;
			a->iNumOfPicts++;
			if(a->iLargestBufferedPicIndex < i)
				a->iLargestBufferedPicIndex = i;
			break;
		}
	}

	PPicBuff picbuf = a->ctx.pPicBuff;
	if(a->iLastGOPRemainPicts > 0){
		a->iMinPOC = IMinInt32;
		firstvalid = -1;
		for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
			if(a->iMinPOC == IMinInt32 && a->pics[i].iPOC > IMinInt32 && a->pics[i].bLastGOP){
				a->iMinPOC = a->pics[i].iPOC;
				a->iPictInfoIndex = i;
				firstvalid = i;
				break;
			}
		}
		for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
			if(i == firstvalid)
				continue;
			if(a->pics[i].iPOC > IMinInt32 && a->pics[i].iPOC < a->iMinPOC && a->pics[i].bLastGOP){
				a->iMinPOC = a->pics[i].iPOC;
				a->iPictInfoIndex = i;
			}
		}
		a->iLastWrittenPOC = a->iMinPOC;
		memmove(&a->info, &a->pics[a->iPictInfoIndex].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->iPictInfoIndex].iPOC = IMinInt32;
		picbuf->ppPic[a->pics[a->iPictInfoIndex].iPicBuffIdx]->iRefCount--;
		a->pics[a->iPictInfoIndex].bLastGOP = false;
		a->iMinPOC = IMinInt32;
		a->iNumOfPicts--;
		a->iLastGOPRemainPicts--;
		if(a->iLastGOPRemainPicts == 0)
			a->iLastWrittenPOC = IMinInt32;
		return;
	}

	if(a->iNumOfPicts > 0){
		a->iMinPOC = IMinInt32;
		firstvalid = -1;
		for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
			if(a->iMinPOC == IMinInt32 && a->pics[i].iPOC > IMinInt32){
				a->iMinPOC = a->pics[i].iPOC;
				a->iPictInfoIndex = i;
				firstvalid = i;
				break;
			}
		}
		for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
			if(i == firstvalid)
				continue;
			if(a->pics[i].iPOC > IMinInt32 && a->pics[i].iPOC < a->iMinPOC){
				a->iMinPOC = a->pics[i].iPOC;
				a->iPictInfoIndex = i;
			}
		}
	}

	if(a->iMinPOC > IMinInt32 && ((a->iLastWrittenPOC > IMinInt32 && a->iMinPOC - a->iLastWrittenPOC <= 1) || a->iMinPOC < a->ctx.pSliceHeader->iPicOrderCntLsb)){
		a->iLastWrittenPOC = a->iMinPOC;
		memmove(&a->info, &a->pics[a->iPictInfoIndex].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->iPictInfoIndex].iPOC = IMinInt32;
		picbuf->ppPic[a->pics[a->iPictInfoIndex].iPicBuffIdx]->iRefCount--;
		a->pics[a->iPictInfoIndex].bLastGOP = false;
		a->iMinPOC = IMinInt32;
		a->iNumOfPicts--;
	}
}

static char *
err2s(int err)
{
	static char t[256];
	char *s, *e;

	t[0] = t[1] = 0;
	s = t;
	e = t+sizeof(t);
	if(err & dsFramePending)       s = seprint(s, e, "|FramePending");
	if(err & dsRefLost)            s = seprint(s, e, "|RefLost");
	if(err & dsBitstreamError)     s = seprint(s, e, "|BitstreamError");
	if(err & dsDepLayerLost)       s = seprint(s, e, "|DepLayerLost");
	if(err & dsNoParamSets)        s = seprint(s, e, "|NoParamSets");
	if(err & dsDataErrorConcealed) s = seprint(s, e, "|DataErrorConcealed");
	if(err & dsRefListNullPtrs)    s = seprint(s, e, "|RefListNullPtrs");
	if(err & dsInvalidArgument)    s = seprint(s, e, "|InvalidArgument");
	if(err & dsInitialOptExpected) s = seprint(s, e, "|InitialOptExpected");
	if(err & dsOutOfMemory)        s = seprint(s, e, "|OutOfMemory");
	if(err & dsDstBufNeedExpan)    s = seprint(s, e, "|DstBufNeedExpan");
	USED(s);

	return t+1;
}

static void
flush(Aux *a)
{
	int i, firstvalid;

	a->iMinPOC = IMinInt32;
	firstvalid = -1;
	for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
		if(a->iMinPOC == IMinInt32 && a->pics[i].iPOC > IMinInt32){
			a->iMinPOC = a->pics[i].iPOC;
			a->iPictInfoIndex = i;
			firstvalid = i;
			break;
		}
	}

	for(i = 0; i <= a->iLargestBufferedPicIndex; i++){
		if(i == firstvalid)
			continue;
		if(a->pics[i].iPOC > IMinInt32 && a->pics[i].iPOC < a->iMinPOC){
			a->iMinPOC = a->pics[i].iPOC;
			a->iPictInfoIndex = i;
		}
	}

	if(a->iMinPOC > IMinInt32){
		a->iLastWrittenPOC = a->iMinPOC;
		memmove(&a->info, &a->pics[a->iPictInfoIndex].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->iPictInfoIndex].iPOC = IMinInt32;
		a->ctx.pPicBuff->ppPic[a->pics[a->iPictInfoIndex].iPicBuffIdx]->iRefCount--;
		a->pics[a->iPictInfoIndex].bLastGOP = false;
		a->iMinPOC = IMinInt32;
		a->iNumOfPicts--;
	}
}

static int
one(Aux *a, Streamframe *sf)
{
	int res;

	if(sf->buf == nil || sf->sz < 1){
		a->ctx.bEndOfStreamFlag = true;
		a->ctx.bInstantDecFlag = true;
	}else{
		a->ctx.bEndOfStreamFlag = false;
	}
	memset(a->data, 0, sizeof(a->data));
	memset(&a->info, 0, sizeof(a->info));
	a->info.uiInBsTimeStamp = sf->timestamp;
	a->ctx.uiTimeStamp = sf->timestamp;
	a->ctx.iErrorCode = dsErrorFree;
	a->ctx.iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL;
	a->ctx.bReferenceLostAtT0Flag = false;
	a->ctx.bCurAuContainLtrMarkSeFlag = false;
	a->ctx.iFrameNumOfAuMarkedLtr = 0;
	a->ctx.iFrameNum = -1;
	a->ctx.iFeedbackTidInAu = -1;
	a->ctx.iFeedbackNalRefIdc = -1;
	res = WelsDecodeBs(&a->ctx, sf->buf, sf->sz, a->data, &a->info, nil);
	a->ctx.bInstantDecFlag = false;
	if(res != 0){
		if(res & dsOutOfMemory)
			return 0;
		werrstr("%s: %.*H", err2s(a->ctx.iErrorCode), MIN(32, sf->sz), sf->buf);
		return -1;
	}

	if(a->info.iBufferStatus != 0 && a->ctx.pSps->uiProfileIdc != 66 && a->ctx.pSps->uiProfileIdc != 83){
		/* non-baseline needs reordering */
		reorder(a);
		sf->timestamp = a->info.uiOutYuvTimeStamp;
	}

	return 0;
}

static int
sendframe(Aux *a, uvlong dt, Channel *c)
{
	int w, h, *stride;
	Frame *f;

	w = a->info.UsrData.sSystemBuffer.iWidth;
	h = a->info.UsrData.sSystemBuffer.iHeight;
	stride = a->info.UsrData.sSystemBuffer.iStride;

	if((f = newframe(w, h, a->data, stride[0], stride[1])) == nil)
		return -1;

	f->crop.left = a->ctx.sFrameCrop.iLeftOffset;
	f->crop.top = a->ctx.sFrameCrop.iTopOffset;
	f->crop.right = w - a->ctx.sFrameCrop.iRightOffset;
	f->crop.bottom = h - a->ctx.sFrameCrop.iBottomOffset;
	f->dt = dt;

	if(sendp(c, f) < 0){
		free(f);
		return -1;
	}

	return 0;
}

static void
decode(void *x)
{
	uvlong start, framenum, lasttimestamp, ts;
	Streamframe sf;
	Decoder *d;
	Channel *c;
	int res, n;
	Aux *a;

	threadsetname("decoder/h264");
	d = x;
	a = d->aux;
	lasttimestamp = 0;
	for(res = 0, framenum = 0; res >= 0 && (res = Sread(d->s, &sf)) == 0 && sf.sz > 0; framenum++){
		if(sf.sz > 4 && sf.buf[0] == 0 && sf.buf[1] == 0){
			if(sf.buf[2] == 1)
				n = sf.buf[3];
			else if(sf.buf[3] == 1)
				n = sf.buf[4];
			else
				n = 0;
			n &= 0x1f;
			if(n == 7 || n == 8){
				/*
				 * FIXME not sure whether that is correct (most likely not)
				 * flush everything on sps/pps
				 * obviously, there might be multiple NALs in a single stream frame
				 */
				while(a->iNumOfPicts > 0){
					flush(a);
					ts = a->info.uiOutYuvTimeStamp;
					if(ts < lasttimestamp){
						werrstr("wrong timestamp at flush: %llud < %llud", ts, lasttimestamp);
						res = -1;
						break;
					}
					if(sendframe(a, (ts - lasttimestamp) * d->timebase * 1000000000ULL, d->frames) < 0)
						goto done;
					lasttimestamp = ts;
				}
				if(res < 0)
					break;
			}
		}

		start = nanosec();
		if((res = one(a, &sf)) != 0)
			break;
		sf.buf = nil;
		sf.sz = 0;
		if((res = one(a, &sf)) != 0)
			break;
		d->decodetime = nanosec() - start;

		if(a->data[0] == nil || sf.timestamp < lasttimestamp)
			continue;

		if(sendframe(a, (sf.timestamp - lasttimestamp) * d->timebase * 1000000000ULL, d->frames) < 0)
			goto done;
		lasttimestamp = sf.timestamp;
	}
	if(res != 0)
		fprint(2, "h264: frame %llud: %r\n", framenum);
	else{
		/* FIXME the frames are finished but there might still be left-overs */
		ResetReorderingPictureBuffers(a, a->pics, false);
		WelsResetRefPic(&a->ctx);
	}

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;

	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->ctx.pPictInfoList = a->pics;
	a->ctx.pPictReoderingStatus = a;
	a->logctx.pfLog = logfun;
	a->ctx.sLogCtx = a->logctx;

	WelsDecoderDefaults(&a->ctx, &a->logctx);
	WelsDecoderSpsPpsDefaults(&a->ctx.sSpsPpsCtx);

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

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

	return res;
}

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

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