Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2023 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "audio/channel_send.h" |
| 12 | |
| 13 | #include <utility> |
| 14 | |
| 15 | #include "api/audio/audio_frame.h" |
| 16 | #include "api/audio_codecs/builtin_audio_encoder_factory.h" |
Danil Chapovalov | ee27f38 | 2023-12-18 12:09:12 | [diff] [blame] | 17 | #include "api/environment/environment.h" |
| 18 | #include "api/environment/environment_factory.h" |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 19 | #include "api/scoped_refptr.h" |
Tony Herre | 9c687460 | 2024-01-26 10:16:45 | [diff] [blame] | 20 | #include "api/test/mock_frame_transformer.h" |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 21 | #include "api/test/mock_transformable_audio_frame.h" |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 22 | #include "api/units/time_delta.h" |
| 23 | #include "api/units/timestamp.h" |
| 24 | #include "call/rtp_transport_controller_send.h" |
Tony Herre | 36500ab | 2023-08-29 10:01:32 | [diff] [blame] | 25 | #include "rtc_base/gunit.h" |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 26 | #include "test/gtest.h" |
| 27 | #include "test/mock_transport.h" |
| 28 | #include "test/scoped_key_value_config.h" |
| 29 | #include "test/time_controller/simulated_time_controller.h" |
| 30 | |
| 31 | namespace webrtc { |
| 32 | namespace voe { |
| 33 | namespace { |
| 34 | |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 35 | using ::testing::Invoke; |
| 36 | using ::testing::NiceMock; |
| 37 | using ::testing::Return; |
Tony Herre | 36500ab | 2023-08-29 10:01:32 | [diff] [blame] | 38 | using ::testing::SaveArg; |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 39 | |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 40 | constexpr int kRtcpIntervalMs = 1000; |
| 41 | constexpr int kSsrc = 333; |
| 42 | constexpr int kPayloadType = 1; |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 43 | constexpr int kSampleRateHz = 48000; |
| 44 | constexpr int kRtpRateHz = 48000; |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 45 | |
| 46 | BitrateConstraints GetBitrateConfig() { |
| 47 | BitrateConstraints bitrate_config; |
| 48 | bitrate_config.min_bitrate_bps = 10000; |
| 49 | bitrate_config.start_bitrate_bps = 100000; |
| 50 | bitrate_config.max_bitrate_bps = 1000000; |
| 51 | return bitrate_config; |
| 52 | } |
| 53 | |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 54 | class ChannelSendTest : public ::testing::Test { |
| 55 | protected: |
| 56 | ChannelSendTest() |
| 57 | : time_controller_(Timestamp::Seconds(1)), |
Danil Chapovalov | ee27f38 | 2023-12-18 12:09:12 | [diff] [blame] | 58 | env_(CreateEnvironment(&field_trials_, |
| 59 | time_controller_.GetClock(), |
| 60 | time_controller_.CreateTaskQueueFactory())), |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 61 | transport_controller_( |
Danil Chapovalov | ee27f38 | 2023-12-18 12:09:12 | [diff] [blame] | 62 | RtpTransportConfig{.env = env_, |
| 63 | .bitrate_config = GetBitrateConfig()}) { |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 64 | channel_ = voe::CreateChannelSend( |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 65 | time_controller_.GetClock(), time_controller_.GetTaskQueueFactory(), |
Danil Chapovalov | ee27f38 | 2023-12-18 12:09:12 | [diff] [blame] | 66 | &transport_, nullptr, &env_.event_log(), nullptr, crypto_options_, |
| 67 | false, kRtcpIntervalMs, kSsrc, nullptr, &transport_controller_, |
| 68 | env_.field_trials()); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 69 | encoder_factory_ = CreateBuiltinAudioEncoderFactory(); |
Philipp Hancke | d209893 | 2023-11-01 18:18:32 | [diff] [blame] | 70 | SdpAudioFormat opus = SdpAudioFormat("opus", kRtpRateHz, 2); |
| 71 | std::unique_ptr<AudioEncoder> encoder = |
| 72 | encoder_factory_->MakeAudioEncoder(kPayloadType, opus, {}); |
| 73 | channel_->SetEncoder(kPayloadType, opus, std::move(encoder)); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 74 | transport_controller_.EnsureStarted(); |
Danil Chapovalov | a2cf8ee | 2023-05-16 11:26:33 | [diff] [blame] | 75 | channel_->RegisterSenderCongestionControlObjects(&transport_controller_); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 76 | ON_CALL(transport_, SendRtcp).WillByDefault(Return(true)); |
| 77 | ON_CALL(transport_, SendRtp).WillByDefault(Return(true)); |
| 78 | } |
| 79 | |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 80 | std::unique_ptr<AudioFrame> CreateAudioFrame(uint8_t data_init_value = 0) { |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 81 | auto frame = std::make_unique<AudioFrame>(); |
| 82 | frame->sample_rate_hz_ = kSampleRateHz; |
| 83 | frame->samples_per_channel_ = kSampleRateHz / 100; |
| 84 | frame->num_channels_ = 1; |
| 85 | frame->set_absolute_capture_timestamp_ms( |
| 86 | time_controller_.GetClock()->TimeInMilliseconds()); |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 87 | int16_t* dest = frame->mutable_data(); |
| 88 | for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_; |
| 89 | i++, dest++) { |
| 90 | *dest = data_init_value; |
| 91 | } |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 92 | return frame; |
| 93 | } |
| 94 | |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 95 | void ProcessNextFrame(std::unique_ptr<AudioFrame> audio_frame) { |
| 96 | channel_->ProcessAndEncodeAudio(std::move(audio_frame)); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 97 | // Advance time to process the task queue. |
| 98 | time_controller_.AdvanceTime(TimeDelta::Millis(10)); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 99 | } |
| 100 | |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 101 | void ProcessNextFrame() { ProcessNextFrame(CreateAudioFrame()); } |
| 102 | |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 103 | GlobalSimulatedTimeController time_controller_; |
| 104 | webrtc::test::ScopedKeyValueConfig field_trials_; |
Danil Chapovalov | ee27f38 | 2023-12-18 12:09:12 | [diff] [blame] | 105 | Environment env_; |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 106 | NiceMock<MockTransport> transport_; |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 107 | CryptoOptions crypto_options_; |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 108 | RtpTransportControllerSend transport_controller_; |
| 109 | std::unique_ptr<ChannelSendInterface> channel_; |
| 110 | rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_; |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 111 | }; |
| 112 | |
| 113 | TEST_F(ChannelSendTest, StopSendShouldResetEncoder) { |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 114 | channel_->StartSend(); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 115 | // Insert two frames which should trigger a new packet. |
| 116 | EXPECT_CALL(transport_, SendRtp).Times(1); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 117 | ProcessNextFrame(); |
| 118 | ProcessNextFrame(); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 119 | |
| 120 | EXPECT_CALL(transport_, SendRtp).Times(0); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 121 | ProcessNextFrame(); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 122 | // StopSend should clear the previous audio frame stored in the encoder. |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 123 | channel_->StopSend(); |
| 124 | channel_->StartSend(); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 125 | // The following frame should not trigger a new packet since the encoder |
| 126 | // needs 20 ms audio. |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 127 | EXPECT_CALL(transport_, SendRtp).Times(0); |
| 128 | ProcessNextFrame(); |
| 129 | } |
| 130 | |
| 131 | TEST_F(ChannelSendTest, IncreaseRtpTimestampByPauseDuration) { |
| 132 | channel_->StartSend(); |
| 133 | uint32_t timestamp; |
| 134 | int sent_packets = 0; |
Harald Alvestrand | d43af91 | 2023-08-15 11:41:45 | [diff] [blame] | 135 | auto send_rtp = [&](rtc::ArrayView<const uint8_t> data, |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 136 | const PacketOptions& options) { |
| 137 | ++sent_packets; |
| 138 | RtpPacketReceived packet; |
Harald Alvestrand | d43af91 | 2023-08-15 11:41:45 | [diff] [blame] | 139 | packet.Parse(data); |
Jakob Ivarsson | db20831 | 2023-01-27 14:13:22 | [diff] [blame] | 140 | timestamp = packet.Timestamp(); |
| 141 | return true; |
| 142 | }; |
| 143 | EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp)); |
| 144 | ProcessNextFrame(); |
| 145 | ProcessNextFrame(); |
| 146 | EXPECT_EQ(sent_packets, 1); |
| 147 | uint32_t first_timestamp = timestamp; |
| 148 | channel_->StopSend(); |
| 149 | time_controller_.AdvanceTime(TimeDelta::Seconds(10)); |
| 150 | channel_->StartSend(); |
| 151 | |
| 152 | ProcessNextFrame(); |
| 153 | ProcessNextFrame(); |
| 154 | EXPECT_EQ(sent_packets, 2); |
| 155 | int64_t timestamp_gap_ms = |
| 156 | static_cast<int64_t>(timestamp - first_timestamp) * 1000 / kRtpRateHz; |
| 157 | EXPECT_EQ(timestamp_gap_ms, 10020); |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 158 | } |
| 159 | |
Tony Herre | 36500ab | 2023-08-29 10:01:32 | [diff] [blame] | 160 | TEST_F(ChannelSendTest, FrameTransformerGetsCorrectTimestamp) { |
| 161 | rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer = |
| 162 | rtc::make_ref_counted<MockFrameTransformer>(); |
| 163 | channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer); |
| 164 | rtc::scoped_refptr<TransformedFrameCallback> callback; |
| 165 | EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback) |
| 166 | .WillOnce(SaveArg<0>(&callback)); |
| 167 | EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); |
| 168 | |
| 169 | absl::optional<uint32_t> sent_timestamp; |
| 170 | auto send_rtp = [&](rtc::ArrayView<const uint8_t> data, |
| 171 | const PacketOptions& options) { |
| 172 | RtpPacketReceived packet; |
| 173 | packet.Parse(data); |
| 174 | if (!sent_timestamp) { |
| 175 | sent_timestamp = packet.Timestamp(); |
| 176 | } |
| 177 | return true; |
| 178 | }; |
| 179 | EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp)); |
| 180 | |
| 181 | channel_->StartSend(); |
| 182 | int64_t transformable_frame_timestamp = -1; |
| 183 | EXPECT_CALL(*mock_frame_transformer, Transform) |
| 184 | .WillOnce([&](std::unique_ptr<TransformableFrameInterface> frame) { |
| 185 | transformable_frame_timestamp = frame->GetTimestamp(); |
| 186 | callback->OnTransformedFrame(std::move(frame)); |
| 187 | }); |
| 188 | // Insert two frames which should trigger a new packet. |
| 189 | ProcessNextFrame(); |
| 190 | ProcessNextFrame(); |
| 191 | |
| 192 | // Ensure the RTP timestamp on the frame passed to the transformer |
| 193 | // includes the RTP offset and matches the actual RTP timestamp on the sent |
| 194 | // packet. |
| 195 | EXPECT_EQ_WAIT(transformable_frame_timestamp, |
| 196 | 0 + channel_->GetRtpRtcp()->StartTimestamp(), 1000); |
| 197 | EXPECT_TRUE_WAIT(sent_timestamp, 1000); |
| 198 | EXPECT_EQ(*sent_timestamp, transformable_frame_timestamp); |
| 199 | } |
Tony Herre | 64437e8 | 2024-04-29 13:13:48 | [diff] [blame] | 200 | |
| 201 | // Ensure that AudioLevel calculations are performed correctly per-packet even |
| 202 | // if there's an async Encoded Frame Transform happening. |
| 203 | TEST_F(ChannelSendTest, AudioLevelsAttachedToCorrectTransformedFrame) { |
| 204 | channel_->SetSendAudioLevelIndicationStatus(true, /*id=*/1); |
| 205 | RtpPacketReceived::ExtensionManager extension_manager; |
| 206 | extension_manager.RegisterByType(1, kRtpExtensionAudioLevel); |
| 207 | |
| 208 | rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer = |
| 209 | rtc::make_ref_counted<MockFrameTransformer>(); |
| 210 | channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer); |
| 211 | rtc::scoped_refptr<TransformedFrameCallback> callback; |
| 212 | EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback) |
| 213 | .WillOnce(SaveArg<0>(&callback)); |
| 214 | EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); |
| 215 | |
| 216 | std::vector<uint8_t> sent_audio_levels; |
| 217 | auto send_rtp = [&](rtc::ArrayView<const uint8_t> data, |
| 218 | const PacketOptions& options) { |
| 219 | RtpPacketReceived packet(&extension_manager); |
| 220 | packet.Parse(data); |
| 221 | RTPHeader header; |
| 222 | packet.GetHeader(&header); |
| 223 | sent_audio_levels.push_back(header.extension.audio_level()->level()); |
| 224 | return true; |
| 225 | }; |
| 226 | EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp)); |
| 227 | |
| 228 | channel_->StartSend(); |
| 229 | std::vector<std::unique_ptr<TransformableFrameInterface>> frames; |
| 230 | EXPECT_CALL(*mock_frame_transformer, Transform) |
| 231 | .Times(2) |
| 232 | .WillRepeatedly([&](std::unique_ptr<TransformableFrameInterface> frame) { |
| 233 | frames.push_back(std::move(frame)); |
| 234 | }); |
| 235 | |
| 236 | // Insert two frames of 7s which should trigger a new packet. |
| 237 | ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/7)); |
| 238 | ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/7)); |
| 239 | |
| 240 | // Insert two more frames of 3s, meaning a second packet is |
| 241 | // prepared and sent to the transform before the first packet has |
| 242 | // been sent. |
| 243 | ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/3)); |
| 244 | ProcessNextFrame(CreateAudioFrame(/*data_init_value=*/3)); |
| 245 | |
| 246 | // Wait for both packets to be encoded and sent to the transform. |
| 247 | EXPECT_EQ_WAIT(frames.size(), 2ul, 1000); |
| 248 | // Complete the transforms on both frames at the same time |
| 249 | callback->OnTransformedFrame(std::move(frames[0])); |
| 250 | callback->OnTransformedFrame(std::move(frames[1])); |
| 251 | |
| 252 | // Allow things posted back to the encoder queue to run. |
| 253 | time_controller_.AdvanceTime(TimeDelta::Millis(10)); |
| 254 | |
| 255 | // Ensure the audio levels on both sent packets is present and |
| 256 | // matches their contents. |
| 257 | EXPECT_EQ_WAIT(sent_audio_levels.size(), 2ul, 1000); |
| 258 | // rms dbov of the packet with raw audio of 7s is 73. |
| 259 | EXPECT_EQ(sent_audio_levels[0], 73); |
| 260 | // rms dbov of the second packet with raw audio of 3s is 81. |
| 261 | EXPECT_EQ(sent_audio_levels[1], 81); |
| 262 | } |
| 263 | |
| 264 | // Ensure that AudioLevels are attached to frames injected into the |
| 265 | // Encoded Frame transform. |
| 266 | TEST_F(ChannelSendTest, AudioLevelsAttachedToInsertedTransformedFrame) { |
| 267 | channel_->SetSendAudioLevelIndicationStatus(true, /*id=*/1); |
| 268 | RtpPacketReceived::ExtensionManager extension_manager; |
| 269 | extension_manager.RegisterByType(1, kRtpExtensionAudioLevel); |
| 270 | |
| 271 | rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer = |
| 272 | rtc::make_ref_counted<MockFrameTransformer>(); |
| 273 | channel_->SetEncoderToPacketizerFrameTransformer(mock_frame_transformer); |
| 274 | rtc::scoped_refptr<TransformedFrameCallback> callback; |
| 275 | EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback) |
| 276 | .WillOnce(SaveArg<0>(&callback)); |
| 277 | EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); |
| 278 | |
| 279 | std::optional<uint8_t> sent_audio_level; |
| 280 | auto send_rtp = [&](rtc::ArrayView<const uint8_t> data, |
| 281 | const PacketOptions& options) { |
| 282 | RtpPacketReceived packet(&extension_manager); |
| 283 | packet.Parse(data); |
| 284 | RTPHeader header; |
| 285 | packet.GetHeader(&header); |
| 286 | sent_audio_level = header.extension.audio_level()->level(); |
| 287 | return true; |
| 288 | }; |
| 289 | EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(send_rtp)); |
| 290 | |
| 291 | channel_->StartSend(); |
| 292 | |
| 293 | time_controller_.AdvanceTime(TimeDelta::Millis(10)); |
| 294 | // Inject a frame encoded elsewhere. |
| 295 | auto mock_frame = std::make_unique<NiceMock<MockTransformableAudioFrame>>(); |
| 296 | uint8_t audio_level = 67; |
| 297 | ON_CALL(*mock_frame, AudioLevel()).WillByDefault(Return(audio_level)); |
| 298 | uint8_t payload[10]; |
| 299 | ON_CALL(*mock_frame, GetData()) |
| 300 | .WillByDefault(Return(rtc::ArrayView<uint8_t>(&payload[0], 10))); |
| 301 | EXPECT_TRUE_WAIT(callback, 1000); |
| 302 | callback->OnTransformedFrame(std::move(mock_frame)); |
| 303 | |
| 304 | // Allow things posted back to the encoder queue to run. |
| 305 | time_controller_.AdvanceTime(TimeDelta::Millis(10)); |
| 306 | |
| 307 | // Ensure the audio levels is set on the sent packet. |
| 308 | EXPECT_TRUE_WAIT(sent_audio_level, 1000); |
| 309 | EXPECT_EQ(*sent_audio_level, audio_level); |
| 310 | } |
Jakob Ivarsson | dcb09ff | 2023-01-25 19:03:56 | [diff] [blame] | 311 | } // namespace |
| 312 | } // namespace voe |
| 313 | } // namespace webrtc |