Validate that H.264 resolutions are spec compliant. Bug: chromium:504690157 Change-Id: I1a932dd0b8e71dbe0207d1e44e03bb495dccff2b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/487600 Commit-Queue: Sergey Silkin <ssilkin@webrtc.org> Auto-Submit: Erik Språng <sprang@webrtc.org> Reviewed-by: Sergey Silkin <ssilkin@webrtc.org> Cr-Commit-Position: refs/heads/main@{#48153}
diff --git a/modules/video_coding/codecs/h264/h264_encoder_impl.cc b/modules/video_coding/codecs/h264/h264_encoder_impl.cc index b682013..69db6ff 100644 --- a/modules/video_coding/codecs/h264/h264_encoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_encoder_impl.cc
@@ -138,6 +138,28 @@ return std::nullopt; } +bool IsValidResolution(int width, int height) { + // H.264 Level 5.2 limits: + // Max macroblocks per frame (MaxFS) = 36864 + // Max width/height in macroblocks = Sqrt(MaxFS * 8) = 543 + // A macroblock is 16x16. + const int64_t width_in_mbs = (static_cast<int64_t>(width) + 15) / 16; + const int64_t height_in_mbs = (static_cast<int64_t>(height) + 15) / 16; + + if (width_in_mbs * height_in_mbs > 36864) { + return false; + } + + // Aspect ratio check: + // PicWidthInMbs <= Sqrt(MaxFS * 8) + // FrameHeightInMbs <= Sqrt(MaxFS * 8) + if (width_in_mbs > 543 || height_in_mbs > 543) { + return false; + } + + return true; +} + } // namespace // Helper method used by H264EncoderImpl::Encode. @@ -264,6 +286,18 @@ codec_.simulcastStream[0].height = codec_.height; } + for (int i = 0; i < number_of_streams; ++i) { + if (!IsValidResolution(codec_.simulcastStream[i].width, + codec_.simulcastStream[i].height)) { + RTC_LOG(LS_ERROR) << "InitEncode: Invalid stream resolution: " + << codec_.simulcastStream[i].width << "x" + << codec_.simulcastStream[i].height; + Release(); + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + } + for (int i = 0, idx = number_of_streams - 1; i < number_of_streams; ++i, --idx) { ISVCEncoder* openh264_encoder;
diff --git a/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc b/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc index b932278..66f9efe 100644 --- a/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc +++ b/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc
@@ -203,6 +203,63 @@ EXPECT_EQ(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE, encoder.Encode(frame, nullptr)); } +TEST(H264EncoderImplTest, RejectsTooLargeResolution) { + H264EncoderImpl encoder(CreateTestEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + + // Level 5.2 MaxFS is 36864 macroblocks. + // 4096 x 2304 = 256 x 144 macroblocks = 36864 macroblocks (Allowed). + codec_settings.width = 4096; + codec_settings.height = 2304; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + + // 4096 x 2320 = 256 x 145 macroblocks = 37120 macroblocks (Too large). + codec_settings.width = 4096; + codec_settings.height = 2320; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_PARAMETER, + encoder.InitEncode(&codec_settings, kSettings)); +} + +TEST(H264EncoderImplTest, RejectsSkewedAspectRatioWidth) { + H264EncoderImpl encoder(CreateTestEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + + // Level 5.2 Max width in macroblocks is 543 (8688 pixels). + // 8688 x 16 = 543 x 1 macroblocks = 543 macroblocks (Allowed). + codec_settings.width = 8688; + codec_settings.height = 16; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + + // 8704 x 16 = 544 x 1 macroblocks = 544 macroblocks (Too wide). + codec_settings.width = 8704; + codec_settings.height = 16; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_PARAMETER, + encoder.InitEncode(&codec_settings, kSettings)); +} + +TEST(H264EncoderImplTest, RejectsSkewedAspectRatioHeight) { + H264EncoderImpl encoder(CreateTestEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + + // Level 5.2 Max height in macroblocks is 543 (8688 pixels). + // 16 x 8688 = 1 x 543 macroblocks = 543 macroblocks (Allowed). + codec_settings.width = 16; + codec_settings.height = 8688; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + + // 16 x 8704 = 1 x 544 macroblocks = 544 macroblocks (Too high). + codec_settings.width = 16; + codec_settings.height = 8704; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_PARAMETER, + encoder.InitEncode(&codec_settings, kSettings)); +} + } // anonymous namespace } // namespace webrtc