diff --git a/audio/BUILD.gn b/audio/BUILD.gn index bbc294bd75..09562b9131 100644 --- a/audio/BUILD.gn +++ b/audio/BUILD.gn @@ -223,6 +223,7 @@ if (rtc_include_tests) { "utility:utility_tests", "//testing/gtest", ] + absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] } rtc_library("channel_receive_unittest") { diff --git a/audio/channel_send.cc b/audio/channel_send.cc index b71f564498..ecf2cb5175 100644 --- a/audio/channel_send.cc +++ b/audio/channel_send.cc @@ -169,7 +169,8 @@ class ChannelSend : public ChannelSendInterface, uint8_t payloadType, uint32_t rtp_timestamp_without_offset, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms) + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs) RTC_RUN_ON(encoder_queue_); void OnReceivedRtt(int64_t rtt_ms); @@ -294,14 +295,15 @@ int32_t ChannelSend::SendData(AudioFrameType frameType, return 0; } return SendRtpAudio(frameType, payloadType, rtp_timestamp, payload, - absolute_capture_timestamp_ms); + absolute_capture_timestamp_ms, /*csrcs=*/{}); } int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp_without_offset, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms) { + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs) { // E2EE Custom Audio Frame Encryption (This is optional). // Keep this buffer around for the lifetime of the send call. rtc::Buffer encrypted_audio_payload; @@ -363,7 +365,8 @@ int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType, .payload = payload, .payload_id = payloadType, .rtp_timestamp = - rtp_timestamp_without_offset + rtp_rtcp_->StartTimestamp()}; + rtp_timestamp_without_offset + rtp_rtcp_->StartTimestamp(), + .csrcs = csrcs}; if (absolute_capture_timestamp_ms > 0) { frame.capture_time = Timestamp::Millis(absolute_capture_timestamp_ms); } @@ -859,12 +862,13 @@ void ChannelSend::InitFrameTransformerDelegate( [this](AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp_with_offset, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms) { + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs) { RTC_DCHECK_RUN_ON(&encoder_queue_); return SendRtpAudio( frameType, payloadType, rtp_timestamp_with_offset - rtp_rtcp_->StartTimestamp(), payload, - absolute_capture_timestamp_ms); + absolute_capture_timestamp_ms, csrcs); }; frame_transformer_delegate_ = rtc::make_ref_counted( diff --git a/audio/channel_send_frame_transformer_delegate.cc b/audio/channel_send_frame_transformer_delegate.cc index f66f29df08..2eea0d2387 100644 --- a/audio/channel_send_frame_transformer_delegate.cc +++ b/audio/channel_send_frame_transformer_delegate.cc @@ -11,6 +11,7 @@ #include "audio/channel_send_frame_transformer_delegate.h" #include +#include namespace webrtc { namespace { @@ -56,6 +57,7 @@ class TransformableOutgoingAudioFrame size_t payload_size, absl::optional absolute_capture_timestamp_ms, uint32_t ssrc, + std::vector csrcs, const std::string& codec_mime_type) : frame_type_(frame_type), payload_type_(payload_type), @@ -63,6 +65,7 @@ class TransformableOutgoingAudioFrame payload_(payload_data, payload_size), absolute_capture_timestamp_ms_(absolute_capture_timestamp_ms), ssrc_(ssrc), + csrcs_(std::move(csrcs)), codec_mime_type_(codec_mime_type) {} ~TransformableOutgoingAudioFrame() override = default; rtc::ArrayView GetData() const override { return payload_; } @@ -81,7 +84,7 @@ class TransformableOutgoingAudioFrame std::string GetMimeType() const override { return codec_mime_type_; } rtc::ArrayView GetContributingSources() const override { - return {}; + return csrcs_; } const absl::optional SequenceNumber() const override { @@ -103,6 +106,7 @@ class TransformableOutgoingAudioFrame rtc::Buffer payload_; absl::optional absolute_capture_timestamp_ms_; uint32_t ssrc_; + std::vector csrcs_; std::string codec_mime_type_; }; } // namespace @@ -143,14 +147,15 @@ void ChannelSendFrameTransformerDelegate::Transform( send_frame_callback_( frame_type, payload_type, rtp_timestamp, rtc::ArrayView(payload_data, payload_size), - absolute_capture_timestamp_ms); + absolute_capture_timestamp_ms, /*csrcs=*/{}); return; } } frame_transformer_->Transform( std::make_unique( frame_type, payload_type, rtp_timestamp, payload_data, payload_size, - absolute_capture_timestamp_ms, ssrc, codec_mimetype)); + absolute_capture_timestamp_ms, ssrc, + /*csrcs=*/std::vector(), codec_mimetype)); } void ChannelSendFrameTransformerDelegate::OnTransformedFrame( @@ -184,17 +189,21 @@ void ChannelSendFrameTransformerDelegate::SendFrame( transformed_frame->GetData(), transformed_frame->AbsoluteCaptureTimestamp() ? *transformed_frame->AbsoluteCaptureTimestamp() - : 0); + : 0, + transformed_frame->GetContributingSources()); } std::unique_ptr CloneSenderAudioFrame( TransformableAudioFrameInterface* original) { + std::vector csrcs; + csrcs.assign(original->GetContributingSources().begin(), + original->GetContributingSources().end()); return std::make_unique( InterfaceFrameTypeToInternalFrameType(original->Type()), original->GetPayloadType(), original->GetTimestamp(), original->GetData().data(), original->GetData().size(), original->AbsoluteCaptureTimestamp(), original->GetSsrc(), - original->GetMimeType()); + std::move(csrcs), original->GetMimeType()); } } // namespace webrtc diff --git a/audio/channel_send_frame_transformer_delegate.h b/audio/channel_send_frame_transformer_delegate.h index bcec018922..97fc14f737 100644 --- a/audio/channel_send_frame_transformer_delegate.h +++ b/audio/channel_send_frame_transformer_delegate.h @@ -35,7 +35,8 @@ class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback { uint8_t payloadType, uint32_t rtp_timestamp_with_offset, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms)>; + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs)>; ChannelSendFrameTransformerDelegate( SendFrameCallback send_frame_callback, rtc::scoped_refptr frame_transformer, diff --git a/audio/channel_send_frame_transformer_delegate_unittest.cc b/audio/channel_send_frame_transformer_delegate_unittest.cc index a47cbd59c8..4dcd15cd95 100644 --- a/audio/channel_send_frame_transformer_delegate_unittest.cc +++ b/audio/channel_send_frame_transformer_delegate_unittest.cc @@ -12,7 +12,9 @@ #include #include +#include +#include "absl/memory/memory.h" #include "rtc_base/task_queue_for_test.h" #include "test/gmock.h" #include "test/gtest.h" @@ -24,10 +26,13 @@ namespace { using ::testing::_; using ::testing::ElementsAre; +using ::testing::ElementsAreArray; using ::testing::NiceMock; using ::testing::Return; using ::testing::SaveArg; +const uint8_t mock_data[] = {1, 2, 3, 4}; + class MockChannelSend { public: MockChannelSend() = default; @@ -39,30 +44,56 @@ class MockChannelSend { uint8_t payloadType, uint32_t rtp_timestamp, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms)); + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs)); ChannelSendFrameTransformerDelegate::SendFrameCallback callback() { return [this](AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp, rtc::ArrayView payload, - int64_t absolute_capture_timestamp_ms) { + int64_t absolute_capture_timestamp_ms, + rtc::ArrayView csrcs) { return SendFrame(frameType, payloadType, rtp_timestamp, payload, - absolute_capture_timestamp_ms); + absolute_capture_timestamp_ms, csrcs); }; } }; -std::unique_ptr CreateMockReceiverFrame() { - const uint8_t mock_data[] = {1, 2, 3, 4}; +std::unique_ptr CreateMockReceiverFrame( + std::vector csrcs) { std::unique_ptr mock_frame = - std::make_unique(); + std::make_unique>(); rtc::ArrayView payload(mock_data); ON_CALL(*mock_frame, GetData).WillByDefault(Return(payload)); ON_CALL(*mock_frame, GetPayloadType).WillByDefault(Return(0)); ON_CALL(*mock_frame, GetDirection) .WillByDefault(Return(TransformableFrameInterface::Direction::kReceiver)); + ON_CALL(*mock_frame, GetContributingSources).WillByDefault(Return(csrcs)); return mock_frame; } +std::unique_ptr CreateFrame() { + TaskQueueForTest channel_queue("channel_queue"); + rtc::scoped_refptr mock_frame_transformer = + rtc::make_ref_counted>(); + MockChannelSend mock_channel; + rtc::scoped_refptr delegate = + rtc::make_ref_counted( + mock_channel.callback(), mock_frame_transformer, &channel_queue); + + std::unique_ptr frame; + ON_CALL(*mock_frame_transformer, Transform) + .WillByDefault( + [&frame]( + std::unique_ptr transform_frame) { + frame = std::move(transform_frame); + }); + delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, mock_data, + sizeof(mock_data), 0, + /*ssrc=*/0, /*mimeType=*/"audio/opus"); + return absl::WrapUnique( + static_cast(frame.release())); +} + // Test that the delegate registers itself with the frame transformer on Init(). TEST(ChannelSendFrameTransformerDelegateTest, RegisterTransformedFrameCallbackOnInit) { @@ -136,15 +167,16 @@ TEST(ChannelSendFrameTransformerDelegateTest, delegate->Init(); ASSERT_TRUE(callback); - const uint8_t data[] = {1, 2, 3, 4}; + std::vector csrcs = {123, 234, 345, 456}; EXPECT_CALL(mock_channel, SendFrame).Times(0); - EXPECT_CALL(mock_channel, SendFrame(_, 0, 0, ElementsAre(1, 2, 3, 4), _)); + EXPECT_CALL(mock_channel, SendFrame(_, 0, 0, ElementsAreArray(mock_data), _, + ElementsAreArray(csrcs))); ON_CALL(*mock_frame_transformer, Transform) - .WillByDefault( - [&callback](std::unique_ptr frame) { - callback->OnTransformedFrame(CreateMockReceiverFrame()); - }); - delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, data, sizeof(data), 0, + .WillByDefault([&](std::unique_ptr frame) { + callback->OnTransformedFrame(CreateMockReceiverFrame(csrcs)); + }); + delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, mock_data, + sizeof(mock_data), 0, /*ssrc=*/0, /*mimeType=*/"audio/opus"); channel_queue.WaitForPreviouslyPostedTasks(); } @@ -188,5 +220,39 @@ TEST(ChannelSendFrameTransformerDelegateTest, ShortCircuitingSkipsTransform) { /*ssrc=*/0, /*mimeType=*/"audio/opus"); } +TEST(ChannelSendFrameTransformerDelegateTest, + CloningSenderFramePreservesInformation) { + std::unique_ptr frame = CreateFrame(); + std::unique_ptr cloned_frame = + CloneSenderAudioFrame(frame.get()); + + EXPECT_EQ(cloned_frame->GetTimestamp(), frame->GetTimestamp()); + EXPECT_EQ(cloned_frame->GetSsrc(), frame->GetSsrc()); + EXPECT_EQ(cloned_frame->Type(), frame->Type()); + EXPECT_EQ(cloned_frame->GetPayloadType(), frame->GetPayloadType()); + EXPECT_EQ(cloned_frame->GetMimeType(), frame->GetMimeType()); + EXPECT_THAT(cloned_frame->GetContributingSources(), + ElementsAreArray(frame->GetContributingSources())); +} + +TEST(ChannelSendFrameTransformerDelegateTest, CloningReceiverFrameWithCsrcs) { + std::unique_ptr frame = + CreateMockReceiverFrame(/*csrcs=*/{123, 234, 345}); + std::unique_ptr cloned_frame = + CloneSenderAudioFrame(frame.get()); + + EXPECT_EQ(cloned_frame->GetTimestamp(), frame->GetTimestamp()); + EXPECT_EQ(cloned_frame->GetSsrc(), frame->GetSsrc()); + EXPECT_EQ(cloned_frame->Type(), frame->Type()); + EXPECT_EQ(cloned_frame->GetPayloadType(), frame->GetPayloadType()); + EXPECT_EQ(cloned_frame->GetMimeType(), frame->GetMimeType()); + EXPECT_EQ(cloned_frame->AbsoluteCaptureTimestamp(), + frame->AbsoluteCaptureTimestamp()); + + ASSERT_NE(frame->GetContributingSources().size(), 0u); + EXPECT_THAT(cloned_frame->GetContributingSources(), + ElementsAreArray(frame->GetContributingSources())); +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.cc b/modules/rtp_rtcp/source/rtp_sender_audio.cc index b826c30e07..9d2258dc66 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio.cc @@ -254,7 +254,8 @@ bool RTPSenderAudio::SendAudio(const RtpAudioFrame& frame) { return false; } - std::unique_ptr packet = rtp_sender_->AllocatePacket(); + std::unique_ptr packet = + rtp_sender_->AllocatePacket(frame.csrcs); packet->SetMarker(MarkerBit(frame.type, frame.payload_id)); packet->SetPayloadType(frame.payload_id); packet->SetTimestamp(frame.rtp_timestamp); diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.h b/modules/rtp_rtcp/source/rtp_sender_audio.h index 662f908216..83a2cb211f 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio.h +++ b/modules/rtp_rtcp/source/rtp_sender_audio.h @@ -61,6 +61,9 @@ class RTPSenderAudio { // header-extension-for-audio-level-indication. // Valid range is [0,127]. Actual value is negative. absl::optional audio_level_dbov; + + // Contributing sources list. + rtc::ArrayView csrcs; }; bool SendAudio(const RtpAudioFrame& frame); diff --git a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc index 0db610c149..724cd3a5e0 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc @@ -222,4 +222,19 @@ TEST_F(RtpSenderAudioTest, CheckMarkerBitForTelephoneEvents) { EXPECT_FALSE(transport_.last_sent_packet().Marker()); } +TEST_F(RtpSenderAudioTest, SendsCsrcs) { + const char payload_name[] = "audio"; + const uint8_t payload_type = 127; + ASSERT_EQ(0, rtp_sender_audio_->RegisterAudioPayload( + payload_name, payload_type, 48000, 0, 1500)); + uint8_t payload[] = {47, 11, 32, 93, 89}; + + std::vector csrcs({123, 456, 789}); + + ASSERT_TRUE(rtp_sender_audio_->SendAudio( + {.payload = payload, .payload_id = payload_type, .csrcs = csrcs})); + + EXPECT_EQ(transport_.last_sent_packet().Csrcs(), csrcs); +} + } // namespace webrtc