blob: 4e070e795154b5941a3f1c6e36848f554aab6963 [file] [log] [blame]
/*
* 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 "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/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 "test/call_test.h"
#include "test/gtest.h"
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_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::kDefaultTimeoutMs) {}
void Reset() {
rtc::CritScope lock(&crit_);
num_sent_frames_ = 0;
num_rendered_frames_ = 0;
}
private:
// Sends kFramesToObserve.
Action OnSendRtp(const uint8_t* packet, size_t length) override {
rtc::CritScope lock(&crit_);
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, length, &header));
EXPECT_EQ(header.ssrc, test::CallTest::kVideoSendSsrcs[0]);
EXPECT_GE(length, header.headerLength + header.paddingLength);
if ((length - header.headerLength) == header.paddingLength)
return SEND_PACKET; // Skip padding, may be sent after OnFrame is called.
if (!last_timestamp_ || header.timestamp != *last_timestamp_) {
// New frame.
if (last_payload_type_) {
bool new_payload_type = header.payloadType != *last_payload_type_;
EXPECT_EQ(num_sent_frames_ == 0, new_payload_type)
<< "Payload type should change after reset.";
}
// Sent enough frames?
if (num_sent_frames_ >= kFramesToObserve)
return DROP_PACKET;
++num_sent_frames_;
sent_timestamps_.push_back(header.timestamp);
}
last_timestamp_ = header.timestamp;
last_payload_type_ = header.payloadType;
return SEND_PACKET;
}
// Verifies that all sent frames are decoded and rendered.
void OnFrame(const VideoFrame& rendered_frame) override {
rtc::CritScope lock(&crit_);
EXPECT_NE(std::find(sent_timestamps_.begin(), sent_timestamps_.end(),
rendered_frame.timestamp()),
sent_timestamps_.end());
// 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();
}
}
rtc::CriticalSection crit_;
absl::optional<uint32_t> last_timestamp_;
absl::optional<uint8_t> last_payload_type_;
int num_sent_frames_ RTC_GUARDED_BY(crit_) = 0;
int num_rendered_frames_ RTC_GUARDED_BY(crit_) = 0;
std::vector<uint32_t> sent_timestamps_ RTC_GUARDED_BY(crit_);
};
} // namespace
class MultiCodecReceiveTest : public test::CallTest {
public:
MultiCodecReceiveTest() {
task_queue_.SendTask([this]() {
CreateCalls();
send_transport_.reset(new test::PacketTransport(
&task_queue_, sender_call_.get(), &observer_,
test::PacketTransport::kSender, kPayloadTypeMap,
absl::make_unique<FakeNetworkPipe>(
Clock::GetRealTimeClock(), absl::make_unique<SimulatedNetwork>(
BuiltInNetworkBehaviorConfig()))));
send_transport_->SetReceiver(receiver_call_->Receiver());
receive_transport_.reset(new test::PacketTransport(
&task_queue_, receiver_call_.get(), &observer_,
test::PacketTransport::kReceiver, kPayloadTypeMap,
absl::make_unique<FakeNetworkPipe>(
Clock::GetRealTimeClock(), absl::make_unique<SimulatedNetwork>(
BuiltInNetworkBehaviorConfig()))));
receive_transport_->SetReceiver(sender_call_->Receiver());
});
}
virtual ~MultiCodecReceiveTest() {
task_queue_.SendTask([this]() {
send_transport_.reset();
receive_transport_.reset();
DestroyCalls();
});
}
struct CodecConfig {
std::string payload_name;
VideoEncoderFactory* encoder_factory;
VideoDecoderFactory* decoder_factory;
size_t num_temporal_layers;
};
void ConfigureEncoder(const CodecConfig& config);
void ConfigureDecoders(const std::vector<CodecConfig>& configs);
void RunTestWithCodecs(const std::vector<CodecConfig>& configs);
private:
const std::map<uint8_t, MediaType> kPayloadTypeMap = {
{CallTest::kPayloadTypeVP8, MediaType::VIDEO},
{CallTest::kPayloadTypeVP9, MediaType::VIDEO},
{CallTest::kPayloadTypeH264, MediaType::VIDEO}};
FrameObserver observer_;
};
void MultiCodecReceiveTest::ConfigureDecoders(
const std::vector<CodecConfig>& configs) {
video_receive_configs_[0].decoders.clear();
// Placing the payload names in a std::set retains the unique names only.
std::set<std::string> unique_payload_names;
for (const auto& config : configs)
if (unique_payload_names.insert(config.payload_name).second) {
VideoReceiveStream::Decoder decoder = test::CreateMatchingDecoder(
PayloadNameToPayloadType(config.payload_name), config.payload_name);
decoder.decoder_factory = config.decoder_factory;
video_receive_configs_[0].decoders.push_back(decoder);
}
}
void MultiCodecReceiveTest::ConfigureEncoder(const CodecConfig& config) {
GetVideoSendConfig()->encoder_settings.encoder_factory =
config.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;
}
void MultiCodecReceiveTest::RunTestWithCodecs(
const std::vector<CodecConfig>& configs) {
EXPECT_TRUE(!configs.empty());
// Create and start call.
task_queue_.SendTask([this, &configs]() {
CreateSendConfig(1, 0, 0, send_transport_.get());
ConfigureEncoder(configs[0]);
CreateMatchingReceiveConfigs(receive_transport_.get());
video_receive_configs_[0].renderer = &observer_;
ConfigureDecoders(configs);
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).
task_queue_.SendTask([this, i, &configs]() {
DestroyVideoSendStreams();
observer_.Reset();
ConfigureEncoder(configs[i]);
CreateVideoSendStreams();
GetVideoSendStream()->Start();
CreateFrameGeneratorCapturer(kFps, kWidth / 2, kHeight / 2);
ConnectVideoSourcesToStreams();
});
EXPECT_TRUE(observer_.Wait()) << "Timed out waiting for frames.";
}
task_queue_.SendTask([this]() {
Stop();
DestroyStreams();
});
}
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory vp9_encoder_factory(
[]() { return VP9Encoder::Create(); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory vp9_decoder_factory(
[]() { return VP9Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 1},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 1},
{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 1}});
}
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9WithTl) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory vp9_encoder_factory(
[]() { return VP9Encoder::Create(); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory vp9_decoder_factory(
[]() { return VP9Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 2},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 2},
{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 2}});
}
#if defined(WEBRTC_USE_H264)
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8H264) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory h264_encoder_factory(
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory h264_decoder_factory(
[]() { return H264Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 1},
{"H264", &h264_encoder_factory, &h264_decoder_factory, 1},
{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 1}});
}
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8H264WithTl) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory h264_encoder_factory(
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory h264_decoder_factory(
[]() { return H264Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 3},
{"H264", &h264_encoder_factory, &h264_decoder_factory, 1},
{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 3}});
}
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9H264) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory vp9_encoder_factory(
[]() { return VP9Encoder::Create(); });
test::FunctionVideoEncoderFactory h264_encoder_factory(
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory vp9_decoder_factory(
[]() { return VP9Decoder::Create(); });
test::FunctionVideoDecoderFactory h264_decoder_factory(
[]() { return H264Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 1},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 1},
{"H264", &h264_encoder_factory, &h264_decoder_factory, 1},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 1}});
}
TEST_F(MultiCodecReceiveTest, SingleStreamReceivesVp8Vp9H264WithTl) {
test::FunctionVideoEncoderFactory vp8_encoder_factory(
[]() { return VP8Encoder::Create(); });
test::FunctionVideoEncoderFactory vp9_encoder_factory(
[]() { return VP9Encoder::Create(); });
test::FunctionVideoEncoderFactory h264_encoder_factory(
[]() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
test::FunctionVideoDecoderFactory vp8_decoder_factory(
[]() { return VP8Decoder::Create(); });
test::FunctionVideoDecoderFactory vp9_decoder_factory(
[]() { return VP9Decoder::Create(); });
test::FunctionVideoDecoderFactory h264_decoder_factory(
[]() { return H264Decoder::Create(); });
RunTestWithCodecs({{"VP8", &vp8_encoder_factory, &vp8_decoder_factory, 3},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 2},
{"H264", &h264_encoder_factory, &h264_decoder_factory, 1},
{"VP9", &vp9_encoder_factory, &vp9_decoder_factory, 3}});
}
#endif // defined(WEBRTC_USE_H264)
} // namespace webrtc