Move FakeCodec to separate target and behave like real encoder.
Add FakeVp8Encoder, change FakeEncoder to use BitrateAllocator for simulcast.
Change call_test to use VP8 payload name for simulcast tests.
Bug: none
Change-Id: I5a34c52e66bbd6c05859729ed14ae87ac26b5969
Reviewed-on: https://webrtc-review.googlesource.com/91861
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24359}
diff --git a/call/BUILD.gn b/call/BUILD.gn
index b512e09..ee06825 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -347,11 +347,13 @@
"../system_wrappers",
"../test:audio_codec_mocks",
"../test:direct_transport",
+ "../test:fake_video_codecs",
"../test:field_trial",
"../test:test_common",
"../test:test_support",
"../test:video_test_common",
"../video:video",
+ "//testing/gmock",
"//testing/gtest",
"//third_party/abseil-cpp/absl/memory",
]
@@ -390,6 +392,7 @@
"../system_wrappers:metrics_default",
"../system_wrappers:runtime_enabled_features_default",
"../test:direct_transport",
+ "../test:fake_video_codecs",
"../test:field_trial",
"../test:fileutils",
"../test:perf_test",
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index 23116b2..2b98036 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -357,6 +357,9 @@
// inactive.
if (layer_bitrates[i]) {
rtp_modules_[i]->SetVideoBitrateAllocation(*layer_bitrates[i]);
+ } else {
+ // Signal a 0 bitrate on a simulcast stream.
+ rtp_modules_[i]->SetVideoBitrateAllocation(VideoBitrateAllocation());
}
}
}
diff --git a/modules/video_coding/utility/default_video_bitrate_allocator.cc b/modules/video_coding/utility/default_video_bitrate_allocator.cc
index a4a40dd..b9d20c5 100644
--- a/modules/video_coding/utility/default_video_bitrate_allocator.cc
+++ b/modules/video_coding/utility/default_video_bitrate_allocator.cc
@@ -12,6 +12,8 @@
#include <stdint.h>
+#include <algorithm>
+
namespace webrtc {
DefaultVideoBitrateAllocator::DefaultVideoBitrateAllocator(
@@ -27,14 +29,23 @@
if (total_bitrate_bps == 0 || !codec_.active)
return allocation;
- if (total_bitrate_bps < codec_.minBitrate * 1000) {
- allocation.SetBitrate(0, 0, codec_.minBitrate * 1000);
- } else if (codec_.maxBitrate > 0 &&
- total_bitrate_bps > codec_.maxBitrate * 1000) {
- allocation.SetBitrate(0, 0, codec_.maxBitrate * 1000);
- } else {
- allocation.SetBitrate(0, 0, total_bitrate_bps);
+ uint32_t allocated_bitrate_bps = total_bitrate_bps;
+ allocated_bitrate_bps =
+ std::max(allocated_bitrate_bps, codec_.minBitrate * 1000);
+ if (codec_.maxBitrate > 0) {
+ allocated_bitrate_bps =
+ std::min(allocated_bitrate_bps, codec_.maxBitrate * 1000);
}
+ size_t num_simulcast_streams =
+ std::max<size_t>(1, codec_.numberOfSimulcastStreams);
+ // The bitrate is split between all the streams in proportion of powers of 2
+ // e.g. 1:2, 1:2:4, etc.
+ for (size_t i = 0; i < num_simulcast_streams; i++) {
+ allocation.SetBitrate(
+ i, 0,
+ allocated_bitrate_bps * (1 << i) / ((1 << num_simulcast_streams) - 1));
+ }
+
return allocation;
}
diff --git a/test/BUILD.gn b/test/BUILD.gn
index ac56bc6..b5ec01b 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -299,23 +299,32 @@
rtc_test("test_support_unittests") {
deps = [
":direct_transport",
+ ":fake_video_codecs",
":fileutils",
+ ":fileutils_unittests",
":perf_test",
":rtp_test_utils",
+ ":test_common",
":test_main",
":test_support",
":test_support_test_artifacts",
":video_test_common",
":video_test_support",
+ "../api:create_simulcast_test_fixture_api",
+ "../api:simulcast_test_fixture_api",
"../api/video:video_frame_i420",
"../modules/rtp_rtcp:rtp_rtcp",
+ "../modules/video_capture",
+ "../modules/video_coding:simulcast_test_fixture_impl",
"../rtc_base:rtc_base_approved",
"../test:single_threaded_task_queue",
+ "//testing/gmock",
"//testing/gtest",
"//third_party/abseil-cpp/absl/memory",
]
sources = [
"direct_transport_unittest.cc",
+ "fake_vp8_encoder_unittest.cc",
"frame_generator_unittest.cc",
"rtp_file_reader_unittest.cc",
"rtp_file_writer_unittest.cc",
@@ -342,6 +351,7 @@
if (is_ios) {
deps += [ ":test_support_unittests_bundle_data" ]
}
+
if (!is_android && !build_with_chromium) {
# This is needed in order to avoid:
# undefined symbol: webrtc::videocapturemodule::VideoCaptureImpl::Create
@@ -488,13 +498,46 @@
]
}
+rtc_source_set("fake_video_codecs") {
+ testonly = true
+ visibility = [ "*" ]
+ sources = [
+ "configurable_frame_size_encoder.cc",
+ "configurable_frame_size_encoder.h",
+ "fake_decoder.cc",
+ "fake_decoder.h",
+ "fake_encoder.cc",
+ "fake_encoder.h",
+ "fake_vp8_encoder.cc",
+ "fake_vp8_encoder.h",
+ ]
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+ deps = [
+ "..:webrtc_common",
+ "../api/video:video_frame_i420",
+ "../api/video_codecs:video_codecs_api",
+ "../common_video:common_video",
+ "../modules/video_coding:video_codec_interface",
+ "../modules/video_coding:video_coding_utility",
+ "../modules/video_coding:webrtc_h264",
+ "../modules/video_coding:webrtc_vp8",
+ "../modules/video_coding:webrtc_vp9",
+ "../rtc_base:checks",
+ "../rtc_base:rtc_base_approved",
+ "../rtc_base:rtc_task_queue",
+ "../rtc_base:sequenced_task_checker",
+ "../system_wrappers",
+ ]
+}
+
rtc_source_set("test_common") {
testonly = true
sources = [
"call_test.cc",
"call_test.h",
- "configurable_frame_size_encoder.cc",
- "configurable_frame_size_encoder.h",
"constants.cc",
"constants.h",
"drifting_clock.cc",
@@ -502,10 +545,6 @@
"encoder_proxy_factory.h",
"encoder_settings.cc",
"encoder_settings.h",
- "fake_decoder.cc",
- "fake_decoder.h",
- "fake_encoder.cc",
- "fake_encoder.h",
"fake_videorenderer.h",
"function_video_decoder_factory.h",
"function_video_encoder_factory.h",
@@ -534,18 +573,17 @@
deps = [
":direct_transport",
+ ":fake_video_codecs",
":fileutils",
":rtp_test_utils",
":test_support",
":video_test_common",
- "..:webrtc_common",
"../api:libjingle_peerconnection_api",
"../api:simulated_network_api",
"../api:transport_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
"../api/audio_codecs:builtin_audio_encoder_factory",
"../api/video:video_frame",
- "../api/video:video_frame_i420",
"../api/video_codecs:video_codecs_api",
"../audio",
"../call",
@@ -555,7 +593,6 @@
"../call:simulated_network",
"../call:simulated_packet_receiver",
"../call:video_stream_api",
- "../common_video",
"../logging:rtc_event_log_api",
"../logging:rtc_event_log_impl_base",
"../media:rtc_internal_video_codecs",
@@ -569,7 +606,6 @@
"../modules/rtp_rtcp",
"../modules/rtp_rtcp:mock_rtp_rtcp",
"../modules/rtp_rtcp:rtp_rtcp_format",
- "../modules/video_coding:video_codec_interface",
"../modules/video_coding:video_coding_utility",
"../modules/video_coding:webrtc_h264",
"../modules/video_coding:webrtc_multiplex",
@@ -577,8 +613,6 @@
"../modules/video_coding:webrtc_vp9",
"../rtc_base:checks",
"../rtc_base:rtc_base_approved",
- "../rtc_base:rtc_task_queue",
- "../rtc_base:sequenced_task_checker",
"../rtc_base/experiments:congestion_controller_experiment",
"../system_wrappers",
"../system_wrappers:field_trial_api",
diff --git a/test/call_test.cc b/test/call_test.cc
index bf6ce2a..3b609c3 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -43,9 +43,14 @@
audio_send_stream_(nullptr),
bbr_network_controller_factory_(new BbrNetworkControllerFactory()),
fake_encoder_factory_([this]() {
- auto encoder = absl::make_unique<test::FakeEncoder>(clock_);
- encoder->SetMaxBitrate(fake_encoder_max_bitrate_);
- return encoder;
+ std::unique_ptr<FakeEncoder> fake_encoder;
+ if (video_encoder_configs_[0].codec_type == kVideoCodecVP8) {
+ fake_encoder = absl::make_unique<FakeVP8Encoder>(clock_);
+ } else {
+ fake_encoder = absl::make_unique<FakeEncoder>(clock_);
+ }
+ fake_encoder->SetMaxBitrate(fake_encoder_max_bitrate_);
+ return fake_encoder;
}),
num_video_streams_(1),
num_audio_streams_(0),
diff --git a/test/call_test.h b/test/call_test.h
index f6c7e7f..eb96cfd 100644
--- a/test/call_test.h
+++ b/test/call_test.h
@@ -20,6 +20,7 @@
#include "test/encoder_settings.h"
#include "test/fake_decoder.h"
#include "test/fake_videorenderer.h"
+#include "test/fake_vp8_encoder.h"
#include "test/frame_generator_capturer.h"
#include "test/function_video_encoder_factory.h"
#include "test/rtp_rtcp_observer.h"
diff --git a/test/configurable_frame_size_encoder.cc b/test/configurable_frame_size_encoder.cc
index 7671a3c..c18c938 100644
--- a/test/configurable_frame_size_encoder.cc
+++ b/test/configurable_frame_size_encoder.cc
@@ -15,7 +15,6 @@
#include "common_video/include/video_frame.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "rtc_base/checks.h"
-#include "test/gtest.h"
namespace webrtc {
namespace test {
diff --git a/test/configurable_frame_size_encoder.h b/test/configurable_frame_size_encoder.h
index b8c3b830d..4ad6749 100644
--- a/test/configurable_frame_size_encoder.h
+++ b/test/configurable_frame_size_encoder.h
@@ -22,7 +22,7 @@
class ConfigurableFrameSizeEncoder : public VideoEncoder {
public:
explicit ConfigurableFrameSizeEncoder(size_t max_frame_size);
- virtual ~ConfigurableFrameSizeEncoder();
+ ~ConfigurableFrameSizeEncoder() override;
int32_t InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
diff --git a/test/fake_decoder.cc b/test/fake_decoder.cc
index 2155008..5b299ef 100644
--- a/test/fake_decoder.cc
+++ b/test/fake_decoder.cc
@@ -12,16 +12,17 @@
#include "api/video/i420_buffer.h"
#include "rtc_base/timeutils.h"
-#include "test/call_test.h"
-#include "test/gtest.h"
namespace webrtc {
namespace test {
+namespace {
+const int kDefaultWidth = 320;
+const int kDefaultHeight = 180;
+} // namespace
+
FakeDecoder::FakeDecoder()
- : callback_(NULL),
- width_(CallTest::kDefaultWidth),
- height_(CallTest::kDefaultHeight) {}
+ : callback_(NULL), width_(kDefaultWidth), height_(kDefaultHeight) {}
int32_t FakeDecoder::InitDecode(const VideoCodec* config,
int32_t number_of_cores) {
@@ -75,7 +76,7 @@
i += sizeof(kStartCode) + 1; // Skip start code and NAL header.
}
if (input._buffer[i] != value) {
- EXPECT_EQ(value, input._buffer[i])
+ RTC_CHECK_EQ(value, input._buffer[i])
<< "Bitstream mismatch between sender and receiver.";
return -1;
}
diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc
index 5a3e2b8..1f90c29 100644
--- a/test/fake_encoder.cc
+++ b/test/fake_encoder.cc
@@ -16,10 +16,10 @@
#include <memory>
#include "common_types.h" // NOLINT(build/include)
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/sleep.h"
-#include "test/gtest.h"
namespace webrtc {
namespace test {
@@ -37,6 +37,9 @@
for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) {
encoded_buffer_[i] = static_cast<uint8_t>(i);
}
+ for (bool& used : used_layers_) {
+ used = false;
+ }
}
void FakeEncoder::SetMaxBitrate(int max_kbps) {
@@ -53,6 +56,7 @@
target_bitrate_.SetBitrate(0, 0, config_.startBitrate * 1000);
configured_input_framerate_ = config_.maxFramerate;
pending_keyframe_ = true;
+ last_frame_info_ = FrameInfo();
return 0;
}
@@ -63,9 +67,7 @@
unsigned char num_simulcast_streams;
SimulcastStream simulcast_streams[kMaxSimulcastStreams];
EncodedImageCallback* callback;
- uint32_t target_bitrate_sum_kbps;
- int max_target_bitrate_kbps;
- size_t num_encoded_bytes;
+ VideoBitrateAllocation target_bitrate;
int framerate;
VideoCodecMode mode;
bool keyframe;
@@ -77,9 +79,7 @@
simulcast_streams[i] = config_.simulcastStream[i];
}
callback = callback_;
- target_bitrate_sum_kbps = target_bitrate_.get_sum_kbps();
- max_target_bitrate_kbps = max_target_bitrate_kbps_;
- num_encoded_bytes = sizeof(encoded_buffer_);
+ target_bitrate = target_bitrate_;
mode = config_.mode;
if (configured_input_framerate_ > 0) {
framerate = configured_input_framerate_;
@@ -90,63 +90,28 @@
pending_keyframe_ = false;
}
- for (FrameType frame_type : *frame_types) {
- if (frame_type == kVideoFrameKey) {
- keyframe = true;
- break;
+ FrameInfo frame_info =
+ NextFrame(frame_types, keyframe, num_simulcast_streams, target_bitrate,
+ simulcast_streams, framerate);
+ for (uint8_t i = 0; i < frame_info.layers.size(); ++i) {
+ if (frame_info.layers[i].size == 0) {
+ // Drop this temporal layer.
+ continue;
}
- }
- RTC_DCHECK_GT(max_framerate, 0);
-
- size_t bitrate =
- std::max(target_bitrate_sum_kbps, simulcast_streams[0].minBitrate);
- if (max_target_bitrate_kbps > 0)
- bitrate = std::min(bitrate, static_cast<size_t>(max_target_bitrate_kbps));
-
- size_t bits_available = bitrate * 1000 / framerate;
-
- RTC_DCHECK_GT(num_simulcast_streams, 0);
- for (unsigned char i = 0; i < num_simulcast_streams; ++i) {
CodecSpecificInfo specifics;
memset(&specifics, 0, sizeof(specifics));
specifics.codecType = kVideoCodecGeneric;
specifics.codecSpecific.generic.simulcast_idx = i;
- size_t min_stream_bits = static_cast<size_t>(
- (simulcast_streams[i].minBitrate * 1000) / framerate);
- size_t max_stream_bits = static_cast<size_t>(
- (simulcast_streams[i].maxBitrate * 1000) / framerate);
- size_t stream_bits =
- (bits_available > max_stream_bits) ? max_stream_bits : bits_available;
- size_t stream_bytes = (stream_bits + 7) / 8;
- if (keyframe) {
- // The first frame is a key frame and should be larger.
- // Store the overshoot bytes and distribute them over the coming frames,
- // so that we on average meet the bitrate target.
- debt_bytes_ += (kKeyframeSizeFactor - 1) * stream_bytes;
- stream_bytes *= kKeyframeSizeFactor;
- } else {
- if (debt_bytes_ > 0) {
- // Pay at most half of the frame size for old debts.
- size_t payment_size = std::min(stream_bytes / 2, debt_bytes_);
- debt_bytes_ -= payment_size;
- stream_bytes -= payment_size;
- }
- }
-
- if (stream_bytes > num_encoded_bytes)
- stream_bytes = num_encoded_bytes;
-
- // Always encode something on the first frame.
- if (min_stream_bits > bits_available && i > 0)
- continue;
-
- std::unique_ptr<uint8_t[]> encoded_buffer(new uint8_t[num_encoded_bytes]);
- memcpy(encoded_buffer.get(), encoded_buffer_, num_encoded_bytes);
- EncodedImage encoded(encoded_buffer.get(), stream_bytes, num_encoded_bytes);
- encoded.SetTimestamp(input_image.timestamp());
+ std::unique_ptr<uint8_t[]> encoded_buffer(
+ new uint8_t[frame_info.layers[i].size]);
+ memcpy(encoded_buffer.get(), encoded_buffer_, frame_info.layers[i].size);
+ EncodedImage encoded(encoded_buffer.get(), frame_info.layers[i].size,
+ sizeof(encoded_buffer_));
+ encoded._timeStamp = input_image.timestamp();
encoded.capture_time_ms_ = input_image.render_time_ms();
- encoded._frameType = (*frame_types)[i];
+ encoded._frameType =
+ frame_info.keyframe ? kVideoFrameKey : kVideoFrameDelta;
encoded._encodedWidth = simulcast_streams[i].width;
encoded._encodedHeight = simulcast_streams[i].height;
encoded.rotation_ = input_image.rotation();
@@ -154,17 +119,77 @@
? VideoContentType::SCREENSHARE
: VideoContentType::UNSPECIFIED;
specifics.codec_name = ImplementationName();
- specifics.codecSpecific.generic.simulcast_idx = i;
- RTC_DCHECK(callback);
if (callback->OnEncodedImage(encoded, &specifics, nullptr).error !=
EncodedImageCallback::Result::OK) {
return -1;
}
- bits_available -= std::min(encoded._length * 8, bits_available);
}
return 0;
}
+FakeEncoder::FrameInfo FakeEncoder::NextFrame(
+ const std::vector<FrameType>* frame_types,
+ bool keyframe,
+ uint8_t num_simulcast_streams,
+ const VideoBitrateAllocation& target_bitrate,
+ SimulcastStream simulcast_streams[kMaxSimulcastStreams],
+ int framerate) {
+ FrameInfo frame_info;
+ frame_info.keyframe = keyframe;
+
+ if (frame_types) {
+ for (FrameType frame_type : *frame_types) {
+ if (frame_type == kVideoFrameKey) {
+ frame_info.keyframe = true;
+ break;
+ }
+ }
+ }
+
+ for (uint8_t i = 0; i < num_simulcast_streams; ++i) {
+ if (target_bitrate.GetBitrate(i, 0) > 0) {
+ int temporal_id = last_frame_info_.layers.size() > i
+ ? ++last_frame_info_.layers[i].temporal_id %
+ simulcast_streams[i].numberOfTemporalLayers
+ : 0;
+ frame_info.layers.emplace_back(0, temporal_id);
+ }
+ }
+
+ if (last_frame_info_.layers.size() < frame_info.layers.size()) {
+ // A new keyframe is needed since a new layer will be added.
+ frame_info.keyframe = true;
+ }
+
+ for (uint8_t i = 0; i < frame_info.layers.size(); ++i) {
+ FrameInfo::SpatialLayer& layer_info = frame_info.layers[i];
+ if (frame_info.keyframe) {
+ layer_info.temporal_id = 0;
+ size_t avg_frame_size =
+ (target_bitrate.GetBitrate(i, 0) + 7) / (8 * framerate);
+
+ // The first frame is a key frame and should be larger.
+ // Store the overshoot bytes and distribute them over the coming frames,
+ // so that we on average meet the bitrate target.
+ debt_bytes_ += (kKeyframeSizeFactor - 1) * avg_frame_size;
+ layer_info.size = kKeyframeSizeFactor * avg_frame_size;
+ } else {
+ size_t avg_frame_size =
+ (target_bitrate.GetBitrate(i, layer_info.temporal_id) + 7) /
+ (8 * framerate);
+ layer_info.size = avg_frame_size;
+ if (debt_bytes_ > 0) {
+ // Pay at most half of the frame size for old debts.
+ size_t payment_size = std::min(avg_frame_size / 2, debt_bytes_);
+ debt_bytes_ -= payment_size;
+ layer_info.size -= payment_size;
+ }
+ }
+ }
+ last_frame_info_ = frame_info;
+ return frame_info;
+}
+
int32_t FakeEncoder::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
rtc::CritScope cs(&crit_sect_);
diff --git a/test/fake_encoder.h b/test/fake_encoder.h
index 071019a..67acebe 100644
--- a/test/fake_encoder.h
+++ b/test/fake_encoder.h
@@ -50,7 +50,30 @@
static const char* kImplementationName;
protected:
+ struct FrameInfo {
+ bool keyframe;
+ struct SpatialLayer {
+ SpatialLayer() = default;
+ SpatialLayer(int size, int temporal_id)
+ : size(size), temporal_id(temporal_id) {}
+ // Size of a current frame in the layer.
+ int size = 0;
+ // Temporal index of a current frame in the layer.
+ int temporal_id = 0;
+ };
+ std::vector<SpatialLayer> layers;
+ };
+
+ FrameInfo NextFrame(const std::vector<FrameType>* frame_types,
+ bool keyframe,
+ uint8_t num_simulcast_streams,
+ const VideoBitrateAllocation& target_bitrate,
+ SimulcastStream simulcast_streams[kMaxSimulcastStreams],
+ int framerate);
+
+ FrameInfo last_frame_info_;
Clock* const clock_;
+
VideoCodec config_ RTC_GUARDED_BY(crit_sect_);
EncodedImageCallback* callback_ RTC_GUARDED_BY(crit_sect_);
VideoBitrateAllocation target_bitrate_ RTC_GUARDED_BY(crit_sect_);
@@ -60,6 +83,7 @@
rtc::CriticalSection crit_sect_;
uint8_t encoded_buffer_[100000];
+ bool used_layers_[kMaxSimulcastStreams];
// Current byte debt to be payed over a number of frames.
// The debt is acquired by keyframes overshooting the bitrate target.
diff --git a/test/fake_vp8_encoder.cc b/test/fake_vp8_encoder.cc
new file mode 100644
index 0000000..9d05297
--- /dev/null
+++ b/test/fake_vp8_encoder.cc
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018 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 "test/fake_vp8_encoder.h"
+
+#include "common_types.h" // NOLINT(build/include)
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/utility/simulcast_utility.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/random.h"
+#include "rtc_base/timeutils.h"
+
+namespace webrtc {
+
+namespace test {
+
+FakeVP8Encoder::FakeVP8Encoder(Clock* clock)
+ : FakeEncoder(clock), callback_(nullptr) {
+ FakeEncoder::RegisterEncodeCompleteCallback(this);
+ sequence_checker_.Detach();
+}
+
+int32_t FakeVP8Encoder::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+ callback_ = callback;
+ return 0;
+}
+
+int32_t FakeVP8Encoder::InitEncode(const VideoCodec* config,
+ int32_t number_of_cores,
+ size_t max_payload_size) {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+ auto result =
+ FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
+ if (result != WEBRTC_VIDEO_CODEC_OK) {
+ return result;
+ }
+
+ int number_of_streams = SimulcastUtility::NumberOfSimulcastStreams(*config);
+ bool doing_simulcast = number_of_streams > 1;
+
+ int num_temporal_layers =
+ doing_simulcast ? config->simulcastStream[0].numberOfTemporalLayers
+ : config->VP8().numberOfTemporalLayers;
+ RTC_DCHECK_GT(num_temporal_layers, 0);
+
+ SetupTemporalLayers(number_of_streams, num_temporal_layers, *config);
+
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeVP8Encoder::Release() {
+ auto result = FakeEncoder::Release();
+ sequence_checker_.Detach();
+ return result;
+}
+
+void FakeVP8Encoder::SetupTemporalLayers(int num_streams,
+ int num_temporal_layers,
+ const VideoCodec& codec) {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+
+ temporal_layers_.clear();
+ for (int i = 0; i < num_streams; ++i) {
+ temporal_layers_.emplace_back(
+ TemporalLayers::CreateTemporalLayers(codec, i));
+ }
+}
+
+void FakeVP8Encoder::PopulateCodecSpecific(
+ CodecSpecificInfo* codec_specific,
+ const TemporalLayers::FrameConfig& tl_config,
+ FrameType frame_type,
+ int stream_idx,
+ uint32_t timestamp) {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+ codec_specific->codecType = kVideoCodecVP8;
+ codec_specific->codec_name = ImplementationName();
+ CodecSpecificInfoVP8* vp8Info = &(codec_specific->codecSpecific.VP8);
+ vp8Info->simulcastIdx = stream_idx;
+ vp8Info->keyIdx = kNoKeyIdx;
+ vp8Info->nonReference = false;
+ temporal_layers_[stream_idx]->PopulateCodecSpecific(
+ frame_type == kVideoFrameKey, tl_config, vp8Info, timestamp);
+}
+
+EncodedImageCallback::Result FakeVP8Encoder::OnEncodedImage(
+ const EncodedImage& encoded_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const RTPFragmentationHeader* fragments) {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+ uint8_t stream_idx = codec_specific_info->codecSpecific.generic.simulcast_idx;
+ CodecSpecificInfo overrided_specific_info;
+ TemporalLayers::FrameConfig tl_config =
+ temporal_layers_[stream_idx]->UpdateLayerConfig(encoded_image._timeStamp);
+ PopulateCodecSpecific(&overrided_specific_info, tl_config,
+ encoded_image._frameType, stream_idx,
+ encoded_image._timeStamp);
+ temporal_layers_[stream_idx]->FrameEncoded(encoded_image._timeStamp,
+ encoded_image._length, -1);
+
+ return callback_->OnEncodedImage(encoded_image, &overrided_specific_info,
+ fragments);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/test/fake_vp8_encoder.h b/test/fake_vp8_encoder.h
new file mode 100644
index 0000000..407361b
--- /dev/null
+++ b/test/fake_vp8_encoder.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef TEST_FAKE_VP8_ENCODER_H_
+#define TEST_FAKE_VP8_ENCODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
+#include "test/fake_encoder.h"
+
+#include "rtc_base/criticalsection.h"
+#include "rtc_base/sequenced_task_checker.h"
+
+namespace webrtc {
+namespace test {
+
+class FakeVP8Encoder : public FakeEncoder, public EncodedImageCallback {
+ public:
+ explicit FakeVP8Encoder(Clock* clock);
+ virtual ~FakeVP8Encoder() = default;
+
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override;
+
+ int32_t InitEncode(const VideoCodec* config,
+ int32_t number_of_cores,
+ size_t max_payload_size) override;
+
+ int32_t Release() override;
+
+ const char* ImplementationName() const override { return "FakeVp8Encoder"; }
+
+ Result OnEncodedImage(const EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragments) override;
+
+ private:
+ void SetupTemporalLayers(int num_streams,
+ int num_temporal_layers,
+ const VideoCodec& codec);
+ void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
+ const TemporalLayers::FrameConfig& tl_config,
+ FrameType frame_type,
+ int stream_idx,
+ uint32_t timestamp);
+
+ rtc::SequencedTaskChecker sequence_checker_;
+ EncodedImageCallback* callback_ RTC_GUARDED_BY(sequence_checker_);
+
+ std::vector<std::unique_ptr<TemporalLayers>> temporal_layers_
+ RTC_GUARDED_BY(sequence_checker_);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_FAKE_VP8_ENCODER_H_
diff --git a/test/fake_vp8_encoder_unittest.cc b/test/fake_vp8_encoder_unittest.cc
new file mode 100644
index 0000000..c79ba0c
--- /dev/null
+++ b/test/fake_vp8_encoder_unittest.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018 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 <memory>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "api/test/create_simulcast_test_fixture.h"
+#include "api/test/simulcast_test_fixture.h"
+#include "modules/video_coding/utility/simulcast_test_fixture_impl.h"
+#include "test/fake_decoder.h"
+#include "test/fake_vp8_encoder.h"
+#include "test/function_video_decoder_factory.h"
+#include "test/function_video_encoder_factory.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+std::unique_ptr<SimulcastTestFixture> CreateSpecificSimulcastTestFixture() {
+ std::unique_ptr<VideoEncoderFactory> encoder_factory =
+ absl::make_unique<FunctionVideoEncoderFactory>([]() {
+ return absl::make_unique<FakeVP8Encoder>(Clock::GetRealTimeClock());
+ });
+ std::unique_ptr<VideoDecoderFactory> decoder_factory =
+ absl::make_unique<FunctionVideoDecoderFactory>(
+ []() { return absl::make_unique<FakeDecoder>(); });
+ return CreateSimulcastTestFixture(std::move(encoder_factory),
+ std::move(decoder_factory),
+ SdpVideoFormat("VP8"));
+}
+} // namespace
+
+TEST(TestFakeVp8Codec, TestKeyFrameRequestsOnAllStreams) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestKeyFrameRequestsOnAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingAllStreams) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestPaddingAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingTwoStreams) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestPaddingTwoStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingTwoStreamsOneMaxedOut) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestPaddingTwoStreamsOneMaxedOut();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingOneStream) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestPaddingOneStream();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingOneStreamTwoMaxedOut) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestPaddingOneStreamTwoMaxedOut();
+}
+
+TEST(TestFakeVp8Codec, TestSendAllStreams) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestSendAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestDisablingStreams) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestDisablingStreams();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneStream) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestSwitchingToOneStream();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneOddStream) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestSwitchingToOneOddStream();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneSmallStream) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestSwitchingToOneSmallStream();
+}
+
+TEST(TestFakeVp8Codec, TestSpatioTemporalLayers333PatternEncoder) {
+ auto fixture = CreateSpecificSimulcastTestFixture();
+ fixture->TestSpatioTemporalLayers333PatternEncoder();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/video/BUILD.gn b/video/BUILD.gn
index d2a52fb..d6e529f 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -361,6 +361,7 @@
"../system_wrappers",
"../system_wrappers:metrics_default",
"../system_wrappers:runtime_enabled_features_default",
+ "../test:fake_video_codecs",
"../test:field_trial",
"../test:rtp_test_utils",
"../test:run_test",
@@ -472,6 +473,7 @@
"../system_wrappers:metrics_api",
"../system_wrappers:metrics_default",
"../test:direct_transport",
+ "../test:fake_video_codecs",
"../test:field_trial",
"../test:fileutils",
"../test:perf_test",
diff --git a/video/end_to_end_tests/extended_reports_tests.cc b/video/end_to_end_tests/extended_reports_tests.cc
index 322a9c3..acffcd6 100644
--- a/video/end_to_end_tests/extended_reports_tests.cc
+++ b/video/end_to_end_tests/extended_reports_tests.cc
@@ -30,7 +30,12 @@
sent_rtcp_rrtr_(0),
sent_rtcp_target_bitrate_(false),
sent_zero_rtcp_target_bitrate_(false),
- sent_rtcp_dlrr_(0) {}
+ sent_rtcp_dlrr_(0),
+ send_transport_(nullptr) {
+ forward_transport_config_.link_capacity_kbps = 500;
+ forward_transport_config_.queue_delay_ms = 0;
+ forward_transport_config_.loss_percent = 0;
+ }
private:
// Receive stream should send RR packets (and RRTR packets if enabled).
@@ -56,6 +61,14 @@
test::RtcpPacketParser parser;
EXPECT_TRUE(parser.Parse(packet, length));
+ if (parser.sender_ssrc() == test::CallTest::kVideoSendSsrcs[1] &&
+ enable_zero_target_bitrate_) {
+ // Reduce bandwidth restriction to disable second stream after it was
+ // enabled for some time.
+ forward_transport_config_.link_capacity_kbps = 200;
+ send_transport_->SetConfig(forward_transport_config_);
+ }
+
sent_rtcp_sr_ += parser.sender_report()->num_packets();
EXPECT_LE(parser.xr()->num_packets(), 1);
if (parser.xr()->num_packets() > 0) {
@@ -64,8 +77,12 @@
++sent_rtcp_dlrr_;
if (parser.xr()->target_bitrate()) {
sent_rtcp_target_bitrate_ = true;
- for (const rtcp::TargetBitrate::BitrateItem& item :
- parser.xr()->target_bitrate()->GetTargetBitrates()) {
+ auto target_bitrates =
+ parser.xr()->target_bitrate()->GetTargetBitrates();
+ if (target_bitrates.empty()) {
+ sent_zero_rtcp_target_bitrate_ = true;
+ }
+ for (const rtcp::TargetBitrate::BitrateItem& item : target_bitrates) {
if (item.target_bitrate_kbps == 0) {
sent_zero_rtcp_target_bitrate_ = true;
break;
@@ -98,39 +115,20 @@
return enable_zero_target_bitrate_ ? 2 : 1;
}
- // This test uses VideoStream settings different from the the default one
- // implemented in DefaultVideoStreamFactory, so it implements its own
- // VideoEncoderConfig::VideoStreamFactoryInterface which is created
- // in ModifyVideoConfigs.
- class ZeroTargetVideoStreamFactory
- : public VideoEncoderConfig::VideoStreamFactoryInterface {
- public:
- ZeroTargetVideoStreamFactory() {}
-
- private:
- std::vector<VideoStream> CreateEncoderStreams(
- int width,
- int height,
- const VideoEncoderConfig& encoder_config) override {
- std::vector<VideoStream> streams =
- test::CreateVideoStreams(width, height, encoder_config);
- // Set one of the streams' target bitrates to zero to test that a
- // bitrate of 0 can be signalled.
- streams[encoder_config.number_of_streams - 1].min_bitrate_bps = 0;
- streams[encoder_config.number_of_streams - 1].target_bitrate_bps = 0;
- streams[encoder_config.number_of_streams - 1].max_bitrate_bps = 0;
- return streams;
- }
- };
+ test::PacketTransport* CreateSendTransport(
+ test::SingleThreadedTaskQueueForTesting* task_queue,
+ Call* sender_call) {
+ send_transport_ = new test::PacketTransport(
+ task_queue, sender_call, this, test::PacketTransport::kSender,
+ test::CallTest::payload_type_map_, forward_transport_config_);
+ return send_transport_;
+ }
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
if (enable_zero_target_bitrate_) {
- encoder_config->video_stream_factory =
- new rtc::RefCountedObject<ZeroTargetVideoStreamFactory>();
-
// Configure VP8 to be able to use simulcast.
send_config->rtp.payload_name = "VP8";
encoder_config->codec_type = kVideoCodecVP8;
@@ -166,6 +164,8 @@
bool sent_rtcp_target_bitrate_ RTC_GUARDED_BY(&crit_);
bool sent_zero_rtcp_target_bitrate_ RTC_GUARDED_BY(&crit_);
int sent_rtcp_dlrr_;
+ DefaultNetworkSimulationConfig forward_transport_config_;
+ test::PacketTransport* send_transport_;
};
TEST_F(ExtendedReportsEndToEndTest,