webrtc/media/engine/webrtc_voice_engine_unittest.cc
Tommi a136ed4085 Add SetTransportCc to ReceiveStreamInterface.
Setting the transport cc flag was only possible post creation for
audio receive streams, while video receive streams need to be recreated.

This CL moves the setter for transport_cc() to where the getter is and
adds boiler plate implementations for the video streams. For audio
streams this splits "SetUseTransportCcAndNackHistory" into two methods,
SetTransportCc and SetNackHistory.

Bug: none
Change-Id: Idbec8217aef10ee77907cebaecdc27b4b0fb18e4
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264443
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37038}
2022-05-30 14:07:04 +00:00

3889 lines
156 KiB
C++

/*
* Copyright (c) 2008 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 "media/engine/webrtc_voice_engine.h"
#include <memory>
#include <utility>
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/transport/field_trial_based_config.h"
#include "call/call.h"
#include "media/base/fake_media_engine.h"
#include "media/base/fake_network_interface.h"
#include "media/base/fake_rtp.h"
#include "media/base/media_constants.h"
#include "media/engine/fake_webrtc_call.h"
#include "modules/audio_device/include/mock_audio_device.h"
#include "modules/audio_processing/include/mock_audio_processing.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/byte_order.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "test/gtest.h"
#include "test/mock_audio_decoder_factory.h"
#include "test/mock_audio_encoder_factory.h"
#include "test/scoped_key_value_config.h"
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::Contains;
using ::testing::Field;
using ::testing::Return;
using ::testing::ReturnPointee;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace {
using webrtc::BitrateConstraints;
constexpr uint32_t kMaxUnsignaledRecvStreams = 4;
const cricket::AudioCodec kPcmuCodec(0, "PCMU", 8000, 64000, 1);
const cricket::AudioCodec kIsacCodec(103, "ISAC", 16000, 32000, 1);
const cricket::AudioCodec kOpusCodec(111, "opus", 48000, 32000, 2);
const cricket::AudioCodec kG722CodecVoE(9, "G722", 16000, 64000, 1);
const cricket::AudioCodec kG722CodecSdp(9, "G722", 8000, 64000, 1);
const cricket::AudioCodec kCn8000Codec(13, "CN", 8000, 0, 1);
const cricket::AudioCodec kCn16000Codec(105, "CN", 16000, 0, 1);
const cricket::AudioCodec kRed48000Codec(112, "RED", 48000, 32000, 2);
const cricket::AudioCodec kTelephoneEventCodec1(106,
"telephone-event",
8000,
0,
1);
const cricket::AudioCodec kTelephoneEventCodec2(107,
"telephone-event",
32000,
0,
1);
const uint32_t kSsrc0 = 0;
const uint32_t kSsrc1 = 1;
const uint32_t kSsrcX = 0x99;
const uint32_t kSsrcY = 0x17;
const uint32_t kSsrcZ = 0x42;
const uint32_t kSsrcW = 0x02;
const uint32_t kSsrcs4[] = {11, 200, 30, 44};
constexpr int kRtpHistoryMs = 5000;
constexpr webrtc::AudioProcessing::Config::GainController1::Mode
kDefaultAgcMode =
#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
webrtc::AudioProcessing::Config::GainController1::kFixedDigital;
#else
webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
#endif
constexpr webrtc::AudioProcessing::Config::NoiseSuppression::Level
kDefaultNsLevel =
webrtc::AudioProcessing::Config::NoiseSuppression::Level::kHigh;
void AdmSetupExpectations(webrtc::test::MockAudioDeviceModule* adm) {
RTC_DCHECK(adm);
// Setup.
EXPECT_CALL(*adm, Init()).WillOnce(Return(0));
EXPECT_CALL(*adm, RegisterAudioCallback(_)).WillOnce(Return(0));
#if defined(WEBRTC_WIN)
EXPECT_CALL(
*adm,
SetPlayoutDevice(
::testing::Matcher<webrtc::AudioDeviceModule::WindowsDeviceType>(
webrtc::AudioDeviceModule::kDefaultCommunicationDevice)))
.WillOnce(Return(0));
#else
EXPECT_CALL(*adm, SetPlayoutDevice(0)).WillOnce(Return(0));
#endif // #if defined(WEBRTC_WIN)
EXPECT_CALL(*adm, InitSpeaker()).WillOnce(Return(0));
EXPECT_CALL(*adm, StereoPlayoutIsAvailable(::testing::_)).WillOnce(Return(0));
EXPECT_CALL(*adm, SetStereoPlayout(false)).WillOnce(Return(0));
#if defined(WEBRTC_WIN)
EXPECT_CALL(
*adm,
SetRecordingDevice(
::testing::Matcher<webrtc::AudioDeviceModule::WindowsDeviceType>(
webrtc::AudioDeviceModule::kDefaultCommunicationDevice)))
.WillOnce(Return(0));
#else
EXPECT_CALL(*adm, SetRecordingDevice(0)).WillOnce(Return(0));
#endif // #if defined(WEBRTC_WIN)
EXPECT_CALL(*adm, InitMicrophone()).WillOnce(Return(0));
EXPECT_CALL(*adm, StereoRecordingIsAvailable(::testing::_))
.WillOnce(Return(0));
EXPECT_CALL(*adm, SetStereoRecording(false)).WillOnce(Return(0));
EXPECT_CALL(*adm, BuiltInAECIsAvailable()).WillOnce(Return(false));
EXPECT_CALL(*adm, BuiltInAGCIsAvailable()).WillOnce(Return(false));
EXPECT_CALL(*adm, BuiltInNSIsAvailable()).WillOnce(Return(false));
// Teardown.
EXPECT_CALL(*adm, StopPlayout()).WillOnce(Return(0));
EXPECT_CALL(*adm, StopRecording()).WillOnce(Return(0));
EXPECT_CALL(*adm, RegisterAudioCallback(nullptr)).WillOnce(Return(0));
EXPECT_CALL(*adm, Terminate()).WillOnce(Return(0));
}
} // namespace
// Tests that our stub library "works".
TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) {
for (bool use_null_apm : {false, true}) {
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateStrict();
AdmSetupExpectations(adm.get());
rtc::scoped_refptr<StrictMock<webrtc::test::MockAudioProcessing>> apm =
use_null_apm ? nullptr
: rtc::make_ref_counted<
StrictMock<webrtc::test::MockAudioProcessing>>();
webrtc::AudioProcessing::Config apm_config;
if (!use_null_apm) {
EXPECT_CALL(*apm, GetConfig()).WillRepeatedly(ReturnPointee(&apm_config));
EXPECT_CALL(*apm, ApplyConfig(_)).WillRepeatedly(SaveArg<0>(&apm_config));
EXPECT_CALL(*apm, DetachAecDump());
}
{
webrtc::FieldTrialBasedConfig trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
nullptr, trials);
engine.Init();
}
}
}
class FakeAudioSink : public webrtc::AudioSinkInterface {
public:
void OnData(const Data& audio) override {}
};
class FakeAudioSource : public cricket::AudioSource {
void SetSink(Sink* sink) override {}
};
class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam<bool> {
public:
WebRtcVoiceEngineTestFake()
: use_null_apm_(GetParam()),
task_queue_factory_(webrtc::CreateDefaultTaskQueueFactory()),
adm_(webrtc::test::MockAudioDeviceModule::CreateStrict()),
apm_(use_null_apm_
? nullptr
: rtc::make_ref_counted<
StrictMock<webrtc::test::MockAudioProcessing>>()),
call_(&field_trials_) {
// AudioDeviceModule.
AdmSetupExpectations(adm_.get());
if (!use_null_apm_) {
// AudioProcessing.
EXPECT_CALL(*apm_, GetConfig())
.WillRepeatedly(ReturnPointee(&apm_config_));
EXPECT_CALL(*apm_, ApplyConfig(_))
.WillRepeatedly(SaveArg<0>(&apm_config_));
EXPECT_CALL(*apm_, DetachAecDump());
}
// Default Options.
// TODO(kwiberg): We should use mock factories here, but a bunch of
// the tests here probe the specific set of codecs provided by the builtin
// factories. Those tests should probably be moved elsewhere.
auto encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
auto decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
engine_.reset(new cricket::WebRtcVoiceEngine(
task_queue_factory_.get(), adm_.get(), encoder_factory, decoder_factory,
nullptr, apm_, nullptr, field_trials_));
engine_->Init();
send_parameters_.codecs.push_back(kPcmuCodec);
recv_parameters_.codecs.push_back(kPcmuCodec);
if (!use_null_apm_) {
// Default Options.
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_TRUE(IsHighPassFilterEnabled());
EXPECT_TRUE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
VerifyGainControlEnabledCorrectly();
VerifyGainControlDefaultSettings();
}
}
bool SetupChannel() {
channel_ = engine_->CreateMediaChannel(&call_, cricket::MediaConfig(),
cricket::AudioOptions(),
webrtc::CryptoOptions());
return (channel_ != nullptr);
}
bool SetupRecvStream() {
if (!SetupChannel()) {
return false;
}
return AddRecvStream(kSsrcX);
}
bool SetupSendStream() {
return SetupSendStream(cricket::StreamParams::CreateLegacy(kSsrcX));
}
bool SetupSendStream(const cricket::StreamParams& sp) {
if (!SetupChannel()) {
return false;
}
if (!channel_->AddSendStream(sp)) {
return false;
}
if (!use_null_apm_) {
EXPECT_CALL(*apm_, set_output_will_be_muted(false));
}
return channel_->SetAudioSend(kSsrcX, true, nullptr, &fake_source_);
}
bool AddRecvStream(uint32_t ssrc) {
EXPECT_TRUE(channel_);
return channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc));
}
void SetupForMultiSendStream() {
EXPECT_TRUE(SetupSendStream());
// Remove stream added in Setup.
EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
EXPECT_TRUE(channel_->RemoveSendStream(kSsrcX));
// Verify the channel does not exist.
EXPECT_FALSE(call_.GetAudioSendStream(kSsrcX));
}
void DeliverPacket(const void* data, int len) {
rtc::CopyOnWriteBuffer packet(reinterpret_cast<const uint8_t*>(data), len);
channel_->OnPacketReceived(packet, /* packet_time_us */ -1);
rtc::Thread::Current()->ProcessMessages(0);
}
void TearDown() override { delete channel_; }
const cricket::FakeAudioSendStream& GetSendStream(uint32_t ssrc) {
const auto* send_stream = call_.GetAudioSendStream(ssrc);
EXPECT_TRUE(send_stream);
return *send_stream;
}
const cricket::FakeAudioReceiveStream& GetRecvStream(uint32_t ssrc) {
const auto* recv_stream = call_.GetAudioReceiveStream(ssrc);
EXPECT_TRUE(recv_stream);
return *recv_stream;
}
const webrtc::AudioSendStream::Config& GetSendStreamConfig(uint32_t ssrc) {
return GetSendStream(ssrc).GetConfig();
}
const webrtc::AudioReceiveStreamInterface::Config& GetRecvStreamConfig(
uint32_t ssrc) {
return GetRecvStream(ssrc).GetConfig();
}
void SetSend(bool enable) {
ASSERT_TRUE(channel_);
if (enable) {
EXPECT_CALL(*adm_, RecordingIsInitialized())
.Times(::testing::AtMost(1))
.WillOnce(Return(false));
EXPECT_CALL(*adm_, Recording())
.Times(::testing::AtMost(1))
.WillOnce(Return(false));
EXPECT_CALL(*adm_, InitRecording())
.Times(::testing::AtMost(1))
.WillOnce(Return(0));
}
channel_->SetSend(enable);
}
void SetSendParameters(const cricket::AudioSendParameters& params) {
ASSERT_TRUE(channel_);
EXPECT_TRUE(channel_->SetSendParameters(params));
}
void SetAudioSend(uint32_t ssrc,
bool enable,
cricket::AudioSource* source,
const cricket::AudioOptions* options = nullptr) {
ASSERT_TRUE(channel_);
if (!use_null_apm_) {
EXPECT_CALL(*apm_, set_output_will_be_muted(!enable));
}
EXPECT_TRUE(channel_->SetAudioSend(ssrc, enable, options, source));
}
void TestInsertDtmf(uint32_t ssrc,
bool caller,
const cricket::AudioCodec& codec) {
EXPECT_TRUE(SetupChannel());
if (caller) {
// If this is a caller, local description will be applied and add the
// send stream.
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
}
// Test we can only InsertDtmf when the other side supports telephone-event.
SetSendParameters(send_parameters_);
SetSend(true);
EXPECT_FALSE(channel_->CanInsertDtmf());
EXPECT_FALSE(channel_->InsertDtmf(ssrc, 1, 111));
send_parameters_.codecs.push_back(codec);
SetSendParameters(send_parameters_);
EXPECT_TRUE(channel_->CanInsertDtmf());
if (!caller) {
// If this is callee, there's no active send channel yet.
EXPECT_FALSE(channel_->InsertDtmf(ssrc, 2, 123));
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
}
// Check we fail if the ssrc is invalid.
EXPECT_FALSE(channel_->InsertDtmf(-1, 1, 111));
// Test send.
cricket::FakeAudioSendStream::TelephoneEvent telephone_event =
GetSendStream(kSsrcX).GetLatestTelephoneEvent();
EXPECT_EQ(-1, telephone_event.payload_type);
EXPECT_TRUE(channel_->InsertDtmf(ssrc, 2, 123));
telephone_event = GetSendStream(kSsrcX).GetLatestTelephoneEvent();
EXPECT_EQ(codec.id, telephone_event.payload_type);
EXPECT_EQ(codec.clockrate, telephone_event.payload_frequency);
EXPECT_EQ(2, telephone_event.event_code);
EXPECT_EQ(123, telephone_event.duration_ms);
}
void TestExtmapAllowMixedCaller(bool extmap_allow_mixed) {
// For a caller, the answer will be applied in set remote description
// where SetSendParameters() is called.
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
send_parameters_.extmap_allow_mixed = extmap_allow_mixed;
SetSendParameters(send_parameters_);
const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
}
void TestExtmapAllowMixedCallee(bool extmap_allow_mixed) {
// For a callee, the answer will be applied in set local description
// where SetExtmapAllowMixed() and AddSendStream() are called.
EXPECT_TRUE(SetupChannel());
channel_->SetExtmapAllowMixed(extmap_allow_mixed);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
}
// Test that send bandwidth is set correctly.
// `codec` is the codec under test.
// `max_bitrate` is a parameter to set to SetMaxSendBandwidth().
// `expected_result` is the expected result from SetMaxSendBandwidth().
// `expected_bitrate` is the expected audio bitrate afterward.
void TestMaxSendBandwidth(const cricket::AudioCodec& codec,
int max_bitrate,
bool expected_result,
int expected_bitrate) {
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(codec);
parameters.max_bandwidth_bps = max_bitrate;
if (expected_result) {
SetSendParameters(parameters);
} else {
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
EXPECT_EQ(expected_bitrate, GetCodecBitrate(kSsrcX));
}
// Sets the per-stream maximum bitrate limit for the specified SSRC.
bool SetMaxBitrateForStream(int32_t ssrc, int bitrate) {
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(ssrc);
EXPECT_EQ(1UL, parameters.encodings.size());
parameters.encodings[0].max_bitrate_bps = bitrate;
return channel_->SetRtpSendParameters(ssrc, parameters).ok();
}
void SetGlobalMaxBitrate(const cricket::AudioCodec& codec, int bitrate) {
cricket::AudioSendParameters send_parameters;
send_parameters.codecs.push_back(codec);
send_parameters.max_bandwidth_bps = bitrate;
SetSendParameters(send_parameters);
}
void CheckSendCodecBitrate(int32_t ssrc,
const char expected_name[],
int expected_bitrate) {
const auto& spec = GetSendStreamConfig(ssrc).send_codec_spec;
EXPECT_EQ(expected_name, spec->format.name);
EXPECT_EQ(expected_bitrate, spec->target_bitrate_bps);
}
absl::optional<int> GetCodecBitrate(int32_t ssrc) {
return GetSendStreamConfig(ssrc).send_codec_spec->target_bitrate_bps;
}
const absl::optional<std::string>& GetAudioNetworkAdaptorConfig(
int32_t ssrc) {
return GetSendStreamConfig(ssrc).audio_network_adaptor_config;
}
void SetAndExpectMaxBitrate(const cricket::AudioCodec& codec,
int global_max,
int stream_max,
bool expected_result,
int expected_codec_bitrate) {
// Clear the bitrate limit from the previous test case.
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcX, -1));
// Attempt to set the requested bitrate limits.
SetGlobalMaxBitrate(codec, global_max);
EXPECT_EQ(expected_result, SetMaxBitrateForStream(kSsrcX, stream_max));
// Verify that reading back the parameters gives results
// consistent with the Set() result.
webrtc::RtpParameters resulting_parameters =
channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(1UL, resulting_parameters.encodings.size());
EXPECT_EQ(expected_result ? stream_max : -1,
resulting_parameters.encodings[0].max_bitrate_bps);
// Verify that the codec settings have the expected bitrate.
EXPECT_EQ(expected_codec_bitrate, GetCodecBitrate(kSsrcX));
}
void SetSendCodecsShouldWorkForBitrates(const char* min_bitrate_kbps,
int expected_min_bitrate_bps,
const char* start_bitrate_kbps,
int expected_start_bitrate_bps,
const char* max_bitrate_kbps,
int expected_max_bitrate_bps) {
EXPECT_TRUE(SetupSendStream());
auto& codecs = send_parameters_.codecs;
codecs.clear();
codecs.push_back(kOpusCodec);
codecs[0].params[cricket::kCodecParamMinBitrate] = min_bitrate_kbps;
codecs[0].params[cricket::kCodecParamStartBitrate] = start_bitrate_kbps;
codecs[0].params[cricket::kCodecParamMaxBitrate] = max_bitrate_kbps;
EXPECT_CALL(*call_.GetMockTransportControllerSend(),
SetSdpBitrateParameters(
AllOf(Field(&BitrateConstraints::min_bitrate_bps,
expected_min_bitrate_bps),
Field(&BitrateConstraints::start_bitrate_bps,
expected_start_bitrate_bps),
Field(&BitrateConstraints::max_bitrate_bps,
expected_max_bitrate_bps))));
SetSendParameters(send_parameters_);
}
void TestSetSendRtpHeaderExtensions(const std::string& ext) {
EXPECT_TRUE(SetupSendStream());
// Ensure extensions are off by default.
EXPECT_EQ(0u, GetSendStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure unknown extensions won't cause an error.
send_parameters_.extensions.push_back(
webrtc::RtpExtension("urn:ietf:params:unknownextention", 1));
SetSendParameters(send_parameters_);
EXPECT_EQ(0u, GetSendStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure extensions stay off with an empty list of headers.
send_parameters_.extensions.clear();
SetSendParameters(send_parameters_);
EXPECT_EQ(0u, GetSendStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure extension is set properly.
const int id = 1;
send_parameters_.extensions.push_back(webrtc::RtpExtension(ext, id));
SetSendParameters(send_parameters_);
EXPECT_EQ(1u, GetSendStreamConfig(kSsrcX).rtp.extensions.size());
EXPECT_EQ(ext, GetSendStreamConfig(kSsrcX).rtp.extensions[0].uri);
EXPECT_EQ(id, GetSendStreamConfig(kSsrcX).rtp.extensions[0].id);
// Ensure extension is set properly on new stream.
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcY)));
EXPECT_NE(call_.GetAudioSendStream(kSsrcX),
call_.GetAudioSendStream(kSsrcY));
EXPECT_EQ(1u, GetSendStreamConfig(kSsrcY).rtp.extensions.size());
EXPECT_EQ(ext, GetSendStreamConfig(kSsrcY).rtp.extensions[0].uri);
EXPECT_EQ(id, GetSendStreamConfig(kSsrcY).rtp.extensions[0].id);
// Ensure all extensions go back off with an empty list.
send_parameters_.codecs.push_back(kPcmuCodec);
send_parameters_.extensions.clear();
SetSendParameters(send_parameters_);
EXPECT_EQ(0u, GetSendStreamConfig(kSsrcX).rtp.extensions.size());
EXPECT_EQ(0u, GetSendStreamConfig(kSsrcY).rtp.extensions.size());
}
void TestSetRecvRtpHeaderExtensions(const std::string& ext) {
EXPECT_TRUE(SetupRecvStream());
// Ensure extensions are off by default.
EXPECT_EQ(0u, GetRecvStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure unknown extensions won't cause an error.
recv_parameters_.extensions.push_back(
webrtc::RtpExtension("urn:ietf:params:unknownextention", 1));
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
EXPECT_EQ(0u, GetRecvStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure extensions stay off with an empty list of headers.
recv_parameters_.extensions.clear();
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
EXPECT_EQ(0u, GetRecvStreamConfig(kSsrcX).rtp.extensions.size());
// Ensure extension is set properly.
const int id = 2;
recv_parameters_.extensions.push_back(webrtc::RtpExtension(ext, id));
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
EXPECT_EQ(1u, GetRecvStreamConfig(kSsrcX).rtp.extensions.size());
EXPECT_EQ(ext, GetRecvStreamConfig(kSsrcX).rtp.extensions[0].uri);
EXPECT_EQ(id, GetRecvStreamConfig(kSsrcX).rtp.extensions[0].id);
// Ensure extension is set properly on new stream.
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_NE(call_.GetAudioReceiveStream(kSsrcX),
call_.GetAudioReceiveStream(kSsrcY));
EXPECT_EQ(1u, GetRecvStreamConfig(kSsrcY).rtp.extensions.size());
EXPECT_EQ(ext, GetRecvStreamConfig(kSsrcY).rtp.extensions[0].uri);
EXPECT_EQ(id, GetRecvStreamConfig(kSsrcY).rtp.extensions[0].id);
// Ensure all extensions go back off with an empty list.
recv_parameters_.extensions.clear();
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
EXPECT_EQ(0u, GetRecvStreamConfig(kSsrcX).rtp.extensions.size());
EXPECT_EQ(0u, GetRecvStreamConfig(kSsrcY).rtp.extensions.size());
}
webrtc::AudioSendStream::Stats GetAudioSendStreamStats() const {
webrtc::AudioSendStream::Stats stats;
stats.local_ssrc = 12;
stats.payload_bytes_sent = 345;
stats.header_and_padding_bytes_sent = 56;
stats.packets_sent = 678;
stats.packets_lost = 9012;
stats.fraction_lost = 34.56f;
stats.codec_name = "codec_name_send";
stats.codec_payload_type = 42;
stats.jitter_ms = 12;
stats.rtt_ms = 345;
stats.audio_level = 678;
stats.apm_statistics.delay_median_ms = 234;
stats.apm_statistics.delay_standard_deviation_ms = 567;
stats.apm_statistics.echo_return_loss = 890;
stats.apm_statistics.echo_return_loss_enhancement = 1234;
stats.apm_statistics.residual_echo_likelihood = 0.432f;
stats.apm_statistics.residual_echo_likelihood_recent_max = 0.6f;
stats.ana_statistics.bitrate_action_counter = 321;
stats.ana_statistics.channel_action_counter = 432;
stats.ana_statistics.dtx_action_counter = 543;
stats.ana_statistics.fec_action_counter = 654;
stats.ana_statistics.frame_length_increase_counter = 765;
stats.ana_statistics.frame_length_decrease_counter = 876;
stats.ana_statistics.uplink_packet_loss_fraction = 987.0;
return stats;
}
void SetAudioSendStreamStats() {
for (auto* s : call_.GetAudioSendStreams()) {
s->SetStats(GetAudioSendStreamStats());
}
}
void VerifyVoiceSenderInfo(const cricket::VoiceSenderInfo& info,
bool is_sending) {
const auto stats = GetAudioSendStreamStats();
EXPECT_EQ(info.ssrc(), stats.local_ssrc);
EXPECT_EQ(info.payload_bytes_sent, stats.payload_bytes_sent);
EXPECT_EQ(info.header_and_padding_bytes_sent,
stats.header_and_padding_bytes_sent);
EXPECT_EQ(info.packets_sent, stats.packets_sent);
EXPECT_EQ(info.packets_lost, stats.packets_lost);
EXPECT_EQ(info.fraction_lost, stats.fraction_lost);
EXPECT_EQ(info.codec_name, stats.codec_name);
EXPECT_EQ(info.codec_payload_type, stats.codec_payload_type);
EXPECT_EQ(info.jitter_ms, stats.jitter_ms);
EXPECT_EQ(info.rtt_ms, stats.rtt_ms);
EXPECT_EQ(info.audio_level, stats.audio_level);
EXPECT_EQ(info.apm_statistics.delay_median_ms,
stats.apm_statistics.delay_median_ms);
EXPECT_EQ(info.apm_statistics.delay_standard_deviation_ms,
stats.apm_statistics.delay_standard_deviation_ms);
EXPECT_EQ(info.apm_statistics.echo_return_loss,
stats.apm_statistics.echo_return_loss);
EXPECT_EQ(info.apm_statistics.echo_return_loss_enhancement,
stats.apm_statistics.echo_return_loss_enhancement);
EXPECT_EQ(info.apm_statistics.residual_echo_likelihood,
stats.apm_statistics.residual_echo_likelihood);
EXPECT_EQ(info.apm_statistics.residual_echo_likelihood_recent_max,
stats.apm_statistics.residual_echo_likelihood_recent_max);
EXPECT_EQ(info.ana_statistics.bitrate_action_counter,
stats.ana_statistics.bitrate_action_counter);
EXPECT_EQ(info.ana_statistics.channel_action_counter,
stats.ana_statistics.channel_action_counter);
EXPECT_EQ(info.ana_statistics.dtx_action_counter,
stats.ana_statistics.dtx_action_counter);
EXPECT_EQ(info.ana_statistics.fec_action_counter,
stats.ana_statistics.fec_action_counter);
EXPECT_EQ(info.ana_statistics.frame_length_increase_counter,
stats.ana_statistics.frame_length_increase_counter);
EXPECT_EQ(info.ana_statistics.frame_length_decrease_counter,
stats.ana_statistics.frame_length_decrease_counter);
EXPECT_EQ(info.ana_statistics.uplink_packet_loss_fraction,
stats.ana_statistics.uplink_packet_loss_fraction);
}
webrtc::AudioReceiveStreamInterface::Stats GetAudioReceiveStreamStats()
const {
webrtc::AudioReceiveStreamInterface::Stats stats;
stats.remote_ssrc = 123;
stats.payload_bytes_rcvd = 456;
stats.header_and_padding_bytes_rcvd = 67;
stats.packets_rcvd = 768;
stats.packets_lost = 101;
stats.codec_name = "codec_name_recv";
stats.codec_payload_type = 42;
stats.jitter_ms = 901;
stats.jitter_buffer_ms = 234;
stats.jitter_buffer_preferred_ms = 567;
stats.delay_estimate_ms = 890;
stats.audio_level = 1234;
stats.total_samples_received = 5678901;
stats.concealed_samples = 234;
stats.concealment_events = 12;
stats.jitter_buffer_delay_seconds = 34;
stats.jitter_buffer_emitted_count = 77;
stats.expand_rate = 5.67f;
stats.speech_expand_rate = 8.90f;
stats.secondary_decoded_rate = 1.23f;
stats.secondary_discarded_rate = 0.12f;
stats.accelerate_rate = 4.56f;
stats.preemptive_expand_rate = 7.89f;
stats.decoding_calls_to_silence_generator = 12;
stats.decoding_calls_to_neteq = 345;
stats.decoding_normal = 67890;
stats.decoding_plc = 1234;
stats.decoding_codec_plc = 1236;
stats.decoding_cng = 5678;
stats.decoding_plc_cng = 9012;
stats.decoding_muted_output = 3456;
stats.capture_start_ntp_time_ms = 7890;
return stats;
}
void SetAudioReceiveStreamStats() {
for (auto* s : call_.GetAudioReceiveStreams()) {
s->SetStats(GetAudioReceiveStreamStats());
}
}
void VerifyVoiceReceiverInfo(const cricket::VoiceReceiverInfo& info) {
const auto stats = GetAudioReceiveStreamStats();
EXPECT_EQ(info.ssrc(), stats.remote_ssrc);
EXPECT_EQ(info.payload_bytes_rcvd, stats.payload_bytes_rcvd);
EXPECT_EQ(info.header_and_padding_bytes_rcvd,
stats.header_and_padding_bytes_rcvd);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.packets_rcvd),
stats.packets_rcvd);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.packets_lost),
stats.packets_lost);
EXPECT_EQ(info.codec_name, stats.codec_name);
EXPECT_EQ(info.codec_payload_type, stats.codec_payload_type);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.jitter_ms), stats.jitter_ms);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.jitter_buffer_ms),
stats.jitter_buffer_ms);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.jitter_buffer_preferred_ms),
stats.jitter_buffer_preferred_ms);
EXPECT_EQ(rtc::checked_cast<unsigned int>(info.delay_estimate_ms),
stats.delay_estimate_ms);
EXPECT_EQ(info.audio_level, stats.audio_level);
EXPECT_EQ(info.total_samples_received, stats.total_samples_received);
EXPECT_EQ(info.concealed_samples, stats.concealed_samples);
EXPECT_EQ(info.concealment_events, stats.concealment_events);
EXPECT_EQ(info.jitter_buffer_delay_seconds,
stats.jitter_buffer_delay_seconds);
EXPECT_EQ(info.jitter_buffer_emitted_count,
stats.jitter_buffer_emitted_count);
EXPECT_EQ(info.expand_rate, stats.expand_rate);
EXPECT_EQ(info.speech_expand_rate, stats.speech_expand_rate);
EXPECT_EQ(info.secondary_decoded_rate, stats.secondary_decoded_rate);
EXPECT_EQ(info.secondary_discarded_rate, stats.secondary_discarded_rate);
EXPECT_EQ(info.accelerate_rate, stats.accelerate_rate);
EXPECT_EQ(info.preemptive_expand_rate, stats.preemptive_expand_rate);
EXPECT_EQ(info.decoding_calls_to_silence_generator,
stats.decoding_calls_to_silence_generator);
EXPECT_EQ(info.decoding_calls_to_neteq, stats.decoding_calls_to_neteq);
EXPECT_EQ(info.decoding_normal, stats.decoding_normal);
EXPECT_EQ(info.decoding_plc, stats.decoding_plc);
EXPECT_EQ(info.decoding_codec_plc, stats.decoding_codec_plc);
EXPECT_EQ(info.decoding_cng, stats.decoding_cng);
EXPECT_EQ(info.decoding_plc_cng, stats.decoding_plc_cng);
EXPECT_EQ(info.decoding_muted_output, stats.decoding_muted_output);
EXPECT_EQ(info.capture_start_ntp_time_ms, stats.capture_start_ntp_time_ms);
}
void VerifyVoiceSendRecvCodecs(const cricket::VoiceMediaInfo& info) const {
EXPECT_EQ(send_parameters_.codecs.size(), info.send_codecs.size());
for (const cricket::AudioCodec& codec : send_parameters_.codecs) {
ASSERT_EQ(info.send_codecs.count(codec.id), 1U);
EXPECT_EQ(info.send_codecs.find(codec.id)->second,
codec.ToCodecParameters());
}
EXPECT_EQ(recv_parameters_.codecs.size(), info.receive_codecs.size());
for (const cricket::AudioCodec& codec : recv_parameters_.codecs) {
ASSERT_EQ(info.receive_codecs.count(codec.id), 1U);
EXPECT_EQ(info.receive_codecs.find(codec.id)->second,
codec.ToCodecParameters());
}
}
void VerifyGainControlEnabledCorrectly() {
EXPECT_TRUE(apm_config_.gain_controller1.enabled);
EXPECT_EQ(kDefaultAgcMode, apm_config_.gain_controller1.mode);
}
void VerifyGainControlDefaultSettings() {
EXPECT_EQ(3, apm_config_.gain_controller1.target_level_dbfs);
EXPECT_EQ(9, apm_config_.gain_controller1.compression_gain_db);
EXPECT_TRUE(apm_config_.gain_controller1.enable_limiter);
}
void VerifyEchoCancellationSettings(bool enabled) {
constexpr bool kDefaultUseAecm =
#if defined(WEBRTC_ANDROID)
true;
#else
false;
#endif
EXPECT_EQ(apm_config_.echo_canceller.enabled, enabled);
EXPECT_EQ(apm_config_.echo_canceller.mobile_mode, kDefaultUseAecm);
}
bool IsHighPassFilterEnabled() {
return apm_config_.high_pass_filter.enabled;
}
protected:
rtc::AutoThread main_thread_;
const bool use_null_apm_;
webrtc::test::ScopedKeyValueConfig field_trials_;
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory_;
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm_;
rtc::scoped_refptr<StrictMock<webrtc::test::MockAudioProcessing>> apm_;
cricket::FakeCall call_;
std::unique_ptr<cricket::WebRtcVoiceEngine> engine_;
cricket::VoiceMediaChannel* channel_ = nullptr;
cricket::AudioSendParameters send_parameters_;
cricket::AudioRecvParameters recv_parameters_;
FakeAudioSource fake_source_;
webrtc::AudioProcessing::Config apm_config_;
};
INSTANTIATE_TEST_SUITE_P(TestBothWithAndWithoutNullApm,
WebRtcVoiceEngineTestFake,
::testing::Values(false, true));
// Tests that we can create and destroy a channel.
TEST_P(WebRtcVoiceEngineTestFake, CreateMediaChannel) {
EXPECT_TRUE(SetupChannel());
}
// Test that we can add a send stream and that it has the correct defaults.
TEST_P(WebRtcVoiceEngineTestFake, CreateSendStream) {
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
EXPECT_EQ(kSsrcX, config.rtp.ssrc);
EXPECT_EQ("", config.rtp.c_name);
EXPECT_EQ(0u, config.rtp.extensions.size());
EXPECT_EQ(static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_),
config.send_transport);
}
// Test that we can add a receive stream and that it has the correct defaults.
TEST_P(WebRtcVoiceEngineTestFake, CreateRecvStream) {
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(AddRecvStream(kSsrcX));
const webrtc::AudioReceiveStreamInterface::Config& config =
GetRecvStreamConfig(kSsrcX);
EXPECT_EQ(kSsrcX, config.rtp.remote_ssrc);
EXPECT_EQ(0xFA17FA17, config.rtp.local_ssrc);
EXPECT_FALSE(config.rtp.transport_cc);
EXPECT_EQ(0u, config.rtp.extensions.size());
EXPECT_EQ(static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_),
config.rtcp_send_transport);
EXPECT_EQ("", config.sync_group);
}
TEST_P(WebRtcVoiceEngineTestFake, OpusSupportsTransportCc) {
const std::vector<cricket::AudioCodec>& codecs = engine_->send_codecs();
bool opus_found = false;
for (const cricket::AudioCodec& codec : codecs) {
if (codec.name == "opus") {
EXPECT_TRUE(HasTransportCc(codec));
opus_found = true;
}
}
EXPECT_TRUE(opus_found);
}
// Test that we set our inbound codecs properly, including changing PT.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecs) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs.push_back(kTelephoneEventCodec2);
parameters.codecs[0].id = 106; // collide with existing CN 32k
parameters.codecs[2].id = 126;
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}},
{106, {"ISAC", 16000, 1}},
{126, {"telephone-event", 8000, 1}},
{107, {"telephone-event", 32000, 1}}})));
}
// Test that we fail to set an unknown inbound codec.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsUnsupportedCodec) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(cricket::AudioCodec(127, "XYZ", 32000, 0, 1));
EXPECT_FALSE(channel_->SetRecvParameters(parameters));
}
// Test that we fail if we have duplicate types in the inbound list.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsDuplicatePayloadType) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs[1].id = kIsacCodec.id;
EXPECT_FALSE(channel_->SetRecvParameters(parameters));
}
// Test that we can decode OPUS without stereo parameters.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpusNoStereo) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kOpusCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}},
{103, {"ISAC", 16000, 1}},
{111, {"opus", 48000, 2}}})));
}
// Test that we can decode OPUS with stereo = 0.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpus0Stereo) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[2].params["stereo"] = "0";
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}},
{103, {"ISAC", 16000, 1}},
{111, {"opus", 48000, 2, {{"stereo", "0"}}}}})));
}
// Test that we can decode OPUS with stereo = 1.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsWithOpus1Stereo) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[2].params["stereo"] = "1";
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}},
{103, {"ISAC", 16000, 1}},
{111, {"opus", 48000, 2, {{"stereo", "1"}}}}})));
}
// Test that changes to recv codecs are applied to all streams.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsWithMultipleStreams) {
EXPECT_TRUE(SetupChannel());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs.push_back(kTelephoneEventCodec2);
parameters.codecs[0].id = 106; // collide with existing CN 32k
parameters.codecs[2].id = 126;
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
for (const auto& ssrc : {kSsrcX, kSsrcY}) {
EXPECT_TRUE(AddRecvStream(ssrc));
EXPECT_THAT(GetRecvStreamConfig(ssrc).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}},
{106, {"ISAC", 16000, 1}},
{126, {"telephone-event", 8000, 1}},
{107, {"telephone-event", 32000, 1}}})));
}
}
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsAfterAddingStreams) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs[0].id = 106; // collide with existing CN 32k
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
const auto& dm = GetRecvStreamConfig(kSsrcX).decoder_map;
ASSERT_EQ(1u, dm.count(106));
EXPECT_EQ(webrtc::SdpAudioFormat("isac", 16000, 1), dm.at(106));
}
// Test that we can apply the same set of codecs again while playing.
TEST_P(WebRtcVoiceEngineTestFake, SetRecvCodecsWhilePlaying) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
channel_->SetPlayout(true);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
// Remapping a payload type to a different codec should fail.
parameters.codecs[0] = kOpusCodec;
parameters.codecs[0].id = kIsacCodec.id;
EXPECT_FALSE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(GetRecvStream(kSsrcX).started());
}
// Test that we can add a codec while playing.
TEST_P(WebRtcVoiceEngineTestFake, AddRecvCodecsWhilePlaying) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
channel_->SetPlayout(true);
parameters.codecs.push_back(kOpusCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(GetRecvStream(kSsrcX).started());
}
// Test that we accept adding the same codec with a different payload type.
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5847
TEST_P(WebRtcVoiceEngineTestFake, ChangeRecvCodecPayloadType) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
++parameters.codecs[0].id;
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
}
// Test that we do allow setting Opus/Red by default.
TEST_P(WebRtcVoiceEngineTestFake, RecvRedDefault) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[1].params[""] = "111/111";
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{111, {"opus", 48000, 2}},
{112, {"red", 48000, 2, {{"", "111/111"}}}}})));
}
TEST_P(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) {
EXPECT_TRUE(SetupSendStream());
// Test that when autobw is enabled, bitrate is kept as the default
// value. autobw is enabled for the following tests because the target
// bitrate is <= 0.
// ISAC, default bitrate == 32000.
TestMaxSendBandwidth(kIsacCodec, 0, true, 32000);
// PCMU, default bitrate == 64000.
TestMaxSendBandwidth(kPcmuCodec, -1, true, 64000);
// opus, default bitrate == 32000 in mono.
TestMaxSendBandwidth(kOpusCodec, -1, true, 32000);
}
TEST_P(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthMultiRateAsCaller) {
EXPECT_TRUE(SetupSendStream());
// ISAC, default bitrate == 32000.
TestMaxSendBandwidth(kIsacCodec, 16000, true, 16000);
// Rates above the max (56000) should be capped.
TestMaxSendBandwidth(kIsacCodec, 100000, true, 32000);
// opus, default bitrate == 64000.
TestMaxSendBandwidth(kOpusCodec, 96000, true, 96000);
TestMaxSendBandwidth(kOpusCodec, 48000, true, 48000);
// Rates above the max (510000) should be capped.
TestMaxSendBandwidth(kOpusCodec, 600000, true, 510000);
}
TEST_P(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthFixedRateAsCaller) {
EXPECT_TRUE(SetupSendStream());
// Test that we can only set a maximum bitrate for a fixed-rate codec
// if it's bigger than the fixed rate.
// PCMU, fixed bitrate == 64000.
TestMaxSendBandwidth(kPcmuCodec, 0, true, 64000);
TestMaxSendBandwidth(kPcmuCodec, 1, false, 64000);
TestMaxSendBandwidth(kPcmuCodec, 128000, true, 64000);
TestMaxSendBandwidth(kPcmuCodec, 32000, false, 64000);
TestMaxSendBandwidth(kPcmuCodec, 64000, true, 64000);
TestMaxSendBandwidth(kPcmuCodec, 63999, false, 64000);
TestMaxSendBandwidth(kPcmuCodec, 64001, true, 64000);
}
TEST_P(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthMultiRateAsCallee) {
EXPECT_TRUE(SetupChannel());
const int kDesiredBitrate = 128000;
cricket::AudioSendParameters parameters;
parameters.codecs = engine_->send_codecs();
parameters.max_bandwidth_bps = kDesiredBitrate;
SetSendParameters(parameters);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
EXPECT_EQ(kDesiredBitrate, GetCodecBitrate(kSsrcX));
}
// Test that bitrate cannot be set for CBR codecs.
// Bitrate is ignored if it is higher than the fixed bitrate.
// Bitrate less then the fixed bitrate is an error.
TEST_P(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthCbr) {
EXPECT_TRUE(SetupSendStream());
// PCMU, default bitrate == 64000.
SetSendParameters(send_parameters_);
EXPECT_EQ(64000, GetCodecBitrate(kSsrcX));
send_parameters_.max_bandwidth_bps = 128000;
SetSendParameters(send_parameters_);
EXPECT_EQ(64000, GetCodecBitrate(kSsrcX));
send_parameters_.max_bandwidth_bps = 128;
EXPECT_FALSE(channel_->SetSendParameters(send_parameters_));
EXPECT_EQ(64000, GetCodecBitrate(kSsrcX));
}
// Test that the per-stream bitrate limit and the global
// bitrate limit both apply.
TEST_P(WebRtcVoiceEngineTestFake, SetMaxBitratePerStream) {
EXPECT_TRUE(SetupSendStream());
// opus, default bitrate == 32000.
SetAndExpectMaxBitrate(kOpusCodec, 0, 0, true, 32000);
SetAndExpectMaxBitrate(kOpusCodec, 48000, 0, true, 48000);
SetAndExpectMaxBitrate(kOpusCodec, 48000, 64000, true, 48000);
SetAndExpectMaxBitrate(kOpusCodec, 64000, 48000, true, 48000);
// CBR codecs allow both maximums to exceed the bitrate.
SetAndExpectMaxBitrate(kPcmuCodec, 0, 0, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 0, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 0, 64001, true, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 64001, true, 64000);
// CBR codecs don't allow per stream maximums to be too low.
SetAndExpectMaxBitrate(kPcmuCodec, 0, 63999, false, 64000);
SetAndExpectMaxBitrate(kPcmuCodec, 64001, 63999, false, 64000);
}
// Test that an attempt to set RtpParameters for a stream that does not exist
// fails.
TEST_P(WebRtcVoiceEngineTestFake, CannotSetMaxBitrateForNonexistentStream) {
EXPECT_TRUE(SetupChannel());
webrtc::RtpParameters nonexistent_parameters =
channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(0u, nonexistent_parameters.encodings.size());
nonexistent_parameters.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_FALSE(
channel_->SetRtpSendParameters(kSsrcX, nonexistent_parameters).ok());
}
TEST_P(WebRtcVoiceEngineTestFake,
CannotSetRtpSendParametersWithIncorrectNumberOfEncodings) {
// This test verifies that setting RtpParameters succeeds only if
// the structure contains exactly one encoding.
// TODO(skvlad): Update this test when we start supporting setting parameters
// for each encoding individually.
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
// Two or more encodings should result in failure.
parameters.encodings.push_back(webrtc::RtpEncodingParameters());
EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
// Zero encodings should also fail.
parameters.encodings.clear();
EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
}
// Changing the SSRC through RtpParameters is not allowed.
TEST_P(WebRtcVoiceEngineTestFake, CannotSetSsrcInRtpSendParameters) {
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
parameters.encodings[0].ssrc = 0xdeadbeef;
EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
}
// Test that a stream will not be sending if its encoding is made
// inactive through SetRtpSendParameters.
TEST_P(WebRtcVoiceEngineTestFake, SetRtpParametersEncodingsActive) {
EXPECT_TRUE(SetupSendStream());
SetSend(true);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
// Get current parameters and change "active" to false.
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
ASSERT_EQ(1u, parameters.encodings.size());
ASSERT_TRUE(parameters.encodings[0].active);
parameters.encodings[0].active = false;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
// Now change it back to active and verify we resume sending.
// This should occur even when other parameters are updated.
parameters.encodings[0].active = true;
parameters.encodings[0].max_bitrate_bps = absl::optional<int>(6000);
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
}
TEST_P(WebRtcVoiceEngineTestFake, SetRtpParametersAdaptivePtime) {
EXPECT_TRUE(SetupSendStream());
// Get current parameters and change "adaptive_ptime" to true.
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
ASSERT_EQ(1u, parameters.encodings.size());
ASSERT_FALSE(parameters.encodings[0].adaptive_ptime);
parameters.encodings[0].adaptive_ptime = true;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
EXPECT_TRUE(GetAudioNetworkAdaptorConfig(kSsrcX));
EXPECT_EQ(16000, GetSendStreamConfig(kSsrcX).min_bitrate_bps);
parameters.encodings[0].adaptive_ptime = false;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
EXPECT_FALSE(GetAudioNetworkAdaptorConfig(kSsrcX));
EXPECT_EQ(32000, GetSendStreamConfig(kSsrcX).min_bitrate_bps);
}
TEST_P(WebRtcVoiceEngineTestFake,
DisablingAdaptivePtimeDoesNotRemoveAudioNetworkAdaptorFromOptions) {
EXPECT_TRUE(SetupSendStream());
send_parameters_.options.audio_network_adaptor = true;
send_parameters_.options.audio_network_adaptor_config = {"1234"};
SetSendParameters(send_parameters_);
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
parameters.encodings[0].adaptive_ptime = false;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
}
TEST_P(WebRtcVoiceEngineTestFake, AdaptivePtimeFieldTrial) {
webrtc::test::ScopedKeyValueConfig override_field_trials(
field_trials_, "WebRTC-Audio-AdaptivePtime/enabled:true/");
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(GetAudioNetworkAdaptorConfig(kSsrcX));
}
// Test that SetRtpSendParameters configures the correct encoding channel for
// each SSRC.
TEST_P(WebRtcVoiceEngineTestFake, RtpParametersArePerStream) {
SetupForMultiSendStream();
// Create send streams.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
}
// Configure one stream to be limited by the stream config, another to be
// limited by the global max, and the third one with no per-stream limit
// (still subject to the global limit).
SetGlobalMaxBitrate(kOpusCodec, 32000);
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[0], 24000));
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[1], 48000));
EXPECT_TRUE(SetMaxBitrateForStream(kSsrcs4[2], -1));
EXPECT_EQ(24000, GetCodecBitrate(kSsrcs4[0]));
EXPECT_EQ(32000, GetCodecBitrate(kSsrcs4[1]));
EXPECT_EQ(32000, GetCodecBitrate(kSsrcs4[2]));
// Remove the global cap; the streams should switch to their respective
// maximums (or remain unchanged if there was no other limit on them.)
SetGlobalMaxBitrate(kOpusCodec, -1);
EXPECT_EQ(24000, GetCodecBitrate(kSsrcs4[0]));
EXPECT_EQ(48000, GetCodecBitrate(kSsrcs4[1]));
EXPECT_EQ(32000, GetCodecBitrate(kSsrcs4[2]));
}
// Test that GetRtpSendParameters returns the currently configured codecs.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpSendParametersCodecs) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
SetSendParameters(parameters);
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
ASSERT_EQ(2u, rtp_parameters.codecs.size());
EXPECT_EQ(kIsacCodec.ToCodecParameters(), rtp_parameters.codecs[0]);
EXPECT_EQ(kPcmuCodec.ToCodecParameters(), rtp_parameters.codecs[1]);
}
// Test that GetRtpSendParameters returns the currently configured RTCP CNAME.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpSendParametersRtcpCname) {
cricket::StreamParams params = cricket::StreamParams::CreateLegacy(kSsrcX);
params.cname = "rtcpcname";
EXPECT_TRUE(SetupSendStream(params));
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
EXPECT_STREQ("rtcpcname", rtp_parameters.rtcp.cname.c_str());
}
TEST_P(WebRtcVoiceEngineTestFake,
DetectRtpSendParameterHeaderExtensionsChange) {
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
rtp_parameters.header_extensions.emplace_back();
EXPECT_NE(0u, rtp_parameters.header_extensions.size());
webrtc::RTCError result =
channel_->SetRtpSendParameters(kSsrcX, rtp_parameters);
EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, result.type());
}
// Test that GetRtpSendParameters returns an SSRC.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpSendParametersSsrc) {
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
ASSERT_EQ(1u, rtp_parameters.encodings.size());
EXPECT_EQ(kSsrcX, rtp_parameters.encodings[0].ssrc);
}
// Test that if we set/get parameters multiple times, we get the same results.
TEST_P(WebRtcVoiceEngineTestFake, SetAndGetRtpSendParameters) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
SetSendParameters(parameters);
webrtc::RtpParameters initial_params = channel_->GetRtpSendParameters(kSsrcX);
// We should be able to set the params we just got.
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, initial_params).ok());
// ... And this shouldn't change the params returned by GetRtpSendParameters.
webrtc::RtpParameters new_params = channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(initial_params, channel_->GetRtpSendParameters(kSsrcX));
}
// Test that max_bitrate_bps in send stream config gets updated correctly when
// SetRtpSendParameters is called.
TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterUpdatesMaxBitrate) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters send_parameters;
send_parameters.codecs.push_back(kOpusCodec);
SetSendParameters(send_parameters);
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
// Expect empty on parameters.encodings[0].max_bitrate_bps;
EXPECT_FALSE(rtp_parameters.encodings[0].max_bitrate_bps);
constexpr int kMaxBitrateBps = 6000;
rtp_parameters.encodings[0].max_bitrate_bps = kMaxBitrateBps;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
const int max_bitrate = GetSendStreamConfig(kSsrcX).max_bitrate_bps;
EXPECT_EQ(max_bitrate, kMaxBitrateBps);
}
// Tests that when RTCRtpEncodingParameters.bitrate_priority gets set to
// a value <= 0, setting the parameters returns false.
TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterInvalidBitratePriority) {
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(1UL, rtp_parameters.encodings.size());
EXPECT_EQ(webrtc::kDefaultBitratePriority,
rtp_parameters.encodings[0].bitrate_priority);
rtp_parameters.encodings[0].bitrate_priority = 0;
EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
rtp_parameters.encodings[0].bitrate_priority = -1.0;
EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
}
// Test that the bitrate_priority in the send stream config gets updated when
// SetRtpSendParameters is set for the VoiceMediaChannel.
TEST_P(WebRtcVoiceEngineTestFake, SetRtpSendParameterUpdatesBitratePriority) {
EXPECT_TRUE(SetupSendStream());
webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
EXPECT_EQ(1UL, rtp_parameters.encodings.size());
EXPECT_EQ(webrtc::kDefaultBitratePriority,
rtp_parameters.encodings[0].bitrate_priority);
double new_bitrate_priority = 2.0;
rtp_parameters.encodings[0].bitrate_priority = new_bitrate_priority;
EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
// The priority should get set for both the audio channel's rtp parameters
// and the audio send stream's audio config.
EXPECT_EQ(
new_bitrate_priority,
channel_->GetRtpSendParameters(kSsrcX).encodings[0].bitrate_priority);
EXPECT_EQ(new_bitrate_priority, GetSendStreamConfig(kSsrcX).bitrate_priority);
}
// Test that GetRtpReceiveParameters returns the currently configured codecs.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpReceiveParametersCodecs) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
webrtc::RtpParameters rtp_parameters =
channel_->GetRtpReceiveParameters(kSsrcX);
ASSERT_EQ(2u, rtp_parameters.codecs.size());
EXPECT_EQ(kIsacCodec.ToCodecParameters(), rtp_parameters.codecs[0]);
EXPECT_EQ(kPcmuCodec.ToCodecParameters(), rtp_parameters.codecs[1]);
}
// Test that GetRtpReceiveParameters returns an SSRC.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpReceiveParametersSsrc) {
EXPECT_TRUE(SetupRecvStream());
webrtc::RtpParameters rtp_parameters =
channel_->GetRtpReceiveParameters(kSsrcX);
ASSERT_EQ(1u, rtp_parameters.encodings.size());
EXPECT_EQ(kSsrcX, rtp_parameters.encodings[0].ssrc);
}
// Test that if we set/get parameters multiple times, we get the same results.
TEST_P(WebRtcVoiceEngineTestFake, SetAndGetRtpReceiveParameters) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
webrtc::RtpParameters initial_params =
channel_->GetRtpReceiveParameters(kSsrcX);
// ... And this shouldn't change the params returned by
// GetRtpReceiveParameters.
webrtc::RtpParameters new_params = channel_->GetRtpReceiveParameters(kSsrcX);
EXPECT_EQ(initial_params, channel_->GetRtpReceiveParameters(kSsrcX));
}
// Test that GetRtpReceiveParameters returns parameters correctly when SSRCs
// aren't signaled. It should return an empty "RtpEncodingParameters" when
// configured to receive an unsignaled stream and no packets have been received
// yet, and start returning the SSRC once a packet has been received.
TEST_P(WebRtcVoiceEngineTestFake, GetRtpReceiveParametersWithUnsignaledSsrc) {
ASSERT_TRUE(SetupChannel());
// Call necessary methods to configure receiving a default stream as
// soon as it arrives.
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
// Call GetDefaultRtpReceiveParameters before configured to receive an
// unsignaled stream. Should return nothing.
EXPECT_EQ(webrtc::RtpParameters(),
channel_->GetDefaultRtpReceiveParameters());
// Set a sink for an unsignaled stream.
std::unique_ptr<FakeAudioSink> fake_sink(new FakeAudioSink());
channel_->SetDefaultRawAudioSink(std::move(fake_sink));
// Call GetDefaultRtpReceiveParameters before the SSRC is known.
webrtc::RtpParameters rtp_parameters =
channel_->GetDefaultRtpReceiveParameters();
ASSERT_EQ(1u, rtp_parameters.encodings.size());
EXPECT_FALSE(rtp_parameters.encodings[0].ssrc);
// Receive PCMU packet (SSRC=1).
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
// The `ssrc` member should still be unset.
rtp_parameters = channel_->GetDefaultRtpReceiveParameters();
ASSERT_EQ(1u, rtp_parameters.encodings.size());
EXPECT_FALSE(rtp_parameters.encodings[0].ssrc);
}
// Test that we apply codecs properly.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecs) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kCn8000Codec);
parameters.codecs[0].id = 96;
parameters.codecs[0].bitrate = 22000;
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_EQ(22000, send_codec_spec.target_bitrate_bps);
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_NE(send_codec_spec.format.clockrate_hz, 8000);
EXPECT_EQ(absl::nullopt, send_codec_spec.cng_payload_type);
EXPECT_FALSE(channel_->CanInsertDtmf());
}
// Test that we use Opus/Red by default when it is
// listed as the first codec and there is an fmtp line.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRed) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[0].params[""] = "111/111";
parameters.codecs.push_back(kOpusCodec);
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(112, send_codec_spec.red_payload_type);
}
// Test that we do not use Opus/Red by default when it is
// listed as the first codec but there is no fmtp line.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedNoFmtp) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs.push_back(kOpusCodec);
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type);
}
// Test that we do not use Opus/Red by default.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedDefault) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[1].params[""] = "111/111";
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type);
}
// Test that the RED fmtp line must match the payload type.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedFmtpMismatch) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[0].params[""] = "8/8";
parameters.codecs.push_back(kOpusCodec);
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type);
}
// Test that the RED fmtp line must show 2..32 payloads.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedFmtpAmountOfRedundancy) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[0].params[""] = "111";
parameters.codecs.push_back(kOpusCodec);
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type);
for (int i = 1; i < 32; i++) {
parameters.codecs[0].params[""] += "/111";
SetSendParameters(parameters);
const auto& send_codec_spec2 = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec2.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec2.format.name.c_str());
EXPECT_EQ(112, send_codec_spec2.red_payload_type);
}
parameters.codecs[0].params[""] += "/111";
SetSendParameters(parameters);
const auto& send_codec_spec3 = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, send_codec_spec3.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec3.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec3.red_payload_type);
}
// Test that WebRtcVoiceEngine reconfigures, rather than recreates its
// AudioSendStream.
TEST_P(WebRtcVoiceEngineTestFake, DontRecreateSendStream) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kCn8000Codec);
parameters.codecs[0].id = 96;
parameters.codecs[0].bitrate = 48000;
const int initial_num = call_.GetNumCreatedSendStreams();
SetSendParameters(parameters);
EXPECT_EQ(initial_num, call_.GetNumCreatedSendStreams());
// Calling SetSendCodec again with same codec which is already set.
// In this case media channel shouldn't send codec to VoE.
SetSendParameters(parameters);
EXPECT_EQ(initial_num, call_.GetNumCreatedSendStreams());
}
// TODO(ossu): Revisit if these tests need to be here, now that these kinds of
// tests should be available in AudioEncoderOpusTest.
// Test that if clockrate is not 48000 for opus, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusBadClockrate) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].clockrate = 50000;
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that if channels=0 for opus, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad0ChannelsNoStereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].channels = 0;
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that if channels=0 for opus, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad0Channels1Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].channels = 0;
parameters.codecs[0].params["stereo"] = "1";
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that if channel is 1 for opus and there's no stereo, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpus1ChannelNoStereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].channels = 1;
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that if channel is 1 for opus and stereo=0, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad1Channel0Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].channels = 1;
parameters.codecs[0].params["stereo"] = "0";
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that if channel is 1 for opus and stereo=1, we fail.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusBad1Channel1Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].channels = 1;
parameters.codecs[0].params["stereo"] = "1";
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that with bitrate=0 and no stereo, bitrate is 32000.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGood0BitrateNoStereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 32000);
}
// Test that with bitrate=0 and stereo=0, bitrate is 32000.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGood0Bitrate0Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].params["stereo"] = "0";
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 32000);
}
// Test that with bitrate=invalid and stereo=0, bitrate is 32000.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodXBitrate0Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].params["stereo"] = "0";
// bitrate that's out of the range between 6000 and 510000 will be clamped.
parameters.codecs[0].bitrate = 5999;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 6000);
parameters.codecs[0].bitrate = 510001;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 510000);
}
// Test that with bitrate=0 and stereo=1, bitrate is 64000.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGood0Bitrate1Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 0;
parameters.codecs[0].params["stereo"] = "1";
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 64000);
}
// Test that with bitrate=invalid and stereo=1, bitrate is 64000.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodXBitrate1Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].params["stereo"] = "1";
// bitrate that's out of the range between 6000 and 510000 will be clamped.
parameters.codecs[0].bitrate = 5999;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 6000);
parameters.codecs[0].bitrate = 510001;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 510000);
}
// Test that with bitrate=N and stereo unset, bitrate is N.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodNBitrateNoStereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 96000;
SetSendParameters(parameters);
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, spec.payload_type);
EXPECT_EQ(96000, spec.target_bitrate_bps);
EXPECT_EQ("opus", spec.format.name);
EXPECT_EQ(2u, spec.format.num_channels);
EXPECT_EQ(48000, spec.format.clockrate_hz);
}
// Test that with bitrate=N and stereo=0, bitrate is N.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodNBitrate0Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 30000;
parameters.codecs[0].params["stereo"] = "0";
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 30000);
}
// Test that with bitrate=N and without any parameters, bitrate is N.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodNBitrateNoParameters) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 30000;
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 30000);
}
// Test that with bitrate=N and stereo=1, bitrate is N.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecOpusGoodNBitrate1Stereo) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].bitrate = 30000;
parameters.codecs[0].params["stereo"] = "1";
SetSendParameters(parameters);
CheckSendCodecBitrate(kSsrcX, "opus", 30000);
}
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsWithBitrates) {
SetSendCodecsShouldWorkForBitrates("100", 100000, "150", 150000, "200",
200000);
}
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsWithHighMaxBitrate) {
SetSendCodecsShouldWorkForBitrates("", 0, "", -1, "10000", 10000000);
}
TEST_P(WebRtcVoiceEngineTestFake,
SetSendCodecsWithoutBitratesUsesCorrectDefaults) {
SetSendCodecsShouldWorkForBitrates("", 0, "", -1, "", -1);
}
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCapsMinAndStartBitrate) {
SetSendCodecsShouldWorkForBitrates("-1", 0, "-100", -1, "", -1);
}
TEST_P(WebRtcVoiceEngineTestFake, SetMaxSendBandwidthForAudioDoesntAffectBwe) {
SetSendCodecsShouldWorkForBitrates("100", 100000, "150", 150000, "200",
200000);
send_parameters_.max_bandwidth_bps = 100000;
// Setting max bitrate should keep previous min bitrate
// Setting max bitrate should not reset start bitrate.
EXPECT_CALL(*call_.GetMockTransportControllerSend(),
SetSdpBitrateParameters(
AllOf(Field(&BitrateConstraints::min_bitrate_bps, 100000),
Field(&BitrateConstraints::start_bitrate_bps, -1),
Field(&BitrateConstraints::max_bitrate_bps, 200000))));
SetSendParameters(send_parameters_);
}
// Test that we can enable NACK with opus as callee.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecEnableNackAsCallee) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
EXPECT_EQ(0, GetRecvStreamConfig(kSsrcX).rtp.nack.rtp_history_ms);
SetSendParameters(parameters);
// NACK should be enabled even with no send stream.
EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcX).rtp.nack.rtp_history_ms);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
}
// Test that we can enable NACK on receive streams.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecEnableNackRecvStreams) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(AddRecvStream(kSsrcY));
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
EXPECT_EQ(0, GetRecvStreamConfig(kSsrcY).rtp.nack.rtp_history_ms);
SetSendParameters(parameters);
EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcY).rtp.nack.rtp_history_ms);
}
// Test that we can disable NACK on receive streams.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecDisableNackRecvStreams) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(AddRecvStream(kSsrcY));
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs[0].AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
SetSendParameters(parameters);
EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcY).rtp.nack.rtp_history_ms);
parameters.codecs.clear();
parameters.codecs.push_back(kOpusCodec);
SetSendParameters(parameters);
EXPECT_EQ(0, GetRecvStreamConfig(kSsrcY).rtp.nack.rtp_history_ms);
}
// Test that NACK is enabled on a new receive stream.
TEST_P(WebRtcVoiceEngineTestFake, AddRecvStreamEnableNack) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs[0].AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
SetSendParameters(parameters);
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcY).rtp.nack.rtp_history_ms);
EXPECT_TRUE(AddRecvStream(kSsrcZ));
EXPECT_EQ(kRtpHistoryMs, GetRecvStreamConfig(kSsrcZ).rtp.nack.rtp_history_ms);
}
TEST_P(WebRtcVoiceEngineTestFake, TransportCcCanBeEnabledAndDisabled) {
EXPECT_TRUE(SetupChannel());
cricket::AudioSendParameters send_parameters;
send_parameters.codecs.push_back(kOpusCodec);
EXPECT_TRUE(send_parameters.codecs[0].feedback_params.params().empty());
SetSendParameters(send_parameters);
cricket::AudioRecvParameters recv_parameters;
recv_parameters.codecs.push_back(kIsacCodec);
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
ASSERT_TRUE(call_.GetAudioReceiveStream(kSsrcX) != nullptr);
EXPECT_FALSE(call_.GetAudioReceiveStream(kSsrcX)->transport_cc());
send_parameters.codecs = engine_->send_codecs();
SetSendParameters(send_parameters);
ASSERT_TRUE(call_.GetAudioReceiveStream(kSsrcX) != nullptr);
EXPECT_TRUE(call_.GetAudioReceiveStream(kSsrcX)->transport_cc());
}
// Test that we can switch back and forth between Opus and ISAC with CN.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsIsacOpusSwitching) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters opus_parameters;
opus_parameters.codecs.push_back(kOpusCodec);
SetSendParameters(opus_parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, spec.payload_type);
EXPECT_STRCASEEQ("opus", spec.format.name.c_str());
}
cricket::AudioSendParameters isac_parameters;
isac_parameters.codecs.push_back(kIsacCodec);
isac_parameters.codecs.push_back(kCn16000Codec);
isac_parameters.codecs.push_back(kOpusCodec);
SetSendParameters(isac_parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(103, spec.payload_type);
EXPECT_STRCASEEQ("ISAC", spec.format.name.c_str());
}
SetSendParameters(opus_parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, spec.payload_type);
EXPECT_STRCASEEQ("opus", spec.format.name.c_str());
}
}
// Test that we handle various ways of specifying bitrate.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsBitrate) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec); // bitrate == 32000
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(103, spec.payload_type);
EXPECT_STRCASEEQ("ISAC", spec.format.name.c_str());
EXPECT_EQ(32000, spec.target_bitrate_bps);
}
parameters.codecs[0].bitrate = 0; // bitrate == default
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(103, spec.payload_type);
EXPECT_STRCASEEQ("ISAC", spec.format.name.c_str());
EXPECT_EQ(32000, spec.target_bitrate_bps);
}
parameters.codecs[0].bitrate = 28000; // bitrate == 28000
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(103, spec.payload_type);
EXPECT_STRCASEEQ("ISAC", spec.format.name.c_str());
EXPECT_EQ(28000, spec.target_bitrate_bps);
}
parameters.codecs[0] = kPcmuCodec; // bitrate == 64000
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(0, spec.payload_type);
EXPECT_STRCASEEQ("PCMU", spec.format.name.c_str());
EXPECT_EQ(64000, spec.target_bitrate_bps);
}
parameters.codecs[0].bitrate = 0; // bitrate == default
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(0, spec.payload_type);
EXPECT_STREQ("PCMU", spec.format.name.c_str());
EXPECT_EQ(64000, spec.target_bitrate_bps);
}
parameters.codecs[0] = kOpusCodec;
parameters.codecs[0].bitrate = 0; // bitrate == default
SetSendParameters(parameters);
{
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(111, spec.payload_type);
EXPECT_STREQ("opus", spec.format.name.c_str());
EXPECT_EQ(32000, spec.target_bitrate_bps);
}
}
// Test that we fail if no codecs are specified.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsNoCodecs) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
EXPECT_FALSE(channel_->SetSendParameters(parameters));
}
// Test that we can set send codecs even with telephone-event codec as the first
// one on the list.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsDTMFOnTop) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs[0].id = 98; // DTMF
parameters.codecs[1].id = 96;
SetSendParameters(parameters);
const auto& spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, spec.payload_type);
EXPECT_STRCASEEQ("ISAC", spec.format.name.c_str());
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
}
// Test that CanInsertDtmf() is governed by the send flag
TEST_P(WebRtcVoiceEngineTestFake, DTMFControlledBySendFlag) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs[0].id = 98; // DTMF
parameters.codecs[1].id = 96;
SetSendParameters(parameters);
EXPECT_FALSE(channel_->CanInsertDtmf());
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
SetSend(false);
EXPECT_FALSE(channel_->CanInsertDtmf());
}
// Test that payload type range is limited for telephone-event codec.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsDTMFPayloadTypeOutOfRange) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kTelephoneEventCodec2);
parameters.codecs.push_back(kIsacCodec);
parameters.codecs[0].id = 0; // DTMF
parameters.codecs[1].id = 96;
SetSendParameters(parameters);
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
parameters.codecs[0].id = 128; // DTMF
EXPECT_FALSE(channel_->SetSendParameters(parameters));
EXPECT_FALSE(channel_->CanInsertDtmf());
parameters.codecs[0].id = 127;
SetSendParameters(parameters);
EXPECT_TRUE(channel_->CanInsertDtmf());
parameters.codecs[0].id = -1; // DTMF
EXPECT_FALSE(channel_->SetSendParameters(parameters));
EXPECT_FALSE(channel_->CanInsertDtmf());
}
// Test that we can set send codecs even with CN codec as the first
// one on the list.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCNOnTop) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs[0].id = 98; // wideband CN
parameters.codecs[1].id = 96;
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(98, send_codec_spec.cng_payload_type);
}
// Test that we set VAD and DTMF types correctly as caller.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCaller) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
// TODO(juberti): cn 32000
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs.push_back(kCn8000Codec);
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs[0].id = 96;
parameters.codecs[2].id = 97; // wideband CN
parameters.codecs[4].id = 98; // DTMF
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(97, send_codec_spec.cng_payload_type);
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
}
// Test that we set VAD and DTMF types correctly as callee.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCNandDTMFAsCallee) {
EXPECT_TRUE(SetupChannel());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
// TODO(juberti): cn 32000
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs.push_back(kCn8000Codec);
parameters.codecs.push_back(kTelephoneEventCodec2);
parameters.codecs[0].id = 96;
parameters.codecs[2].id = 97; // wideband CN
parameters.codecs[4].id = 98; // DTMF
SetSendParameters(parameters);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(97, send_codec_spec.cng_payload_type);
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
}
// Test that we only apply VAD if we have a CN codec that matches the
// send codec clockrate.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCNNoMatch) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
// Set ISAC(16K) and CN(16K). VAD should be activated.
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs[1].id = 97;
SetSendParameters(parameters);
{
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(97, send_codec_spec.cng_payload_type);
}
// Set PCMU(8K) and CN(16K). VAD should not be activated.
parameters.codecs[0] = kPcmuCodec;
SetSendParameters(parameters);
{
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_STRCASEEQ("PCMU", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.cng_payload_type);
}
// Set PCMU(8K) and CN(8K). VAD should be activated.
parameters.codecs[1] = kCn8000Codec;
SetSendParameters(parameters);
{
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_STRCASEEQ("PCMU", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(13, send_codec_spec.cng_payload_type);
}
// Set ISAC(16K) and CN(8K). VAD should not be activated.
parameters.codecs[0] = kIsacCodec;
SetSendParameters(parameters);
{
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.cng_payload_type);
}
}
// Test that we perform case-insensitive matching of codec names.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsCaseInsensitive) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs.push_back(kCn8000Codec);
parameters.codecs.push_back(kTelephoneEventCodec1);
parameters.codecs[0].name = "iSaC";
parameters.codecs[0].id = 96;
parameters.codecs[2].id = 97; // wideband CN
parameters.codecs[4].id = 98; // DTMF
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(97, send_codec_spec.cng_payload_type);
SetSend(true);
EXPECT_TRUE(channel_->CanInsertDtmf());
}
TEST_P(WebRtcVoiceEngineTestFake,
SupportsTransportSequenceNumberHeaderExtension) {
const std::vector<webrtc::RtpExtension> header_extensions =
GetDefaultEnabledRtpHeaderExtensions(*engine_);
EXPECT_THAT(header_extensions,
Contains(::testing::Field(
"uri", &RtpExtension::uri,
webrtc::RtpExtension::kTransportSequenceNumberUri)));
}
// Test support for audio level header extension.
TEST_P(WebRtcVoiceEngineTestFake, SendAudioLevelHeaderExtensions) {
TestSetSendRtpHeaderExtensions(webrtc::RtpExtension::kAudioLevelUri);
}
TEST_P(WebRtcVoiceEngineTestFake, RecvAudioLevelHeaderExtensions) {
TestSetRecvRtpHeaderExtensions(webrtc::RtpExtension::kAudioLevelUri);
}
// Test support for transport sequence number header extension.
TEST_P(WebRtcVoiceEngineTestFake, SendTransportSequenceNumberHeaderExtensions) {
TestSetSendRtpHeaderExtensions(
webrtc::RtpExtension::kTransportSequenceNumberUri);
}
TEST_P(WebRtcVoiceEngineTestFake, RecvTransportSequenceNumberHeaderExtensions) {
TestSetRecvRtpHeaderExtensions(
webrtc::RtpExtension::kTransportSequenceNumberUri);
}
// Test that we can create a channel and start sending on it.
TEST_P(WebRtcVoiceEngineTestFake, Send) {
EXPECT_TRUE(SetupSendStream());
SetSendParameters(send_parameters_);
SetSend(true);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
SetSend(false);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
}
// Test that a channel will send if and only if it has a source and is enabled
// for sending.
TEST_P(WebRtcVoiceEngineTestFake, SendStateWithAndWithoutSource) {
EXPECT_TRUE(SetupSendStream());
SetSendParameters(send_parameters_);
SetAudioSend(kSsrcX, true, nullptr);
SetSend(true);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
SetAudioSend(kSsrcX, true, &fake_source_);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
SetAudioSend(kSsrcX, true, nullptr);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
}
// Test that a channel is muted/unmuted.
TEST_P(WebRtcVoiceEngineTestFake, SendStateMuteUnmute) {
EXPECT_TRUE(SetupSendStream());
SetSendParameters(send_parameters_);
EXPECT_FALSE(GetSendStream(kSsrcX).muted());
SetAudioSend(kSsrcX, true, nullptr);
EXPECT_FALSE(GetSendStream(kSsrcX).muted());
SetAudioSend(kSsrcX, false, nullptr);
EXPECT_TRUE(GetSendStream(kSsrcX).muted());
}
// Test that SetSendParameters() does not alter a stream's send state.
TEST_P(WebRtcVoiceEngineTestFake, SendStateWhenStreamsAreRecreated) {
EXPECT_TRUE(SetupSendStream());
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
// Turn on sending.
SetSend(true);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
// Changing RTP header extensions will recreate the AudioSendStream.
send_parameters_.extensions.push_back(
webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 12));
SetSendParameters(send_parameters_);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
// Turn off sending.
SetSend(false);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
// Changing RTP header extensions will recreate the AudioSendStream.
send_parameters_.extensions.clear();
SetSendParameters(send_parameters_);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
}
// Test that we can create a channel and start playing out on it.
TEST_P(WebRtcVoiceEngineTestFake, Playout) {
EXPECT_TRUE(SetupRecvStream());
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
channel_->SetPlayout(true);
EXPECT_TRUE(GetRecvStream(kSsrcX).started());
channel_->SetPlayout(false);
EXPECT_FALSE(GetRecvStream(kSsrcX).started());
}
// Test that we can add and remove send streams.
TEST_P(WebRtcVoiceEngineTestFake, CreateAndDeleteMultipleSendStreams) {
SetupForMultiSendStream();
// Set the global state for sending.
SetSend(true);
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
SetAudioSend(ssrc, true, &fake_source_);
// Verify that we are in a sending state for all the created streams.
EXPECT_TRUE(GetSendStream(ssrc).IsSending());
}
EXPECT_EQ(arraysize(kSsrcs4), call_.GetAudioSendStreams().size());
// Delete the send streams.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(channel_->RemoveSendStream(ssrc));
EXPECT_FALSE(call_.GetAudioSendStream(ssrc));
EXPECT_FALSE(channel_->RemoveSendStream(ssrc));
}
EXPECT_EQ(0u, call_.GetAudioSendStreams().size());
}
// Test SetSendCodecs correctly configure the codecs in all send streams.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsWithMultipleSendStreams) {
SetupForMultiSendStream();
// Create send streams.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
}
cricket::AudioSendParameters parameters;
// Set ISAC(16K) and CN(16K). VAD should be activated.
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kCn16000Codec);
parameters.codecs[1].id = 97;
SetSendParameters(parameters);
// Verify ISAC and VAD are corrected configured on all send channels.
for (uint32_t ssrc : kSsrcs4) {
ASSERT_TRUE(call_.GetAudioSendStream(ssrc) != nullptr);
const auto& send_codec_spec =
*call_.GetAudioSendStream(ssrc)->GetConfig().send_codec_spec;
EXPECT_STRCASEEQ("ISAC", send_codec_spec.format.name.c_str());
EXPECT_EQ(1u, send_codec_spec.format.num_channels);
EXPECT_EQ(97, send_codec_spec.cng_payload_type);
}
// Change to PCMU(8K) and CN(16K).
parameters.codecs[0] = kPcmuCodec;
SetSendParameters(parameters);
for (uint32_t ssrc : kSsrcs4) {
ASSERT_TRUE(call_.GetAudioSendStream(ssrc) != nullptr);
const auto& send_codec_spec =
*call_.GetAudioSendStream(ssrc)->GetConfig().send_codec_spec;
EXPECT_STRCASEEQ("PCMU", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.cng_payload_type);
}
}
// Test we can SetSend on all send streams correctly.
TEST_P(WebRtcVoiceEngineTestFake, SetSendWithMultipleSendStreams) {
SetupForMultiSendStream();
// Create the send channels and they should be a "not sending" date.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
SetAudioSend(ssrc, true, &fake_source_);
EXPECT_FALSE(GetSendStream(ssrc).IsSending());
}
// Set the global state for starting sending.
SetSend(true);
for (uint32_t ssrc : kSsrcs4) {
// Verify that we are in a sending state for all the send streams.
EXPECT_TRUE(GetSendStream(ssrc).IsSending());
}
// Set the global state for stopping sending.
SetSend(false);
for (uint32_t ssrc : kSsrcs4) {
// Verify that we are in a stop state for all the send streams.
EXPECT_FALSE(GetSendStream(ssrc).IsSending());
}
}
// Test we can set the correct statistics on all send streams.
TEST_P(WebRtcVoiceEngineTestFake, GetStatsWithMultipleSendStreams) {
SetupForMultiSendStream();
// Create send streams.
for (uint32_t ssrc : kSsrcs4) {
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(ssrc)));
}
// Create a receive stream to check that none of the send streams end up in
// the receive stream stats.
EXPECT_TRUE(AddRecvStream(kSsrcY));
// We need send codec to be set to get all stats.
SetSendParameters(send_parameters_);
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
SetAudioSendStreamStats();
// Check stats for the added streams.
{
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
cricket::VoiceMediaInfo info;
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
// We have added 4 send streams. We should see empty stats for all.
EXPECT_EQ(static_cast<size_t>(arraysize(kSsrcs4)), info.senders.size());
for (const auto& sender : info.senders) {
VerifyVoiceSenderInfo(sender, false);
}
VerifyVoiceSendRecvCodecs(info);
// We have added one receive stream. We should see empty stats.
EXPECT_EQ(info.receivers.size(), 1u);
EXPECT_EQ(info.receivers[0].ssrc(), 0u);
}
// Remove the kSsrcY stream. No receiver stats.
{
cricket::VoiceMediaInfo info;
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
EXPECT_EQ(static_cast<size_t>(arraysize(kSsrcs4)), info.senders.size());
EXPECT_EQ(0u, info.receivers.size());
}
// Deliver a new packet - a default receive stream should be created and we
// should see stats again.
{
cricket::VoiceMediaInfo info;
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
SetAudioReceiveStreamStats();
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
EXPECT_EQ(static_cast<size_t>(arraysize(kSsrcs4)), info.senders.size());
EXPECT_EQ(1u, info.receivers.size());
VerifyVoiceReceiverInfo(info.receivers[0]);
VerifyVoiceSendRecvCodecs(info);
}
}
// Test that we can add and remove receive streams, and do proper send/playout.
// We can receive on multiple streams while sending one stream.
TEST_P(WebRtcVoiceEngineTestFake, PlayoutWithMultipleStreams) {
EXPECT_TRUE(SetupSendStream());
// Start playout without a receive stream.
SetSendParameters(send_parameters_);
channel_->SetPlayout(true);
// Adding another stream should enable playout on the new stream only.
EXPECT_TRUE(AddRecvStream(kSsrcY));
SetSend(true);
EXPECT_TRUE(GetSendStream(kSsrcX).IsSending());
// Make sure only the new stream is played out.
EXPECT_TRUE(GetRecvStream(kSsrcY).started());
// Adding yet another stream should have stream 2 and 3 enabled for playout.
EXPECT_TRUE(AddRecvStream(kSsrcZ));
EXPECT_TRUE(GetRecvStream(kSsrcY).started());
EXPECT_TRUE(GetRecvStream(kSsrcZ).started());
// Stop sending.
SetSend(false);
EXPECT_FALSE(GetSendStream(kSsrcX).IsSending());
// Stop playout.
channel_->SetPlayout(false);
EXPECT_FALSE(GetRecvStream(kSsrcY).started());
EXPECT_FALSE(GetRecvStream(kSsrcZ).started());
// Restart playout and make sure recv streams are played out.
channel_->SetPlayout(true);
EXPECT_TRUE(GetRecvStream(kSsrcY).started());
EXPECT_TRUE(GetRecvStream(kSsrcZ).started());
// Now remove the recv streams.
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcZ));
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
}
TEST_P(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) {
EXPECT_TRUE(SetupSendStream());
send_parameters_.options.audio_network_adaptor = true;
send_parameters_.options.audio_network_adaptor_config = {"1234"};
SetSendParameters(send_parameters_);
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
}
TEST_P(WebRtcVoiceEngineTestFake, AudioSendResetAudioNetworkAdaptor) {
EXPECT_TRUE(SetupSendStream());
send_parameters_.options.audio_network_adaptor = true;
send_parameters_.options.audio_network_adaptor_config = {"1234"};
SetSendParameters(send_parameters_);
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
cricket::AudioOptions options;
options.audio_network_adaptor = false;
SetAudioSend(kSsrcX, true, nullptr, &options);
EXPECT_EQ(absl::nullopt, GetAudioNetworkAdaptorConfig(kSsrcX));
}
TEST_P(WebRtcVoiceEngineTestFake, AudioNetworkAdaptorNotGetOverridden) {
EXPECT_TRUE(SetupSendStream());
send_parameters_.options.audio_network_adaptor = true;
send_parameters_.options.audio_network_adaptor_config = {"1234"};
SetSendParameters(send_parameters_);
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
const int initial_num = call_.GetNumCreatedSendStreams();
cricket::AudioOptions options;
options.audio_network_adaptor = absl::nullopt;
// Unvalued `options.audio_network_adaptor` should not reset audio network
// adaptor.
SetAudioSend(kSsrcX, true, nullptr, &options);
// AudioSendStream not expected to be recreated.
EXPECT_EQ(initial_num, call_.GetNumCreatedSendStreams());
EXPECT_EQ(send_parameters_.options.audio_network_adaptor_config,
GetAudioNetworkAdaptorConfig(kSsrcX));
}
// Test that we can set the outgoing SSRC properly.
// SSRC is set in SetupSendStream() by calling AddSendStream.
TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrc) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
}
TEST_P(WebRtcVoiceEngineTestFake, GetStats) {
// Setup. We need send codec to be set to get all stats.
EXPECT_TRUE(SetupSendStream());
// SetupSendStream adds a send stream with kSsrcX, so the receive
// stream has to use a different SSRC.
EXPECT_TRUE(AddRecvStream(kSsrcY));
SetSendParameters(send_parameters_);
EXPECT_TRUE(channel_->SetRecvParameters(recv_parameters_));
SetAudioSendStreamStats();
// Check stats for the added streams.
{
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
cricket::VoiceMediaInfo info;
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
// We have added one send stream. We should see the stats we've set.
EXPECT_EQ(1u, info.senders.size());
VerifyVoiceSenderInfo(info.senders[0], false);
// We have added one receive stream. We should see empty stats.
EXPECT_EQ(info.receivers.size(), 1u);
EXPECT_EQ(info.receivers[0].ssrc(), 0u);
}
// Start sending - this affects some reported stats.
{
cricket::VoiceMediaInfo info;
SetSend(true);
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
VerifyVoiceSenderInfo(info.senders[0], true);
VerifyVoiceSendRecvCodecs(info);
}
// Remove the kSsrcY stream. No receiver stats.
{
cricket::VoiceMediaInfo info;
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrcY));
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
EXPECT_EQ(1u, info.senders.size());
EXPECT_EQ(0u, info.receivers.size());
}
// Deliver a new packet - a default receive stream should be created and we
// should see stats again.
{
cricket::VoiceMediaInfo info;
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
SetAudioReceiveStreamStats();
EXPECT_CALL(*adm_, GetPlayoutUnderrunCount()).WillOnce(Return(0));
EXPECT_EQ(true,
channel_->GetStats(&info, /*get_and_clear_legacy_stats=*/true));
EXPECT_EQ(1u, info.senders.size());
EXPECT_EQ(1u, info.receivers.size());
VerifyVoiceReceiverInfo(info.receivers[0]);
VerifyVoiceSendRecvCodecs(info);
}
}
// Test that we can set the outgoing SSRC properly with multiple streams.
// SSRC is set in SetupSendStream() by calling AddSendStream.
TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrcWithMultipleStreams) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
}
// Test that the local SSRC is the same on sending and receiving channels if the
// receive channel is created before the send channel.
TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrcAfterCreatingReceiveChannel) {
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX));
EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
}
// Test that we can properly receive packets.
TEST_P(WebRtcVoiceEngineTestFake, Recv) {
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(AddRecvStream(1));
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_TRUE(
GetRecvStream(1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
}
// Test that we can properly receive packets on multiple streams.
TEST_P(WebRtcVoiceEngineTestFake, RecvWithMultipleStreams) {
EXPECT_TRUE(SetupChannel());
const uint32_t ssrc1 = 1;
const uint32_t ssrc2 = 2;
const uint32_t ssrc3 = 3;
EXPECT_TRUE(AddRecvStream(ssrc1));
EXPECT_TRUE(AddRecvStream(ssrc2));
EXPECT_TRUE(AddRecvStream(ssrc3));
// Create packets with the right SSRCs.
unsigned char packets[4][sizeof(kPcmuFrame)];
for (size_t i = 0; i < arraysize(packets); ++i) {
memcpy(packets[i], kPcmuFrame, sizeof(kPcmuFrame));
rtc::SetBE32(packets[i] + 8, static_cast<uint32_t>(i));
}
const cricket::FakeAudioReceiveStream& s1 = GetRecvStream(ssrc1);
const cricket::FakeAudioReceiveStream& s2 = GetRecvStream(ssrc2);
const cricket::FakeAudioReceiveStream& s3 = GetRecvStream(ssrc3);
EXPECT_EQ(s1.received_packets(), 0);
EXPECT_EQ(s2.received_packets(), 0);
EXPECT_EQ(s3.received_packets(), 0);
DeliverPacket(packets[0], sizeof(packets[0]));
EXPECT_EQ(s1.received_packets(), 0);
EXPECT_EQ(s2.received_packets(), 0);
EXPECT_EQ(s3.received_packets(), 0);
DeliverPacket(packets[1], sizeof(packets[1]));
EXPECT_EQ(s1.received_packets(), 1);
EXPECT_TRUE(s1.VerifyLastPacket(packets[1], sizeof(packets[1])));
EXPECT_EQ(s2.received_packets(), 0);
EXPECT_EQ(s3.received_packets(), 0);
DeliverPacket(packets[2], sizeof(packets[2]));
EXPECT_EQ(s1.received_packets(), 1);
EXPECT_EQ(s2.received_packets(), 1);
EXPECT_TRUE(s2.VerifyLastPacket(packets[2], sizeof(packets[2])));
EXPECT_EQ(s3.received_packets(), 0);
DeliverPacket(packets[3], sizeof(packets[3]));
EXPECT_EQ(s1.received_packets(), 1);
EXPECT_EQ(s2.received_packets(), 1);
EXPECT_EQ(s3.received_packets(), 1);
EXPECT_TRUE(s3.VerifyLastPacket(packets[3], sizeof(packets[3])));
EXPECT_TRUE(channel_->RemoveRecvStream(ssrc3));
EXPECT_TRUE(channel_->RemoveRecvStream(ssrc2));
EXPECT_TRUE(channel_->RemoveRecvStream(ssrc1));
}
// Test that receiving on an unsignaled stream works (a stream is created).
TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaled) {
EXPECT_TRUE(SetupChannel());
EXPECT_EQ(0u, call_.GetAudioReceiveStreams().size());
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
EXPECT_TRUE(
GetRecvStream(kSsrc1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
}
// Tests that when we add a stream without SSRCs, but contains a stream_id
// that it is stored and its stream id is later used when the first packet
// arrives to properly create a receive stream with a sync label.
TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaledSsrcWithSignaledStreamId) {
const char kSyncLabel[] = "sync_label";
EXPECT_TRUE(SetupChannel());
cricket::StreamParams unsignaled_stream;
unsignaled_stream.set_stream_ids({kSyncLabel});
ASSERT_TRUE(channel_->AddRecvStream(unsignaled_stream));
// The stream shouldn't have been created at this point because it doesn't
// have any SSRCs.
EXPECT_EQ(0u, call_.GetAudioReceiveStreams().size());
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
EXPECT_TRUE(
GetRecvStream(kSsrc1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
EXPECT_EQ(kSyncLabel, GetRecvStream(kSsrc1).GetConfig().sync_group);
// Remset the unsignaled stream to clear the cached parameters. If a new
// default unsignaled receive stream is created it will not have a sync group.
channel_->ResetUnsignaledRecvStream();
channel_->RemoveRecvStream(kSsrc1);
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
EXPECT_TRUE(
GetRecvStream(kSsrc1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
EXPECT_TRUE(GetRecvStream(kSsrc1).GetConfig().sync_group.empty());
}
TEST_P(WebRtcVoiceEngineTestFake,
ResetUnsignaledRecvStreamDeletesAllDefaultStreams) {
ASSERT_TRUE(SetupChannel());
// No receive streams to start with.
ASSERT_TRUE(call_.GetAudioReceiveStreams().empty());
// Deliver a couple packets with unsignaled SSRCs.
unsigned char packet[sizeof(kPcmuFrame)];
memcpy(packet, kPcmuFrame, sizeof(kPcmuFrame));
rtc::SetBE32(&packet[8], 0x1234);
DeliverPacket(packet, sizeof(packet));
rtc::SetBE32(&packet[8], 0x5678);
DeliverPacket(packet, sizeof(packet));
// Verify that the receive streams were created.
const auto& receivers1 = call_.GetAudioReceiveStreams();
ASSERT_EQ(receivers1.size(), 2u);
// Should remove all default streams.
channel_->ResetUnsignaledRecvStream();
const auto& receivers2 = call_.GetAudioReceiveStreams();
EXPECT_EQ(0u, receivers2.size());
}
// Test that receiving N unsignaled stream works (streams will be created), and
// that packets are forwarded to them all.
TEST_P(WebRtcVoiceEngineTestFake, RecvMultipleUnsignaled) {
EXPECT_TRUE(SetupChannel());
unsigned char packet[sizeof(kPcmuFrame)];
memcpy(packet, kPcmuFrame, sizeof(kPcmuFrame));
// Note that SSRC = 0 is not supported.
for (uint32_t ssrc = 1; ssrc < (1 + kMaxUnsignaledRecvStreams); ++ssrc) {
rtc::SetBE32(&packet[8], ssrc);
DeliverPacket(packet, sizeof(packet));
// Verify we have one new stream for each loop iteration.
EXPECT_EQ(ssrc, call_.GetAudioReceiveStreams().size());
EXPECT_EQ(1, GetRecvStream(ssrc).received_packets());
EXPECT_TRUE(GetRecvStream(ssrc).VerifyLastPacket(packet, sizeof(packet)));
}
// Sending on the same SSRCs again should not create new streams.
for (uint32_t ssrc = 1; ssrc < (1 + kMaxUnsignaledRecvStreams); ++ssrc) {
rtc::SetBE32(&packet[8], ssrc);
DeliverPacket(packet, sizeof(packet));
EXPECT_EQ(kMaxUnsignaledRecvStreams, call_.GetAudioReceiveStreams().size());
EXPECT_EQ(2, GetRecvStream(ssrc).received_packets());
EXPECT_TRUE(GetRecvStream(ssrc).VerifyLastPacket(packet, sizeof(packet)));
}
// Send on another SSRC, the oldest unsignaled stream (SSRC=1) is replaced.
constexpr uint32_t kAnotherSsrc = 667;
rtc::SetBE32(&packet[8], kAnotherSsrc);
DeliverPacket(packet, sizeof(packet));
const auto& streams = call_.GetAudioReceiveStreams();
EXPECT_EQ(kMaxUnsignaledRecvStreams, streams.size());
size_t i = 0;
for (uint32_t ssrc = 2; ssrc < (1 + kMaxUnsignaledRecvStreams); ++ssrc, ++i) {
EXPECT_EQ(ssrc, streams[i]->GetConfig().rtp.remote_ssrc);
EXPECT_EQ(2, streams[i]->received_packets());
}
EXPECT_EQ(kAnotherSsrc, streams[i]->GetConfig().rtp.remote_ssrc);
EXPECT_EQ(1, streams[i]->received_packets());
// Sanity check that we've checked all streams.
EXPECT_EQ(kMaxUnsignaledRecvStreams, (i + 1));
}
// Test that a default channel is created even after a signaled stream has been
// added, and that this stream will get any packets for unknown SSRCs.
TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaledAfterSignaled) {
EXPECT_TRUE(SetupChannel());
unsigned char packet[sizeof(kPcmuFrame)];
memcpy(packet, kPcmuFrame, sizeof(kPcmuFrame));
// Add a known stream, send packet and verify we got it.
const uint32_t signaled_ssrc = 1;
rtc::SetBE32(&packet[8], signaled_ssrc);
EXPECT_TRUE(AddRecvStream(signaled_ssrc));
DeliverPacket(packet, sizeof(packet));
EXPECT_TRUE(
GetRecvStream(signaled_ssrc).VerifyLastPacket(packet, sizeof(packet)));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
// Note that the first unknown SSRC cannot be 0, because we only support
// creating receive streams for SSRC!=0.
const uint32_t unsignaled_ssrc = 7011;
rtc::SetBE32(&packet[8], unsignaled_ssrc);
DeliverPacket(packet, sizeof(packet));
EXPECT_TRUE(
GetRecvStream(unsignaled_ssrc).VerifyLastPacket(packet, sizeof(packet)));
EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
DeliverPacket(packet, sizeof(packet));
EXPECT_EQ(2, GetRecvStream(unsignaled_ssrc).received_packets());
rtc::SetBE32(&packet[8], signaled_ssrc);
DeliverPacket(packet, sizeof(packet));
EXPECT_EQ(2, GetRecvStream(signaled_ssrc).received_packets());
EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
}
// Two tests to verify that adding a receive stream with the same SSRC as a
// previously added unsignaled stream will only recreate underlying stream
// objects if the stream parameters have changed.
TEST_P(WebRtcVoiceEngineTestFake, AddRecvStreamAfterUnsignaled_NoRecreate) {
EXPECT_TRUE(SetupChannel());
// Spawn unsignaled stream with SSRC=1.
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
EXPECT_TRUE(
GetRecvStream(1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
// Verify that the underlying stream object in Call is not recreated when a
// stream with SSRC=1 is added.
const auto& streams = call_.GetAudioReceiveStreams();
EXPECT_EQ(1u, streams.size());
int audio_receive_stream_id = streams.front()->id();
EXPECT_TRUE(AddRecvStream(1));
EXPECT_EQ(1u, streams.size());
EXPECT_EQ(audio_receive_stream_id, streams.front()->id());
}
TEST_P(WebRtcVoiceEngineTestFake, AddRecvStreamAfterUnsignaled_Updates) {
EXPECT_TRUE(SetupChannel());
// Spawn unsignaled stream with SSRC=1.
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
EXPECT_TRUE(
GetRecvStream(1).VerifyLastPacket(kPcmuFrame, sizeof(kPcmuFrame)));
// Verify that the underlying stream object in Call gets updated when a
// stream with SSRC=1 is added, and which has changed stream parameters.
const auto& streams = call_.GetAudioReceiveStreams();
EXPECT_EQ(1u, streams.size());
// The sync_group id should be empty.
EXPECT_TRUE(streams.front()->GetConfig().sync_group.empty());
const std::string new_stream_id("stream_id");
int audio_receive_stream_id = streams.front()->id();
cricket::StreamParams stream_params;
stream_params.ssrcs.push_back(1);
stream_params.set_stream_ids({new_stream_id});
EXPECT_TRUE(channel_->AddRecvStream(stream_params));
EXPECT_EQ(1u, streams.size());
// The audio receive stream should not have been recreated.
EXPECT_EQ(audio_receive_stream_id, streams.front()->id());
// The sync_group id should now match with the new stream params.
EXPECT_EQ(new_stream_id, streams.front()->GetConfig().sync_group);
}
// Test that AddRecvStream creates new stream.
TEST_P(WebRtcVoiceEngineTestFake, AddRecvStream) {
EXPECT_TRUE(SetupRecvStream());
EXPECT_TRUE(AddRecvStream(1));
}
// Test that after adding a recv stream, we do not decode more codecs than
// those previously passed into SetRecvCodecs.
TEST_P(WebRtcVoiceEngineTestFake, AddRecvStreamUnsupportedCodec) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kIsacCodec);
parameters.codecs.push_back(kPcmuCodec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{0, {"PCMU", 8000, 1}}, {103, {"ISAC", 16000, 1}}})));
}
// Test that we properly clean up any streams that were added, even if
// not explicitly removed.
TEST_P(WebRtcVoiceEngineTestFake, StreamCleanup) {
EXPECT_TRUE(SetupSendStream());
SetSendParameters(send_parameters_);
EXPECT_TRUE(AddRecvStream(1));
EXPECT_TRUE(AddRecvStream(2));
EXPECT_EQ(1u, call_.GetAudioSendStreams().size());
EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
delete channel_;
channel_ = NULL;
EXPECT_EQ(0u, call_.GetAudioSendStreams().size());
EXPECT_EQ(0u, call_.GetAudioReceiveStreams().size());
}
TEST_P(WebRtcVoiceEngineTestFake, TestAddRecvStreamSuccessWithZeroSsrc) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(AddRecvStream(0));
}
TEST_P(WebRtcVoiceEngineTestFake, TestAddRecvStreamFailWithSameSsrc) {
EXPECT_TRUE(SetupChannel());
EXPECT_TRUE(AddRecvStream(1));
EXPECT_FALSE(AddRecvStream(1));
}
// Test the InsertDtmf on default send stream as caller.
TEST_P(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCaller) {
TestInsertDtmf(0, true, kTelephoneEventCodec1);
}
// Test the InsertDtmf on default send stream as callee
TEST_P(WebRtcVoiceEngineTestFake, InsertDtmfOnDefaultSendStreamAsCallee) {
TestInsertDtmf(0, false, kTelephoneEventCodec2);
}
// Test the InsertDtmf on specified send stream as caller.
TEST_P(WebRtcVoiceEngineTestFake, InsertDtmfOnSendStreamAsCaller) {
TestInsertDtmf(kSsrcX, true, kTelephoneEventCodec2);
}
// Test the InsertDtmf on specified send stream as callee.
TEST_P(WebRtcVoiceEngineTestFake, InsertDtmfOnSendStreamAsCallee) {
TestInsertDtmf(kSsrcX, false, kTelephoneEventCodec1);
}
// Test propagation of extmap allow mixed setting.
TEST_P(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedAsCaller) {
TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/true);
}
TEST_P(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedDisabledAsCaller) {
TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/false);
}
TEST_P(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedAsCallee) {
TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/true);
}
TEST_P(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedDisabledAsCallee) {
TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/false);
}
TEST_P(WebRtcVoiceEngineTestFake, SetAudioOptions) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_CALL(*adm_, BuiltInAECIsAvailable())
.Times(8)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, BuiltInAGCIsAvailable())
.Times(4)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, BuiltInNSIsAvailable())
.Times(2)
.WillRepeatedly(Return(false));
EXPECT_EQ(200u, GetRecvStreamConfig(kSsrcY).jitter_buffer_max_packets);
EXPECT_FALSE(GetRecvStreamConfig(kSsrcY).jitter_buffer_fast_accelerate);
// Nothing set in AudioOptions, so everything should be as default.
send_parameters_.options = cricket::AudioOptions();
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_TRUE(IsHighPassFilterEnabled());
}
EXPECT_EQ(200u, GetRecvStreamConfig(kSsrcY).jitter_buffer_max_packets);
EXPECT_FALSE(GetRecvStreamConfig(kSsrcY).jitter_buffer_fast_accelerate);
// Turn echo cancellation off
send_parameters_.options.echo_cancellation = false;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/false);
}
// Turn echo cancellation back on, with settings, and make sure
// nothing else changed.
send_parameters_.options.echo_cancellation = true;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
}
// Turn off echo cancellation and delay agnostic aec.
send_parameters_.options.echo_cancellation = false;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/false);
}
// Restore AEC to be on to work with the following tests.
send_parameters_.options.echo_cancellation = true;
SetSendParameters(send_parameters_);
// Turn off AGC
send_parameters_.options.auto_gain_control = false;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(apm_config_.gain_controller1.enabled);
}
// Turn AGC back on
send_parameters_.options.auto_gain_control = true;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_TRUE(apm_config_.gain_controller1.enabled);
}
// Turn off other options.
send_parameters_.options.noise_suppression = false;
send_parameters_.options.highpass_filter = false;
send_parameters_.options.stereo_swapping = true;
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(IsHighPassFilterEnabled());
EXPECT_TRUE(apm_config_.gain_controller1.enabled);
EXPECT_FALSE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
}
// Set options again to ensure it has no impact.
SetSendParameters(send_parameters_);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_TRUE(apm_config_.gain_controller1.enabled);
EXPECT_FALSE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
}
}
TEST_P(WebRtcVoiceEngineTestFake, InitRecordingOnSend) {
EXPECT_CALL(*adm_, RecordingIsInitialized()).WillOnce(Return(false));
EXPECT_CALL(*adm_, Recording()).WillOnce(Return(false));
EXPECT_CALL(*adm_, InitRecording()).Times(1);
std::unique_ptr<cricket::VoiceMediaChannel> channel(
engine_->CreateMediaChannel(&call_, cricket::MediaConfig(),
cricket::AudioOptions(),
webrtc::CryptoOptions()));
channel->SetSend(true);
}
TEST_P(WebRtcVoiceEngineTestFake, SkipInitRecordingOnSend) {
EXPECT_CALL(*adm_, RecordingIsInitialized()).Times(0);
EXPECT_CALL(*adm_, Recording()).Times(0);
EXPECT_CALL(*adm_, InitRecording()).Times(0);
cricket::AudioOptions options;
options.init_recording_on_send = false;
std::unique_ptr<cricket::VoiceMediaChannel> channel(
engine_->CreateMediaChannel(&call_, cricket::MediaConfig(), options,
webrtc::CryptoOptions()));
channel->SetSend(true);
}
TEST_P(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) {
EXPECT_TRUE(SetupSendStream());
EXPECT_CALL(*adm_, BuiltInAECIsAvailable())
.Times(use_null_apm_ ? 4 : 8)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, BuiltInAGCIsAvailable())
.Times(use_null_apm_ ? 7 : 8)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, BuiltInNSIsAvailable())
.Times(use_null_apm_ ? 5 : 8)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, RecordingIsInitialized())
.Times(2)
.WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, Recording()).Times(2).WillRepeatedly(Return(false));
EXPECT_CALL(*adm_, InitRecording()).Times(2).WillRepeatedly(Return(0));
std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel1(
static_cast<cricket::WebRtcVoiceMediaChannel*>(
engine_->CreateMediaChannel(&call_, cricket::MediaConfig(),
cricket::AudioOptions(),
webrtc::CryptoOptions())));
std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel2(
static_cast<cricket::WebRtcVoiceMediaChannel*>(
engine_->CreateMediaChannel(&call_, cricket::MediaConfig(),
cricket::AudioOptions(),
webrtc::CryptoOptions())));
// Have to add a stream to make SetSend work.
cricket::StreamParams stream1;
stream1.ssrcs.push_back(1);
channel1->AddSendStream(stream1);
cricket::StreamParams stream2;
stream2.ssrcs.push_back(2);
channel2->AddSendStream(stream2);
// AEC and AGC and NS
cricket::AudioSendParameters parameters_options_all = send_parameters_;
parameters_options_all.options.echo_cancellation = true;
parameters_options_all.options.auto_gain_control = true;
parameters_options_all.options.noise_suppression = true;
EXPECT_TRUE(channel1->SetSendParameters(parameters_options_all));
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
VerifyGainControlEnabledCorrectly();
EXPECT_TRUE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
EXPECT_EQ(parameters_options_all.options, channel1->options());
EXPECT_TRUE(channel2->SetSendParameters(parameters_options_all));
VerifyEchoCancellationSettings(/*enabled=*/true);
VerifyGainControlEnabledCorrectly();
EXPECT_EQ(parameters_options_all.options, channel2->options());
}
// unset NS
cricket::AudioSendParameters parameters_options_no_ns = send_parameters_;
parameters_options_no_ns.options.noise_suppression = false;
EXPECT_TRUE(channel1->SetSendParameters(parameters_options_no_ns));
cricket::AudioOptions expected_options = parameters_options_all.options;
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
VerifyGainControlEnabledCorrectly();
expected_options.echo_cancellation = true;
expected_options.auto_gain_control = true;
expected_options.noise_suppression = false;
EXPECT_EQ(expected_options, channel1->options());
}
// unset AGC
cricket::AudioSendParameters parameters_options_no_agc = send_parameters_;
parameters_options_no_agc.options.auto_gain_control = false;
EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc));
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(apm_config_.gain_controller1.enabled);
EXPECT_TRUE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
expected_options.echo_cancellation = true;
expected_options.auto_gain_control = false;
expected_options.noise_suppression = true;
EXPECT_EQ(expected_options, channel2->options());
}
EXPECT_TRUE(channel_->SetSendParameters(parameters_options_all));
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
VerifyGainControlEnabledCorrectly();
EXPECT_TRUE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
}
channel1->SetSend(true);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
VerifyGainControlEnabledCorrectly();
EXPECT_FALSE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
}
channel2->SetSend(true);
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(apm_config_.gain_controller1.enabled);
EXPECT_TRUE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
}
// Make sure settings take effect while we are sending.
cricket::AudioSendParameters parameters_options_no_agc_nor_ns =
send_parameters_;
parameters_options_no_agc_nor_ns.options.auto_gain_control = false;
parameters_options_no_agc_nor_ns.options.noise_suppression = false;
EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc_nor_ns));
if (!use_null_apm_) {
VerifyEchoCancellationSettings(/*enabled=*/true);
EXPECT_FALSE(apm_config_.gain_controller1.enabled);
EXPECT_FALSE(apm_config_.noise_suppression.enabled);
EXPECT_EQ(apm_config_.noise_suppression.level, kDefaultNsLevel);
expected_options.echo_cancellation = true;
expected_options.auto_gain_control = false;
expected_options.noise_suppression = false;
EXPECT_EQ(expected_options, channel2->options());
}
}
// This test verifies DSCP settings are properly applied on voice media channel.
TEST_P(WebRtcVoiceEngineTestFake, TestSetDscpOptions) {
EXPECT_TRUE(SetupSendStream());
cricket::FakeNetworkInterface network_interface;
cricket::MediaConfig config;
std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel;
webrtc::RtpParameters parameters;
channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
engine_->CreateMediaChannel(&call_, config, cricket::AudioOptions(),
webrtc::CryptoOptions())));
channel->SetInterface(&network_interface);
// Default value when DSCP is disabled should be DSCP_DEFAULT.
EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
channel->SetInterface(nullptr);
config.enable_dscp = true;
channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
engine_->CreateMediaChannel(&call_, config, cricket::AudioOptions(),
webrtc::CryptoOptions())));
channel->SetInterface(&network_interface);
EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
// Create a send stream to configure
EXPECT_TRUE(
channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcZ)));
parameters = channel->GetRtpSendParameters(kSsrcZ);
ASSERT_FALSE(parameters.encodings.empty());
// Various priorities map to various dscp values.
parameters.encodings[0].network_priority = webrtc::Priority::kHigh;
ASSERT_TRUE(channel->SetRtpSendParameters(kSsrcZ, parameters).ok());
EXPECT_EQ(rtc::DSCP_EF, network_interface.dscp());
parameters.encodings[0].network_priority = webrtc::Priority::kVeryLow;
ASSERT_TRUE(channel->SetRtpSendParameters(kSsrcZ, parameters).ok());
EXPECT_EQ(rtc::DSCP_CS1, network_interface.dscp());
// Packets should also self-identify their dscp in PacketOptions.
const uint8_t kData[10] = {0};
EXPECT_TRUE(channel->SendRtcp(kData, sizeof(kData)));
EXPECT_EQ(rtc::DSCP_CS1, network_interface.options().dscp);
channel->SetInterface(nullptr);
// Verify that setting the option to false resets the
// DiffServCodePoint.
config.enable_dscp = false;
channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
engine_->CreateMediaChannel(&call_, config, cricket::AudioOptions(),
webrtc::CryptoOptions())));
channel->SetInterface(&network_interface);
// Default value when DSCP is disabled should be DSCP_DEFAULT.
EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
channel->SetInterface(nullptr);
}
TEST_P(WebRtcVoiceEngineTestFake, SetOutputVolume) {
EXPECT_TRUE(SetupChannel());
EXPECT_FALSE(channel_->SetOutputVolume(kSsrcY, 0.5));
cricket::StreamParams stream;
stream.ssrcs.push_back(kSsrcY);
EXPECT_TRUE(channel_->AddRecvStream(stream));
EXPECT_DOUBLE_EQ(1, GetRecvStream(kSsrcY).gain());
EXPECT_TRUE(channel_->SetOutputVolume(kSsrcY, 3));
EXPECT_DOUBLE_EQ(3, GetRecvStream(kSsrcY).gain());
}
TEST_P(WebRtcVoiceEngineTestFake, SetOutputVolumeUnsignaledRecvStream) {
EXPECT_TRUE(SetupChannel());
// Spawn an unsignaled stream by sending a packet - gain should be 1.
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_DOUBLE_EQ(1, GetRecvStream(kSsrc1).gain());
// Should remember the volume "2" which will be set on new unsignaled streams,
// and also set the gain to 2 on existing unsignaled streams.
EXPECT_TRUE(channel_->SetDefaultOutputVolume(2));
EXPECT_DOUBLE_EQ(2, GetRecvStream(kSsrc1).gain());
// Spawn an unsignaled stream by sending a packet - gain should be 2.
unsigned char pcmuFrame2[sizeof(kPcmuFrame)];
memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame));
rtc::SetBE32(&pcmuFrame2[8], kSsrcX);
DeliverPacket(pcmuFrame2, sizeof(pcmuFrame2));
EXPECT_DOUBLE_EQ(2, GetRecvStream(kSsrcX).gain());
// Setting gain for all unsignaled streams.
EXPECT_TRUE(channel_->SetDefaultOutputVolume(3));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_DOUBLE_EQ(3, GetRecvStream(kSsrc1).gain());
}
EXPECT_DOUBLE_EQ(3, GetRecvStream(kSsrcX).gain());
// Setting gain on an individual stream affects only that.
EXPECT_TRUE(channel_->SetOutputVolume(kSsrcX, 4));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_DOUBLE_EQ(3, GetRecvStream(kSsrc1).gain());
}
EXPECT_DOUBLE_EQ(4, GetRecvStream(kSsrcX).gain());
}
TEST_P(WebRtcVoiceEngineTestFake, BaseMinimumPlayoutDelayMs) {
EXPECT_TRUE(SetupChannel());
EXPECT_FALSE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 200));
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
cricket::StreamParams stream;
stream.ssrcs.push_back(kSsrcY);
EXPECT_TRUE(channel_->AddRecvStream(stream));
EXPECT_EQ(0, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcY, 300));
EXPECT_EQ(300, GetRecvStream(kSsrcY).base_mininum_playout_delay_ms());
}
TEST_P(WebRtcVoiceEngineTestFake,
BaseMinimumPlayoutDelayMsUnsignaledRecvStream) {
// Here base minimum delay is abbreviated to delay in comments for shortness.
EXPECT_TRUE(SetupChannel());
// Spawn an unsignaled stream by sending a packet - delay should be 0.
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
// Check that it doesn't provide default values for unknown ssrc.
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
// Check that default value for unsignaled streams is 0.
EXPECT_EQ(0, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
// Should remember the delay 100 which will be set on new unsignaled streams,
// and also set the delay to 100 on existing unsignaled streams.
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 100));
EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
// Check that it doesn't provide default values for unknown ssrc.
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
// Spawn an unsignaled stream by sending a packet - delay should be 100.
unsigned char pcmuFrame2[sizeof(kPcmuFrame)];
memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame));
rtc::SetBE32(&pcmuFrame2[8], kSsrcX);
DeliverPacket(pcmuFrame2, sizeof(pcmuFrame2));
EXPECT_EQ(100, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
// Setting delay with SSRC=0 should affect all unsignaled streams.
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrc0, 300));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
}
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
// Setting delay on an individual stream affects only that.
EXPECT_TRUE(channel_->SetBaseMinimumPlayoutDelayMs(kSsrcX, 400));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc1).value_or(-1));
}
EXPECT_EQ(400, channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1));
EXPECT_EQ(300, channel_->GetBaseMinimumPlayoutDelayMs(kSsrc0).value_or(-1));
// Check that it doesn't provide default values for unknown ssrc.
EXPECT_FALSE(channel_->GetBaseMinimumPlayoutDelayMs(kSsrcY).has_value());
}
TEST_P(WebRtcVoiceEngineTestFake, SetsSyncGroupFromStreamId) {
const uint32_t kAudioSsrc = 123;
const std::string kStreamId = "AvSyncLabel";
EXPECT_TRUE(SetupSendStream());
cricket::StreamParams sp = cricket::StreamParams::CreateLegacy(kAudioSsrc);
sp.set_stream_ids({kStreamId});
// Creating two channels to make sure that sync label is set properly for both
// the default voice channel and following ones.
EXPECT_TRUE(channel_->AddRecvStream(sp));
sp.ssrcs[0] += 1;
EXPECT_TRUE(channel_->AddRecvStream(sp));
ASSERT_EQ(2u, call_.GetAudioReceiveStreams().size());
EXPECT_EQ(kStreamId,
call_.GetAudioReceiveStream(kAudioSsrc)->GetConfig().sync_group)
<< "SyncGroup should be set based on stream id";
EXPECT_EQ(kStreamId,
call_.GetAudioReceiveStream(kAudioSsrc + 1)->GetConfig().sync_group)
<< "SyncGroup should be set based on stream id";
}
// TODO(solenberg): Remove, once recv streams are configured through Call.
// (This is then covered by TestSetRecvRtpHeaderExtensions.)
TEST_P(WebRtcVoiceEngineTestFake, ConfiguresAudioReceiveStreamRtpExtensions) {
// Test that setting the header extensions results in the expected state
// changes on an associated Call.
std::vector<uint32_t> ssrcs;
ssrcs.push_back(223);
ssrcs.push_back(224);
EXPECT_TRUE(SetupSendStream());
SetSendParameters(send_parameters_);
for (uint32_t ssrc : ssrcs) {
EXPECT_TRUE(
channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(ssrc)));
}
EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
for (uint32_t ssrc : ssrcs) {
const auto* s = call_.GetAudioReceiveStream(ssrc);
EXPECT_NE(nullptr, s);
EXPECT_EQ(0u, s->GetConfig().rtp.extensions.size());
}
// Set up receive extensions.
const std::vector<webrtc::RtpExtension> header_extensions =
GetDefaultEnabledRtpHeaderExtensions(*engine_);
cricket::AudioRecvParameters recv_parameters;
recv_parameters.extensions = header_extensions;
channel_->SetRecvParameters(recv_parameters);
EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
for (uint32_t ssrc : ssrcs) {
const auto* s = call_.GetAudioReceiveStream(ssrc);
EXPECT_NE(nullptr, s);
const auto& s_exts = s->GetConfig().rtp.extensions;
EXPECT_EQ(header_extensions.size(), s_exts.size());
for (const auto& e_ext : header_extensions) {
for (const auto& s_ext : s_exts) {
if (e_ext.id == s_ext.id) {
EXPECT_EQ(e_ext.uri, s_ext.uri);
}
}
}
}
// Disable receive extensions.
channel_->SetRecvParameters(cricket::AudioRecvParameters());
for (uint32_t ssrc : ssrcs) {
const auto* s = call_.GetAudioReceiveStream(ssrc);
EXPECT_NE(nullptr, s);
EXPECT_EQ(0u, s->GetConfig().rtp.extensions.size());
}
}
TEST_P(WebRtcVoiceEngineTestFake, DeliverAudioPacket_Call) {
// Test that packets are forwarded to the Call when configured accordingly.
const uint32_t kAudioSsrc = 1;
rtc::CopyOnWriteBuffer kPcmuPacket(kPcmuFrame, sizeof(kPcmuFrame));
static const unsigned char kRtcp[] = {
0x80, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
rtc::CopyOnWriteBuffer kRtcpPacket(kRtcp, sizeof(kRtcp));
EXPECT_TRUE(SetupSendStream());
cricket::WebRtcVoiceMediaChannel* media_channel =
static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
SetSendParameters(send_parameters_);
EXPECT_TRUE(media_channel->AddRecvStream(
cricket::StreamParams::CreateLegacy(kAudioSsrc)));
EXPECT_EQ(1u, call_.GetAudioReceiveStreams().size());
const cricket::FakeAudioReceiveStream* s =
call_.GetAudioReceiveStream(kAudioSsrc);
EXPECT_EQ(0, s->received_packets());
channel_->OnPacketReceived(kPcmuPacket, /* packet_time_us */ -1);
rtc::Thread::Current()->ProcessMessages(0);
EXPECT_EQ(1, s->received_packets());
}
// All receive channels should be associated with the first send channel,
// since they do not send RTCP SR.
TEST_P(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_SendCreatedFirst) {
EXPECT_TRUE(SetupSendStream());
EXPECT_TRUE(AddRecvStream(kSsrcY));
EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcZ)));
EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc);
EXPECT_TRUE(AddRecvStream(kSsrcW));
EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcW).rtp.local_ssrc);
}
TEST_P(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_RecvCreatedFirst) {
EXPECT_TRUE(SetupRecvStream());
EXPECT_EQ(0xFA17FA17u, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcY)));
EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
EXPECT_TRUE(AddRecvStream(kSsrcZ));
EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc);
EXPECT_TRUE(
channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcW)));
EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc);
EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc);
}
TEST_P(WebRtcVoiceEngineTestFake, SetRawAudioSink) {
EXPECT_TRUE(SetupChannel());
std::unique_ptr<FakeAudioSink> fake_sink_1(new FakeAudioSink());
std::unique_ptr<FakeAudioSink> fake_sink_2(new FakeAudioSink());
// Setting the sink before a recv stream exists should do nothing.
channel_->SetRawAudioSink(kSsrcX, std::move(fake_sink_1));
EXPECT_TRUE(AddRecvStream(kSsrcX));
EXPECT_EQ(nullptr, GetRecvStream(kSsrcX).sink());
// Now try actually setting the sink.
channel_->SetRawAudioSink(kSsrcX, std::move(fake_sink_2));
EXPECT_NE(nullptr, GetRecvStream(kSsrcX).sink());
// Now try resetting it.
channel_->SetRawAudioSink(kSsrcX, nullptr);
EXPECT_EQ(nullptr, GetRecvStream(kSsrcX).sink());
}
TEST_P(WebRtcVoiceEngineTestFake, SetRawAudioSinkUnsignaledRecvStream) {
EXPECT_TRUE(SetupChannel());
std::unique_ptr<FakeAudioSink> fake_sink_1(new FakeAudioSink());
std::unique_ptr<FakeAudioSink> fake_sink_2(new FakeAudioSink());
std::unique_ptr<FakeAudioSink> fake_sink_3(new FakeAudioSink());
std::unique_ptr<FakeAudioSink> fake_sink_4(new FakeAudioSink());
// Should be able to set a default sink even when no stream exists.
channel_->SetDefaultRawAudioSink(std::move(fake_sink_1));
// Spawn an unsignaled stream by sending a packet - it should be assigned the
// default sink.
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
// Try resetting the default sink.
channel_->SetDefaultRawAudioSink(nullptr);
EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink());
// Try setting the default sink while the default stream exists.
channel_->SetDefaultRawAudioSink(std::move(fake_sink_2));
EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
// If we remove and add a default stream, it should get the same sink.
EXPECT_TRUE(channel_->RemoveRecvStream(kSsrc1));
DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame));
EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
// Spawn another unsignaled stream - it should be assigned the default sink
// and the previous unsignaled stream should lose it.
unsigned char pcmuFrame2[sizeof(kPcmuFrame)];
memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame));
rtc::SetBE32(&pcmuFrame2[8], kSsrcX);
DeliverPacket(pcmuFrame2, sizeof(pcmuFrame2));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink());
}
EXPECT_NE(nullptr, GetRecvStream(kSsrcX).sink());
// Reset the default sink - the second unsignaled stream should lose it.
channel_->SetDefaultRawAudioSink(nullptr);
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink());
}
EXPECT_EQ(nullptr, GetRecvStream(kSsrcX).sink());
// Try setting the default sink while two streams exists.
channel_->SetDefaultRawAudioSink(std::move(fake_sink_3));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink());
}
EXPECT_NE(nullptr, GetRecvStream(kSsrcX).sink());
// Try setting the sink for the first unsignaled stream using its known SSRC.
channel_->SetRawAudioSink(kSsrc1, std::move(fake_sink_4));
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink());
}
EXPECT_NE(nullptr, GetRecvStream(kSsrcX).sink());
if (kMaxUnsignaledRecvStreams > 1) {
EXPECT_NE(GetRecvStream(kSsrc1).sink(), GetRecvStream(kSsrcX).sink());
}
}
// Test that, just like the video channel, the voice channel communicates the
// network state to the call.
TEST_P(WebRtcVoiceEngineTestFake, OnReadyToSendSignalsNetworkState) {
EXPECT_TRUE(SetupChannel());
EXPECT_EQ(webrtc::kNetworkUp,
call_.GetNetworkState(webrtc::MediaType::AUDIO));
EXPECT_EQ(webrtc::kNetworkUp,
call_.GetNetworkState(webrtc::MediaType::VIDEO));
channel_->OnReadyToSend(false);
EXPECT_EQ(webrtc::kNetworkDown,
call_.GetNetworkState(webrtc::MediaType::AUDIO));
EXPECT_EQ(webrtc::kNetworkUp,
call_.GetNetworkState(webrtc::MediaType::VIDEO));
channel_->OnReadyToSend(true);
EXPECT_EQ(webrtc::kNetworkUp,
call_.GetNetworkState(webrtc::MediaType::AUDIO));
EXPECT_EQ(webrtc::kNetworkUp,
call_.GetNetworkState(webrtc::MediaType::VIDEO));
}
// Test that playout is still started after changing parameters
TEST_P(WebRtcVoiceEngineTestFake, PreservePlayoutWhenRecreateRecvStream) {
SetupRecvStream();
channel_->SetPlayout(true);
EXPECT_TRUE(GetRecvStream(kSsrcX).started());
// Changing RTP header extensions will recreate the
// AudioReceiveStreamInterface.
cricket::AudioRecvParameters parameters;
parameters.extensions.push_back(
webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 12));
channel_->SetRecvParameters(parameters);
EXPECT_TRUE(GetRecvStream(kSsrcX).started());
}
// Tests when GetSources is called with non-existing ssrc, it will return an
// empty list of RtpSource without crashing.
TEST_P(WebRtcVoiceEngineTestFake, GetSourcesWithNonExistingSsrc) {
// Setup an recv stream with `kSsrcX`.
SetupRecvStream();
cricket::WebRtcVoiceMediaChannel* media_channel =
static_cast<cricket::WebRtcVoiceMediaChannel*>(channel_);
// Call GetSources with `kSsrcY` which doesn't exist.
std::vector<webrtc::RtpSource> sources = media_channel->GetSources(kSsrcY);
EXPECT_EQ(0u, sources.size());
}
// Tests that the library initializes and shuts down properly.
TEST(WebRtcVoiceEngineTest, StartupShutdown) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
// If the VoiceEngine wants to gather available codecs early, that's fine
// but we never want it to create a decoder at this stage.
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
nullptr, field_trials);
engine.Init();
webrtc::RtcEventLogNull event_log;
webrtc::Call::Config call_config(&event_log);
call_config.trials = &field_trials;
call_config.task_queue_factory = task_queue_factory.get();
auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
cricket::VoiceMediaChannel* channel = engine.CreateMediaChannel(
call.get(), cricket::MediaConfig(), cricket::AudioOptions(),
webrtc::CryptoOptions());
EXPECT_TRUE(channel != nullptr);
delete channel;
}
}
// Tests that reference counting on the external ADM is correct.
TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
auto adm = rtc::make_ref_counted<
::testing::NiceMock<webrtc::test::MockAudioDeviceModule>>();
{
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
nullptr, field_trials);
engine.Init();
webrtc::RtcEventLogNull event_log;
webrtc::Call::Config call_config(&event_log);
call_config.trials = &field_trials;
call_config.task_queue_factory = task_queue_factory.get();
auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
cricket::VoiceMediaChannel* channel = engine.CreateMediaChannel(
call.get(), cricket::MediaConfig(), cricket::AudioOptions(),
webrtc::CryptoOptions());
EXPECT_TRUE(channel != nullptr);
delete channel;
}
// The engine/channel should have dropped their references.
EXPECT_EQ(adm.release()->Release(),
rtc::RefCountReleaseStatus::kDroppedLastRef);
}
}
// Verify the payload id of common audio codecs, including CN, ISAC, and G722.
TEST(WebRtcVoiceEngineTest, HasCorrectPayloadTypeMapping) {
for (bool use_null_apm : {false, true}) {
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
// TODO(ossu): Why are the payload types of codecs with non-static payload
// type assignments checked here? It shouldn't really matter.
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
nullptr, field_trials);
engine.Init();
for (const cricket::AudioCodec& codec : engine.send_codecs()) {
auto is_codec = [&codec](const char* name, int clockrate = 0) {
return absl::EqualsIgnoreCase(codec.name, name) &&
(clockrate == 0 || codec.clockrate == clockrate);
};
if (is_codec("CN", 16000)) {
EXPECT_EQ(105, codec.id);
} else if (is_codec("CN", 32000)) {
EXPECT_EQ(106, codec.id);
} else if (is_codec("ISAC", 16000)) {
EXPECT_EQ(103, codec.id);
} else if (is_codec("ISAC", 32000)) {
EXPECT_EQ(104, codec.id);
} else if (is_codec("G722", 8000)) {
EXPECT_EQ(9, codec.id);
} else if (is_codec("telephone-event", 8000)) {
EXPECT_EQ(126, codec.id);
// TODO(solenberg): 16k, 32k, 48k DTMF should be dynamically assigned.
// Remove these checks once both send and receive side assigns payload
// types dynamically.
} else if (is_codec("telephone-event", 16000)) {
EXPECT_EQ(113, codec.id);
} else if (is_codec("telephone-event", 32000)) {
EXPECT_EQ(112, codec.id);
} else if (is_codec("telephone-event", 48000)) {
EXPECT_EQ(110, codec.id);
} else if (is_codec("opus")) {
EXPECT_EQ(111, codec.id);
ASSERT_TRUE(codec.params.find("minptime") != codec.params.end());
EXPECT_EQ("10", codec.params.find("minptime")->second);
ASSERT_TRUE(codec.params.find("useinbandfec") != codec.params.end());
EXPECT_EQ("1", codec.params.find("useinbandfec")->second);
}
}
}
}
// Tests that VoE supports at least 32 channels
TEST(WebRtcVoiceEngineTest, Has32Channels) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
nullptr, field_trials);
engine.Init();
webrtc::RtcEventLogNull event_log;
webrtc::Call::Config call_config(&event_log);
call_config.trials = &field_trials;
call_config.task_queue_factory = task_queue_factory.get();
auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
cricket::VoiceMediaChannel* channels[32];
size_t num_channels = 0;
while (num_channels < arraysize(channels)) {
cricket::VoiceMediaChannel* channel = engine.CreateMediaChannel(
call.get(), cricket::MediaConfig(), cricket::AudioOptions(),
webrtc::CryptoOptions());
if (!channel)
break;
channels[num_channels++] = channel;
}
size_t expected = arraysize(channels);
EXPECT_EQ(expected, num_channels);
while (num_channels > 0) {
delete channels[--num_channels];
}
}
}
// Test that we set our preferred codecs properly.
TEST(WebRtcVoiceEngineTest, SetRecvCodecs) {
rtc::AutoThread main_thread;
for (bool use_null_apm : {false, true}) {
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
// TODO(ossu): I'm not sure of the intent of this test. It's either:
// - Check that our builtin codecs are usable by Channel.
// - The codecs provided by the engine is usable by Channel.
// It does not check that the codecs in the RecvParameters are actually
// what we sent in - though it's probably reasonable to expect so, if
// SetRecvParameters returns true.
// I think it will become clear once audio decoder injection is completed.
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(),
webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
webrtc::CreateBuiltinAudioDecoderFactory(), nullptr, apm, nullptr,
field_trials);
engine.Init();
webrtc::RtcEventLogNull event_log;
webrtc::Call::Config call_config(&event_log);
call_config.trials = &field_trials;
call_config.task_queue_factory = task_queue_factory.get();
auto call = absl::WrapUnique(webrtc::Call::Create(call_config));
cricket::WebRtcVoiceMediaChannel channel(
&engine, cricket::MediaConfig(), cricket::AudioOptions(),
webrtc::CryptoOptions(), call.get());
cricket::AudioRecvParameters parameters;
parameters.codecs = engine.recv_codecs();
EXPECT_TRUE(channel.SetRecvParameters(parameters));
}
}
TEST(WebRtcVoiceEngineTest, CollectRecvCodecs) {
for (bool use_null_apm : {false, true}) {
std::vector<webrtc::AudioCodecSpec> specs;
webrtc::AudioCodecSpec spec1{{"codec1", 48000, 2, {{"param1", "value1"}}},
{48000, 2, 16000, 10000, 20000}};
spec1.info.allow_comfort_noise = false;
spec1.info.supports_network_adaption = true;
specs.push_back(spec1);
webrtc::AudioCodecSpec spec2{{"codec2", 32000, 1}, {32000, 1, 32000}};
spec2.info.allow_comfort_noise = false;
specs.push_back(spec2);
specs.push_back(webrtc::AudioCodecSpec{
{"codec3", 16000, 1, {{"param1", "value1b"}, {"param2", "value2"}}},
{16000, 1, 13300}});
specs.push_back(
webrtc::AudioCodecSpec{{"codec4", 8000, 1}, {8000, 1, 64000}});
specs.push_back(
webrtc::AudioCodecSpec{{"codec5", 8000, 2}, {8000, 1, 64000}});
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory();
rtc::scoped_refptr<webrtc::MockAudioEncoderFactory> unused_encoder_factory =
webrtc::MockAudioEncoderFactory::CreateUnusedFactory();
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> mock_decoder_factory =
rtc::make_ref_counted<webrtc::MockAudioDecoderFactory>();
EXPECT_CALL(*mock_decoder_factory.get(), GetSupportedDecoders())
.WillOnce(Return(specs));
rtc::scoped_refptr<webrtc::test::MockAudioDeviceModule> adm =
webrtc::test::MockAudioDeviceModule::CreateNice();
rtc::scoped_refptr<webrtc::AudioProcessing> apm =
use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create();
webrtc::FieldTrialBasedConfig field_trials;
cricket::WebRtcVoiceEngine engine(
task_queue_factory.get(), adm.get(), unused_encoder_factory,
mock_decoder_factory, nullptr, apm, nullptr, field_trials);
engine.Init();
auto codecs = engine.recv_codecs();
EXPECT_EQ(11u, codecs.size());
// Rather than just ASSERTing that there are enough codecs, ensure that we
// can check the actual values safely, to provide better test results.
auto get_codec = [&codecs](size_t index) -> const cricket::AudioCodec& {
static const cricket::AudioCodec missing_codec(0, "<missing>", 0, 0, 0);
if (codecs.size() > index)
return codecs[index];
return missing_codec;
};
// Ensure the general codecs are generated first and in order.
for (size_t i = 0; i != specs.size(); ++i) {
EXPECT_EQ(specs[i].format.name, get_codec(i).name);
EXPECT_EQ(specs[i].format.clockrate_hz, get_codec(i).clockrate);
EXPECT_EQ(specs[i].format.num_channels, get_codec(i).channels);
EXPECT_EQ(specs[i].format.parameters, get_codec(i).params);
}
// Find the index of a codec, or -1 if not found, so that we can easily
// check supplementary codecs are ordered after the general codecs.
auto find_codec = [&codecs](const webrtc::SdpAudioFormat& format) -> int {
for (size_t i = 0; i != codecs.size(); ++i) {
const cricket::AudioCodec& codec = codecs[i];
if (absl::EqualsIgnoreCase(codec.name, format.name) &&
codec.clockrate == format.clockrate_hz &&
codec.channels == format.num_channels) {
return rtc::checked_cast<int>(i);
}
}
return -1;
};
// Ensure all supplementary codecs are generated last. Their internal
// ordering is not important. Without this cast, the comparison turned
// unsigned and, thus, failed for -1.
const int num_specs = static_cast<int>(specs.size());
EXPECT_GE(find_codec({"cn", 8000, 1}), num_specs);
EXPECT_GE(find_codec({"cn", 16000, 1}), num_specs);
EXPECT_EQ(find_codec({"cn", 32000, 1}), -1);
EXPECT_GE(find_codec({"telephone-event", 8000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 16000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 32000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 48000, 1}), num_specs);
}
}