Allow feeding a Sender encoded videoframe into a Receiver Transform

Instead of crashing with a CHECK fail when an insertable stream of a
Video RTPReceiver is given a frame from an RTPSender's insertable
stream, construct a reasonable analogous receive 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.

Bug: chromium:1250638
Change-Id: I02e0f1d9d35c16dc12718927c5200ff7cf4407e3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/301181
Reviewed-by: Palak Agarwal <agpalak@google.com>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Commit-Queue: Tony Herre <herre@google.com>
Cr-Commit-Position: refs/heads/main@{#39888}
This commit is contained in:
Tony Herre 2023-04-18 13:52:38 +00:00 committed by WebRTC LUCI CQ
parent 95e7a0398c
commit 580b0f944b
3 changed files with 77 additions and 5 deletions

View File

@ -638,6 +638,7 @@ if (rtc_include_tests) {
"../../api:frame_transformer_factory",
"../../api:libjingle_peerconnection_api",
"../../api:mock_frame_encryptor",
"../../api:mock_transformable_video_frame",
"../../api:rtp_headers",
"../../api:rtp_packet_info",
"../../api:rtp_parameters",

View File

@ -115,13 +115,44 @@ void RtpVideoStreamReceiverFrameTransformerDelegate::OnTransformedFrame(
void RtpVideoStreamReceiverFrameTransformerDelegate::ManageFrame(
std::unique_ptr<TransformableFrameInterface> frame) {
RTC_DCHECK_RUN_ON(&network_sequence_checker_);
RTC_CHECK_EQ(frame->GetDirection(),
TransformableFrameInterface::Direction::kReceiver);
if (!receiver_)
return;
auto transformed_frame = absl::WrapUnique(
static_cast<TransformableVideoReceiverFrame*>(frame.release()));
receiver_->ManageFrame(std::move(*transformed_frame).ExtractFrame());
if (frame->GetDirection() ==
TransformableFrameInterface::Direction::kReceiver) {
auto transformed_frame = absl::WrapUnique(
static_cast<TransformableVideoReceiverFrame*>(frame.release()));
receiver_->ManageFrame(std::move(*transformed_frame).ExtractFrame());
} else {
RTC_CHECK_EQ(frame->GetDirection(),
TransformableFrameInterface::Direction::kSender);
// This frame is actually an frame encoded locally, to be sent, but has been
// fed back into this receiver's insertable stream writer.
// Create a reasonable RtpFrameObject as if this frame had been received
// over RTP, reusing the frameId as an analog for the RTP sequence number,
// and handle it as if it had been received.
// TODO(https://crbug.com/1250638): Rewrite the receiver's codepaths after
// this transform to be transport-agnostic and not need a faked rtp
// sequence number.
auto transformed_frame = absl::WrapUnique(
static_cast<TransformableVideoFrameInterface*>(frame.release()));
VideoFrameMetadata metadata = transformed_frame->Metadata();
RTPVideoHeader video_header = RTPVideoHeader::FromMetadata(metadata);
VideoSendTiming timing;
rtc::ArrayView<const uint8_t> data = transformed_frame->GetData();
receiver_->ManageFrame(std::make_unique<RtpFrameObject>(
/*first_seq_num=*/metadata.GetFrameId().value_or(0),
/*last_seq_num=*/metadata.GetFrameId().value_or(0),
/*markerBit=*/video_header.is_last_frame_in_picture,
/*times_nacked=*/0,
/*first_packet_received_time=*/0,
/*last_packet_received_time=*/0,
/*rtp_timestamp=*/transformed_frame->GetTimestamp(),
/*ntp_time_ms=*/0, timing, transformed_frame->GetPayloadType(),
metadata.GetCodec(), metadata.GetRotation(), metadata.GetContentType(),
video_header, video_header.color_space, RtpPacketInfos(),
EncodedImageBuffer::Create(data.data(), data.size())));
}
}
} // namespace webrtc

View File

@ -17,6 +17,7 @@
#include "absl/memory/memory.h"
#include "api/call/transport.h"
#include "api/test/mock_transformable_video_frame.h"
#include "api/units/timestamp.h"
#include "call/video_receive_stream.h"
#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
@ -31,6 +32,7 @@ namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
std::unique_ptr<RtpFrameObject> CreateRtpFrameObject(
@ -175,5 +177,43 @@ TEST(RtpVideoStreamReceiverFrameTransformerDelegateTest,
delegate->TransformFrame(CreateRtpFrameObject(video_header, csrcs));
}
TEST(RtpVideoStreamReceiverFrameTransformerDelegateTest,
SenderFramesAreConvertedToReceiverFrames) {
rtc::AutoThread main_thread_;
TestRtpVideoFrameReceiver receiver;
auto mock_frame_transformer =
rtc::make_ref_counted<NiceMock<MockFrameTransformer>>();
auto delegate =
rtc::make_ref_counted<RtpVideoStreamReceiverFrameTransformerDelegate>(
&receiver, mock_frame_transformer, rtc::Thread::Current(),
/*remote_ssrc*/ 1111);
auto mock_sender_frame =
std::make_unique<NiceMock<MockTransformableVideoFrame>>();
ON_CALL(*mock_sender_frame, GetDirection)
.WillByDefault(Return(TransformableFrameInterface::Direction::kSender));
VideoFrameMetadata metadata;
metadata.SetCodec(kVideoCodecVP8);
metadata.SetRTPVideoHeaderCodecSpecifics(RTPVideoHeaderVP8());
ON_CALL(*mock_sender_frame, Metadata).WillByDefault(Return(metadata));
rtc::scoped_refptr<EncodedImageBufferInterface> buffer =
EncodedImageBuffer::Create(1);
ON_CALL(*mock_sender_frame, GetData)
.WillByDefault(Return(rtc::ArrayView<const uint8_t>(*buffer)));
rtc::scoped_refptr<TransformedFrameCallback> callback;
EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameSinkCallback)
.WillOnce(SaveArg<0>(&callback));
delegate->Init();
ASSERT_TRUE(callback);
EXPECT_CALL(receiver, ManageFrame)
.WillOnce([&](std::unique_ptr<RtpFrameObject> frame) {
EXPECT_EQ(frame->codec_type(), metadata.GetCodec());
});
callback->OnTransformedFrame(std::move(mock_sender_frame));
rtc::ThreadManager::ProcessAllMessageQueuesForTesting();
}
} // namespace
} // namespace webrtc