ref: 82b18def5d91a37c264fdc4b4860ea2af664acfc
dir: /decoder_h264.c/
#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){
/* FIXME this happens in some videos... */
//werrstr("wrong timestamp at flush: %llud < %llud", ts, lasttimestamp);
//res = -1;
//break;
continue;
}
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,
};