OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (c) 2017 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 "webrtc/modules/video_coding/encoded_frame.h" |
| 14 #include "webrtc/modules/video_coding/generic_encoder.h" |
| 15 #include "webrtc/modules/video_coding/include/video_coding_defines.h" |
| 16 #include "webrtc/test/gtest.h" |
| 17 |
| 18 namespace webrtc { |
| 19 namespace test { |
| 20 namespace { |
| 21 inline size_t FrameSize(const size_t& min_frame_size, |
| 22 const size_t& max_frame_size, |
| 23 const int& s, |
| 24 const int& i) { |
| 25 return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size); |
| 26 } |
| 27 |
| 28 class FakeEncodedImageCallback : public EncodedImageCallback { |
| 29 public: |
| 30 FakeEncodedImageCallback() : last_frame_was_timing_(false) {} |
| 31 Result OnEncodedImage(const EncodedImage& encoded_image, |
| 32 const CodecSpecificInfo* codec_specific_info, |
| 33 const RTPFragmentationHeader* fragmentation) override { |
| 34 last_frame_was_timing_ = encoded_image.timing_.is_timing_frame; |
| 35 return Result::OK; |
| 36 }; |
| 37 |
| 38 bool WasTimingFrame() { return last_frame_was_timing_; } |
| 39 |
| 40 private: |
| 41 bool last_frame_was_timing_; |
| 42 }; |
| 43 |
| 44 enum class FrameType { |
| 45 kNormal, |
| 46 kTiming, |
| 47 kDropped, |
| 48 }; |
| 49 |
| 50 // Emulates |num_frames| on |num_streams| frames with capture timestamps |
| 51 // increased by 1 from 0. Size of each frame is between |
| 52 // |min_frame_size| and |max_frame_size|, outliers are counted relatevely to |
| 53 // |average_frame_sizes[]| for each stream. |
| 54 std::vector<std::vector<FrameType>> GetTimingFrames( |
| 55 const int64_t delay_ms, |
| 56 const size_t min_frame_size, |
| 57 const size_t max_frame_size, |
| 58 std::vector<size_t> average_frame_sizes, |
| 59 const int num_streams, |
| 60 const int num_frames) { |
| 61 FakeEncodedImageCallback sink; |
| 62 VCMEncodedFrameCallback callback(&sink, nullptr); |
| 63 const size_t kFramerate = 30; |
| 64 callback.SetTimingFramesThresholds( |
| 65 {delay_ms, kDefaultOutlierFrameSizePercent}); |
| 66 callback.OnFrameRateChanged(kFramerate); |
| 67 int s, i; |
| 68 std::vector<std::vector<FrameType>> result(num_streams); |
| 69 for (s = 0; s < num_streams; ++s) |
| 70 callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s); |
| 71 int64_t current_timestamp = 0; |
| 72 for (i = 0; i < num_frames; ++i) { |
| 73 current_timestamp += 1; |
| 74 for (s = 0; s < num_streams; ++s) { |
| 75 // every (5+s)-th frame is dropped on s-th stream by design. |
| 76 bool dropped = i % (5 + s) == 0; |
| 77 |
| 78 EncodedImage image; |
| 79 CodecSpecificInfo codec_specific; |
| 80 image._length = FrameSize(min_frame_size, max_frame_size, s, i); |
| 81 image.capture_time_ms_ = current_timestamp; |
| 82 codec_specific.codecType = kVideoCodecGeneric; |
| 83 codec_specific.codecSpecific.generic.simulcast_idx = s; |
| 84 callback.OnEncodeStarted(current_timestamp, s); |
| 85 if (dropped) { |
| 86 result[s].push_back(FrameType::kDropped); |
| 87 continue; |
| 88 } |
| 89 callback.OnEncodedImage(image, &codec_specific, nullptr); |
| 90 if (sink.WasTimingFrame()) { |
| 91 result[s].push_back(FrameType::kTiming); |
| 92 } else { |
| 93 result[s].push_back(FrameType::kNormal); |
| 94 } |
| 95 } |
| 96 } |
| 97 return result; |
| 98 } |
| 99 } // namespace |
| 100 |
| 101 TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) { |
| 102 const int64_t kDelayMs = 29; |
| 103 const size_t kMinFrameSize = 10; |
| 104 const size_t kMaxFrameSize = 20; |
| 105 const int kNumFrames = 1000; |
| 106 const int kNumStreams = 3; |
| 107 // No outliers as 1000 is larger than anything from range [10,20]. |
| 108 const std::vector<size_t> kAverageSize = {1000, 1000, 1000}; |
| 109 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize, |
| 110 kAverageSize, kNumStreams, kNumFrames); |
| 111 // Timing frames should be tirggered every delayMs. |
| 112 // As no outliers are expected, frames on all streams have to be |
| 113 // marked together. |
| 114 int last_timing_frame = -1; |
| 115 for (int i = 0; i < kNumFrames; ++i) { |
| 116 int num_normal = 0; |
| 117 int num_timing = 0; |
| 118 int num_dropped = 0; |
| 119 for (int s = 0; s < kNumStreams; ++s) { |
| 120 if (frames[s][i] == FrameType::kTiming) { |
| 121 ++num_timing; |
| 122 } else if (frames[s][i] == FrameType::kNormal) { |
| 123 ++num_normal; |
| 124 } else { |
| 125 ++num_dropped; |
| 126 } |
| 127 } |
| 128 // Can't have both normal and timing frames at the same timstamp. |
| 129 EXPECT_TRUE(num_timing == 0 || num_normal == 0); |
| 130 if (num_dropped < kNumStreams) { |
| 131 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) { |
| 132 // If didn't have timing frames for a period, current sent frame has to |
| 133 // be one. No normal frames should be sent. |
| 134 EXPECT_EQ(num_normal, 0); |
| 135 } else { |
| 136 // No unneeded timing frames should be sent. |
| 137 EXPECT_EQ(num_timing, 0); |
| 138 } |
| 139 } |
| 140 if (num_timing > 0) |
| 141 last_timing_frame = i; |
| 142 } |
| 143 } |
| 144 |
| 145 TEST(TestVCMEncodedFrameCallback, MarksOutliers) { |
| 146 const int64_t kDelayMs = 29; |
| 147 const size_t kMinFrameSize = 2495; |
| 148 const size_t kMaxFrameSize = 2505; |
| 149 const int kNumFrames = 1000; |
| 150 const int kNumStreams = 3; |
| 151 // Possible outliers as 1000 lies in range [995, 1005]. |
| 152 const std::vector<size_t> kAverageSize = {998, 1000, 1004}; |
| 153 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize, |
| 154 kAverageSize, kNumStreams, kNumFrames); |
| 155 // All outliers should be marked. |
| 156 for (int i = 0; i < kNumFrames; ++i) { |
| 157 for (int s = 0; s < kNumStreams; ++s) { |
| 158 if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >= |
| 159 kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) { |
| 160 // Too big frame. May be dropped or timing, but not normal. |
| 161 EXPECT_NE(frames[s][i], FrameType::kNormal); |
| 162 } |
| 163 } |
| 164 } |
| 165 } |
| 166 |
| 167 } // namespace test |
| 168 } // namespace webrtc |
OLD | NEW |