simple_encoder_wrapper_unittests.cc (11775B)
1 /* 2 * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <memory> 12 #include <optional> 13 #include <utility> 14 15 #include "api/video/i420_buffer.h" // IWYU pragma: keep 16 #include "api/video_codecs/libaom_av1_encoder_factory.h" 17 #include "api/video_codecs/simple_encoder_wrapper.h" 18 #include "api/video_codecs/video_encoder_factory_interface.h" 19 #include "api/video_codecs/video_encoding_general.h" 20 #include "test/gmock.h" 21 #include "test/gtest.h" 22 #include "test/testsupport/file_utils.h" 23 #include "test/testsupport/frame_reader.h" 24 25 namespace webrtc { 26 27 using ::testing::Eq; 28 using ::testing::Gt; 29 using ::testing::IsEmpty; 30 using ::testing::Ne; 31 using ::testing::Not; 32 using ::testing::NotNull; 33 using ::testing::UnorderedElementsAre; 34 using PredictionConstraints = 35 VideoEncoderFactoryInterface::Capabilities::PredictionConstraints; 36 37 namespace { 38 39 std::unique_ptr<test::FrameReader> CreateFrameReader() { 40 return CreateY4mFrameReader( 41 test::ResourcePath("reference_video_640x360_30fps", "y4m"), 42 test::YuvFrameReaderImpl::RepeatMode::kPingPong); 43 } 44 45 TEST(SimpleEncoderWrapper, SupportedSvcModesOnlyL1T1) { 46 PredictionConstraints constraints = { 47 .num_buffers = 2, 48 .max_references = 2, 49 .max_temporal_layers = 1, 50 .buffer_space_type = 51 PredictionConstraints::BufferSpaceType::kSingleKeyframe, 52 .max_spatial_layers = 1, 53 .scaling_factors = {{1, 1}}, 54 }; 55 56 EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints), 57 UnorderedElementsAre("L1T1")); 58 } 59 60 TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL1T3) { 61 PredictionConstraints constraints = { 62 .num_buffers = 8, 63 .max_references = 1, 64 .max_temporal_layers = 3, 65 .buffer_space_type = 66 PredictionConstraints::BufferSpaceType::kSingleKeyframe, 67 .max_spatial_layers = 1, 68 .scaling_factors = {{1, 1}, {1, 2}}, 69 }; 70 71 EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints), 72 UnorderedElementsAre("L1T1", "L1T2", "L1T3")); 73 } 74 75 TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL3T3Key) { 76 PredictionConstraints constraints = { 77 .num_buffers = 8, 78 .max_references = 2, 79 .max_temporal_layers = 3, 80 .buffer_space_type = 81 PredictionConstraints::BufferSpaceType::kSingleKeyframe, 82 .max_spatial_layers = 3, 83 .scaling_factors = {{1, 1}, {1, 2}}, 84 }; 85 86 EXPECT_THAT( 87 SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints), 88 UnorderedElementsAre("L1T1", "L1T2", "L1T3", "L2T1", "L2T1_KEY", "L2T2", 89 "L2T2_KEY", "L2T3", "L2T3_KEY", "L3T1", "L3T1_KEY", 90 "L3T2", "L3T2_KEY", "L3T3", "L3T3_KEY", "S2T1", 91 "S2T2", "S2T3", "S3T1", "S3T2", "S3T3")); 92 } 93 94 TEST(SimpleEncoderWrapper, SupportedSvcModesUpToS3T3) { 95 PredictionConstraints constraints = { 96 .num_buffers = 8, 97 .max_references = 2, 98 .max_temporal_layers = 3, 99 .buffer_space_type = 100 PredictionConstraints::BufferSpaceType::kMultiInstance, 101 .max_spatial_layers = 3, 102 .scaling_factors = {{1, 1}, {1, 2}}, 103 }; 104 105 EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints), 106 UnorderedElementsAre("L1T1", "L1T2", "L1T3", "S2T1", "S2T2", 107 "S2T3", "S3T1", "S3T2", "S3T3")); 108 } 109 110 TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL3T3KeyWithHScaling) { 111 PredictionConstraints constraints = { 112 .num_buffers = 8, 113 .max_references = 2, 114 .max_temporal_layers = 3, 115 .buffer_space_type = 116 PredictionConstraints::BufferSpaceType::kSingleKeyframe, 117 .max_spatial_layers = 3, 118 .scaling_factors = {{1, 1}, {1, 2}, {2, 3}}, 119 }; 120 121 EXPECT_THAT( 122 SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints), 123 UnorderedElementsAre( 124 "L1T1", "L1T2", "L1T3", "L2T1", "L2T1h", "L2T1_KEY", "L2T1h_KEY", 125 "L2T2", "L2T2h", "L2T2_KEY", "L2T2h_KEY", "L2T3", "L2T3h", "L2T3_KEY", 126 "L2T3h_KEY", "L3T1", "L3T1h", "L3T1_KEY", "L3T1h_KEY", "L3T2", 127 "L3T2h", "L3T2_KEY", "L3T2h_KEY", "L3T3", "L3T3h", "L3T3_KEY", 128 "L3T3h_KEY", "S2T1", "S2T1h", "S2T2", "S2T2h", "S2T3", "S2T3h", 129 "S3T1", "S3T1h", "S3T2", "S3T2h", "S3T3", "S3T3h")); 130 } 131 132 // TD: The encoder wrapper shouldn't really use an actual encoder 133 // implementation for testing, but hey, this is just a PoC. 134 TEST(SimpleEncoderWrapper, EncodeL1T1) { 135 auto encoder = LibaomAv1EncoderFactory().CreateEncoder( 136 {.max_encode_dimensions = {.width = 1080, .height = 720}, 137 .encoding_format = {.sub_sampling = EncodingFormat::k420, 138 .bit_depth = 8}, 139 .rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(), 140 .max_number_of_threads = 1}, 141 {}); 142 143 std::unique_ptr<SimpleEncoderWrapper> simple_encoder = 144 SimpleEncoderWrapper::Create(std::move(encoder), "L1T1"); 145 146 ASSERT_THAT(simple_encoder, NotNull()); 147 148 simple_encoder->SetEncodeQp(30); 149 simple_encoder->SetEncodeFps(15); 150 auto frame_reader = CreateFrameReader(); 151 152 int num_callbacks = 0; 153 simple_encoder->Encode( 154 frame_reader->PullFrame(), /*force_keyframe=*/true, 155 [&](const SimpleEncoderWrapper::EncodeResult& result) { 156 ++num_callbacks; 157 ASSERT_THAT(result.oh_no, Eq(false)); 158 EXPECT_THAT(result.dependency_structure, Ne(std::nullopt)); 159 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 160 EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe)); 161 EXPECT_THAT(result.generic_frame_info.spatial_id, Eq(0)); 162 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 163 }); 164 165 simple_encoder->Encode( 166 frame_reader->PullFrame(), /*force_keyframe=*/false, 167 [&](const SimpleEncoderWrapper::EncodeResult& result) { 168 ++num_callbacks; 169 ASSERT_THAT(result.oh_no, Eq(false)); 170 EXPECT_THAT(result.dependency_structure, Eq(std::nullopt)); 171 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 172 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 173 EXPECT_THAT(result.generic_frame_info.spatial_id, Eq(0)); 174 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 175 }); 176 } 177 178 TEST(SimpleEncoderWrapper, EncodeL2T2_KEY) { 179 auto encoder = LibaomAv1EncoderFactory().CreateEncoder( 180 {.max_encode_dimensions = {.width = 1080, .height = 720}, 181 .encoding_format = {.sub_sampling = EncodingFormat::k420, 182 .bit_depth = 8}, 183 .rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(), 184 .max_number_of_threads = 1}, 185 {}); 186 187 std::unique_ptr<SimpleEncoderWrapper> simple_encoder = 188 SimpleEncoderWrapper::Create(std::move(encoder), "L2T2_KEY"); 189 190 ASSERT_THAT(simple_encoder, NotNull()); 191 192 simple_encoder->SetEncodeQp(30); 193 simple_encoder->SetEncodeFps(15); 194 auto frame_reader = CreateFrameReader(); 195 196 int num_callbacks = 0; 197 simple_encoder->Encode( 198 frame_reader->PullFrame(), /*force_keyframe=*/true, 199 [&](const SimpleEncoderWrapper::EncodeResult& result) { 200 ASSERT_THAT(result.oh_no, Eq(false)); 201 if (result.generic_frame_info.spatial_id == 0) { 202 ++num_callbacks; 203 EXPECT_THAT(result.dependency_structure, Ne(std::nullopt)); 204 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 205 EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe)); 206 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 207 } else if (result.generic_frame_info.spatial_id == 1) { 208 ++num_callbacks; 209 EXPECT_THAT(result.dependency_structure, Eq(std::nullopt)); 210 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 211 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 212 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 213 } 214 }); 215 216 simple_encoder->Encode( 217 frame_reader->PullFrame(), /*force_keyframe=*/false, 218 [&](const SimpleEncoderWrapper::EncodeResult& result) { 219 ASSERT_THAT(result.oh_no, Eq(false)); 220 if (result.generic_frame_info.spatial_id == 0) { 221 ++num_callbacks; 222 EXPECT_THAT(result.dependency_structure, Eq(std::nullopt)); 223 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 224 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 225 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1)); 226 } else if (result.generic_frame_info.spatial_id == 1) { 227 ++num_callbacks; 228 EXPECT_THAT(result.dependency_structure, Eq(std::nullopt)); 229 EXPECT_THAT(result.bitstream_data, Not(IsEmpty())); 230 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 231 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1)); 232 } 233 }); 234 235 EXPECT_THAT(num_callbacks, Eq(4)); 236 } 237 238 TEST(SimpleEncoderWrapper, EncodeL1T3ForceKeyframe) { 239 auto encoder = LibaomAv1EncoderFactory().CreateEncoder( 240 {.max_encode_dimensions = {.width = 1080, .height = 720}, 241 .encoding_format = {.sub_sampling = EncodingFormat::k420, 242 .bit_depth = 8}, 243 .rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(), 244 .max_number_of_threads = 1}, 245 {}); 246 247 std::unique_ptr<SimpleEncoderWrapper> simple_encoder = 248 SimpleEncoderWrapper::Create(std::move(encoder), "L1T3"); 249 250 ASSERT_THAT(simple_encoder, NotNull()); 251 252 simple_encoder->SetEncodeQp(30); 253 simple_encoder->SetEncodeFps(15); 254 auto frame_reader = CreateFrameReader(); 255 256 int num_callbacks = 0; 257 simple_encoder->Encode( 258 frame_reader->PullFrame(), /*force_keyframe=*/true, 259 [&](const SimpleEncoderWrapper::EncodeResult& result) { 260 ++num_callbacks; 261 ASSERT_THAT(result.oh_no, Eq(false)); 262 EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe)); 263 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 264 }); 265 266 simple_encoder->Encode( 267 frame_reader->PullFrame(), /*force_keyframe=*/false, 268 [&](const SimpleEncoderWrapper::EncodeResult& result) { 269 ++num_callbacks; 270 ASSERT_THAT(result.oh_no, Eq(false)); 271 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 272 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(2)); 273 }); 274 275 simple_encoder->Encode( 276 frame_reader->PullFrame(), /*force_keyframe=*/false, 277 [&](const SimpleEncoderWrapper::EncodeResult& result) { 278 ++num_callbacks; 279 ASSERT_THAT(result.oh_no, Eq(false)); 280 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 281 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1)); 282 }); 283 284 simple_encoder->Encode( 285 frame_reader->PullFrame(), /*force_keyframe=*/true, 286 [&](const SimpleEncoderWrapper::EncodeResult& result) { 287 ++num_callbacks; 288 ASSERT_THAT(result.oh_no, Eq(false)); 289 EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe)); 290 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0)); 291 }); 292 293 simple_encoder->Encode( 294 frame_reader->PullFrame(), /*force_keyframe=*/false, 295 [&](const SimpleEncoderWrapper::EncodeResult& result) { 296 ++num_callbacks; 297 ASSERT_THAT(result.oh_no, Eq(false)); 298 EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame)); 299 EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(2)); 300 }); 301 302 EXPECT_THAT(num_callbacks, Eq(5)); 303 } 304 305 } // namespace 306 } // namespace webrtc