add interface and basic implementaion and UT for EncoderStatistics

This commit is contained in:
Sijia Chen 2014-10-22 11:35:17 +08:00
parent d720122a37
commit a765197b73
8 changed files with 237 additions and 2 deletions

View File

@ -103,6 +103,9 @@ typedef enum {
ENCODER_OPTION_TRACE_CALLBACK, // a void (*)(void* context, int level, const char* message) function which receives log messages
ENCODER_OPTION_TRACE_CALLBACK_CONTEXT,
ENCODER_OPTION_GET_STATISTICS, //read only
ENCODER_OPTION_STATISTICS_LOG_INTERVAL, // log interval in milliseconds
// advanced algorithmetic settings
ENCODER_OPTION_IS_LOSSLESS_LINK
} ENCODER_OPTION;
@ -120,7 +123,9 @@ typedef enum {
DECODER_OPTION_ERROR_CON_IDC, //not finished yet, indicate decoder error concealment status, in progress
DECODER_OPTION_TRACE_LEVEL,
DECODER_OPTION_TRACE_CALLBACK, // a void (*)(void* context, int level, const char* message) function which receives log messages
DECODER_OPTION_TRACE_CALLBACK_CONTEXT
DECODER_OPTION_TRACE_CALLBACK_CONTEXT,
DECODER_OPTION_GET_STATISTICS
} DECODER_OPTION;
@ -455,4 +460,38 @@ typedef struct TagParserBsInfo {
int iSpsHeightInPixel; //required SPS height info
} SParserBsInfo, PParserBsInfo;
typedef struct TagVideoEncoderStatistics {
unsigned int uWidth; // the width of encoded frame
unsigned int uHeight; // the height of encoded frame
//following standard, will be 16x aligned, if there are multiple spatial, this is of the highest
float fAverageFrameSpeedInMs; // Average_Encoding_Time
// rate control related
float fAverageFrameRate; // the average frame rate in, calculate since encoding starts, supposed that the input timestamp is in unit of ms
float fLatestFrameRate; // the frame rate in, in the last second, supposed that the input timestamp is in unit of ms (? useful for checking BR, but is it easy to calculate?
unsigned int uiBitRate; // sendrate in Bits per second, calculated within the set time-window
unsigned int uiInputFrameCount; // number of frames
unsigned int uiSkippedFrameCount; // number of frames
unsigned int uiResolutionChangeTimes; // uiResolutionChangeTimes
unsigned int uIDRReqNum; // number of IDR requests
unsigned int uIDRSentNum; // number of actual IDRs sent
unsigned int uLTRSentNum; // number of LTR sent/marked
} SEncoderStatistics; // in building, coming soon
typedef struct TagVideoDecoderStatistics {
unsigned int uWidth; // the width of encode/decode frame
unsigned int uHeight; // the height of encode/decode frame
float fAverageFrameSpeedInMs; // Average_Decoding_Time
unsigned int uiDecodedFrameCount; // number of frames
unsigned int uiResolutionChangeTimes; // uiResolutionChangeTimes
unsigned int
uiAvgEcRatio; // when EC is on, the average ratio of correct or EC areas, can be an indicator of reconstruction quality
unsigned int uIDRReqNum; // number of actual IDR request
unsigned int uLTRReqNum; // number of actual LTR request
unsigned int uIDRRecvNum; // number of actual IDR received
} SDecoderStatistics; // in building, coming soon
#endif//WELS_VIDEO_CODEC_APPLICATION_DEFINITION_H__

View File

@ -216,6 +216,12 @@ typedef struct TagWelsEncCtx {
SStatSliceInfo sPerInfo;
#endif//STAT_OUTPUT
//related to Statistics
int64_t uiStartTimestamp;
SEncoderStatistics sEncoderStatistics;
int32_t iStatisticsLogInterval;
int64_t iLastStatisticsLogTs;
int32_t iEncoderError;
WELS_MUTEX mutexEncoderError;
bool bDeliveryFlag;

View File

@ -224,6 +224,10 @@ int64_t iAvgCost2Bits;
int64_t iCost2BitsIntra;
int32_t iBaseQp;
long long uiLastTimeStamp;
//for statistics and online adjustments
int32_t iActualBitRate; // TODO: to complete later
float fLatestFrameRate; // TODO: to complete later
} SWelsSvcRc;
typedef void (*PWelsRCPictureInitFunc) (void* pCtx);

