Commit 35325f40 for openh264
commit 35325f4040c2be0f86246c4a8923f7fc04c1a998
Author: Philipp Hancke <philipp.hancke@googlemail.com>
Date: Wed Aug 5 09:08:30 2026 +0200
psnr: add coverage for the per-frame PSNR API (#3975)
actually adding test coverage for
https://github.com/cisco/openh264/pull/3824/
diff --git a/test/api/encoder_test.cpp b/test/api/encoder_test.cpp
index 4d2e4f5a..e79be608 100644
--- a/test/api/encoder_test.cpp
+++ b/test/api/encoder_test.cpp
@@ -634,3 +634,82 @@ TEST_F(EncoderInitTest, CustomChromaPlaneStridesInvalidSrc) {
rv = encoder_->EncodeFrame(&pic, &info);
ASSERT_EQ(cmUnsupportedData, rv);
}
+
+// SSourcePicture.bPsnrY/U/V asks for the PSNR of a single frame and the result
+// is handed back in SLayerBSInfo.rPsnr. 176 is not a multiple of 32, so the
+// luma plane covers both the vectorized part of WelsCalcPsnr and the columns
+// along the right edge that it sums itself.
+TEST_F(EncoderInitTest, PerFramePsnr) {
+ SEncParamExt param;
+ encoder_->GetDefaultParams(¶m);
+
+ param.iUsageType = CAMERA_VIDEO_REAL_TIME;
+ param.iPicWidth = 176;
+ param.iPicHeight = 144;
+ 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 = 0;
+
+ int rv = encoder_->InitializeExt(¶m);
+ ASSERT_EQ(0, rv);
+
+ const int strideY = param.iPicWidth;
+ const int strideU = param.iPicWidth >> 1;
+ const int strideV = param.iPicWidth >> 1;
+ std::vector<uint8_t> bufY(strideY * param.iPicHeight);
+ std::vector<uint8_t> bufU(strideU * (param.iPicHeight >> 1));
+ std::vector<uint8_t> bufV(strideV * (param.iPicHeight >> 1));
+ GeneratePattern(bufY.data(), strideY, bufU.data(), strideU, bufV.data(), strideV, param.iPicWidth, param.iPicHeight);
+
+ SSourcePicture pic;
+ memset(&pic, 0, sizeof(SSourcePicture));
+ pic.iPicWidth = param.iPicWidth;
+ pic.iPicHeight = param.iPicHeight;
+ pic.iColorFormat = videoFormatI420;
+ pic.iStride[0] = strideY;
+ pic.iStride[1] = strideU;
+ pic.iStride[2] = strideV;
+ pic.pData[0] = bufY.data();
+ pic.pData[1] = bufU.data();
+ pic.pData[2] = bufV.data();
+
+ SFrameBSInfo info;
+
+ // nothing was asked for, so nothing is reported
+ memset(&info, 0, sizeof(SFrameBSInfo));
+ rv = encoder_->EncodeFrame(&pic, &info);
+ ASSERT_EQ(0, rv);
+ ASSERT_GT(info.iLayerNum, 0);
+ EXPECT_FLOAT_EQ(0.0f, info.sLayerInfo[0].rPsnr[0]);
+ EXPECT_FLOAT_EQ(0.0f, info.sLayerInfo[0].rPsnr[1]);
+ EXPECT_FLOAT_EQ(0.0f, info.sLayerInfo[0].rPsnr[2]);
+
+ // ask for a single component, the other two stay untouched
+ memset(&info, 0, sizeof(SFrameBSInfo));
+ pic.bPsnrY = true;
+ rv = encoder_->EncodeFrame(&pic, &info);
+ ASSERT_EQ(0, rv);
+ ASSERT_GT(info.iLayerNum, 0);
+ EXPECT_GT(info.sLayerInfo[0].rPsnr[0], 30.0f);
+ EXPECT_LE(info.sLayerInfo[0].rPsnr[0], 99.99f);
+ EXPECT_FLOAT_EQ(0.0f, info.sLayerInfo[0].rPsnr[1]);
+ EXPECT_FLOAT_EQ(0.0f, info.sLayerInfo[0].rPsnr[2]);
+
+ // ask for all three of them
+ memset(&info, 0, sizeof(SFrameBSInfo));
+ pic.bPsnrU = true;
+ pic.bPsnrV = true;
+ rv = encoder_->EncodeFrame(&pic, &info);
+ ASSERT_EQ(0, rv);
+ ASSERT_GT(info.iLayerNum, 0);
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_GT(info.sLayerInfo[0].rPsnr[i], 30.0f) << "component " << i;
+ EXPECT_LE(info.sLayerInfo[0].rPsnr[i], 99.99f) << "component " << i;
+ }
+}