Ensure cloning and then sending audio encoded frames propagates CSRCs
Bug: chromium:1508337
Change-Id: I9f28fc0958d28bc97f9378a46fbec3e45148736f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/330260
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Commit-Queue: Tony Herre <herre@google.com>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41337}
diff --git a/audio/BUILD.gn b/audio/BUILD.gn
index bbc294b..09562b9 100644
--- a/audio/BUILD.gn
+++ b/audio/BUILD.gn
@@ -223,6 +223,7 @@
"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 b71f564..ecf2cb5 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -169,7 +169,8 @@
uint8_t payloadType,
uint32_t rtp_timestamp_without_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms)
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs)
RTC_RUN_ON(encoder_queue_);
void OnReceivedRtt(int64_t rtt_ms);
@@ -294,14 +295,15 @@
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<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> 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 @@
.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 @@
[this](AudioFrameType frameType, uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> 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<ChannelSendFrameTransformerDelegate>(
diff --git a/audio/channel_send_frame_transformer_delegate.cc b/audio/channel_send_frame_transformer_delegate.cc
index f66f29d..2eea0d2 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 <utility>
+#include <vector>
namespace webrtc {
namespace {
@@ -56,6 +57,7 @@
size_t payload_size,
absl::optional<uint64_t> absolute_capture_timestamp_ms,
uint32_t ssrc,
+ std::vector<uint32_t> csrcs,
const std::string& codec_mime_type)
: frame_type_(frame_type),
payload_type_(payload_type),
@@ -63,6 +65,7 @@
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<const uint8_t> GetData() const override { return payload_; }
@@ -81,7 +84,7 @@
std::string GetMimeType() const override { return codec_mime_type_; }
rtc::ArrayView<const uint32_t> GetContributingSources() const override {
- return {};
+ return csrcs_;
}
const absl::optional<uint16_t> SequenceNumber() const override {
@@ -103,6 +106,7 @@
rtc::Buffer payload_;
absl::optional<uint64_t> absolute_capture_timestamp_ms_;
uint32_t ssrc_;
+ std::vector<uint32_t> csrcs_;
std::string codec_mime_type_;
};
} // namespace
@@ -143,14 +147,15 @@
send_frame_callback_(
frame_type, payload_type, rtp_timestamp,
rtc::ArrayView<const uint8_t>(payload_data, payload_size),
- absolute_capture_timestamp_ms);
+ absolute_capture_timestamp_ms, /*csrcs=*/{});
return;
}
}
frame_transformer_->Transform(
std::make_unique<TransformableOutgoingAudioFrame>(
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<uint32_t>(), codec_mimetype));
}
void ChannelSendFrameTransformerDelegate::OnTransformedFrame(
@@ -184,17 +189,21 @@
transformed_frame->GetData(),
transformed_frame->AbsoluteCaptureTimestamp()
? *transformed_frame->AbsoluteCaptureTimestamp()
- : 0);
+ : 0,
+ transformed_frame->GetContributingSources());
}
std::unique_ptr<TransformableAudioFrameInterface> CloneSenderAudioFrame(
TransformableAudioFrameInterface* original) {
+ std::vector<uint32_t> csrcs;
+ csrcs.assign(original->GetContributingSources().begin(),
+ original->GetContributingSources().end());
return std::make_unique<TransformableOutgoingAudioFrame>(
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 bcec018..97fc14f 100644
--- a/audio/channel_send_frame_transformer_delegate.h
+++ b/audio/channel_send_frame_transformer_delegate.h
@@ -35,7 +35,8 @@
uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms)>;
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs)>;
ChannelSendFrameTransformerDelegate(
SendFrameCallback send_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
diff --git a/audio/channel_send_frame_transformer_delegate_unittest.cc b/audio/channel_send_frame_transformer_delegate_unittest.cc
index a47cbd5..4dcd15c 100644
--- a/audio/channel_send_frame_transformer_delegate_unittest.cc
+++ b/audio/channel_send_frame_transformer_delegate_unittest.cc
@@ -12,7 +12,9 @@
#include <memory>
#include <utility>
+#include <vector>
+#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 @@
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 @@
uint8_t payloadType,
uint32_t rtp_timestamp,
rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms));
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs));
ChannelSendFrameTransformerDelegate::SendFrameCallback callback() {
return [this](AudioFrameType frameType, uint8_t payloadType,
uint32_t rtp_timestamp, rtc::ArrayView<const uint8_t> payload,
- int64_t absolute_capture_timestamp_ms) {
+ int64_t absolute_capture_timestamp_ms,
+ rtc::ArrayView<const uint32_t> csrcs) {
return SendFrame(frameType, payloadType, rtp_timestamp, payload,
- absolute_capture_timestamp_ms);
+ absolute_capture_timestamp_ms, csrcs);
};
}
};
-std::unique_ptr<MockTransformableAudioFrame> CreateMockReceiverFrame() {
- const uint8_t mock_data[] = {1, 2, 3, 4};
+std::unique_ptr<TransformableAudioFrameInterface> CreateMockReceiverFrame(
+ std::vector<const uint32_t> csrcs) {
std::unique_ptr<MockTransformableAudioFrame> mock_frame =
- std::make_unique<MockTransformableAudioFrame>();
+ std::make_unique<NiceMock<MockTransformableAudioFrame>>();
rtc::ArrayView<const uint8_t> 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<TransformableAudioFrameInterface> CreateFrame() {
+ TaskQueueForTest channel_queue("channel_queue");
+ rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+ rtc::make_ref_counted<NiceMock<MockFrameTransformer>>();
+ MockChannelSend mock_channel;
+ rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate =
+ rtc::make_ref_counted<ChannelSendFrameTransformerDelegate>(
+ mock_channel.callback(), mock_frame_transformer, &channel_queue);
+
+ std::unique_ptr<TransformableFrameInterface> frame;
+ ON_CALL(*mock_frame_transformer, Transform)
+ .WillByDefault(
+ [&frame](
+ std::unique_ptr<TransformableFrameInterface> 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<webrtc::TransformableAudioFrameInterface*>(frame.release()));
+}
+
// Test that the delegate registers itself with the frame transformer on Init().
TEST(ChannelSendFrameTransformerDelegateTest,
RegisterTransformedFrameCallbackOnInit) {
@@ -136,15 +167,16 @@
delegate->Init();
ASSERT_TRUE(callback);
- const uint8_t data[] = {1, 2, 3, 4};
+ std::vector<const uint32_t> 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<TransformableFrameInterface> frame) {
- callback->OnTransformedFrame(CreateMockReceiverFrame());
- });
- delegate->Transform(AudioFrameType::kEmptyFrame, 0, 0, data, sizeof(data), 0,
+ .WillByDefault([&](std::unique_ptr<TransformableFrameInterface> 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 @@
/*ssrc=*/0, /*mimeType=*/"audio/opus");
}
+TEST(ChannelSendFrameTransformerDelegateTest,
+ CloningSenderFramePreservesInformation) {
+ std::unique_ptr<TransformableAudioFrameInterface> frame = CreateFrame();
+ std::unique_ptr<TransformableAudioFrameInterface> 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<TransformableAudioFrameInterface> frame =
+ CreateMockReceiverFrame(/*csrcs=*/{123, 234, 345});
+ std::unique_ptr<TransformableAudioFrameInterface> 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 b826c30..9d2258d 100644
--- a/modules/rtp_rtcp/source/rtp_sender_audio.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_audio.cc
@@ -254,7 +254,8 @@
return false;
}
- std::unique_ptr<RtpPacketToSend> packet = rtp_sender_->AllocatePacket();
+ std::unique_ptr<RtpPacketToSend> 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 662f908..83a2cb2 100644
--- a/modules/rtp_rtcp/source/rtp_sender_audio.h
+++ b/modules/rtp_rtcp/source/rtp_sender_audio.h
@@ -61,6 +61,9 @@
// header-extension-for-audio-level-indication.
// Valid range is [0,127]. Actual value is negative.
absl::optional<int> audio_level_dbov;
+
+ // Contributing sources list.
+ rtc::ArrayView<const uint32_t> 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 0db610c..724cd3a 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 @@
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<uint32_t> 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