Skip to content
Open
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
10 changes: 8 additions & 2 deletions codec/encoder/core/src/svc_mode_decision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,14 @@ bool MdInterSCDPskipProcess (sWelsEncCtx* pEncCtx, SWelsMD* pWelsMd, SSlice* pSl
PredSkipMv (pMbCache, &sVaaPredSkipMv);

if (eSkipMode == SCROLLED) {
sCurMbMv[1].iMvX = static_cast<int16_t> (pVaaExt->sScrollDetectInfo.iScrollMvX << 2);
sCurMbMv[1].iMvY = static_cast<int16_t> (pVaaExt->sScrollDetectInfo.iScrollMvY << 2);
sCurMbMv[1].iMvX =
static_cast<int16_t>(WELS_CLIP3(pVaaExt->sScrollDetectInfo.iScrollMvX,
-pEncCtx->iMvRange, pEncCtx->iMvRange)
<< 2);
sCurMbMv[1].iMvY =
static_cast<int16_t>(WELS_CLIP3(pVaaExt->sScrollDetectInfo.iScrollMvY,
-pEncCtx->iMvRange, pEncCtx->iMvRange)
<< 2);
}

bool bMbSkipFlag = (LD32 (&sVaaPredSkipMv) == LD32 (&sCurMbMv[eSkipMode])) ;
Expand Down
10 changes: 10 additions & 0 deletions codec/encoder/core/src/wels_preprocess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,16 @@ ESceneChangeIdc CWelsPreProcessScreen::DetectSceneChange (SPicture* pCurPicture,

if (ret == 0) {
m_pInterfaceVp->Get (iMethodIdx, (void*) (pScrollDetectInfo));
// Ensure detected scroll motion vectors stay within the configured
// motion vector range.
if (pScrollDetectInfo->bScrollDetectFlag) {
pScrollDetectInfo->iScrollMvX =
WELS_CLIP3(pScrollDetectInfo->iScrollMvX, -m_pEncCtx->iMvRange,
m_pEncCtx->iMvRange);
pScrollDetectInfo->iScrollMvY =
WELS_CLIP3(pScrollDetectInfo->iScrollMvY, -m_pEncCtx->iMvRange,
m_pEncCtx->iMvRange);
}
}
sSceneChangeResult.sScrollResult = pVaaExt->sScrollDetectInfo;
}
Expand Down
92 changes: 92 additions & 0 deletions test/api/encoder_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,95 @@ TEST_F(EncoderInitTest, VeryLargeSlices) {
ASSERT_EQ(0, rv);
}
}

// This test verifies that the encoder correctly handles screen content
// sequences with large vertical scrolling motion vectors. It ensures that
// motion vector difference table indexing remains within allocated bounds when
// scroll detection predicts large vertical displacements at high quantization
// parameters.
TEST_F(EncoderInitTest, ScreenContentScrollMotionVectorBounds) {
SEncParamExt param;
encoder_->GetDefaultParams(&param);

param.iUsageType = SCREEN_CONTENT_REAL_TIME;
param.iPicWidth = 640;
param.iPicHeight = 1800;
param.fMaxFrameRate = 30.0f;
param.iSpatialLayerNum = 1;
param.iRCMode = RC_OFF_MODE;

param.sSpatialLayers[0].iVideoWidth = param.iPicWidth;
param.sSpatialLayers[0].iVideoHeight = param.iPicHeight;
param.sSpatialLayers[0].fFrameRate = param.fMaxFrameRate;
param.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;
param.sSpatialLayers[0].iDLayerQp = 51;
param.iMinQp = 51;
param.iMaxQp = 51;

int rv = encoder_->InitializeExt(&param);
ASSERT_EQ(0, rv);

SFrameBSInfo info;
memset(&info, 0, sizeof(SFrameBSInfo));

int width = param.iPicWidth;
int height = param.iPicHeight;
int frameSize = width * height * 3 / 2;
std::vector<uint8_t> frame0(frameSize, 128);
std::vector<uint8_t> frame1(frameSize, 128);

// Fill frame0 luma with pseudo-random patterns to ensure CheckLine qualifies
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
frame0[y * width + x] =
static_cast<uint8_t>((x * 29 + y * 43 + 17) % 251);
}
}

// Frame 1: shift vertically by -512 pixels (content moving downward by 512)
int scroll_mv = -512;
for (int y = 0; y < height; ++y) {
if (y + scroll_mv >= 0 && y + scroll_mv < height) {
memcpy(&frame1[y * width], &frame0[(y + scroll_mv) * width], width);
} else {
for (int x = 0; x < width; ++x) {
frame1[y * width + x] =
static_cast<uint8_t>((x * 53 + y * 71 + 101) & 0xFF);
}
}
}

// Modify macroblock (1, 32) at pixel rows 512..527 and cols 16..31 in frame1.
// MB(0, 32) will be skipped by scroll detection with MV -2048.
// MB(1, 32) cannot be skipped due to this modification, forcing it into
// motion estimation where it uses MB(0, 32)'s scrolled MV as a predictor
// during vertical full search.
for (int y = 512; y < 528; ++y) {
for (int x = 16; x < 32; ++x) {
frame1[y * width + x] ^= 0xFF;
}
}

SSourcePicture pic;
memset(&pic, 0, sizeof(SSourcePicture));
pic.iPicWidth = width;
pic.iPicHeight = height;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = width;
pic.iStride[1] = pic.iStride[2] = width >> 1;
pic.pData[0] = frame0.data();
pic.pData[1] = frame0.data() + width * height;
pic.pData[2] = pic.pData[1] + (width * height >> 2);

// Encode Frame 0 (Base pattern)
rv = encoder_->EncodeFrame(&pic, &info);
ASSERT_EQ(0, rv);
pic.uiTimeStamp += 33;

// Encode Frame 1 (Scrolled pattern with modified MB)
pic.pData[0] = frame1.data();
pic.pData[1] = frame1.data() + width * height;
pic.pData[2] = pic.pData[1] + (width * height >> 2);
rv = encoder_->EncodeFrame(&pic, &info);
ASSERT_EQ(0, rv);
}
Loading