blob: 1eeb8453341bf5c9fb3e6e0c4175afb683bfe776 [file] [log] [blame]
/*------------------------------------------------------------------------------
-- --
-- This software is confidential and proprietary and may be used --
-- only as expressly authorized by a licensing agreement from --
-- --
-- Hantro Products Oy. --
-- --
-- (C) COPYRIGHT 2006 HANTRO PRODUCTS OY --
-- ALL RIGHTS RESERVED --
-- --
-- The entire notice above must be reproduced --
-- on all copies and should not be removed. --
-- --
--------------------------------------------------------------------------------
*/
#include "vp8ratecontrol.h"
#include <memory.h>
#include "vp8quanttable.h"
#include "libvpu/rk_vepu_debug.h"
#define DIV(a, b) (((a) + (SIGN(a) * (b)) / 2) / (b))
#define DSCY 64 /* n * 64 */
#define I32_MAX 2147483647 /* 2 ^ 31 - 1 */
#define QP_DELTA 4
#define RC_ERROR_RESET 0x7fffffff
static int32_t InitialQp(int32_t bits, int32_t pels);
static void PicSkip(vp8RateControl_s* rc);
static void PicQuantLimit(vp8RateControl_s* rc);
static int32_t VirtualBuffer(vp8VirtualBuffer_s* vb, int32_t timeInc);
static void PicQuant(vp8RateControl_s* rc);
static int32_t avg_rc_error(linReg_s* p);
static void update_rc_error(linReg_s* p, int32_t bits);
static int32_t gop_avg_qp(vp8RateControl_s* rc);
static int32_t new_pic_quant(linReg_s* p, int32_t bits, true_e useQpDeltaLimit);
static void update_tables(linReg_s* p, int32_t qp, int32_t bits);
static void update_model(linReg_s* p);
static int32_t lin_sy(int32_t* qp, int32_t* r, int32_t n);
static int32_t lin_sx(int32_t* qp, int32_t n);
static int32_t lin_sxy(int32_t* qp, int32_t* r, int32_t n);
static int32_t lin_nsxx(int32_t* qp, int32_t n);
void VP8InitRc(vp8RateControl_s* rc, uint32_t newStream) {
vp8VirtualBuffer_s* vb = &rc->virtualBuffer;
int32_t maxBps;
if (rc->qpMax >= QINDEX_RANGE)
rc->qpMax = QINDEX_RANGE - 1;
if (rc->qpMin < 0)
rc->qpMin = 0;
/* Limit bitrate settings that are way over head.
* Maximum limit is half of the uncompressed YUV bitrate (12bpp). */
maxBps = rc->mbPerPic * 16 * 16 * 6; /* Max bits per frame */
maxBps = VP8Calculate(maxBps, rc->outRateNum, rc->outRateDenom);
if (maxBps < 0)
maxBps = I32_MAX;
vb->bitRate = MIN(vb->bitRate, maxBps);
vb->bitPerPic = VP8Calculate(vb->bitRate, rc->outRateDenom, rc->outRateNum);
/* QP -1: Initial QP estimation done by RC */
if (rc->qpHdr == -1)
rc->qpHdr = InitialQp(vb->bitPerPic, rc->mbPerPic * 16 * 16);
PicQuantLimit(rc);
VPU_PLG_DBG("InitRc:\n picRc %i\n picSkip %i\n",
rc->picRc, rc->picSkip);
VPU_PLG_DBG(" qpHdr %i\n qpMin,Max %i,%i\n",
rc->qpHdr, rc->qpMin, rc->qpMax);
VPU_PLG_DBG(" BitRate %i\n BitPerPic %i\n",
vb->bitRate, vb->bitPerPic);
/* If changing QP/bitrate between frames don't reset GOP RC */
if (!newStream)
return;
rc->qpHdrPrev = rc->qpHdr;
rc->fixedQp = rc->qpHdr;
rc->frameCoded = ENCHW_YES;
rc->currFrameIntra = 1;
rc->prevFrameIntra = 0;
rc->frameCnt = 0;
rc->gopQpSum = 0;
rc->gopQpDiv = 0;
rc->targetPicSize = 0;
rc->frameBitCnt = 0;
memset(&rc->linReg, 0, sizeof(linReg_s));
rc->linReg.qs[0] = AcQLookup[QINDEX_RANGE - 1];
rc->linReg.qp_prev = rc->qpHdr;
vb->gopRem = rc->gopLen;
vb->timeScale = rc->outRateNum;
update_rc_error(&rc->rError, RC_ERROR_RESET);
}
/*------------------------------------------------------------------------------
InitialQp() Returns sequence initial quantization parameter based on the
configured resolution and bitrate.
------------------------------------------------------------------------------*/
static int32_t InitialQp(int32_t bits, int32_t pels) {
/* Table with resulting average bits/pixel as a function of QP.
* The table is calculated by encoding a set of 4CIF resolution video
* clips with fixed QP. */
const int32_t qp_tbl[2][12] = {
{ 47, 57, 73, 93, 122, 155, 214, 294, 373, 506, 781, 0x7FFFFFFF },
{ 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 } };
const int32_t upscale = 8000;
int32_t i = -1;
/* prevents overflow, QP would anyway be 10 with this high bitrate
for all resolutions under and including 1920x1088 */
if (bits > 1000000)
return 10;
/* Make room for multiplication */
pels >>= 8;
bits >>= 5;
/* Adjust the bits value for the current resolution */
bits *= pels + 250;
ASSERT(pels > 0);
ASSERT(bits > 0);
bits /= 350 + (3 * pels) / 4;
bits = VP8Calculate(bits, upscale, pels << 6);
while (qp_tbl[0][++i] < bits);
VPU_PLG_DBG("BPP %d\n", bits);
return qp_tbl[1][i];
}
/*------------------------------------------------------------------------------
VirtualBuffer() Return difference of target and real buffer fullness.
Virtual buffer and real bit count grow until one second. After one second
output bit rate per second is removed from virtualBitCnt and realBitCnt. Bit
drifting has been taken care.
If the leaky bucket in VBR mode becomes empty (e.g. underflow), those R * T_e
bits are lost and must be decremented from virtualBitCnt. (NOTE: Drift
calculation will mess virtualBitCnt up, so the loss is added to realBitCnt)
------------------------------------------------------------------------------*/
static int32_t VirtualBuffer(vp8VirtualBuffer_s* vb, int32_t timeInc) {
int32_t drift, target;
/* Saturate realBitCnt, this is to prevent overflows caused by much greater
bitrate setting than is really possible to reach */
if (vb->realBitCnt > 0x1FFFFFFF)
vb->realBitCnt = 0x1FFFFFFF;
if (vb->realBitCnt < -0x1FFFFFFF)
vb->realBitCnt = -0x1FFFFFFF;
vb->picTimeInc += timeInc;
vb->virtualBitCnt += VP8Calculate(vb->bitRate, timeInc, vb->timeScale);
target = vb->virtualBitCnt - vb->realBitCnt;
/* Saturate target, prevents rc going totally out of control.
This situation should never happen. */
if (target > 0x1FFFFFFF)
target = 0x1FFFFFFF;
if (target < -0x1FFFFFFF)
target = -0x1FFFFFFF;
/* picTimeInc must be in range of [0, timeScale) */
while (vb->picTimeInc >= vb->timeScale) {
vb->picTimeInc -= vb->timeScale;
vb->virtualBitCnt -= vb->bitRate;
vb->realBitCnt -= vb->bitRate;
}
drift = VP8Calculate(vb->bitRate, vb->picTimeInc, vb->timeScale);
drift -= vb->virtualBitCnt;
vb->virtualBitCnt += drift;
VPU_PLG_DBG("virtualBitCnt: %7i\nrealBitCnt: %7i",
vb->virtualBitCnt, vb->realBitCnt);
VPU_PLG_DBG(" diff bits: %7i\n", target);
return target;
}
/*------------------------------------------------------------------------------
VP8BeforePicRc() Update virtual buffer and calculate picInitQp for current
picture.
------------------------------------------------------------------------------*/
void VP8BeforePicRc(vp8RateControl_s* rc, uint32_t timeInc,
uint32_t frameTypeIntra) {
vp8VirtualBuffer_s* vb = &rc->virtualBuffer;
int32_t brDiff = 0;
rc->frameCoded = ENCHW_YES;
rc->currFrameIntra = frameTypeIntra;
VPU_PLG_DBG("BEFORE PIC RC: pic=%d\n", rc->frameCnt);
VPU_PLG_DBG("Frame type: %7i timeInc: %7i\n", frameTypeIntra, timeInc);
if (rc->currFrameIntra || vb->gopRem == 1) {
vb->gopRem = rc->gopLen;
} else {
vb->gopRem--;
}
/* Use virtual buffer to calculate the difference of target bitrate
* and actual bitrate */
brDiff = VirtualBuffer(&rc->virtualBuffer, (int32_t)timeInc);
/* Calculate target size for this picture */
rc->targetPicSize =
vb->bitPerPic + DIV(brDiff, MAX(rc->virtualBuffer.gopRem, 3));
rc->targetPicSize = MAX(0, rc->targetPicSize);
if (rc->picSkip)
PicSkip(rc);
/* determine initial quantization parameter for current picture */
PicQuant(rc);
/* quantization parameter user defined limitations */
PicQuantLimit(rc);
/* Store the start QP, before any adjustment */
rc->qpHdrPrev = rc->qpHdr;
if (rc->currFrameIntra) {
if (rc->fixedIntraQp)
rc->qpHdr = rc->fixedIntraQp;
else if (!rc->prevFrameIntra)
rc->qpHdr += rc->intraQpDelta;
/* quantization parameter user defined limitations still apply */
PicQuantLimit(rc);
} else {
/* trace the QP over GOP, excluding Intra QP */
rc->gopQpSum += rc->qpHdr;
rc->gopQpDiv++;
}
VPU_PLG_DBG("Frame coded %7d ", rc->frameCoded);
VPU_PLG_DBG("Frame qpHdr %7d ", rc->qpHdr);
VPU_PLG_DBG("GopRem: %7d ", vb->gopRem);
VPU_PLG_DBG("Target bits: %7d \n", rc->targetPicSize);
VPU_PLG_DBG("Rd: %7d\n", avg_rc_error(&rc->rError));
}
/*----------------------------------------------------------------------------
VP8AfterPicRc() Update RC statistics after encoding frame.
-----------------------------------------------------------------------------*/
void VP8AfterPicRc(vp8RateControl_s* rc, uint32_t byteCnt) {
vp8VirtualBuffer_s* vb = &rc->virtualBuffer;
int32_t bitCnt = (int32_t)byteCnt * 8;
rc->frameCnt++;
rc->frameBitCnt = bitCnt;
rc->prevFrameIntra = rc->currFrameIntra;
vb->realBitCnt += bitCnt;
VPU_PLG_DBG("AfterPicRc:\n");
VPU_PLG_DBG("BitCnt %7d\n", bitCnt);
VPU_PLG_DBG("BitErr/avg %6d%% ",
((bitCnt - vb->bitPerPic) * 100) / (vb->bitPerPic + 1));
VPU_PLG_DBG("BitErr/target %6d%%\n",
rc->targetPicSize ?
(((bitCnt - rc->targetPicSize) * 100) / rc->targetPicSize) : -1);
/* Needs number of bits used for residual */
if ((!rc->currFrameIntra) || (rc->gopLen == 1)) {
update_tables(&rc->linReg, rc->qpHdrPrev,
VP8Calculate(bitCnt, 256, rc->mbPerPic));
if (vb->gopRem == rc->gopLen - 1) {
/* First INTER frame of GOP */
update_rc_error(&rc->rError, RC_ERROR_RESET);
VPU_PLG_DBG("P --- I --- D ---\n");
} else {
/* Store the error between target and actual frame size
* Saturate the error to avoid inter frames with
* mostly intra MBs to affect too much */
update_rc_error(&rc->rError,
MIN(bitCnt - rc->targetPicSize, 2 * rc->targetPicSize));
}
update_model(&rc->linReg);
} else {
VPU_PLG_DBG("P xxx I xxx D xxx\n");
}
}
/*----------------------------------------------------------------------------
PicSkip() Decrease framerate if not enough bits available.
-----------------------------------------------------------------------------*/
void PicSkip(vp8RateControl_s* rc) {
vp8VirtualBuffer_s* vb = &rc->virtualBuffer;
int32_t bitAvailable = vb->virtualBitCnt - vb->realBitCnt;
int32_t skipIncLimit = -vb->bitPerPic / 3;
int32_t skipDecLimit = vb->bitPerPic / 3;
/* When frameRc is enabled, skipFrameTarget is not allowed to be > 1
* This makes sure that not too many frames is skipped and lets
* the frameRc adjust QP instead of skipping many frames */
if (((rc->picRc == ENCHW_NO) || (vb->skipFrameTarget == 0)) &&
(bitAvailable < skipIncLimit))
vb->skipFrameTarget++;
if ((bitAvailable > skipDecLimit) && vb->skipFrameTarget > 0)
vb->skipFrameTarget--;
if (vb->skippedFrames < vb->skipFrameTarget) {
vb->skippedFrames++;
rc->frameCoded = ENCHW_NO;
} else {
vb->skippedFrames = 0;
}
}
/*----------------------------------------------------------------------------
PicQuant() Calculate quantization parameter for next frame. In the beginning
of GOP use previous GOP average QP and otherwise find new QP
using the target size and previous frames QPs and bit counts.
-----------------------------------------------------------------------------*/
void PicQuant(vp8RateControl_s* rc) {
int32_t qp = 0;
int32_t avgRcError, bits;
true_e useQpDeltaLimit = ENCHW_YES;
if (rc->picRc != ENCHW_YES) {
rc->qpHdr = rc->fixedQp;
VPU_PLG_DBG("R/cx: xxxx QP: xx xx D: xxxx newQP: xx\n");
return;
}
/* Determine initial quantization parameter for current picture */
if (rc->currFrameIntra) {
/* If all frames or every other frame is intra we calculate new QP
* for intra the same way as for inter */
if (rc->gopLen == 1 || rc->gopLen == 2) {
qp = new_pic_quant(&rc->linReg,
VP8Calculate(rc->targetPicSize, 256, rc->mbPerPic),
useQpDeltaLimit);
} else {
VPU_PLG_DBG("R/cx: xxxx QP: xx xx D: xxxx newQP: xx\n");
qp = gop_avg_qp(rc);
}
if (qp) {
rc->qpHdr = qp;
}
} else if (rc->prevFrameIntra) {
/* Previous frame was intra, use the same QP */
VPU_PLG_DBG("R/cx: xxxx QP: == == D: ==== newQP: ==\n");
rc->qpHdr = rc->qpHdrPrev;
} else {
/* Calculate new QP by matching to previous frames R-Q curve */
avgRcError = avg_rc_error(&rc->rError);
bits = VP8Calculate(rc->targetPicSize - avgRcError, 256, rc->mbPerPic);
rc->qpHdr = new_pic_quant(&rc->linReg, bits, useQpDeltaLimit);
}
}
/*----------------------------------------------------------------------------
PicQuantLimit()
-----------------------------------------------------------------------------*/
void PicQuantLimit(vp8RateControl_s* rc) {
rc->qpHdr = MIN(rc->qpMax, MAX(rc->qpMin, rc->qpHdr));
}
/*------------------------------------------------------------------------------
Calculate() I try to avoid overflow and calculate good enough result of a*b/c
------------------------------------------------------------------------------*/
int32_t VP8Calculate(int32_t a, int32_t b, int32_t c) {
uint32_t left = 32;
uint32_t right = 0;
uint32_t shift;
int32_t sign = 1;
int32_t tmp;
uint32_t utmp;
if (a == 0 || b == 0) {
return 0;
} else if ((a * b / b) == a && c != 0) {
return (a * b / c);
}
if (a < 0) {
sign = -1;
a = -a;
}
if (b < 0) {
sign *= -1;
b = -b;
}
if (c < 0) {
sign *= -1;
c = -c;
}
if (c == 0) {
return 0x7FFFFFFF * sign;
}
if (b > a) {
tmp = b;
b = a;
a = tmp;
}
for (--left; (((uint32_t)a << left) >> left) != (uint32_t)a; --left) ;
left--; /* unsigned values have one more bit on left,
we want signed accuracy. shifting signed values gives
lint warnings */
while (((uint32_t)b >> right) > (uint32_t)c) {
right++;
}
if (right > left) {
return 0x7FFFFFFF * sign;
} else {
shift = left - right;
utmp = (((uint32_t)a << shift) / (uint32_t)c * (uint32_t)b);
utmp = (utmp >> shift) * sign;
return (int32_t)utmp;
}
}
/*------------------------------------------------------------------------------
avg_rc_error() PI(D)-control for rate prediction error.
------------------------------------------------------------------------------*/
static int32_t avg_rc_error(linReg_s* p) {
return DIV(p->bits[2] * 4 + p->bits[1] * 6 + p->bits[0] * 0, 100);
}
/*------------------------------------------------------------------------------
update_rc_error() Update PI(D)-control values
------------------------------------------------------------------------------*/
static void update_rc_error(linReg_s* p, int32_t bits) {
p->len = 3;
if (bits == (int32_t)RC_ERROR_RESET) {
/* RESET */
p->bits[0] = 0;
p->bits[1] = 0;
p->bits[2] = 0;
return;
}
p->bits[0] = bits - p->bits[2]; /* Derivative */
p->bits[1] = bits + p->bits[1]; /* Integral */
p->bits[2] = bits; /* Proportional */
VPU_PLG_DBG("P %7d I %7d D %7d\n", p->bits[2], p->bits[1], p->bits[0]);
}
/*------------------------------------------------------------------------------
gop_avg_qp() Average quantization parameter of P frames of the previous GOP.
------------------------------------------------------------------------------*/
int32_t gop_avg_qp(vp8RateControl_s* rc) {
int32_t avgQp = 0;
if (rc->gopQpSum) {
avgQp = DIV(rc->gopQpSum, rc->gopQpDiv);
}
rc->gopQpSum = 0;
rc->gopQpDiv = 0;
return avgQp;
}
/*------------------------------------------------------------------------------
new_pic_quant() Calculate new quantization parameter from the 2nd degree R-Q
equation. Further adjust Qp for "smoother" visual quality.
------------------------------------------------------------------------------*/
static int32_t new_pic_quant(linReg_s* p, int32_t bits, true_e useQpDeltaLimit) {
int32_t tmp, qp_best = p->qp_prev, qp = p->qp_prev, diff;
int32_t diff_prev = 0, qp_prev = 0, diff_best = 0x7FFFFFFF;
VPU_PLG_DBG("R/cx: %7d ",bits);
if (p->a1 == 0 && p->a2 == 0) {
VPU_PLG_DBG(" QP: xx xx D: ==== newQP: %2d\n", qp);
return qp;
}
/* Target bits is negative => increase QP by maximum allowed */
if (bits <= 0) {
if (useQpDeltaLimit)
qp = MIN(QINDEX_RANGE - 1, MAX(0, qp + QP_DELTA));
else
qp = MIN(QINDEX_RANGE - 1, MAX(0, qp + 10));
VPU_PLG_DBG(" QP: xx xx D: ---- newQP: %2d\n", qp);
return qp;
}
/* Find the qp that has the best match on fitted curve */
do {
tmp = DIV(p->a1, AcQLookup[qp]);
tmp += DIV(p->a2, AcQLookup[qp] * AcQLookup[qp]);
diff = ABS(tmp - bits);
if (diff < diff_best) {
if (diff_best == 0x7FFFFFFF) {
diff_prev = diff;
qp_prev = qp;
} else {
diff_prev = diff_best;
qp_prev = qp_best;
}
diff_best = diff;
qp_best = qp;
if ((tmp - bits) <= 0) {
if (qp < 1) {
break;
}
qp--;
} else {
if (qp >= QINDEX_RANGE - 1) {
break;
}
qp++;
}
} else {
break;
}
} while ((qp >= 0) && (qp < QINDEX_RANGE));
qp = qp_best;
VPU_PLG_DBG(" QP: %2d %2d D: %7d", qp, qp_prev, diff_prev - diff_best);
/* Limit Qp change for smoother visual quality */
if (useQpDeltaLimit) {
tmp = qp - p->qp_prev;
if (tmp > QP_DELTA) {
qp = p->qp_prev + QP_DELTA;
} else if (tmp < -QP_DELTA) {
qp = p->qp_prev - QP_DELTA;
}
}
return qp;
}
/*------------------------------------------------------------------------------
update_tables() only statistics of PSLICE, please.
------------------------------------------------------------------------------*/
static void update_tables(linReg_s* p, int32_t qp, int32_t bits) {
const int32_t clen = 10;
int32_t tmp = p->pos;
p->qp_prev = qp;
p->qs[tmp] = AcQLookup[qp];
p->bits[tmp] = bits;
if (++p->pos >= clen) {
p->pos = 0;
}
if (p->len < clen) {
p->len++;
}
}
/*------------------------------------------------------------------------------
update_model() Update model parameter by Linear Regression.
------------------------------------------------------------------------------*/
static void update_model(linReg_s* p) {
int32_t* qs = p->qs, *r = p->bits, n = p->len;
int32_t i, a1, a2, sx = lin_sx(qs, n), sy = lin_sy(qs, r, n);
for (i = 0; i < n; i++) {
VPU_PLG_DBG("model: qs: %i r: %i\n",qs[i], r[i]);
}
a1 = lin_sxy(qs, r, n);
a1 = a1 < I32_MAX / n ? a1 * n : I32_MAX;
VPU_PLG_DBG("model: sy: %i sx: %i\n", sy, sx);
if (sy == 0) {
a1 = 0;
} else {
a1 -= (sx < I32_MAX / sy) ? sx * sy : I32_MAX;
}
a2 = (lin_nsxx(qs, n) - (sx * sx));
if (a2 == 0) {
if (p->a1 == 0) {
/* If encountered in the beginning */
a1 = 0;
} else {
a1 = (p->a1 * 2) / 3;
}
} else {
a1 = VP8Calculate(a1, DSCY, a2);
}
/* Value of a1 shouldn't be excessive (small) */
a1 = MAX(a1, -4096 * DSCY);
a1 = MIN(a1, 4096 * DSCY - 1);
ASSERT(ABS(a1) * sx >= 0);
ASSERT(sx * DSCY >= 0);
a2 = DIV(sy * DSCY, n) - DIV(a1 * sx, n);
VPU_PLG_DBG("model: a2:%9d a1:%8d\n", a2, a1);
if (p->len > 0) {
p->a1 = a1;
p->a2 = a2;
}
}
/*------------------------------------------------------------------------------
lin_sy() calculate value of Sy for n points.
------------------------------------------------------------------------------*/
static int32_t lin_sy(int32_t* qp, int32_t* r, int32_t n) {
int32_t sum = 0;
while (n--) {
sum += qp[n] * qp[n] * r[n];
if (sum < 0) {
return I32_MAX / DSCY;
}
}
return DIV(sum, DSCY);
}
/*------------------------------------------------------------------------------
lin_sx() calculate value of Sx for n points.
------------------------------------------------------------------------------*/
static int32_t lin_sx(int32_t* qp, int32_t n) {
int32_t tmp = 0;
while (n--) {
ASSERT(qp[n]);
tmp += qp[n];
}
return tmp;
}
/*------------------------------------------------------------------------------
lin_sxy() calculate value of Sxy for n points.
------------------------------------------------------------------------------*/
static int32_t lin_sxy(int32_t* qp, int32_t* r, int32_t n) {
int32_t tmp, sum = 0;
while (n--) {
tmp = qp[n] * qp[n] * qp[n];
if (tmp > r[n]) {
sum += DIV(tmp, DSCY) * r[n];
} else {
sum += tmp * DIV(r[n], DSCY);
}
if (sum < 0) {
return I32_MAX;
}
}
return sum;
}
/*------------------------------------------------------------------------------
lin_nsxx() calculate value of n * Sxy for n points.
------------------------------------------------------------------------------*/
static int32_t lin_nsxx(int32_t* qp, int32_t n) {
int32_t tmp = 0, sum = 0, d = n;
while (n--) {
tmp = qp[n];
tmp *= tmp;
sum += d * tmp;
}
return sum;
}