| Index: webrtc/modules/video_coding/generic_encoder_unittest.cc
|
| diff --git a/webrtc/modules/video_coding/generic_encoder_unittest.cc b/webrtc/modules/video_coding/generic_encoder_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3de5c333fb52d3aef40c17eb2cb7c4d0c3606d5b
|
| --- /dev/null
|
| +++ b/webrtc/modules/video_coding/generic_encoder_unittest.cc
|
| @@ -0,0 +1,168 @@
|
| +/*
|
| + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +
|
| +#include <vector>
|
| +
|
| +#include "webrtc/modules/video_coding/encoded_frame.h"
|
| +#include "webrtc/modules/video_coding/generic_encoder.h"
|
| +#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
| +#include "webrtc/test/gtest.h"
|
| +
|
| +namespace webrtc {
|
| +namespace test {
|
| +namespace {
|
| +inline size_t FrameSize(const size_t& min_frame_size,
|
| + const size_t& max_frame_size,
|
| + const int& s,
|
| + const int& i) {
|
| + return min_frame_size + (s + 1) * i % (max_frame_size - min_frame_size);
|
| +}
|
| +
|
| +class FakeEncodedImageCallback : public EncodedImageCallback {
|
| + public:
|
| + FakeEncodedImageCallback() : last_frame_was_timing_(false) {}
|
| + Result OnEncodedImage(const EncodedImage& encoded_image,
|
| + const CodecSpecificInfo* codec_specific_info,
|
| + const RTPFragmentationHeader* fragmentation) override {
|
| + last_frame_was_timing_ = encoded_image.timing_.is_timing_frame;
|
| + return Result::OK;
|
| + };
|
| +
|
| + bool WasTimingFrame() { return last_frame_was_timing_; }
|
| +
|
| + private:
|
| + bool last_frame_was_timing_;
|
| +};
|
| +
|
| +enum class FrameType {
|
| + kNormal,
|
| + kTiming,
|
| + kDropped,
|
| +};
|
| +
|
| +// Emulates |num_frames| on |num_streams| frames with capture timestamps
|
| +// increased by 1 from 0. Size of each frame is between
|
| +// |min_frame_size| and |max_frame_size|, outliers are counted relatevely to
|
| +// |average_frame_sizes[]| for each stream.
|
| +std::vector<std::vector<FrameType>> GetTimingFrames(
|
| + const int64_t delay_ms,
|
| + const size_t min_frame_size,
|
| + const size_t max_frame_size,
|
| + std::vector<size_t> average_frame_sizes,
|
| + const int num_streams,
|
| + const int num_frames) {
|
| + FakeEncodedImageCallback sink;
|
| + VCMEncodedFrameCallback callback(&sink, nullptr);
|
| + const size_t kFramerate = 30;
|
| + callback.SetTimingFramesThresholds(
|
| + {delay_ms, kDefaultOutlierFrameSizePercent});
|
| + callback.OnFrameRateChanged(kFramerate);
|
| + int s, i;
|
| + std::vector<std::vector<FrameType>> result(num_streams);
|
| + for (s = 0; s < num_streams; ++s)
|
| + callback.OnTargetBitrateChanged(average_frame_sizes[s] * kFramerate, s);
|
| + int64_t current_timestamp = 0;
|
| + for (i = 0; i < num_frames; ++i) {
|
| + current_timestamp += 1;
|
| + for (s = 0; s < num_streams; ++s) {
|
| + // every (5+s)-th frame is dropped on s-th stream by design.
|
| + bool dropped = i % (5 + s) == 0;
|
| +
|
| + EncodedImage image;
|
| + CodecSpecificInfo codec_specific;
|
| + image._length = FrameSize(min_frame_size, max_frame_size, s, i);
|
| + image.capture_time_ms_ = current_timestamp;
|
| + codec_specific.codecType = kVideoCodecGeneric;
|
| + codec_specific.codecSpecific.generic.simulcast_idx = s;
|
| + callback.OnEncodeStarted(current_timestamp, s);
|
| + if (dropped) {
|
| + result[s].push_back(FrameType::kDropped);
|
| + continue;
|
| + }
|
| + callback.OnEncodedImage(image, &codec_specific, nullptr);
|
| + if (sink.WasTimingFrame()) {
|
| + result[s].push_back(FrameType::kTiming);
|
| + } else {
|
| + result[s].push_back(FrameType::kNormal);
|
| + }
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +} // namespace
|
| +
|
| +TEST(TestVCMEncodedFrameCallback, MarksTimingFramesPeriodicallyTogether) {
|
| + const int64_t kDelayMs = 29;
|
| + const size_t kMinFrameSize = 10;
|
| + const size_t kMaxFrameSize = 20;
|
| + const int kNumFrames = 1000;
|
| + const int kNumStreams = 3;
|
| + // No outliers as 1000 is larger than anything from range [10,20].
|
| + const std::vector<size_t> kAverageSize = {1000, 1000, 1000};
|
| + auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
|
| + kAverageSize, kNumStreams, kNumFrames);
|
| + // Timing frames should be tirggered every delayMs.
|
| + // As no outliers are expected, frames on all streams have to be
|
| + // marked together.
|
| + int last_timing_frame = -1;
|
| + for (int i = 0; i < kNumFrames; ++i) {
|
| + int num_normal = 0;
|
| + int num_timing = 0;
|
| + int num_dropped = 0;
|
| + for (int s = 0; s < kNumStreams; ++s) {
|
| + if (frames[s][i] == FrameType::kTiming) {
|
| + ++num_timing;
|
| + } else if (frames[s][i] == FrameType::kNormal) {
|
| + ++num_normal;
|
| + } else {
|
| + ++num_dropped;
|
| + }
|
| + }
|
| + // Can't have both normal and timing frames at the same timstamp.
|
| + EXPECT_TRUE(num_timing == 0 || num_normal == 0);
|
| + if (num_dropped < kNumStreams) {
|
| + if (last_timing_frame == -1 || i >= last_timing_frame + kDelayMs) {
|
| + // If didn't have timing frames for a period, current sent frame has to
|
| + // be one. No normal frames should be sent.
|
| + EXPECT_EQ(num_normal, 0);
|
| + } else {
|
| + // No unneeded timing frames should be sent.
|
| + EXPECT_EQ(num_timing, 0);
|
| + }
|
| + }
|
| + if (num_timing > 0)
|
| + last_timing_frame = i;
|
| + }
|
| +}
|
| +
|
| +TEST(TestVCMEncodedFrameCallback, MarksOutliers) {
|
| + const int64_t kDelayMs = 29;
|
| + const size_t kMinFrameSize = 2495;
|
| + const size_t kMaxFrameSize = 2505;
|
| + const int kNumFrames = 1000;
|
| + const int kNumStreams = 3;
|
| + // Possible outliers as 1000 lies in range [995, 1005].
|
| + const std::vector<size_t> kAverageSize = {998, 1000, 1004};
|
| + auto frames = GetTimingFrames(kDelayMs, kMinFrameSize, kMaxFrameSize,
|
| + kAverageSize, kNumStreams, kNumFrames);
|
| + // All outliers should be marked.
|
| + for (int i = 0; i < kNumFrames; ++i) {
|
| + for (int s = 0; s < kNumStreams; ++s) {
|
| + if (FrameSize(kMinFrameSize, kMaxFrameSize, s, i) >=
|
| + kAverageSize[s] * kDefaultOutlierFrameSizePercent / 100) {
|
| + // Too big frame. May be dropped or timing, but not normal.
|
| + EXPECT_NE(frames[s][i], FrameType::kNormal);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace test
|
| +} // namespace webrtc
|
|
|