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 generic_encoder_tests { |
| 20 |
| 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 Normal, |
| 46 Timing, |
| 47 Dropped, |
| 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::Dropped); |
| 87 continue; |
| 88 } |
| 89 callback.OnEncodedImage(image, &codec_specific, nullptr); |
| 90 if (sink.WasTimingFrame()) { |
| 91 result[s].push_back(FrameType::Timing); |
| 92 } else { |
| 93 result[s].push_back(FrameType::Normal); |
| 94 } |
| 95 } |
| 96 } |
| 97 return result; |
| 98 } |
| 99 |
| 100 TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) { |
| 101 const int64_t kDelayMs = 29; |
| 102 const size_t kMinFrameSize = 10; |
| 103 const size_t kMaxFrameSize = 20; |
| 104 const int kNumFrames = 1000; |
| 105 const int kNumStreams = 3; |
| 106 // No outliers as 1000 is larger than anything from range [10,20]. |
| 107 const std::vector<size_t> kAverageSize = {1000, 1000, 1000}; |
| 108 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize, |
| 109 kAverageSize, kNumStreams, kNumFrames); |
| 110 // Timing frames should be tirggered every delayMs. |
| 111 // As no outliers are expected, frames on all streams have to be |
| 112 // marked together. |
| 113 int last_timing_frame = -1; |
| 114 int i, s; |
| 115 for (i = 0; i < kNumFrames; ++i) { |
| 116 int num_normal = 0; |
| 117 int num_timing = 0; |
| 118 int num_dropped = 0; |
| 119 for (s = 0; s < kNumStreams; ++s) { |
| 120 if (frames[s][i] == FrameType::Timing) |
| 121 ++num_timing; |
| 122 else if (frames[s][i] == FrameType::Normal) |
| 123 ++num_normal; |
| 124 else |
| 125 ++num_dropped; |
| 126 } |
| 127 // Can't have both normal and timing frames at the same timstamp. |
| 128 EXPECT_TRUE(num_timing == 0 || num_normal == 0); |
| 129 if (num_dropped < kNumStreams) { |
| 130 if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) { |
| 131 // If didn't have timing frames for a period, current sent frame has to |
| 132 // be one. No normal frames should be sent. |
| 133 EXPECT_EQ(num_normal, 0); |
| 134 } else { |
| 135 // No unneeded timing frames should be sent. |
| 136 EXPECT_EQ(num_timing, 0); |
| 137 } |
| 138 } |
| 139 if (num_timing > 0) |
| 140 last_timing_frame = i; |
| 141 } |
| 142 } |
| 143 |
| 144 TEST(TestVCMEncodedFrameCallback, MarksOutliers) { |
| 145 const int64_t kDelayMs = 29; |
| 146 const size_t kMinFrameSize = 2495; |
| 147 const size_t kMaxFrameSize = 2505; |
| 148 const int kNumFrames = 1000; |
| 149 const int kNumStreams = 3; |
| 150 // Possible outliers as 1000 lies in range [995, 1005]. |
| 151 const std::vector<size_t> kAverageSize = {998, 1000, 1004}; |
| 152 auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize, |
| 153 kAverageSize, kNumStreams, kNumFrames); |
| 154 // All outliers should be marked. |
| 155 int i, s; |
| 156 for (i = 0; i < kNumFrames; ++i) { |
| 157 for (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::Normal); |
| 162 } |
| 163 } |
| 164 } |
| 165 } |
| 166 |
| 167 } // namespace generic_encoder_tests |
| 168 } // namespace webrtc |
OLD | NEW |