1 /* 2 * Copyright (c) 2013 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 <vector> 12 13 #include "testing/gtest/include/gtest/gtest.h" 14 #include "webrtc/common.h" 15 #include "webrtc/modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h" 16 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h" 17 #include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h" 18 #include "webrtc/modules/video_coding/main/interface/mock/mock_vcm_callbacks.h" 19 #include "webrtc/modules/video_coding/main/interface/video_coding.h" 20 #include "webrtc/modules/video_coding/main/source/video_coding_impl.h" 21 #include "webrtc/modules/video_coding/main/test/test_util.h" 22 #include "webrtc/system_wrappers/interface/clock.h" 23 #include "webrtc/system_wrappers/interface/scoped_ptr.h" 24 #include "webrtc/test/frame_generator.h" 25 #include "webrtc/test/testsupport/fileutils.h" 26 #include "webrtc/test/testsupport/gtest_disable.h" 27 28 using ::testing::_; 29 using ::testing::AllOf; 30 using ::testing::ElementsAre; 31 using ::testing::ElementsAreArray; 32 using ::testing::Field; 33 using ::testing::NiceMock; 34 using ::testing::Pointee; 35 using ::testing::Return; 36 using ::testing::FloatEq; 37 using std::vector; 38 using webrtc::test::FrameGenerator; 39 40 namespace webrtc { 41 namespace vcm { 42 namespace { 43 enum { 44 kMaxNumberOfTemporalLayers = 3 45 }; 46 47 struct Vp8StreamInfo { 48 float framerate_fps[kMaxNumberOfTemporalLayers]; 49 int bitrate_kbps[kMaxNumberOfTemporalLayers]; 50 }; 51 52 MATCHER_P(MatchesVp8StreamInfo, expected, "") { 53 bool res = true; 54 for (int tl = 0; tl < kMaxNumberOfTemporalLayers; ++tl) { 55 if (fabs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) { 56 *result_listener << " framerate_fps[" << tl 57 << "] = " << arg.framerate_fps[tl] << " (expected " 58 << expected.framerate_fps[tl] << ") "; 59 res = false; 60 } 61 if (abs(expected.bitrate_kbps[tl] - arg.bitrate_kbps[tl]) > 10) { 62 *result_listener << " bitrate_kbps[" << tl 63 << "] = " << arg.bitrate_kbps[tl] << " (expected " 64 << expected.bitrate_kbps[tl] << ") "; 65 res = false; 66 } 67 } 68 return res; 69 } 70 71 class EmptyFrameGenerator : public FrameGenerator { 72 public: 73 I420VideoFrame* NextFrame() OVERRIDE { frame_.ResetSize(); return &frame_; } 74 75 private: 76 I420VideoFrame frame_; 77 }; 78 79 class PacketizationCallback : public VCMPacketizationCallback { 80 public: 81 PacketizationCallback(Clock* clock) 82 : clock_(clock), start_time_ms_(clock_->TimeInMilliseconds()) {} 83 84 virtual ~PacketizationCallback() {} 85 86 virtual int32_t SendData(FrameType frame_type, 87 uint8_t payload_type, 88 uint32_t timestamp, 89 int64_t capture_time_ms, 90 const uint8_t* payload_data, 91 uint32_t payload_size, 92 const RTPFragmentationHeader& fragmentation_header, 93 const RTPVideoHeader* rtp_video_header) { 94 assert(rtp_video_header); 95 frame_data_.push_back(FrameData(payload_size, *rtp_video_header)); 96 return 0; 97 } 98 99 void Reset() { 100 frame_data_.clear(); 101 start_time_ms_ = clock_->TimeInMilliseconds(); 102 } 103 104 float FramerateFpsWithinTemporalLayer(int temporal_layer) { 105 return CountFramesWithinTemporalLayer(temporal_layer) * 106 (1000.0 / interval_ms()); 107 } 108 109 float BitrateKbpsWithinTemporalLayer(int temporal_layer) { 110 return SumPayloadBytesWithinTemporalLayer(temporal_layer) * 8.0 / 111 interval_ms(); 112 } 113 114 Vp8StreamInfo CalculateVp8StreamInfo() { 115 Vp8StreamInfo info; 116 for (int tl = 0; tl < 3; ++tl) { 117 info.framerate_fps[tl] = FramerateFpsWithinTemporalLayer(tl); 118 info.bitrate_kbps[tl] = BitrateKbpsWithinTemporalLayer(tl); 119 } 120 return info; 121 } 122 123 private: 124 struct FrameData { 125 FrameData() {} 126 127 FrameData(uint32_t payload_size, const RTPVideoHeader& rtp_video_header) 128 : payload_size(payload_size), rtp_video_header(rtp_video_header) {} 129 130 uint32_t payload_size; 131 RTPVideoHeader rtp_video_header; 132 }; 133 134 int64_t interval_ms() { 135 int64_t diff = (clock_->TimeInMilliseconds() - start_time_ms_); 136 EXPECT_GT(diff, 0); 137 return diff; 138 } 139 140 int CountFramesWithinTemporalLayer(int temporal_layer) { 141 int frames = 0; 142 for (size_t i = 0; i < frame_data_.size(); ++i) { 143 EXPECT_EQ(kRtpVideoVp8, frame_data_[i].rtp_video_header.codec); 144 if (frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx <= 145 temporal_layer) { 146 frames++; 147 } 148 } 149 return frames; 150 } 151 152 int SumPayloadBytesWithinTemporalLayer(int temporal_layer) { 153 int payload_size = 0; 154 for (size_t i = 0; i < frame_data_.size(); ++i) { 155 EXPECT_EQ(kRtpVideoVp8, frame_data_[i].rtp_video_header.codec); 156 if (frame_data_[i].rtp_video_header.codecHeader.VP8.temporalIdx <= 157 temporal_layer) { 158 payload_size += frame_data_[i].payload_size; 159 } 160 } 161 return payload_size; 162 } 163 164 Clock* clock_; 165 int64_t start_time_ms_; 166 vector<FrameData> frame_data_; 167 }; 168 169 class TestVideoSender : public ::testing::Test { 170 protected: 171 // Note: simulated clock starts at 1 seconds, since parts of webrtc use 0 as 172 // a special case (e.g. frame rate in media optimization). 173 TestVideoSender() : clock_(1000), packetization_callback_(&clock_) {} 174 175 virtual void SetUp() { 176 sender_.reset(new VideoSender(&clock_, &post_encode_callback_)); 177 EXPECT_EQ(0, sender_->InitializeSender()); 178 EXPECT_EQ(0, sender_->RegisterTransportCallback(&packetization_callback_)); 179 } 180 181 void AddFrame() { 182 assert(generator_.get()); 183 sender_->AddVideoFrame(*generator_->NextFrame(), NULL, NULL); 184 } 185 186 SimulatedClock clock_; 187 PacketizationCallback packetization_callback_; 188 MockEncodedImageCallback post_encode_callback_; 189 scoped_ptr<VideoSender> sender_; 190 scoped_ptr<FrameGenerator> generator_; 191 }; 192 193 class TestVideoSenderWithMockEncoder : public TestVideoSender { 194 protected: 195 static const int kDefaultWidth = 1280; 196 static const int kDefaultHeight = 720; 197 static const int kNumberOfStreams = 3; 198 static const int kNumberOfLayers = 3; 199 static const int kUnusedPayloadType = 10; 200 201 virtual void SetUp() { 202 TestVideoSender::SetUp(); 203 generator_.reset(new EmptyFrameGenerator()); 204 EXPECT_EQ( 205 0, 206 sender_->RegisterExternalEncoder(&encoder_, kUnusedPayloadType, false)); 207 memset(&settings_, 0, sizeof(settings_)); 208 EXPECT_EQ(0, VideoCodingModule::Codec(kVideoCodecVP8, &settings_)); 209 settings_.numberOfSimulcastStreams = kNumberOfStreams; 210 ConfigureStream(kDefaultWidth / 4, 211 kDefaultHeight / 4, 212 100, 213 &settings_.simulcastStream[0]); 214 ConfigureStream(kDefaultWidth / 2, 215 kDefaultHeight / 2, 216 500, 217 &settings_.simulcastStream[1]); 218 ConfigureStream( 219 kDefaultWidth, kDefaultHeight, 1200, &settings_.simulcastStream[2]); 220 settings_.plType = kUnusedPayloadType; // Use the mocked encoder. 221 EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200)); 222 } 223 224 virtual void TearDown() { sender_.reset(); } 225 226 void ExpectIntraRequest(int stream) { 227 if (stream == -1) { 228 // No intra request expected. 229 EXPECT_CALL( 230 encoder_, 231 Encode(_, 232 _, 233 Pointee(ElementsAre(kDeltaFrame, kDeltaFrame, kDeltaFrame)))) 234 .Times(1).WillRepeatedly(Return(0)); 235 return; 236 } 237 assert(stream >= 0); 238 assert(stream < kNumberOfStreams); 239 std::vector<VideoFrameType> frame_types(kNumberOfStreams, kDeltaFrame); 240 frame_types[stream] = kKeyFrame; 241 EXPECT_CALL( 242 encoder_, 243 Encode(_, 244 _, 245 Pointee(ElementsAreArray(&frame_types[0], frame_types.size())))) 246 .Times(1).WillRepeatedly(Return(0)); 247 } 248 249 static void ConfigureStream(int width, 250 int height, 251 int max_bitrate, 252 SimulcastStream* stream) { 253 assert(stream); 254 stream->width = width; 255 stream->height = height; 256 stream->maxBitrate = max_bitrate; 257 stream->numberOfTemporalLayers = kNumberOfLayers; 258 stream->qpMax = 45; 259 } 260 261 VideoCodec settings_; 262 NiceMock<MockVideoEncoder> encoder_; 263 }; 264 265 TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequests) { 266 EXPECT_EQ(0, sender_->IntraFrameRequest(0)); 267 ExpectIntraRequest(0); 268 AddFrame(); 269 ExpectIntraRequest(-1); 270 AddFrame(); 271 272 EXPECT_EQ(0, sender_->IntraFrameRequest(1)); 273 ExpectIntraRequest(1); 274 AddFrame(); 275 ExpectIntraRequest(-1); 276 AddFrame(); 277 278 EXPECT_EQ(0, sender_->IntraFrameRequest(2)); 279 ExpectIntraRequest(2); 280 AddFrame(); 281 ExpectIntraRequest(-1); 282 AddFrame(); 283 284 EXPECT_EQ(-1, sender_->IntraFrameRequest(3)); 285 ExpectIntraRequest(-1); 286 AddFrame(); 287 288 EXPECT_EQ(-1, sender_->IntraFrameRequest(-1)); 289 ExpectIntraRequest(-1); 290 AddFrame(); 291 } 292 293 TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequestsInternalCapture) { 294 // De-register current external encoder. 295 EXPECT_EQ(0, 296 sender_->RegisterExternalEncoder(NULL, kUnusedPayloadType, false)); 297 // Register encoder with internal capture. 298 EXPECT_EQ( 299 0, sender_->RegisterExternalEncoder(&encoder_, kUnusedPayloadType, true)); 300 EXPECT_EQ(0, sender_->RegisterSendCodec(&settings_, 1, 1200)); 301 ExpectIntraRequest(0); 302 EXPECT_EQ(0, sender_->IntraFrameRequest(0)); 303 ExpectIntraRequest(1); 304 EXPECT_EQ(0, sender_->IntraFrameRequest(1)); 305 ExpectIntraRequest(2); 306 EXPECT_EQ(0, sender_->IntraFrameRequest(2)); 307 // No requests expected since these indices are out of bounds. 308 EXPECT_EQ(-1, sender_->IntraFrameRequest(3)); 309 EXPECT_EQ(-1, sender_->IntraFrameRequest(-1)); 310 } 311 312 class TestVideoSenderWithVp8 : public TestVideoSender { 313 public: 314 TestVideoSenderWithVp8() 315 : codec_bitrate_kbps_(300), available_bitrate_kbps_(1000) {} 316 317 virtual void SetUp() { 318 TestVideoSender::SetUp(); 319 320 const char* input_video = "foreman_cif"; 321 const int width = 352; 322 const int height = 288; 323 generator_.reset(FrameGenerator::CreateFromYuvFile( 324 test::ResourcePath(input_video, "yuv").c_str(), width, height)); 325 326 codec_ = MakeVp8VideoCodec(width, height, 3); 327 codec_.minBitrate = 10; 328 codec_.startBitrate = codec_bitrate_kbps_; 329 codec_.maxBitrate = codec_bitrate_kbps_; 330 EXPECT_EQ(0, sender_->RegisterSendCodec(&codec_, 1, 1200)); 331 } 332 333 static VideoCodec MakeVp8VideoCodec(int width, 334 int height, 335 int temporal_layers) { 336 VideoCodec codec; 337 memset(&codec, 0, sizeof(codec)); 338 EXPECT_EQ(0, VideoCodingModule::Codec(kVideoCodecVP8, &codec)); 339 codec.width = width; 340 codec.height = height; 341 codec.codecSpecific.VP8.numberOfTemporalLayers = temporal_layers; 342 return codec; 343 } 344 345 void InsertFrames(float framerate, float seconds) { 346 for (int i = 0; i < seconds * framerate; ++i) { 347 clock_.AdvanceTimeMilliseconds(1000.0f / framerate); 348 EXPECT_CALL(post_encode_callback_, Encoded(_, NULL, NULL)) 349 .WillOnce(Return(0)); 350 AddFrame(); 351 352 // SetChannelParameters needs to be called frequently to propagate 353 // framerate from the media optimization into the encoder. 354 // Note: SetChannelParameters fails if less than 2 frames are in the 355 // buffer since it will fail to calculate the framerate. 356 if (i != 0) { 357 EXPECT_EQ(VCM_OK, 358 sender_->SetChannelParameters( 359 available_bitrate_kbps_ * 1000, 0, 200)); 360 } 361 } 362 } 363 364 Vp8StreamInfo SimulateWithFramerate(float framerate) { 365 const float short_simulation_interval = 5.0; 366 const float long_simulation_interval = 10.0; 367 // It appears that this 5 seconds simulation is needed to allow 368 // bitrate and framerate to stabilize. 369 InsertFrames(framerate, short_simulation_interval); 370 packetization_callback_.Reset(); 371 372 InsertFrames(framerate, long_simulation_interval); 373 return packetization_callback_.CalculateVp8StreamInfo(); 374 } 375 376 protected: 377 VideoCodec codec_; 378 int codec_bitrate_kbps_; 379 int available_bitrate_kbps_; 380 }; 381 382 TEST_F(TestVideoSenderWithVp8, 383 DISABLED_ON_ANDROID(FixedTemporalLayersStrategy)) { 384 const int low_b = codec_bitrate_kbps_ * kVp8LayerRateAlloction[2][0]; 385 const int mid_b = codec_bitrate_kbps_ * kVp8LayerRateAlloction[2][1]; 386 const int high_b = codec_bitrate_kbps_ * kVp8LayerRateAlloction[2][2]; 387 { 388 Vp8StreamInfo expected = {{7.5, 15.0, 30.0}, {low_b, mid_b, high_b}}; 389 EXPECT_THAT(SimulateWithFramerate(30.0), MatchesVp8StreamInfo(expected)); 390 } 391 { 392 Vp8StreamInfo expected = {{3.75, 7.5, 15.0}, {low_b, mid_b, high_b}}; 393 EXPECT_THAT(SimulateWithFramerate(15.0), MatchesVp8StreamInfo(expected)); 394 } 395 } 396 397 TEST_F(TestVideoSenderWithVp8, 398 DISABLED_ON_ANDROID(RealTimeTemporalLayersStrategy)) { 399 Config extra_options; 400 extra_options.Set<TemporalLayers::Factory>( 401 new RealTimeTemporalLayersFactory()); 402 VideoCodec codec = MakeVp8VideoCodec(352, 288, 3); 403 codec.extra_options = &extra_options; 404 codec.minBitrate = 10; 405 codec.startBitrate = codec_bitrate_kbps_; 406 codec.maxBitrate = codec_bitrate_kbps_; 407 EXPECT_EQ(0, sender_->RegisterSendCodec(&codec, 1, 1200)); 408 409 const int low_b = codec_bitrate_kbps_ * 0.4; 410 const int mid_b = codec_bitrate_kbps_ * 0.6; 411 const int high_b = codec_bitrate_kbps_; 412 413 { 414 Vp8StreamInfo expected = {{7.5, 15.0, 30.0}, {low_b, mid_b, high_b}}; 415 EXPECT_THAT(SimulateWithFramerate(30.0), MatchesVp8StreamInfo(expected)); 416 } 417 { 418 Vp8StreamInfo expected = {{5.0, 10.0, 20.0}, {low_b, mid_b, high_b}}; 419 EXPECT_THAT(SimulateWithFramerate(20.0), MatchesVp8StreamInfo(expected)); 420 } 421 { 422 Vp8StreamInfo expected = {{7.5, 15.0, 15.0}, {mid_b, high_b, high_b}}; 423 EXPECT_THAT(SimulateWithFramerate(15.0), MatchesVp8StreamInfo(expected)); 424 } 425 { 426 Vp8StreamInfo expected = {{5.0, 10.0, 10.0}, {mid_b, high_b, high_b}}; 427 EXPECT_THAT(SimulateWithFramerate(10.0), MatchesVp8StreamInfo(expected)); 428 } 429 { 430 // TODO(andresp): Find out why this fails with framerate = 7.5 431 Vp8StreamInfo expected = {{7.0, 7.0, 7.0}, {high_b, high_b, high_b}}; 432 EXPECT_THAT(SimulateWithFramerate(7.0), MatchesVp8StreamInfo(expected)); 433 } 434 } 435 } // namespace 436 } // namespace vcm 437 } // namespace webrtc 438