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:
NextFrame()73 I420VideoFrame* NextFrame() OVERRIDE { frame_.ResetSize(); return &frame_; }
74
75 private:
76 I420VideoFrame frame_;
77 };
78
79 class PacketizationCallback : public VCMPacketizationCallback {
80 public:
PacketizationCallback(Clock * clock)81 PacketizationCallback(Clock* clock)
82 : clock_(clock), start_time_ms_(clock_->TimeInMilliseconds()) {}
83
~PacketizationCallback()84 virtual ~PacketizationCallback() {}
85
SendData(FrameType frame_type,uint8_t payload_type,uint32_t timestamp,int64_t capture_time_ms,const uint8_t * payload_data,uint32_t payload_size,const RTPFragmentationHeader & fragmentation_header,const RTPVideoHeader * rtp_video_header)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
Reset()99 void Reset() {
100 frame_data_.clear();
101 start_time_ms_ = clock_->TimeInMilliseconds();
102 }
103
FramerateFpsWithinTemporalLayer(int temporal_layer)104 float FramerateFpsWithinTemporalLayer(int temporal_layer) {
105 return CountFramesWithinTemporalLayer(temporal_layer) *
106 (1000.0 / interval_ms());
107 }
108
BitrateKbpsWithinTemporalLayer(int temporal_layer)109 float BitrateKbpsWithinTemporalLayer(int temporal_layer) {
110 return SumPayloadBytesWithinTemporalLayer(temporal_layer) * 8.0 /
111 interval_ms();
112 }
113
CalculateVp8StreamInfo()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 {
FrameDatawebrtc::vcm::__anoncc49d1dd0111::PacketizationCallback::FrameData125 FrameData() {}
126
FrameDatawebrtc::vcm::__anoncc49d1dd0111::PacketizationCallback::FrameData127 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
interval_ms()134 int64_t interval_ms() {
135 int64_t diff = (clock_->TimeInMilliseconds() - start_time_ms_);
136 EXPECT_GT(diff, 0);
137 return diff;
138 }
139
CountFramesWithinTemporalLayer(int temporal_layer)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
SumPayloadBytesWithinTemporalLayer(int temporal_layer)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).
TestVideoSender()173 TestVideoSender() : clock_(1000), packetization_callback_(&clock_) {}
174
SetUp()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
AddFrame()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
SetUp()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
TearDown()224 virtual void TearDown() { sender_.reset(); }
225
ExpectIntraRequest(int stream)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
ConfigureStream(int width,int height,int max_bitrate,SimulcastStream * stream)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
TEST_F(TestVideoSenderWithMockEncoder,TestIntraRequests)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
TEST_F(TestVideoSenderWithMockEncoder,TestIntraRequestsInternalCapture)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:
TestVideoSenderWithVp8()314 TestVideoSenderWithVp8()
315 : codec_bitrate_kbps_(300), available_bitrate_kbps_(1000) {}
316
SetUp()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
MakeVp8VideoCodec(int width,int height,int temporal_layers)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
InsertFrames(float framerate,float seconds)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
SimulateWithFramerate(float framerate)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
TEST_F(TestVideoSenderWithVp8,DISABLED_ON_ANDROID (FixedTemporalLayersStrategy))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
TEST_F(TestVideoSenderWithVp8,DISABLED_ON_ANDROID (RealTimeTemporalLayersStrategy))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