View File

@ -55,6 +55,8 @@
#define MAX_TRACE_LOG_SIZE (50 * (1<<20)) // max trace log size: 50 MB, overwrite occur if log file size exceeds this size
#endif//MAX_TRACE_LOG_SIZE
#define STATISTICS_LOG_INTERVAL_MS (5000) // output statistics log every 5s
/* MB width in pixels for specified colorspace I420 usually used in codec */
#define MB_WIDTH_LUMA 16
#define MB_WIDTH_CHROMA (MB_WIDTH_LUMA>>1)

View File

@ -2085,6 +2085,8 @@ int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pCodingPar
);
#endif//MEMORY_MONITOR
pCtx->iStatisticsLogInterval = STATISTICS_LOG_INTERVAL_MS;
*ppCtx = pCtx;
WelsLog (pLogCtx, WELS_LOG_DEBUG, "WelsInitEncoderExt(), pCtx= 0x%p.", (void*)pCtx);
@ -3736,6 +3738,8 @@ int32_t WelsEncoderParamAdjust (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pNewPa
(PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage
uiTmpIdrPicId = (*ppCtx)->sPSOVector.uiIdrPicId;
SEncoderStatistics sTempEncoderStatistics = (*ppCtx)->sEncoderStatistics;
WelsUninitEncoderExt (ppCtx);
/* Update new parameters */
@ -3746,10 +3750,13 @@ int32_t WelsEncoderParamAdjust (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pNewPa
(*ppCtx)->pVpp->WelsPreprocessReset (*ppCtx);
//if WelsInitEncoderExt succeed
//load back the needed structure
//for FLEXIBLE_PARASET_ID
memcpy ((*ppCtx)->sPSOVector.sParaSetOffsetVariable, sTmpPsoVariable,
(PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage
(*ppCtx)->sPSOVector.uiIdrPicId = uiTmpIdrPicId;
//for sEncoderStatistics
(*ppCtx)->sEncoderStatistics = sTempEncoderStatistics;
} else {
/* maybe adjustment introduced in bitrate or little settings adjustment and so on.. */
pNewParam->iNumRefFrame = WELS_CLIP3 (pNewParam->iNumRefFrame, MIN_REF_PIC_COUNT,

View File

@ -102,6 +102,8 @@ class CWelsH264SVCEncoder : public ISVCEncoder {
void CheckLevelSetting (int32_t iLayer, ELevelIdc uiLevelIdc);
void CheckReferenceNumSetting (int32_t iNumRef);
void TraceParamInfo(SEncParamExt *pParam);
void UpdateStatistics(const int64_t kiCurrentFrameTs, EVideoFrameType eFrameType, const int64_t kiCurrentFrameMs);
sWelsEncCtx* m_pEncContext;
welsCodecTrace* m_pWelsTrace;

View File

@ -42,6 +42,7 @@
#include "ref_list_mgr_svc.h"
#include <time.h>
#include <measure_time.h>
#if defined(_WIN32) /*&& defined(_DEBUG)*/
#include <windows.h>
@ -417,7 +418,9 @@ int CWelsH264SVCEncoder::EncodeFrame (const SSourcePicture* kpSrcPic, SFrameBSIn
int CWelsH264SVCEncoder ::EncodeFrameInternal (const SSourcePicture* pSrcPic, SFrameBSInfo* pBsInfo) {
const int64_t kiBeforeFrameUs = WelsTime();
const int32_t kiEncoderReturn = WelsEncoderEncodeExt (m_pEncContext, pBsInfo, pSrcPic);
const int64_t kiCurrentFrameMs = (WelsTime() - kiBeforeFrameUs) / 1000;
if (kiEncoderReturn == ENC_RETURN_MEMALLOCERR) {
WelsUninitEncoderExt (&m_pEncContext);
@ -427,6 +430,9 @@ int CWelsH264SVCEncoder::EncodeFrameInternal (const SSourcePicture* pSrcPic, SF
kiEncoderReturn);
return cmUnkonwReason;
}
UpdateStatistics (pSrcPic->uiTimeStamp, pBsInfo->eFrameType, kiCurrentFrameMs);
///////////////////for test
#ifdef OUTPUT_BIT_STREAM
if (pBsInfo->eFrameType != videoFrameTypeInvalid && pBsInfo->eFrameType != videoFrameTypeSkip) {
@ -586,6 +592,61 @@ void CWelsH264SVCEncoder::TraceParamInfo (SEncParamExt* pParam) {
++ i;
}
}
void CWelsH264SVCEncoder::UpdateStatistics (const int64_t kiCurrentFrameTs, EVideoFrameType eFrameType,
const int64_t kiCurrentFrameMs) {
SEncoderStatistics* pStatistics = & (m_pEncContext->sEncoderStatistics);
int32_t iMaxDid = m_pEncContext->pSvcParam->iSpatialLayerNum - 1;
if ((0 != pStatistics->uWidth && 0 != pStatistics->uHeight)
&& (pStatistics->uWidth != m_pEncContext->pSvcParam->sDependencyLayers[iMaxDid].iActualWidth
|| pStatistics->uHeight != m_pEncContext->pSvcParam->sDependencyLayers[iMaxDid].iActualHeight)) {
pStatistics->uiResolutionChangeTimes ++;
}
pStatistics->uWidth = m_pEncContext->pSvcParam->sDependencyLayers[iMaxDid].iActualWidth;
pStatistics->uHeight = m_pEncContext->pSvcParam->sDependencyLayers[iMaxDid].iActualHeight;
int32_t iProcessedFrameCount = pStatistics->uiInputFrameCount - pStatistics->uiSkippedFrameCount;
const bool kbCurrentFrameSkipped = (videoFrameTypeSkip == eFrameType);
if (!kbCurrentFrameSkipped && (iProcessedFrameCount + 1) != 0) {
pStatistics->fAverageFrameSpeedInMs = (iProcessedFrameCount * pStatistics->fAverageFrameSpeedInMs +
kiCurrentFrameMs) / (iProcessedFrameCount + 1);
}
pStatistics->uiInputFrameCount ++;
pStatistics->uiSkippedFrameCount += (kbCurrentFrameSkipped ? 1 : 0);
// rate control related
if (0 != m_pEncContext->uiStartTimestamp && kiCurrentFrameTs > m_pEncContext->uiStartTimestamp + 800) {
pStatistics->fAverageFrameRate = pStatistics->uiInputFrameCount * 1000 /
(kiCurrentFrameTs - m_pEncContext->uiStartTimestamp);
} else {
m_pEncContext->uiStartTimestamp = kiCurrentFrameTs;
}
pStatistics->fLatestFrameRate = m_pEncContext->pWelsSvcRc->fLatestFrameRate; //TODO: finish the calculation in RC
pStatistics->uiBitRate = m_pEncContext->pWelsSvcRc->iActualBitRate; //TODO: finish the calculation in RC
if (videoFrameTypeIDR == eFrameType || videoFrameTypeI == eFrameType) {
pStatistics->uIDRSentNum ++;
}
if (m_pEncContext->pLtr->bLTRMarkingFlag) {
pStatistics->uLTRSentNum ++;
}
//TODO: update uIDRSentNum in forceIDR
if (m_pEncContext->iStatisticsLogInterval > 0) {
if (WELS_ABS (kiCurrentFrameTs - m_pEncContext->iLastStatisticsLogTs) > m_pEncContext->iStatisticsLogInterval) {
WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
"EncoderStatistics: %dx%d, SpeedInMs: %f, AverFrameRate=%f, LastFrameRate=%f, LatestBitRate=%d, uiInputFrameCount=%d, uiSkippedFrameCount=%d, uiResolutionChangeTimes=%d, uIDRReqNum=%d, uIDRSentNum=%d, uLTRSentNum=%d",
pStatistics->uWidth, pStatistics->uHeight, pStatistics->fAverageFrameSpeedInMs,
pStatistics->fAverageFrameRate, pStatistics->fLatestFrameRate, pStatistics->uiBitRate,
pStatistics->uiInputFrameCount, pStatistics->uiSkippedFrameCount,
pStatistics->uiResolutionChangeTimes, pStatistics->uIDRReqNum, pStatistics->uIDRSentNum, pStatistics->uLTRSentNum);
m_pEncContext->iLastStatisticsLogTs = kiCurrentFrameTs;
}
}
}
/************************************************************************
* InDataFormat, IDRInterval, SVC Encode Param, Frame Rate, Bitrate,..
************************************************************************/
@ -919,6 +980,16 @@ int CWelsH264SVCEncoder::SetOption (ENCODER_OPTION eOptionId, void* pOption) {
m_pEncContext->pSvcParam->iComplexityMode = (ECOMPLEXITY_MODE)iValue;
}
break;
case ENCODER_OPTION_GET_STATISTICS: {
WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING,
"CWelsH264SVCEncoder::SetOption():ENCODER_OPTION_GET_STATISTICS: this option is get-only!");
}
break;
case ENCODER_OPTION_STATISTICS_LOG_INTERVAL: {
int32_t iValue = * (static_cast<int32_t*> (pOption));
m_pEncContext->iStatisticsLogInterval = iValue;
}
break;
case ENCODER_OPTION_IS_LOSSLESS_LINK: {
bool bValue = * (static_cast<bool*> (pOption));
m_pEncContext->pSvcParam->bIsLosslessLink = bValue;
@ -1026,6 +1097,30 @@ int CWelsH264SVCEncoder::GetOption (ENCODER_OPTION eOptionId, void* pOption) {
}
}
break;
case ENCODER_OPTION_GET_STATISTICS: {
SEncoderStatistics* pStatistics = (static_cast<SEncoderStatistics*> (pOption));
pStatistics->uWidth = m_pEncContext->sEncoderStatistics.uWidth;
pStatistics->uHeight = m_pEncContext->sEncoderStatistics.uHeight;
pStatistics->fAverageFrameSpeedInMs = m_pEncContext->sEncoderStatistics.fAverageFrameSpeedInMs;
// rate control related
pStatistics->fAverageFrameRate = m_pEncContext->sEncoderStatistics.fAverageFrameRate;
pStatistics->fLatestFrameRate = m_pEncContext->sEncoderStatistics.fLatestFrameRate;
pStatistics->uiBitRate = m_pEncContext->sEncoderStatistics.uiBitRate;
pStatistics->uiInputFrameCount = m_pEncContext->sEncoderStatistics.uiInputFrameCount;
pStatistics->uiSkippedFrameCount = m_pEncContext->sEncoderStatistics.uiSkippedFrameCount;
pStatistics->uiResolutionChangeTimes = m_pEncContext->sEncoderStatistics.uiResolutionChangeTimes;
pStatistics->uIDRReqNum = m_pEncContext->sEncoderStatistics.uIDRReqNum;
pStatistics->uIDRSentNum = m_pEncContext->sEncoderStatistics.uIDRSentNum;
pStatistics->uLTRSentNum = m_pEncContext->sEncoderStatistics.uLTRSentNum;
}
break;
case ENCODER_OPTION_STATISTICS_LOG_INTERVAL: {
* ((int32_t*)pOption) = m_pEncContext->iStatisticsLogInterval;
}
break;
case ENCODER_OPTION_COMPLEXITY: {
* ((int32_t*)pOption) = m_pEncContext->pSvcParam->iComplexityMode;
}

View File

@ -661,3 +661,83 @@ TEST_F (EncoderInterfaceTest, EncodeParameterSets) {
TEST_F (EncoderInterfaceTest, BasicReturnTypeTest) {
//TODO
}
TEST_F (EncoderInterfaceTest, GetStatistics) {
SEncParamBase sEncParamBase;
GetValidEncParamBase (&sEncParamBase);
int iResult = pPtrEnc->Initialize (&sEncParamBase);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
if (iResult != cmResultSuccess) {
fprintf (stderr, "Unexpected ParamBase? \
iUsageType=%d, Pic=%dx%d, TargetBitrate=%d, iRCMode=%d, fMaxFrameRate=%.1f\n",
sEncParamBase.iUsageType, sEncParamBase.iPicWidth, sEncParamBase.iPicHeight,
sEncParamBase.iTargetBitrate, sEncParamBase.iRCMode, sEncParamBase.fMaxFrameRate);
}
PrepareOneSrcFrame();
EncodeOneIDRandP (pPtrEnc);
SEncoderStatistics sEncoderStatistics;
iResult = pPtrEnc->GetOption (ENCODER_OPTION_GET_STATISTICS, &sEncoderStatistics);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
EXPECT_EQ (sEncoderStatistics.uiInputFrameCount, 2);
EXPECT_EQ (sEncoderStatistics.uIDRSentNum, 1);
EXPECT_EQ (sEncoderStatistics.uiResolutionChangeTimes, 0);
EXPECT_EQ (sEncoderStatistics.uWidth, sEncParamBase.iPicWidth);
EXPECT_EQ (sEncoderStatistics.uHeight, sEncParamBase.iPicHeight);
// try param change
// 1, get the existing
pPtrEnc->GetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, pParamExt);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
// 2, change the reoslution
GetValidEncParamBase (&sEncParamBase);
int32_t knownResolutionChangeTimes = 0;
if (pParamExt->iPicWidth != sEncParamBase.iPicWidth || pParamExt->iPicHeight != sEncParamBase.iPicHeight) {
knownResolutionChangeTimes = 1;
}
pParamExt->iPicWidth = pParamExt->sSpatialLayers[0].iVideoWidth = sEncParamBase.iPicWidth;
pParamExt->iPicHeight = pParamExt->sSpatialLayers[0].iVideoHeight = sEncParamBase.iPicHeight;
pPtrEnc->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, pParamExt);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
// 3, code one frame
PrepareOneSrcFrame();
iResult = pPtrEnc->EncodeFrame (pSrcPic, &sFbi);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
iResult = pPtrEnc->GetOption (ENCODER_OPTION_GET_STATISTICS, &sEncoderStatistics);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
EXPECT_EQ (sEncoderStatistics.uiInputFrameCount, 3);
EXPECT_EQ (sEncoderStatistics.uIDRSentNum, 2);
EXPECT_EQ (sEncoderStatistics.uiResolutionChangeTimes, knownResolutionChangeTimes);
EXPECT_EQ (sEncoderStatistics.uWidth, sEncParamBase.iPicWidth);
EXPECT_EQ (sEncoderStatistics.uHeight, sEncParamBase.iPicHeight);
// 4, change log interval
int32_t iInterval = 0;
iResult = pPtrEnc->GetOption (ENCODER_OPTION_STATISTICS_LOG_INTERVAL, &iInterval);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
EXPECT_EQ (iInterval, 5000);
int32_t iInterval2 = 2000;
iResult = pPtrEnc->SetOption (ENCODER_OPTION_STATISTICS_LOG_INTERVAL, &iInterval2);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
iResult = pPtrEnc->GetOption (ENCODER_OPTION_STATISTICS_LOG_INTERVAL, &iInterval);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
EXPECT_EQ (iInterval, iInterval2);
iInterval2 = 0;
iResult = pPtrEnc->SetOption (ENCODER_OPTION_STATISTICS_LOG_INTERVAL, &iInterval2);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
iResult = pPtrEnc->GetOption (ENCODER_OPTION_STATISTICS_LOG_INTERVAL, &iInterval);
EXPECT_EQ (iResult, static_cast<int> (cmResultSuccess));
EXPECT_EQ (iInterval, iInterval2);
// finish
pPtrEnc->Uninitialize();
}