mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

Bug: webrtc:42226041 Change-Id: Ife3548bda3042a7447b7c50f48f023a2bc0bc443 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/362103 Commit-Queue: Lionel Koenig <lionelk@webrtc.org> Reviewed-by: Jesus de Vicente Pena <devicentepena@webrtc.org> Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43017}
408 lines
15 KiB
C++
408 lines
15 KiB
C++
/*
|
|
* Copyright 2023 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 "audio/channel_send.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "api/array_view.h"
|
|
#include "api/audio/audio_frame.h"
|
|
#include "api/audio_codecs/audio_encoder.h"
|
|
#include "api/audio_codecs/audio_encoder_factory.h"
|
|
#include "api/audio_codecs/audio_format.h"
|
|
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
|
#include "api/call/bitrate_allocation.h"
|
|
#include "api/call/transport.h"
|
|
#include "api/crypto/crypto_options.h"
|
|
#include "api/environment/environment.h"
|
|
#include "api/environment/environment_factory.h"
|
|
#include "api/frame_transformer_interface.h"
|
|
#include "api/make_ref_counted.h"
|
|
#include "api/rtp_headers.h"
|
|
#include "api/scoped_refptr.h"
|
|
#include "api/test/mock_frame_transformer.h"
|
|
#include "api/test/mock_transformable_audio_frame.h"
|
|
#include "api/transport/bitrate_settings.h"
|
|
#include "api/units/data_rate.h"
|
|
#include "api/units/time_delta.h"
|
|
#include "api/units/timestamp.h"
|
|
#include "call/rtp_transport_config.h"
|
|
#include "call/rtp_transport_controller_send.h"
|
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
|
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
|
#include "rtc_base/gunit.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/mock_transport.h"
|
|
#include "test/scoped_key_value_config.h"
|
|
#include "test/time_controller/simulated_time_controller.h"
|
|
|
|
namespace webrtc {
|
|
namespace voe {
|
|
namespace {
|
|
|
|
using ::testing::Invoke;
|
|
using ::testing::NiceMock;
|
|
using ::testing::Return;
|
|
using ::testing::SaveArg;
|
|
|
|
constexpr int kRtcpIntervalMs = 1000;
|
|
constexpr int kSsrc = 333;
|
|
constexpr int kPayloadType = 1;
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr int kRtpRateHz = 48000;
|
|
|
|
BitrateConstraints GetBitrateConfig() {
|
|
BitrateConstraints bitrate_config;
|
|
bitrate_config.min_bitrate_bps = 10000;
|
|
bitrate_config.start_bitrate_bps = 100000;
|
|
bitrate_config.max_bitrate_bps = 1000000;
|
|
return bitrate_config;
|
|
}
|
|
|
|
class ChannelSendTest : public ::testing::Test {
|
|
protected:
|
|
ChannelSendTest()
|
|
: time_controller_(Timestamp::Seconds(1)),
|
|
env_(CreateEnvironment(&field_trials_,
|
|
time_controller_.GetClock(),
|
|
time_controller_.CreateTaskQueueFactory())),
|
|
transport_controller_(
|
|
RtpTransportConfig{.env = env_,
|
|
.bitrate_config = GetBitrateConfig()}) {
|
|
channel_ = voe::CreateChannelSend(env_, &transport_, nullptr, nullptr,
|
|
crypto_options_, false, kRtcpIntervalMs,
|
|
kSsrc, nullptr, &transport_controller_);
|
|
encoder_factory_ = CreateBuiltinAudioEncoderFactory();
|
|
SdpAudioFormat opus = SdpAudioFormat("opus", kRtpRateHz, 2);
|
|
std::unique_ptr<AudioEncoder> encoder =
|
|
encoder_factory_->Create(env_, opus, {.payload_type = kPayloadType});
|
|
channel_->SetEncoder(kPayloadType, opus, std::move(encoder));
|
|
transport_controller_.EnsureStarted();
|
|
channel_->RegisterSenderCongestionControlObjects(&transport_controller_);
|
|
ON_CALL(transport_, SendRtcp).WillByDefault(Return(true));
|
|
ON_CALL(transport_, SendRtp).WillByDefault(Return(true));
|
|
}
|
|
|
|
std::unique_ptr<AudioFrame> CreateAudioFrame(uint8_t data_init_value = 0) {
|
|
auto frame = std::make_unique<AudioFrame>();
|
|
frame->sample_rate_hz_ = kSampleRateHz;
|
|
frame->samples_per_channel_ = kSampleRateHz / 100;
|
|
frame->num_channels_ = 1;
|
|
frame->set_absolute_capture_timestamp_ms(
|
|
time_controller_.GetClock()->TimeInMilliseconds());
|
|
int16_t* dest = frame->mutable_data();
|
|
for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_;
|
|
i++, dest++) {
|
|
*dest = data_init_value;
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
void ProcessNextFrame(std::unique_ptr<AudioFrame> audio_frame) {
|
|
channel_->ProcessAndEncodeAudio(std::move(audio_frame));
|
|
// Advance time to process the task queue.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(10));
|
|
}
|
|
|
|
void ProcessNextFrame() { ProcessNextFrame(CreateAudioFrame()); }
|
|
|
|
GlobalSimulatedTimeController time_controller_;
|
|
webrtc::test::ScopedKeyValueConfig field_trials_;
|
|
Environment env_;
|
|
NiceMock<MockTransport> transport_;
|
|
CryptoOptions crypto_options_;
|
|
RtpTransportControllerSend transport_controller_;
|
|
std::unique_ptr<ChannelSendInterface> channel_;
|
|
rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_;
|
|
};
|
|
|
|
TEST_F(ChannelSendTest, StopSendShouldResetEncoder) {
|
|
channel_->StartSend();
|
|
// Insert two frames which should trigger a new packet.
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
|
|
EXPECT_CALL(transport_, SendRtp).Times(0);
|
|
ProcessNextFrame();
|
|
// StopSend should clear the previous audio frame stored in the encoder.
|
|
channel_->StopSend();
|
|
|
|
channel_->StartSend();
|
|
// The following frame should not trigger a new packet since the encoder
|
|
// needs 20 ms audio.
|
|
EXPECT_CALL(transport_, SendRtp).Times(0);
|
|
ProcessNextFrame();
|
|
}
|
|
|
|
TEST_F(ChannelSendTest, IncreaseRtpTimestampByPauseDuration) {
|
|
channel_->StartSend();
|
|
uint32_t timestamp;
|
|
int sent_packets = 0;
|
|
auto send_rtp = [&](rtc::ArrayView<const uint8_t> data,
|
|
const PacketOptions& options) {
|
|
++sent_packets;
|
|
RtpPacketReceived packet;
|
|
packet.Parse(data);
|
|
timestamp = packet.Timestamp();
|
|
return true;
|
|
};
|
|
EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp));
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
EXPECT_EQ(sent_packets, 1);
|
|
uint32_t first_timestamp = timestamp;
|
|
channel_->StopSend();
|
|
time_controller_.AdvanceTime(TimeDelta::Seconds(10));
|
|
channel_->StartSend();
|
|
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
EXPECT_EQ(sent_packets, 2);
|
|
int64_t timestamp_gap_ms =
|
|
static_cast<int64_t>(timestamp - first_timestamp) * 1000 / kRtpRateHz;
|
|
EXPECT_EQ(timestamp_gap_ms, 10020);
|
|
}
|
|
|
|
TEST_F(ChannelSendTest, FrameTransformerGetsCorrectTimestamp) {
|
|
rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
|
|
rtc::make_ref_counted<MockFrameTransformer>();
|
|
channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer);
|
|
rtc::scoped_refptr<TransformedFrameCallback> callback;
|
|
EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
|
|
.WillOnce(SaveArg<0>(&callback));
|
|
EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback);
|
|
|
|
std::optional<uint32_t> sent_timestamp;
|
|
auto send_rtp = [&](rtc::ArrayView<const uint8_t> data,
|
|
const PacketOptions& options) {
|
|
RtpPacketReceived packet;
|
|
packet.Parse(data);
|
|
if (!sent_timestamp) {
|
|
sent_timestamp = packet.Timestamp();
|
|
}
|
|
return true;
|
|
};
|
|
EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp));
|
|
|
|
channel_->StartSend();
|
|
int64_t transformable_frame_timestamp = -1;
|
|
EXPECT_CALL(*mock_frame_transformer, Transform)
|
|
.WillOnce([&](std::unique_ptr<TransformableFrameInterface> frame) {
|
|
transformable_frame_timestamp = frame->GetTimestamp();
|
|
callback->OnTransformedFrame(std::move(frame));
|
|
});
|
|
// Insert two frames which should trigger a new packet.
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
|
|
// Ensure the RTP timestamp on the frame passed to the transformer
|
|
// includes the RTP offset and matches the actual RTP timestamp on the sent
|
|
// packet.
|
|
EXPECT_EQ_WAIT(transformable_frame_timestamp,
|
|
0 + channel_->GetRtpRtcp()->StartTimestamp(), 1000);
|
|
EXPECT_TRUE_WAIT(sent_timestamp, 1000);
|
|
EXPECT_EQ(*sent_timestamp, transformable_frame_timestamp);
|
|
}
|
|
|
|
// Ensure that AudioLevel calculations are performed correctly per-packet even
|
|
// if there's an async Encoded Frame Transform happening.
|
|
TEST_F(ChannelSendTest, AudioLevelsAttachedToCorrectTransformedFrame) {
|
|
channel_->SetSendAudioLevelIndicationStatus(true, /*id=*/1);
|
|
RtpPacketReceived::ExtensionManager extension_manager;
|
|
extension_manager.RegisterByType(1, kRtpExtensionAudioLevel);
|
|
|
|
rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
|
|
rtc::make_ref_counted<MockFrameTransformer>();
|
|
channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer);
|
|
rtc::scoped_refptr<TransformedFrameCallback> callback;
|
|
EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
|
|
.WillOnce(SaveArg<0>(&callback));
|
|
EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback);
|
|
|
|
std::vector<uint8_t> sent_audio_levels;
|
|
auto send_rtp = [&](rtc::ArrayView<const uint8_t> data,
|
|
const PacketOptions& options) {
|
|
RtpPacketReceived packet(&extension_manager);
|
|
packet.Parse(data);
|
|
RTPHeader header;
|
|
packet.GetHeader(&header);
|
|
sent_audio_levels.push_back(header.extension.audio_level()->level());
|
|
return true;
|
|
};
|
|
EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp));
|
|
|
|
channel_->StartSend();
|
|
std::vector<std::unique_ptr<TransformableFrameInterface>> frames;
|
|
EXPECT_CALL(*mock_frame_transformer, Transform)
|
|
.Times(2)
|
|
.WillRepeatedly([&](std::unique_ptr<TransformableFrameInterface> frame) {
|
|
frames.push_back(std::move(frame));
|
|
});
|
|
|
|
// Insert two frames of 7s which should trigger a new packet.
|
|
ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/7));
|
|
ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/7));
|
|
|
|
// Insert two more frames of 3s, meaning a second packet is
|
|
// prepared and sent to the transform before the first packet has
|
|
// been sent.
|
|
ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/3));
|
|
ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/3));
|
|
|
|
// Wait for both packets to be encoded and sent to the transform.
|
|
EXPECT_EQ_WAIT(frames.size(), 2ul, 1000);
|
|
// Complete the transforms on both frames at the same time
|
|
callback->OnTransformedFrame(std::move(frames[0]));
|
|
callback->OnTransformedFrame(std::move(frames[1]));
|
|
|
|
// Allow things posted back to the encoder queue to run.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(10));
|
|
|
|
// Ensure the audio levels on both sent packets is present and
|
|
// matches their contents.
|
|
EXPECT_EQ_WAIT(sent_audio_levels.size(), 2ul, 1000);
|
|
// rms dbov of the packet with raw audio of 7s is 73.
|
|
EXPECT_EQ(sent_audio_levels[0], 73);
|
|
// rms dbov of the second packet with raw audio of 3s is 81.
|
|
EXPECT_EQ(sent_audio_levels[1], 81);
|
|
}
|
|
|
|
// Ensure that AudioLevels are attached to frames injected into the
|
|
// Encoded Frame transform.
|
|
TEST_F(ChannelSendTest, AudioLevelsAttachedToInsertedTransformedFrame) {
|
|
channel_->SetSendAudioLevelIndicationStatus(true, /*id=*/1);
|
|
RtpPacketReceived::ExtensionManager extension_manager;
|
|
extension_manager.RegisterByType(1, kRtpExtensionAudioLevel);
|
|
|
|
rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
|
|
rtc::make_ref_counted<MockFrameTransformer>();
|
|
channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer);
|
|
rtc::scoped_refptr<TransformedFrameCallback> callback;
|
|
EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
|
|
.WillOnce(SaveArg<0>(&callback));
|
|
EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback);
|
|
|
|
std::optional<uint8_t> sent_audio_level;
|
|
auto send_rtp = [&](rtc::ArrayView<const uint8_t> data,
|
|
const PacketOptions& options) {
|
|
RtpPacketReceived packet(&extension_manager);
|
|
packet.Parse(data);
|
|
RTPHeader header;
|
|
packet.GetHeader(&header);
|
|
sent_audio_level = header.extension.audio_level()->level();
|
|
return true;
|
|
};
|
|
EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp));
|
|
|
|
channel_->StartSend();
|
|
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(10));
|
|
// Inject a frame encoded elsewhere.
|
|
auto mock_frame = std::make_unique<NiceMock<MockTransformableAudioFrame>>();
|
|
uint8_t audio_level = 67;
|
|
ON_CALL(*mock_frame, AudioLevel()).WillByDefault(Return(audio_level));
|
|
uint8_t payload[10];
|
|
ON_CALL(*mock_frame, GetData())
|
|
.WillByDefault(Return(rtc::ArrayView<uint8_t>(&payload[0], 10)));
|
|
EXPECT_TRUE_WAIT(callback, 1000);
|
|
callback->OnTransformedFrame(std::move(mock_frame));
|
|
|
|
// Allow things posted back to the encoder queue to run.
|
|
time_controller_.AdvanceTime(TimeDelta::Millis(10));
|
|
|
|
// Ensure the audio levels is set on the sent packet.
|
|
EXPECT_TRUE_WAIT(sent_audio_level, 1000);
|
|
EXPECT_EQ(*sent_audio_level, audio_level);
|
|
}
|
|
|
|
// Ensure that GetUsedRate returns null if no frames are coded.
|
|
TEST_F(ChannelSendTest, NoUsedRateInitially) {
|
|
channel_->StartSend();
|
|
auto used_rate = channel_->GetUsedRate();
|
|
EXPECT_EQ(used_rate, std::nullopt);
|
|
}
|
|
|
|
// Ensure that GetUsedRate returns value with one coded frame.
|
|
TEST_F(ChannelSendTest, ValidUsedRateWithOneCodedFrame) {
|
|
channel_->StartSend();
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
auto used_rate = channel_->GetUsedRate();
|
|
EXPECT_GT(used_rate.value().bps(), 0);
|
|
}
|
|
|
|
// Ensure that GetUsedRate returns value with one coded frame.
|
|
TEST_F(ChannelSendTest, UsedRateIsLargerofLastTwoFrames) {
|
|
channel_->StartSend();
|
|
channel_->CallEncoder(
|
|
[&](AudioEncoder* encoder) { encoder->OnReceivedOverhead(72); });
|
|
DataRate lowrate = DataRate::BitsPerSec(40000);
|
|
DataRate highrate = DataRate::BitsPerSec(80000);
|
|
BitrateAllocationUpdate update;
|
|
update.bwe_period = TimeDelta::Millis(100);
|
|
|
|
update.target_bitrate = lowrate;
|
|
channel_->OnBitrateAllocation(update);
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
// Last two frames have rates [32kbps, -], yielding 32kbps.
|
|
auto used_rate_1 = channel_->GetUsedRate();
|
|
|
|
update.target_bitrate = highrate;
|
|
channel_->OnBitrateAllocation(update);
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
// Last two frames have rates [54kbps, 32kbps], yielding 54kbps
|
|
auto used_rate_2 = channel_->GetUsedRate();
|
|
|
|
update.target_bitrate = lowrate;
|
|
channel_->OnBitrateAllocation(update);
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
// Last two frames have rates [32kbps 54kbps], yielding 54kbps
|
|
auto used_rate_3 = channel_->GetUsedRate();
|
|
|
|
EXPECT_GT(used_rate_2, used_rate_1);
|
|
EXPECT_EQ(used_rate_3, used_rate_2);
|
|
}
|
|
|
|
// Test that we gracefully handle packets while the congestion control objects
|
|
// are not configured. This can happen during calls
|
|
// AudioSendStream::ConfigureStream
|
|
TEST_F(ChannelSendTest, EnqueuePacketsGracefullyHandlesNonInitializedPacer) {
|
|
EXPECT_CALL(transport_, SendRtp).Times(1);
|
|
channel_->StartSend();
|
|
channel_->ResetSenderCongestionControlObjects();
|
|
// This should trigger a packet, but congestion control is not configured
|
|
// so it should be dropped
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
|
|
channel_->RegisterSenderCongestionControlObjects(&transport_controller_);
|
|
// Now that we reconfigured the congestion control objects the new frame
|
|
// should be processed
|
|
ProcessNextFrame();
|
|
ProcessNextFrame();
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace voe
|
|
} // namespace webrtc
|