Add a clone method to the video frame transformer API.
This will clone an encoded video frame into a sender frame,
preserving metadata as much as possible.
Bug: webrtc:14708
Change-Id: I6f68d2ee65ef85c32cc3c142a41346b81ba73533
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/284701
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38733}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index c661dc5..798fa13 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -1466,3 +1466,19 @@
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
+
+rtc_library("frame_transformer_factory") {
+ visibility = [ "*" ]
+ sources = [
+ "frame_transformer_factory.cc",
+ "frame_transformer_factory.h",
+ ]
+ deps = [
+ ":frame_transformer_interface",
+ ":scoped_refptr",
+ "../modules/rtp_rtcp",
+ "../rtc_base:refcount",
+ "video:encoded_frame",
+ "video:video_frame_metadata",
+ ]
+}
diff --git a/api/frame_transformer_factory.cc b/api/frame_transformer_factory.cc
new file mode 100644
index 0000000..af08372
--- /dev/null
+++ b/api/frame_transformer_factory.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/frame_transformer_factory.h"
+
+#include "modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h"
+
+namespace webrtc {
+
+std::unique_ptr<TransformableVideoFrameInterface> CreateVideoSenderFrame() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<TransformableVideoFrameInterface> CreateVideoReceiverFrame() {
+ RTC_CHECK_NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<TransformableVideoFrameInterface> CloneVideoFrame(
+ TransformableVideoFrameInterface* original) {
+ // At the moment, only making sender frames from receiver frames is supported.
+ return CloneSenderVideoFrame(original);
+}
+
+} // namespace webrtc
diff --git a/api/frame_transformer_factory.h b/api/frame_transformer_factory.h
new file mode 100644
index 0000000..8ba9c29
--- /dev/null
+++ b/api/frame_transformer_factory.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_FRAME_TRANSFORMER_FACTORY_H_
+#define API_FRAME_TRANSFORMER_FACTORY_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/frame_transformer_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/video/encoded_frame.h"
+#include "api/video/video_frame_metadata.h"
+
+// This file contains EXPERIMENTAL functions to create video frames from
+// either an old video frame or directly from parameters.
+// These functions will be used in Chrome functionality to manipulate
+// encoded frames from Javascript.
+namespace webrtc {
+
+// TODO(bugs.webrtc.org/14708): Add the required parameters to these APIs.
+std::unique_ptr<TransformableVideoFrameInterface> CreateVideoSenderFrame();
+// TODO(bugs.webrtc.org/14708): Consider whether Receiver frames ever make sense
+// to create.
+std::unique_ptr<TransformableVideoFrameInterface> CreateVideoReceiverFrame();
+// Creates a new frame with the same metadata as the original.
+// The original can be a sender or receiver frame.
+RTC_EXPORT std::unique_ptr<TransformableVideoFrameInterface> CloneVideoFrame(
+ TransformableVideoFrameInterface* original);
+} // namespace webrtc
+
+#endif // API_FRAME_TRANSFORMER_FACTORY_H_
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index ead114b..e046882 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -596,6 +596,7 @@
]
deps = [
":fec_test_helper",
+ ":frame_transformer_factory_unittest",
":mock_rtp_rtcp",
":rtcp_transceiver",
":rtp_packetizer_av1_test_helper",
@@ -605,6 +606,7 @@
"../../api:array_view",
"../../api:create_time_controller",
"../../api:field_trials_registry",
+ "../../api:frame_transformer_factory",
"../../api:libjingle_peerconnection_api",
"../../api:mock_frame_encryptor",
"../../api:rtp_headers",
@@ -669,3 +671,19 @@
]
}
}
+
+rtc_source_set("frame_transformer_factory_unittest") {
+ testonly = true
+ sources = [ "source/frame_transformer_factory_unittest.cc" ]
+ deps = [
+ "../../api:frame_transformer_factory",
+ "../../api:transport_api",
+ "../../call:video_stream_api",
+ "../../modules/rtp_rtcp",
+ "../../rtc_base:rtc_event",
+ "../../test:mock_frame_transformer",
+ "../../test:test_support",
+ "../../video",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
+}
diff --git a/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc b/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc
new file mode 100644
index 0000000..e011a76
--- /dev/null
+++ b/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/frame_transformer_factory.h"
+
+#include <cstdio>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "api/call/transport.h"
+#include "call/video_receive_stream.h"
+#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
+#include "rtc_base/event.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/mock_frame_transformer.h"
+
+namespace webrtc {
+namespace {
+
+using testing::NiceMock;
+using testing::Return;
+
+class MockTransformableVideoFrame
+ : public webrtc::TransformableVideoFrameInterface {
+ public:
+ MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const override));
+ MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t> data), (override));
+ MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
+ MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
+ MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
+ MOCK_METHOD(TransformableFrameInterface::Direction,
+ GetDirection,
+ (),
+ (const, override));
+ MOCK_METHOD(bool, IsKeyFrame, (), (const, override));
+ MOCK_METHOD(std::vector<uint8_t>, GetAdditionalData, (), (const, override));
+ MOCK_METHOD(const webrtc::VideoFrameMetadata&,
+ GetMetadata,
+ (),
+ (const, override));
+};
+
+TEST(FrameTransformerFactory, CloneVideoFrame) {
+ NiceMock<MockTransformableVideoFrame> original_frame;
+ uint8_t data[10];
+ std::fill_n(data, 10, 5);
+ rtc::ArrayView<uint8_t> data_view(data);
+ EXPECT_CALL(original_frame, GetData()).WillRepeatedly(Return(data_view));
+ auto cloned_frame = CloneVideoFrame(&original_frame);
+ EXPECT_EQ(cloned_frame->GetData().size(), 10u);
+ EXPECT_THAT(cloned_frame->GetData(), testing::Each(5u));
+}
+
+} // namespace
+} // namespace webrtc
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 3e42781..a60c393 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
@@ -18,6 +18,7 @@
#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
#include "modules/rtp_rtcp/source/rtp_sender_video.h"
#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
namespace webrtc {
namespace {
@@ -161,8 +162,9 @@
EnsureEncoderQueueCreated();
- if (!sender_)
+ if (!sender_) {
return;
+ }
rtc::scoped_refptr<RTPSenderVideoFrameTransformerDelegate> delegate(this);
encoder_queue_->PostTask(
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
@@ -212,4 +214,37 @@
sender_ = nullptr;
}
}
+
+std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
+ TransformableVideoFrameInterface* original) {
+ auto encoded_image_buffer = EncodedImageBuffer::Create(
+ original->GetData().data(), original->GetData().size());
+ EncodedImage encoded_image;
+ encoded_image.SetEncodedData(encoded_image_buffer);
+ RTPVideoHeader new_header;
+ absl::optional<VideoCodecType> new_codec_type;
+ // TODO(bugs.webrtc.org/14708): Figure out a way to get the header information
+ // without casting to TransformableVideoSenderFrame.
+ if (original->GetDirection() ==
+ TransformableFrameInterface::Direction::kSender) {
+ // TODO(bugs.webrtc.org/14708): Figure out a way to bulletproof this cast.
+ auto original_as_sender =
+ static_cast<TransformableVideoSenderFrame*>(original);
+ new_header = original_as_sender->GetHeader();
+ new_codec_type = original_as_sender->GetCodecType();
+ } else {
+ // TODO(bugs.webrtc.org/14708): Make this codec dependent
+ new_header.video_type_header.emplace<RTPVideoHeaderVP8>();
+ new_codec_type = kVideoCodecVP8;
+ // TODO(bugs.webrtc.org/14708): Fill in the new_header when it's not
+ // `Direction::kSender`
+ }
+ // TODO(bugs.webrtc.org/14708): Fill in other EncodedImage parameters
+ return std::make_unique<TransformableVideoSenderFrame>(
+ encoded_image, new_header, original->GetPayloadType(), new_codec_type,
+ original->GetTimestamp(),
+ absl::nullopt, // expected_retransmission_time_ms
+ original->GetSsrc());
+}
+
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
index 0c2a643..04cdfd3 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
+++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h
@@ -87,6 +87,10 @@
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> owned_encoder_queue_;
};
+// Method to support cloning a Sender frame from another frame
+std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
+ TransformableVideoFrameInterface* original);
+
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_FRAME_TRANSFORMER_DELEGATE_H_
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
index 1352712..3eacda8 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
@@ -17,6 +17,7 @@
#include "absl/memory/memory.h"
#include "api/field_trials_registry.h"
+#include "api/frame_transformer_factory.h"
#include "api/rtp_headers.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h"
@@ -38,6 +39,7 @@
#include "modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
#include "rtc_base/rate_limiter.h"
#include "rtc_base/thread.h"
#include "test/gmock.h"
@@ -1645,5 +1647,45 @@
kDefaultExpectedRetransmissionTimeMs);
}
+TEST_F(RtpSenderVideoWithFrameTransformerTest,
+ OnTransformedFrameSendsVideoWhenCloned) {
+ auto mock_frame_transformer =
+ rtc::make_ref_counted<NiceMock<MockFrameTransformer>>();
+ rtc::scoped_refptr<TransformedFrameCallback> callback;
+ EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameSinkCallback)
+ .WillOnce(SaveArg<0>(&callback));
+ std::unique_ptr<RTPSenderVideo> rtp_sender_video =
+ CreateSenderWithFrameTransformer(mock_frame_transformer);
+ ASSERT_TRUE(callback);
+
+ auto encoded_image = CreateDefaultEncodedImage();
+ RTPVideoHeader video_header;
+ video_header.frame_type = VideoFrameType::kVideoFrameKey;
+ ON_CALL(*mock_frame_transformer, Transform)
+ .WillByDefault(
+ [&callback](std::unique_ptr<TransformableFrameInterface> frame) {
+ auto clone = CloneVideoFrame(
+ static_cast<TransformableVideoFrameInterface*>(frame.get()));
+ EXPECT_TRUE(clone);
+ callback->OnTransformedFrame(std::move(clone));
+ });
+ auto encoder_queue = time_controller_.GetTaskQueueFactory()->CreateTaskQueue(
+ "encoder_queue", TaskQueueFactory::Priority::NORMAL);
+ encoder_queue->PostTask([&] {
+ rtp_sender_video->SendEncodedImage(kPayload, kType, kTimestamp,
+ *encoded_image, video_header,
+ kDefaultExpectedRetransmissionTimeMs);
+ });
+ time_controller_.AdvanceTime(TimeDelta::Zero());
+ EXPECT_EQ(transport_.packets_sent(), 1);
+ encoder_queue->PostTask([&] {
+ rtp_sender_video->SendEncodedImage(kPayload, kType, kTimestamp,
+ *encoded_image, video_header,
+ kDefaultExpectedRetransmissionTimeMs);
+ });
+ time_controller_.AdvanceTime(TimeDelta::Zero());
+ EXPECT_EQ(transport_.packets_sent(), 2);
+}
+
} // namespace
} // namespace webrtc