Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #479 (fix dts calculation when frames are missing on input) #491

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions source/Lib/CommonLib/Picture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ Picture::Picture()
, sliceDataNumBins ( 0 )
, cts ( 0 )
, ctsValid ( false )
, picsInMissing ( 0 )
, picOutOffset ( 0 )
, isPreAnalysis ( false )
, m_picShared ( nullptr )
, gopAdaptedQP ( 0 )
Expand Down Expand Up @@ -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 );
Expand Down
2 changes: 2 additions & 0 deletions source/Lib/CommonLib/Picture.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
34 changes: 31 additions & 3 deletions source/Lib/EncoderLib/EncGOP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions source/Lib/EncoderLib/EncGOP.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 38 additions & 4 deletions test/vvenclibtest/vvenclibtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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 ))
Expand All @@ -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;
}
Expand All @@ -964,7 +972,7 @@ static int runEncoder( vvenc_config& c, uint64_t framesToEncode )
goto fail;
}

if( framesRcvd >= framesToEncode )
if( framesRcvd >= (framesToEncode+numMissingFrames) )
{
eof = true;
}
Expand Down Expand Up @@ -1089,6 +1097,31 @@ int checkTimestampsInvalid()
return 0;
}

int checkDtsDefault()
{
std::vector <int> 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 );
Expand Down Expand Up @@ -1123,6 +1156,7 @@ int testTimestamps()
{
testfunc( "checkTimestampsDefault", &checkTimestampsDefault, false );
testfunc( "checkTimestampsDefaultInvalid", &checkTimestampsInvalid, true );
testfunc( "checkDtsDefault", &checkDtsDefault, false );

return 0;
}
Expand Down