ref: 709cb723bba3aa43ba40a5c70dba0b337bae54e2
dir: /decoder_h264.c/
#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;
uvlong start;
d = x;
a = d->aux;
lasttimestamp = 0;
start = nanosec();
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;
}
d->decodetime = nanosec() - start;
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;
stride = a->info.UsrData.sSystemBuffer.iStride;
if((f = newframe(w, h, a->data, stride[0], stride[1])) == nil)
continue;
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;
f->dt = (sf.timestamp - lasttimestamp) * d->timebase * 1000000000ULL;
lasttimestamp = sf.timestamp;
if(sendp(d->frames, f) < 0){
free(f);
goto done;
}
start = nanosec();
}
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,
};