diff --git a/source/Lib/CommonLib/Picture.cpp b/source/Lib/CommonLib/Picture.cpp index b93029f5e..3d2679118 100644 --- a/source/Lib/CommonLib/Picture.cpp +++ b/source/Lib/CommonLib/Picture.cpp @@ -172,6 +172,8 @@ Picture::Picture() , sliceDataNumBins ( 0 ) , cts ( 0 ) , ctsValid ( false ) + , picsInMissing ( 0 ) + , picOutOffset ( 0 ) , isPreAnalysis ( false ) , m_picShared ( nullptr ) , gopAdaptedQP ( 0 ) @@ -246,6 +248,11 @@ void Picture::reset() picApsGlobal = nullptr; refApsGlobal = nullptr; + cts = 0; + ctsValid = false; + picsInMissing = 0; + picOutOffset = 0; + picVA.reset(); std::fill_n( m_sharedBufs, (int)NUM_PIC_TYPES, nullptr ); diff --git a/source/Lib/CommonLib/Picture.h b/source/Lib/CommonLib/Picture.h index 088ffdf6b..8cd2a9d32 100644 --- a/source/Lib/CommonLib/Picture.h +++ b/source/Lib/CommonLib/Picture.h @@ -242,6 +242,8 @@ struct Picture : public UnitArea int sliceDataNumBins; uint64_t cts; bool ctsValid; + int64_t picsInMissing; // summed up missing frames on input + int64_t picOutOffset; // signalization of pic offset to re-calc dts bool isPreAnalysis; PicShared* m_picShared; diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index 730cd2999..3ee5c5e96 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -134,6 +134,9 @@ EncGOP::EncGOP( MsgLog& logger ) , m_disableLMCSIP ( false ) , m_lastCodingNum ( -1 ) , m_numPicsCoded ( 0 ) + , m_numPicsInMissing ( 0 ) + , m_numPicsOutOffset ( 0 ) + , m_lastCts ( 0 ) , m_pocRecOut ( 0 ) , m_ticksPerFrameMul4 ( 0 ) , m_lastIDR ( 0 ) @@ -244,6 +247,27 @@ void EncGOP::initPicture( Picture* pic ) pic->encTime.startTimer(); pic->TLayer = pic->gopEntry->m_temporalId; + if( pic->ctsValid ) + { + if( m_lastCts ) + { + int64_t ticksPerFrame = m_ticksPerFrameMul4/4; + int64_t expectedCtsDiff = (m_pcEncCfg->m_TicksPerSecond > 0 ) ? ticksPerFrame : 1; + int64_t ctsDiff = pic->cts - m_lastCts; + + if( ctsDiff >= (expectedCtsDiff<<1) || ctsDiff < 0 ) + { + // signalize that frames are missing at that particular picture + pic->picOutOffset = (m_pcEncCfg->m_TicksPerSecond > 0 ) ? (ctsDiff - ticksPerFrame)/ticksPerFrame : ctsDiff-1; + m_numPicsInMissing += pic->picOutOffset; + } + } + m_lastCts = pic->cts; + } + if( m_numPicsInMissing ) + { + pic->picsInMissing = m_numPicsInMissing; + } pic->setSccFlags( m_pcEncCfg ); @@ -2438,13 +2462,17 @@ void EncGOP::xWritePicture( Picture& pic, AccessUnitList& au, bool isEncodeLtRef if( pic.ctsValid ) { - const int64_t iDiffFrames = m_numPicsCoded - pic.poc; + const int64_t iDiffFrames = m_numPicsCoded - pic.poc - pic.picsInMissing; au.cts = pic.cts; au.ctsValid = pic.ctsValid; + if ( pic.picOutOffset ) + { + m_numPicsOutOffset += pic.picOutOffset; + } if( m_pcEncCfg->m_TicksPerSecond > 0 ) - au.dts = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer ) * m_ticksPerFrameMul4 ) / 4 + au.cts; + au.dts = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer + m_numPicsOutOffset ) * m_ticksPerFrameMul4 ) / 4 + au.cts; else - au.dts = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer )) + au.cts; + au.dts = ( ( iDiffFrames - m_pcEncCfg->m_maxTLayer + m_numPicsOutOffset )) + au.cts; au.dtsValid = pic.ctsValid; } diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h index 1f44fd02e..a9ec65ea0 100644 --- a/source/Lib/EncoderLib/EncGOP.h +++ b/source/Lib/EncoderLib/EncGOP.h @@ -149,6 +149,9 @@ class EncGOP : public EncStage bool m_disableLMCSIP; int m_lastCodingNum; int m_numPicsCoded; + int m_numPicsInMissing; + int m_numPicsOutOffset; + uint64_t m_lastCts; int m_pocRecOut; int m_ticksPerFrameMul4; int m_lastIDR; diff --git a/test/vvenclibtest/vvenclibtest.cpp b/test/vvenclibtest/vvenclibtest.cpp index e725a871f..c62650d2a 100644 --- a/test/vvenclibtest/vvenclibtest.cpp +++ b/test/vvenclibtest/vvenclibtest.cpp @@ -895,7 +895,7 @@ int checkSDKStringApiInvalid() return ret; } -static int runEncoder( vvenc_config& c, uint64_t framesToEncode ) +static int runEncoder( vvenc_config& c, uint64_t framesToEncode, bool emulateMissingFrames = false ) { uint64_t ctsDiff = (c.m_TicksPerSecond > 0) ? (uint64_t)c.m_TicksPerSecond * (uint64_t)c.m_FrameScale / (uint64_t)c.m_FrameRate : 1; // expected cts diff between frames uint64_t ctsOffset = (c.m_TicksPerSecond > 0) ? (uint64_t)c.m_TicksPerSecond : (uint64_t)c.m_FrameRate/(uint64_t)c.m_FrameScale; // start with offset 1sec, to generate cts/dts > 0 @@ -922,6 +922,8 @@ static int runEncoder( vvenc_config& c, uint64_t framesToEncode ) bool eof = false; bool encodeDone = false; uint64_t framesRcvd = 0; + uint64_t numMissingFrames = emulateMissingFrames ? 10 : 0; + while( !eof || !encodeDone ) { vvencYUVBuffer* inputPtr = nullptr; @@ -931,6 +933,11 @@ static int runEncoder( vvenc_config& c, uint64_t framesToEncode ) yuvPicture->cts = (c.m_TicksPerSecond > 0) ? (ctsOffset + (framesRcvd * (uint64_t)c.m_TicksPerSecond * (uint64_t)c.m_FrameScale / (uint64_t)c.m_FrameRate)) : (ctsOffset + framesRcvd); yuvPicture->ctsValid = true; framesRcvd++; + + if( emulateMissingFrames && framesRcvd == framesToEncode>>1 ) + { + framesRcvd+=numMissingFrames; // emulate missing pictures + } } if( 0 != vvenc_encode( enc, inputPtr, AU, &encodeDone )) @@ -950,10 +957,11 @@ static int runEncoder( vvenc_config& c, uint64_t framesToEncode ) { if ( AU->dts <= lastDts ){ //std::cout << " AU dts " << AU->dts << " <= " << lastDts << " - dts must always increase" << std::endl; - }else{ + goto fail; + }else if (!emulateMissingFrames){ //std::cout << " AU dts " << AU->dts << " but expecting " << lastDts + ctsDiff << " lastDts " << lastDts << std::endl; + goto fail; } - goto fail; } lastDts = AU->dts; } @@ -964,7 +972,7 @@ static int runEncoder( vvenc_config& c, uint64_t framesToEncode ) goto fail; } - if( framesRcvd >= framesToEncode ) + if( framesRcvd >= (framesToEncode+numMissingFrames) ) { eof = true; } @@ -1089,6 +1097,31 @@ int checkTimestampsInvalid() return 0; } +int checkDtsDefault() +{ + std::vector tickspersecVec; + tickspersecVec.push_back(90000); + tickspersecVec.push_back(-1); + + for( auto & tickspersec : tickspersecVec ) + { + vvenc_config c; + vvenc_init_default( &c, 176,144, 60, VVENC_RC_OFF, 55, vvencPresetMode::VVENC_FASTER ); + c.m_internChromaFormat = VVENC_CHROMA_420; + + c.m_FrameRate = 50; + c.m_FrameScale = 1; + c.m_TicksPerSecond = tickspersec; + uint64_t frames= c.m_FrameRate/c.m_FrameScale * 2; + + if( 0 != runEncoder(c,frames, true) ) + { + return -1; + } + } + return 0; +} + int testLibCallingOrder() { testfunc( "callingOrderInvalidUninit", &callingOrderInvalidUninit, true ); @@ -1123,6 +1156,7 @@ int testTimestamps() { testfunc( "checkTimestampsDefault", &checkTimestampsDefault, false ); testfunc( "checkTimestampsDefaultInvalid", &checkTimestampsInvalid, true ); + testfunc( "checkDtsDefault", &checkDtsDefault, false ); return 0; }