blob: 5f3e1068a383f7ea76158fab156fb2df78caf919 [file] [edit]
#include <gtest/gtest.h>
#include <cstring>
#include "decoder_context.h"
#include "manage_dec_ref.h"
using namespace WelsDec;
// Regression test for the MMCO5 stale sTmpRefPic snapshot.
//
// In the threaded predecessor-handoff path (decoder_core.cpp), the decoder
// snapshots pCtx->sRefPic into pCtx->sTmpRefPic, calls WelsMarkAsRef() with a
// non-NULL pLastDec (which makes WelsMarkAsRef() operate on sTmpRefPic rather
// than sRefPic), and later publishes the (possibly mutated) sTmpRefPic back
// into the successor context's sRefPic. Before the fix, MMCO_RESET (MMCO5)
// called WelsResetRefPic(pCtx), which hard-codes &pCtx->sRefPic and ignores
// the active list the caller selected, so sTmpRefPic kept a pointer to a
// picture that had just been unreferenced and made recyclable.
//
// This test drives WelsMarkAsRef() directly (bypassing the full decode
// pipeline) with a synthetic MMCO5 marking and a pre-populated stale
// short-term reference shared between sRefPic and sTmpRefPic (mirroring the
// real "sTmpRefPic = sRefPic" snapshot), then asserts the stale entry does
// not survive in sTmpRefPic. Unlike an end-to-end bitstream decode, this is
// deterministic and isolated from unrelated threaded-decode defects that can
// otherwise also crash a full decode of a real stream under threading.
class ManageDecRefMmco5Test : public ::testing::Test {
protected:
virtual void SetUp() {
memset (&ctx_, 0, sizeof (ctx_));
memset (&dqLayer_, 0, sizeof (dqLayer_));
memset (&refMarking_, 0, sizeof (refMarking_));
memset (&sps_, 0, sizeof (sps_));
memset (&pps_, 0, sizeof (pps_));
memset (&lastDecPicInfo_, 0, sizeof (lastDecPicInfo_));
memset (&nalUnit_, 0, sizeof (nalUnit_));
memset (&stalePic_, 0, sizeof (stalePic_));
memset (&newPic_, 0, sizeof (newPic_));
// A non-IDR slice signalling adaptive reference picture marking with a
// single MMCO_RESET (MMCO5) command.
refMarking_.bAdaptiveRefPicMarkingModeFlag = true;
refMarking_.sMmcoRef[0].uiMmcoType = MMCO_RESET;
refMarking_.sMmcoRef[1].uiMmcoType = MMCO_END;
sps_.iSpsId = 0;
sps_.iNumRefFrames = 4;
sps_.uiLog2MaxFrameNum = 4;
pps_.iPpsId = 0;
dqLayer_.pRefPicMarking = &refMarking_;
dqLayer_.sLayerInfo.pSps = &sps_; // read directly by MMCO()
nalUnit_.sNalHeaderExt.sNalUnitHeader.eNalUnitType = NAL_UNIT_CODED_SLICE; // non-IDR
nalUnit_.sNalHeaderExt.bIdrFlag = false;
nalUnitPtr_ = &nalUnit_;
accessUnit_.pNalUnitsList = &nalUnitPtr_;
accessUnit_.uiStartPos = 0;
accessUnit_.uiEndPos = 0;
ctx_.pCurDqLayer = &dqLayer_;
ctx_.pSps = &sps_;
ctx_.pPps = &pps_;
ctx_.pAccessUnitList = &accessUnit_;
ctx_.pLastDecPicInfo = &lastDecPicInfo_;
// Stale short-term reference picture shared by sRefPic and sTmpRefPic,
// mirroring the real threaded snapshot (sTmpRefPic = sRefPic is a plain
// struct copy of the same picture pointers, done once in decoder_core.cpp
// just before WelsMarkAsRef() is called).
stalePic_.iRefCount = 0; // eligible for SetUnRef() to clear it
stalePic_.eSliceType = I_SLICE; // makes SetUnRef() return before it
// touches pRefPic[list][], which this
// test does not populate
stalePic_.iFrameNum = 999; // must differ from newPic_.iFrameNum so
// AddShortTermToList() does not treat
// this as a duplicate-frame_num replace
ctx_.sRefPic.pShortRefList[LIST_0][0] = &stalePic_;
ctx_.sRefPic.uiShortRefCount[LIST_0] = 1;
ctx_.sTmpRefPic = ctx_.sRefPic; // the real snapshot idiom
newPic_.iFrameNum = 0;
}
SWelsDecoderContext ctx_;
SDqLayer dqLayer_;
SRefPicMarking refMarking_;
SSps sps_;
SPps pps_;
SWelsLastDecPicInfo lastDecPicInfo_;
SNalUnit nalUnit_;
PNalUnit nalUnitPtr_;
SAccessUnit accessUnit_;
SPicture stalePic_;
SPicture newPic_;
};
TEST_F (ManageDecRefMmco5Test, Mmco5ResetInvalidatesThreadedSnapshot) {
ASSERT_EQ (0, WelsMarkAsRef (&ctx_, &newPic_));
// With the bug, the pre-reset stale pointer survives in sTmpRefPic
// (shifted, not cleared) alongside the newly-added picture, inflating the
// short-term ref count to 2. With the fix, only the newly-added picture
// remains.
EXPECT_EQ (1u, ctx_.sTmpRefPic.uiShortRefCount[LIST_0]);
for (uint32_t i = 0; i < ctx_.sTmpRefPic.uiShortRefCount[LIST_0]; ++i) {
EXPECT_NE (&stalePic_, ctx_.sTmpRefPic.pShortRefList[LIST_0][i])
<< "stale reference picture pointer leaked into sTmpRefPic after MMCO5 reset";
}
// sRefPic (the persistent, non-threaded list) is always cleared directly
// by WelsResetRefPic(), independent of this fix.
EXPECT_EQ (0u, ctx_.sRefPic.uiShortRefCount[LIST_0]);
EXPECT_EQ (0u, ctx_.sRefPic.uiLongRefCount[LIST_0]);
}