summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc')
-rw-r--r--third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc291
1 files changed, 291 insertions, 0 deletions
diff --git a/third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc b/third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc
new file mode 100644
index 0000000000..d8ac606bfd
--- /dev/null
+++ b/third_party/libwebrtc/video/end_to_end_tests/multi_codec_receive_tests.cc
@@ -0,0 +1,291 @@
+/*
+ * 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 "api/test/simulated_network.h"
+#include "api/test/video/function_video_encoder_factory.h"
+#include "call/fake_network_pipe.h"
+#include "call/simulated_network.h"
+#include "modules/include/module_common_types_public.h"
+#include "modules/rtp_rtcp/source/rtp_packet.h"
+#include "modules/video_coding/codecs/h264/include/h264.h"
+#include "modules/video_coding/codecs/vp8/include/vp8.h"
+#include "modules/video_coding/codecs/vp9/include/vp9.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "test/call_test.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::Contains;
+
+namespace webrtc {
+namespace {
+constexpr int kWidth = 1280;
+constexpr int kHeight = 720;
+constexpr int kFps = 30;
+constexpr int kFramesToObserve = 10;
+
+uint8_t PayloadNameToPayloadType(const std::string& payload_name) {
+ if (payload_name == "VP8") {
+ return test::CallTest::kPayloadTypeVP8;
+ } else if (payload_name == "VP9") {
+ return test::CallTest::kPayloadTypeVP9;
+ } else if (payload_name == "H264") {
+ return test::CallTest::kPayloadTypeH264;
+ } else {
+ RTC_DCHECK_NOTREACHED();
+ return 0;
+ }
+}
+
+int RemoveOlderOrEqual(uint32_t timestamp, std::vector<uint32_t>* timestamps) {
+ int num_removed = 0;
+ while (!timestamps->empty()) {
+ auto it = timestamps->begin();
+ if (IsNewerTimestamp(*it, timestamp))
+ break;
+
+ timestamps->erase(it);
+ ++num_removed;
+ }
+ return num_removed;
+}
+
+class FrameObserver : public test::RtpRtcpObserver,
+ public rtc::VideoSinkInterface<VideoFrame> {
+ public:
+ FrameObserver() : test::RtpRtcpObserver(test::CallTest::kDefaultTimeout) {}
+
+ void Reset(uint8_t expected_payload_type) {
+ MutexLock lock(&mutex_);
+ num_sent_frames_ = 0;
+ num_rendered_frames_ = 0;
+ expected_payload_type_ = expected_payload_type;
+ }
+
+ private:
+ // Sends kFramesToObserve.
+ Action OnSendRtp(const uint8_t* packet, size_t length) override {
+ MutexLock lock(&mutex_);
+
+ RtpPacket rtp_packet;
+ EXPECT_TRUE(rtp_packet.Parse(packet, length));
+ EXPECT_EQ(rtp_packet.Ssrc(), test::CallTest::kVideoSendSsrcs[0]);
+ if (rtp_packet.payload_size() == 0)
+ return SEND_PACKET; // Skip padding, may be sent after OnFrame is called.
+
+ if (expected_payload_type_ &&
+ rtp_packet.PayloadType() != expected_payload_type_.value()) {
+ return DROP_PACKET; // All frames sent.
+ }
+
+ if (!last_timestamp_ || rtp_packet.Timestamp() != *last_timestamp_) {
+ // New frame.
+ // Sent enough frames?
+ if (num_sent_frames_ >= kFramesToObserve)
+ return DROP_PACKET;
+
+ ++num_sent_frames_;
+ sent_timestamps_.push_back(rtp_packet.Timestamp());
+ }
+
+ last_timestamp_ = rtp_packet.Timestamp();
+ return SEND_PACKET;
+ }
+
+ // Verifies that all sent frames are decoded and rendered.
+ void OnFrame(const VideoFrame& rendered_frame) override {
+ MutexLock lock(&mutex_);
+ EXPECT_THAT(sent_timestamps_, Contains(rendered_frame.timestamp()));
+
+ // Remove old timestamps too, only the newest decoded frame is rendered.
+ num_rendered_frames_ +=
+ RemoveOlderOrEqual(rendered_frame.timestamp(), &sent_timestamps_);
+
+ if (num_rendered_frames_ >= kFramesToObserve) {
+ EXPECT_TRUE(sent_timestamps_.empty()) << "All sent frames not decoded.";
+ observation_complete_.Set();
+ }
+ }
+
+ Mutex mutex_;
+ absl::optional<uint32_t> last_timestamp_; // Only accessed from pacer thread.
+ absl::optional<uint8_t> expected_payload_type_ RTC_GUARDED_BY(mutex_);
+ int num_sent_frames_ RTC_GUARDED_BY(mutex_) = 0;
+ int num_rendered_frames_ RTC_GUARDED_BY(mutex_) = 0;
+ std::vector<uint32_t> sent_timestamps_ RTC_GUARDED_BY(mutex_);
+};
+} // namespace
+
+class MultiCodecReceiveTest : public test::CallTest {
+ public:
+ MultiCodecReceiveTest() {
+ SendTask(task_queue(), [this]() {
+ CreateCalls();
+ CreateSendTransport(BuiltInNetworkBehaviorConfig(), &observer_);
+ CreateReceiveTransport(BuiltInNetworkBehaviorConfig(), &observer_);
+ });
+ }
+
+ virtual ~MultiCodecReceiveTest() {
+ SendTask(task_queue(), [this]() {
+ send_transport_.reset();
+ receive_transport_.reset();
+ DestroyCalls();
+ });
+ }
+
+ struct CodecConfig {
+ std::string payload_name;
+ size_t num_temporal_layers;
+ };
+
+ void ConfigureEncoder(const CodecConfig& config,
+ VideoEncoderFactory* encoder_factory);
+ void ConfigureDecoders(const std::vector<CodecConfig>& configs,
+ VideoDecoderFactory* decoder_factory);
+ void RunTestWithCodecs(const std::vector<CodecConfig>& configs);
+
+ private:
+ FrameObserver observer_;
+};
+
+void MultiCodecReceiveTest::ConfigureDecoders(
+ const std::vector<CodecConfig>& configs,
+ VideoDecoderFactory* decoder_factory) {
+ video_receive_configs_[0].decoders.clear();
+ // Placing the payload names in a std::set retains the unique names only.
+ video_receive_configs_[0].decoder_factory = decoder_factory;
+ std::set<std::string> unique_payload_names;
+ for (const auto& config : configs)
+ if (unique_payload_names.insert(config.payload_name).second) {
+ VideoReceiveStreamInterface::Decoder decoder =
+ test::CreateMatchingDecoder(
+ PayloadNameToPayloadType(config.payload_name),
+ config.payload_name);
+
+ video_receive_configs_[0].decoders.push_back(decoder);
+ }
+}
+
+void MultiCodecReceiveTest::ConfigureEncoder(
+ const CodecConfig& config,
+ VideoEncoderFactory* encoder_factory) {
+ GetVideoSendConfig()->encoder_settings.encoder_factory = encoder_factory;
+ GetVideoSendConfig()->rtp.payload_name = config.payload_name;
+ GetVideoSendConfig()->rtp.payload_type =
+ PayloadNameToPayloadType(config.payload_name);
+ GetVideoEncoderConfig()->codec_type =
+ PayloadStringToCodecType(config.payload_name);
+ EXPECT_EQ(1u, GetVideoEncoderConfig()->simulcast_layers.size());
+ GetVideoEncoderConfig()->simulcast_layers[0].num_temporal_layers =
+ config.num_temporal_layers;
+ GetVideoEncoderConfig()->video_format.name = config.payload_name;
+}
+
+void MultiCodecReceiveTest::RunTestWithCodecs(
+ const std::vector<CodecConfig>& configs) {
+ EXPECT_TRUE(!configs.empty());
+
+ test::FunctionVideoEncoderFactory encoder_factory(
+ [](const SdpVideoFormat& format) -> std::unique_ptr<VideoEncoder> {
+ if (format.name == "VP8") {
+ return VP8Encoder::Create();
+ }
+ if (format.name == "VP9") {
+ return VP9Encoder::Create();
+ }
+ if (format.name == "H264") {
+ return H264Encoder::Create(cricket::VideoCodec("H264"));
+ }
+ RTC_DCHECK_NOTREACHED() << format.name;
+ return nullptr;
+ });
+ test::FunctionVideoDecoderFactory decoder_factory(
+ [](const SdpVideoFormat& format) -> std::unique_ptr<VideoDecoder> {
+ if (format.name == "VP8") {
+ return VP8Decoder::Create();
+ }
+ if (format.name == "VP9") {
+ return VP9Decoder::Create();
+ }
+ if (format.name == "H264") {
+ return H264Decoder::Create();
+ }
+ RTC_DCHECK_NOTREACHED() << format.name;
+ return nullptr;
+ });
+ // Create and start call.
+ SendTask(task_queue(),
+ [this, &configs, &encoder_factory, &decoder_factory]() {
+ CreateSendConfig(1, 0, 0);
+ ConfigureEncoder(configs[0], &encoder_factory);
+ CreateMatchingReceiveConfigs();
+ video_receive_configs_[0].renderer = &observer_;
+ // Disable to avoid post-decode frame dropping in
+ // VideoRenderFrames.
+ video_receive_configs_[0].enable_prerenderer_smoothing = false;
+ ConfigureDecoders(configs, &decoder_factory);
+ CreateVideoStreams();
+ CreateFrameGeneratorCapturer(kFps, kWidth, kHeight);
+ Start();
+ });
+ EXPECT_TRUE(observer_.Wait()) << "Timed out waiting for frames.";
+
+ for (size_t i = 1; i < configs.size(); ++i) {
+ // Recreate VideoSendStream with new config (codec, temporal layers).
+ SendTask(task_queue(), [this, i, &configs, &encoder_factory]() {
+ DestroyVideoSendStreams();
+ observer_.Reset(PayloadNameToPayloadType(configs[i].payload_name));
+
+ ConfigureEncoder(configs[i], &encoder_factory);
+ CreateVideoSendStreams();
+ GetVideoSendStream()->Start();
+ CreateFrameGeneratorCapturer(kFps, kWidth / 2, kHeight / 2);
+ ConnectVideoSourcesToStreams();
+ });
+ EXPECT_TRUE(observer_.Wait()) << "Timed out waiting for frames.";
+ }
+
+ SendTask(task_queue(), [this]() {
+ Stop();
+ DestroyStreams();
+ });
+}
+
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9) {
+ RunTestWithCodecs({{"VP8", 1}, {"VP9", 1}, {"VP8", 1}});
+}
+
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9WithTl) {
+ RunTestWithCodecs({{"VP8", 2}, {"VP9", 2}, {"VP8", 2}});
+}
+
+#if defined(WEBRTC_USE_H264)
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8H264) {
+ RunTestWithCodecs({{"VP8", 1}, {"H264", 1}, {"VP8", 1}});
+}
+
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8H264WithTl) {
+ RunTestWithCodecs({{"VP8", 3}, {"H264", 1}, {"VP8", 3}});
+}
+
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9H264) {
+ RunTestWithCodecs({{"VP8", 1}, {"VP9", 1}, {"H264", 1}, {"VP9", 1}});
+}
+
+TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9H264WithTl) {
+ RunTestWithCodecs({{"VP8", 3}, {"VP9", 2}, {"H264", 1}, {"VP9", 3}});
+}
+#endif // defined(WEBRTC_USE_H264)
+
+} // namespace webrtc