Allow feeding a Receiver encoded videoframe into a Sender Transform
Instead of crashing with a CHECK fail when an insertable stream of a
Video RTPSender is given a frame from an RTPReceiver's insertable
stream, construct a reasonable analogous sender frame and pass it
through to be decoded.
A small step towards removing the split we have between Sender and
Receiver implementations of TransformableFrameInterface which just
confuses users of the API.
Counterpart to https://webrtc-review.googlesource.com/c/src/+/301181 in
the opposite direction.
Bug: chromium:1250638
Change-Id: If66da7d553f14979ff1c5b4e00bff715f58cfce0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/303480
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Commit-Queue: Tony Herre <herre@google.com>
Reviewed-by: Palak Agarwal <agpalak@google.com>
Cr-Commit-Position: refs/heads/main@{#39963}
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
index 34b4af0..7dfd7ca 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc
@@ -158,21 +158,34 @@
void RTPSenderVideoFrameTransformerDelegate::SendVideo(
std::unique_ptr<TransformableFrameInterface> transformed_frame) const {
RTC_DCHECK_RUN_ON(transformation_queue_.get());
- RTC_CHECK_EQ(transformed_frame->GetDirection(),
- TransformableFrameInterface::Direction::kSender);
MutexLock lock(&sender_lock_);
if (!sender_)
return;
- auto* transformed_video_frame =
- static_cast<TransformableVideoSenderFrame*>(transformed_frame.get());
- sender_->SendVideo(transformed_video_frame->GetPayloadType(),
- transformed_video_frame->GetCodecType(),
- transformed_video_frame->GetTimestamp(),
- transformed_video_frame->GetCaptureTimeMs(),
- transformed_video_frame->GetData(),
- transformed_video_frame->GetHeader(),
- transformed_video_frame->GetExpectedRetransmissionTimeMs(),
- transformed_video_frame->Metadata().GetCsrcs());
+ if (transformed_frame->GetDirection() ==
+ TransformableFrameInterface::Direction::kSender) {
+ auto* transformed_video_frame =
+ static_cast<TransformableVideoSenderFrame*>(transformed_frame.get());
+ sender_->SendVideo(
+ transformed_video_frame->GetPayloadType(),
+ transformed_video_frame->GetCodecType(),
+ transformed_video_frame->GetTimestamp(),
+ transformed_video_frame->GetCaptureTimeMs(),
+ transformed_video_frame->GetData(),
+ transformed_video_frame->GetHeader(),
+ transformed_video_frame->GetExpectedRetransmissionTimeMs(),
+ transformed_video_frame->Metadata().GetCsrcs());
+ } else {
+ auto* transformed_video_frame =
+ static_cast<TransformableVideoFrameInterface*>(transformed_frame.get());
+ VideoFrameMetadata metadata = transformed_video_frame->Metadata();
+ sender_->SendVideo(
+ transformed_video_frame->GetPayloadType(), metadata.GetCodec(),
+ transformed_video_frame->GetTimestamp(),
+ /*capture_time_ms=*/0, transformed_video_frame->GetData(),
+ RTPVideoHeader::FromMetadata(metadata),
+ /*expected_retransmission_time_ms_=*/absl::nullopt,
+ metadata.GetCsrcs());
+ }
}
void RTPSenderVideoFrameTransformerDelegate::SetVideoStructureUnderLock(
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
index ad1d0b3..a3cd81e 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc
@@ -12,6 +12,7 @@
#include <utility>
+#include "api/test/mock_transformable_video_frame.h"
#include "rtc_base/event.h"
#include "test/gmock.h"
#include "test/gtest.h"
@@ -22,6 +23,8 @@
namespace {
using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
using ::testing::SaveArg;
using ::testing::WithoutArgs;
@@ -217,5 +220,54 @@
EXPECT_EQ(metadata.GetCsrcs(), actual_metadata.GetCsrcs());
}
+TEST_F(RtpSenderVideoFrameTransformerDelegateTest,
+ ReceiverFrameConvertedToSenderFrame) {
+ auto delegate = rtc::make_ref_counted<RTPSenderVideoFrameTransformerDelegate>(
+ &test_sender_, frame_transformer_,
+ /*ssrc=*/1111, /*csrcs=*/std::vector<uint32_t>(),
+ time_controller_.CreateTaskQueueFactory().get());
+
+ const uint8_t payload_type = 1;
+ const uint32_t timestamp = 2;
+ const std::vector<uint32_t> frame_csrcs = {123, 456, 789};
+
+ auto mock_receiver_frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ ON_CALL(*mock_receiver_frame, GetDirection)
+ .WillByDefault(Return(TransformableFrameInterface::Direction::kReceiver));
+ VideoFrameMetadata metadata;
+ metadata.SetCodec(kVideoCodecVP8);
+ metadata.SetRTPVideoHeaderCodecSpecifics(RTPVideoHeaderVP8());
+ metadata.SetCsrcs(frame_csrcs);
+ ON_CALL(*mock_receiver_frame, Metadata).WillByDefault(Return(metadata));
+ rtc::ArrayView<const uint8_t> buffer =
+ (rtc::ArrayView<const uint8_t>)*EncodedImageBuffer::Create(1);
+ ON_CALL(*mock_receiver_frame, GetData).WillByDefault(Return(buffer));
+ ON_CALL(*mock_receiver_frame, GetPayloadType)
+ .WillByDefault(Return(payload_type));
+ ON_CALL(*mock_receiver_frame, GetTimestamp).WillByDefault(Return(timestamp));
+
+ rtc::scoped_refptr<TransformedFrameCallback> callback;
+ EXPECT_CALL(*frame_transformer_, RegisterTransformedFrameSinkCallback)
+ .WillOnce(SaveArg<0>(&callback));
+ delegate->Init();
+ ASSERT_TRUE(callback);
+
+ rtc::Event event;
+ EXPECT_CALL(test_sender_,
+ SendVideo(payload_type, absl::make_optional(kVideoCodecVP8),
+ timestamp, /*capture_time_ms=*/0, buffer, _,
+ /*expected_retransmission_time_ms_=*/
+ (absl::optional<int64_t>)absl::nullopt, frame_csrcs))
+ .WillOnce(WithoutArgs([&] {
+ event.Set();
+ return true;
+ }));
+
+ callback->OnTransformedFrame(std::move(mock_receiver_frame));
+
+ event.Wait(TimeDelta::Seconds(1));
+}
+
} // namespace
} // namespace webrtc