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
18 changes: 18 additions & 0 deletions codec/encoder/core/src/slice_multi_threading.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,24 @@ void DynamicAdjustSlicing (sWelsEncCtx* pCtx,
return;
}
iMinimalMbNum = iNumMbInEachGom;
} else {
// If the number of requested slices equals or exceeds the total macroblocks
// in the frame, each slice cannot be assigned even 1 macroblock. In this
// case, do not adjust slice sizes.
if (kiCountSliceNum >= kiCountNumMb) {
WelsLog(&(pCtx->sLogCtx), WELS_LOG_WARNING,
"[MT] DynamicAdjustSlicing(), requested slice number (%d) equals "
"or exceeds total macroblocks (%d), do not adjust",
kiCountSliceNum, kiCountNumMb);
return;
} else if (iMinimalMbNum * kiCountSliceNum >= kiCountNumMb) {
// When rate control is off, iMinimalMbNum defaults to 1 macroblock row
// (iMbWidth). For extreme aspect ratios (such as very wide resolutions),
// requiring 1 row per slice may exceed total frame capacity. Fall back to
// 1 macroblock per slice to ensure valid upper bounds and allow proper
// load balancing across slices.
iMinimalMbNum = 1;
}
}

if (kiCountSliceNum < 2 || (kiCountSliceNum & 0x01)) // we need suppose uiSliceNum is even for multiple threading
Expand Down
6 changes: 6 additions & 0 deletions codec/encoder/core/src/svc_enc_slice_segment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ bool GomValidCheckSliceMbNum (const int32_t kiMbWidth, const int32_t kiMbHeight,
int32_t iCurNumMbAssigning = 0;

iMinimalMbNum = iGomSize;
// Ensure that the minimum macroblock requirement across all slices does not
// exceed total frame capacity, preventing negative calculations for remaining
// macroblocks.
if (iMinimalMbNum * static_cast<int32_t>(kuiSliceNum) > kiMbNumInFrame) {
return false;
}
while (uiSliceIdx + 1 < kuiSliceNum) {
iMaximalMbNum = iNumMbLeft - (kuiSliceNum - uiSliceIdx - 1) * iMinimalMbNum; // get maximal num_mb in left parts
// make sure one GOM at least in each slice for safe
Expand Down
1 change: 1 addition & 0 deletions codec/encoder/core/src/svc_encode_slice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,7 @@ int32_t InitOneSliceInThread (sWelsEncCtx* pCtx,
pSlice->sSliceBs.uiBsPos = 0;
pSlice->sSliceBs.iNalIndex = 0;
pSlice->sSliceBs.pBsBuffer = pCtx->pSliceThreading->pThreadBsBuffer[kiSlcBuffIdx];
pSlice->sSliceBs.uiSize = pCtx->iFrameBsSize;

return ENC_RETURN_SUCCESS;
}
Expand Down
75 changes: 75 additions & 0 deletions test/api/encoder_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,78 @@ TEST_F(EncoderInitTest, VeryLargeSlices) {
ASSERT_EQ(0, rv);
}
}

// This test verifies dynamic slice adjustment when encoding frames with extreme
// aspect ratios (very wide resolution) in RC_OFF_MODE using multiple
// slices/threads. It ensures that macroblocks are correctly distributed across
// slices without producing negative slice limits or invalid memory access when
// slice encoding speeds vary.
TEST_F(EncoderInitTest, DynamicAdjustSlicingExtremeAspectRatio) {
SEncParamExt param;
encoder_->GetDefaultParams(&param);

param.iUsageType = CAMERA_VIDEO_REAL_TIME;
// Extreme wide aspect ratio: frame height is only 2 macroblock rows (32
// pixels), which tests slicing behavior when row-based heuristics exceed
// frame dimensions.
param.iPicWidth = 32752;
param.iPicHeight = 32;
param.fMaxFrameRate = 30.0f;
param.iSpatialLayerNum = 1;
param.iMultipleThreadIdc = 4;
param.iRCMode = RC_OFF_MODE;
// Enable load balancing and high complexity mode to trigger dynamic slice
// adjustments.
param.bUseLoadBalancing = true;
param.iComplexityMode = HIGH_COMPLEXITY;
param.iMinQp = 0;
param.iMaxQp = 51;

param.sSpatialLayers[0].iVideoWidth = param.iPicWidth;
param.sSpatialLayers[0].iVideoHeight = param.iPicHeight;
param.sSpatialLayers[0].fFrameRate = param.fMaxFrameRate;
param.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
param.sSpatialLayers[0].sSliceArgument.uiSliceNum = 4;
param.sSpatialLayers[0].iDLayerQp = 24;

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

int frameSize = param.iPicWidth * param.iPicHeight * 3 / 2;
BufferedData buf;
buf.SetLength(frameSize);
ASSERT_EQ(buf.Length(), (size_t)frameSize);

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

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

for (int i = 0; i < 10; i++) {
// Generate varying random noise in Slices 1..3 while keeping Slice 0
// completely flat (zeros). This creates a significant encoding speed
// differential, causing the encoder to assign a larger proportion of
// macroblocks to Slice 0 during dynamic load balancing.
for (int idx = 0; idx < frameSize; idx++) {
buf.data()[idx] = rand() % 256;
}
for (int y = 0; y < 16; y++) {
memset(pic.pData[0] + y * pic.iStride[0], 0, 16368);
}
for (int y = 0; y < 8; y++) {
memset(pic.pData[1] + y * pic.iStride[1], 0, 8184);
memset(pic.pData[2] + y * pic.iStride[2], 0, 8184);
}
rv = encoder_->EncodeFrame(&pic, &info);
ASSERT_EQ(0, rv);
}
}
Loading