Implement constructor in RTCEncodedVideoFrame
This is inline with the algorithm described in https://github.com/w3c/webrtc-encoded-transform/compare/main...palak8669:webrtc-encoded-transform:constructor_frame
This is a reland of https://chromium-review.googlesource.com/c/chromium/src/+/5295578 which got reverted due
to wrong include rules.
Bug: 14709
Change-Id: I5da20d10295908b2592c3c7202aa56fd206bf908
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5307134
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Palak Agarwal <agpalak@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1268408}
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index aa6331b..53e115f5 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4221,6 +4221,7 @@
kStorageAccessAPI_requestStorageAccess_BeyondCookies_SharedWorker = 4857,
kStorageAccessAPI_requestStorageAccess_BeyondCookies_SharedWorker_Use = 4858,
kV8PointerEvent_GetCoalescedEvents_Method = 4859,
+ kV8RTCEncodedVideoFrame_Constructor = 4860,
kCSSFunctions = 4861,
kCSSPageRule = 4862,
kV8RTCRtpReceiver_JitterBufferTarget_AttributeGetter = 4863,
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
index 396a86e..6864ce8 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.cc
@@ -60,15 +60,13 @@
if (!metadata->hasWidth() || !metadata->hasHeight() ||
!metadata->hasSpatialIndex() || !metadata->hasTemporalIndex() ||
!metadata->hasRtpTimestamp()) {
- error_message = "Member(s) missing in RTCEncodedVideoFrameMetadata.";
+ error_message = "new metadata has member(s) missing.";
return false;
}
// This might happen if the dependency descriptor is not set.
if (!metadata->hasFrameId() && metadata->hasDependencies()) {
- error_message =
- "frameID missing, but has dependencies in "
- "RTCEncodedVideoFrameMetadata.";
+ error_message = "new metadata has frameID missing, but has dependencies";
return false;
}
if (!metadata->hasDependencies()) {
@@ -78,7 +76,7 @@
// Ensure there are at most 8 deps. Enforced in WebRTC's
// RtpGenericFrameDescriptor::AddFrameDependencyDiff().
if (metadata->dependencies().size() > kMaxNumDependencies) {
- error_message = "Too many dependencies.";
+ error_message = "new metadata has too many dependencies.";
return false;
}
// Require deps to all be before frame_id, but within 2^14 of it. Enforced in
@@ -86,7 +84,7 @@
for (const int64_t dep : metadata->dependencies()) {
if ((dep >= metadata->frameId()) ||
((metadata->frameId() - dep) >= (1 << 14))) {
- error_message = "Invalid frame dependency.";
+ error_message = "new metadata has invalid frame dependencies.";
return false;
}
}
@@ -96,6 +94,38 @@
} // namespace
+RTCEncodedVideoFrame* RTCEncodedVideoFrame::Create(
+ RTCEncodedVideoFrame* original_frame,
+ ExceptionState& exception_state) {
+ return RTCEncodedVideoFrame::Create(original_frame, nullptr, exception_state);
+}
+
+RTCEncodedVideoFrame* RTCEncodedVideoFrame::Create(
+ RTCEncodedVideoFrame* original_frame,
+ RTCEncodedVideoFrameMetadata* new_metadata,
+ ExceptionState& exception_state) {
+ RTCEncodedVideoFrame* new_frame;
+ if (original_frame) {
+ new_frame = MakeGarbageCollected<RTCEncodedVideoFrame>(
+ original_frame->Delegate()->CloneWebRtcFrame());
+ } else {
+ exception_state.ThrowDOMException(
+ DOMExceptionCode::kInvalidAccessError,
+ "Cannot create a new VideoFrame from an empty VideoFrame");
+ return nullptr;
+ }
+ if (new_metadata) {
+ String error_message;
+ if (!new_frame->SetMetadata(new_metadata, error_message)) {
+ exception_state.ThrowDOMException(
+ DOMExceptionCode::kInvalidModificationError,
+ "Cannot create a new VideoFrame: " + error_message);
+ return nullptr;
+ }
+ }
+ return new_frame;
+}
+
RTCEncodedVideoFrame::RTCEncodedVideoFrame(
std::unique_ptr<webrtc::TransformableVideoFrameInterface> webrtc_frame)
: delegate_(base::MakeRefCounted<RTCEncodedVideoFrameDelegate>(
@@ -115,7 +145,11 @@
void RTCEncodedVideoFrame::setTimestamp(uint32_t timestamp,
ExceptionState& exception_state) {
- delegate_->SetRtpTimestamp(timestamp, exception_state);
+ String error_message;
+ if (!delegate_->SetRtpTimestamp(timestamp, error_message)) {
+ exception_state.ThrowDOMException(
+ DOMExceptionCode::kInvalidModificationError, error_message);
+ }
}
DOMArrayBuffer* RTCEncodedVideoFrame::data() const {
@@ -172,47 +206,38 @@
return metadata;
}
-void RTCEncodedVideoFrame::setMetadata(RTCEncodedVideoFrameMetadata* metadata,
- ExceptionState& exception_state) {
+bool RTCEncodedVideoFrame::SetMetadata(
+ const RTCEncodedVideoFrameMetadata* metadata,
+ String& error_message) {
const std::optional<webrtc::VideoFrameMetadata> original_webrtc_metadata =
delegate_->GetMetadata();
if (!original_webrtc_metadata) {
- exception_state.ThrowDOMException(
- DOMExceptionCode::kInvalidModificationError,
- "Cannot set metadata on an empty frame.");
- return;
+ error_message = "underlying webrtc frame is an empty frame.";
+ return false;
}
- String error_message;
if (!ValidateMetadata(metadata, error_message)) {
- exception_state.ThrowDOMException(
- DOMExceptionCode::kInvalidModificationError, error_message);
- return;
+ return false;
}
RTCEncodedVideoFrameMetadata* original_metadata = getMetadata();
if (!original_metadata) {
- exception_state.ThrowDOMException(
- DOMExceptionCode::kInvalidModificationError,
- "Internal error when calling setMetadata.");
- return;
+ error_message = "internal error when calling getMetadata().";
+ return false;
}
if (!IsAllowedSetMetadataChange(original_metadata, metadata) &&
!base::FeatureList::IsEnabled(
kAllowRTCEncodedVideoFrameSetMetadataAllFields)) {
- exception_state.ThrowDOMException(
- DOMExceptionCode::kInvalidModificationError,
- "Invalid modification of RTCEncodedVideoFrameMetadata.");
- return;
+ error_message = "invalid modification of RTCEncodedVideoFrameMetadata.";
+ return false;
}
if ((metadata->hasPayloadType() != original_metadata->hasPayloadType()) ||
(metadata->hasPayloadType() &&
metadata->payloadType() != original_metadata->payloadType())) {
- exception_state.ThrowDOMException(
- DOMExceptionCode::kInvalidModificationError,
- "Invalid modification of payloadType in RTCEncodedVideoFrameMetadata.");
- return;
+ error_message =
+ "invalid modification of payloadType in RTCEncodedVideoFrameMetadata.";
+ return false;
}
// Initialize the new metadata from original_metadata to account for fields
@@ -238,8 +263,18 @@
webrtc_metadata.SetCsrcs(csrcs);
}
- delegate_->SetMetadata(webrtc_metadata);
- delegate_->SetRtpTimestamp(metadata->rtpTimestamp(), exception_state);
+ return delegate_->SetMetadata(webrtc_metadata, error_message) &&
+ delegate_->SetRtpTimestamp(metadata->rtpTimestamp(), error_message);
+}
+
+void RTCEncodedVideoFrame::setMetadata(RTCEncodedVideoFrameMetadata* metadata,
+ ExceptionState& exception_state) {
+ String error_message;
+ if (!SetMetadata(metadata, error_message)) {
+ exception_state.ThrowDOMException(
+ DOMExceptionCode::kInvalidModificationError,
+ "Cannot setMetadata: " + error_message);
+ }
}
void RTCEncodedVideoFrame::setData(DOMArrayBuffer* data) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
index 36b156b4..2db3b64 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h
@@ -34,6 +34,12 @@
DEFINE_WRAPPERTYPEINFO();
public:
+ static RTCEncodedVideoFrame* Create(RTCEncodedVideoFrame* original_frame,
+ ExceptionState& exception_state);
+ static RTCEncodedVideoFrame* Create(
+ RTCEncodedVideoFrame* original_frame,
+ RTCEncodedVideoFrameMetadata* new_metadata,
+ ExceptionState& exception_state);
explicit RTCEncodedVideoFrame(
std::unique_ptr<webrtc::TransformableVideoFrameInterface> webrtc_frame);
explicit RTCEncodedVideoFrame(
@@ -46,6 +52,8 @@
void setTimestamp(uint32_t timestamp, ExceptionState& exception_state);
DOMArrayBuffer* data() const;
RTCEncodedVideoFrameMetadata* getMetadata() const;
+ bool SetMetadata(const RTCEncodedVideoFrameMetadata* metadata,
+ String& error_message);
void setMetadata(RTCEncodedVideoFrameMetadata* metadata,
ExceptionState& exception_state);
void setData(DOMArrayBuffer*);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
index 4d4b181..70ecf0df 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
@@ -14,6 +14,8 @@
[
Exposed=(Window, DedicatedWorker)
] interface RTCEncodedVideoFrame {
+ [RuntimeEnabled=RTCEncodedFrameSetMetadata, Measure, RaisesException]
+ constructor (RTCEncodedVideoFrame originalFrame, optional RTCEncodedVideoFrameMetadata newMetadata);
readonly attribute RTCEncodedVideoFrameType type;
readonly attribute unsigned long timestamp; // RTP timestamp.
attribute ArrayBuffer data;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
index 4ef0c3d..7644d540 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.cc
@@ -33,16 +33,15 @@
return webrtc_frame_ ? webrtc_frame_->GetTimestamp() : 0;
}
-void RTCEncodedVideoFrameDelegate::SetRtpTimestamp(
- uint32_t timestamp,
- ExceptionState& exception_state) {
+bool RTCEncodedVideoFrameDelegate::SetRtpTimestamp(uint32_t timestamp,
+ String& error_message) {
base::AutoLock lock(lock_);
- if (webrtc_frame_) {
- webrtc_frame_->SetRTPTimestamp(timestamp);
- } else {
- exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
- "Video frame is empty.");
+ if (!webrtc_frame_) {
+ error_message = "underlying webrtc frame is empty.";
+ return false;
}
+ webrtc_frame_->SetRTPTimestamp(timestamp);
+ return true;
}
std::optional<webrtc::Timestamp>
@@ -98,13 +97,16 @@
: std::nullopt;
}
-void RTCEncodedVideoFrameDelegate::SetMetadata(
- const webrtc::VideoFrameMetadata& metadata) {
+bool RTCEncodedVideoFrameDelegate::SetMetadata(
+ const webrtc::VideoFrameMetadata& metadata,
+ String& error_message) {
base::AutoLock lock(lock_);
if (!webrtc_frame_) {
- return;
+ error_message = "underlying webrtc frame is empty.";
+ return false;
}
webrtc_frame_->SetMetadata(metadata);
+ return true;
}
std::unique_ptr<webrtc::TransformableVideoFrameInterface>
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
index d956207..d330a483 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h
@@ -20,7 +20,6 @@
namespace blink {
class DOMArrayBuffer;
-class ExceptionState;
// This class wraps a WebRTC video frame and allows making shallow
// copies. Its purpose is to support making RTCEncodedVideoFrames
@@ -33,14 +32,15 @@
String Type() const;
uint32_t RtpTimestamp() const;
- void SetRtpTimestamp(uint32_t timestamp, ExceptionState& exception_state);
+ bool SetRtpTimestamp(uint32_t timestamp, String& error_message);
std::optional<webrtc::Timestamp> PresentationTimestamp() const;
DOMArrayBuffer* CreateDataBuffer() const;
void SetData(const DOMArrayBuffer* data);
std::optional<uint8_t> PayloadType() const;
std::optional<std::string> MimeType() const;
std::optional<webrtc::VideoFrameMetadata> GetMetadata() const;
- void SetMetadata(const webrtc::VideoFrameMetadata& metadata);
+ bool SetMetadata(const webrtc::VideoFrameMetadata& metadata,
+ String& error_message);
std::unique_ptr<webrtc::TransformableVideoFrameInterface> PassWebRtcFrame();
std::unique_ptr<webrtc::TransformableVideoFrameInterface> CloneWebRtcFrame();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_test.cc
index 06701d4..3ad009b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_test.cc
@@ -82,6 +82,7 @@
webrtc_metadata.SetRTPVideoHeaderCodecSpecifics(webrtc_vp8_specifics);
ON_CALL(*frame, Metadata()).WillByDefault(Return(webrtc_metadata));
+ ON_CALL(*frame, GetSsrc()).WillByDefault(Return(7));
return webrtc_metadata;
}
@@ -157,22 +158,32 @@
encoded_frame.setMetadata(empty_metadata, exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(exception_state.Message(),
- "Member(s) missing in RTCEncodedVideoFrameMetadata.");
+ "Cannot setMetadata: new metadata has member(s) missing.");
}
-RTCEncodedVideoFrameMetadata* CreateMetadata() {
+RTCEncodedVideoFrameMetadata* CreateMetadata(bool change_all_fields = false) {
RTCEncodedVideoFrameMetadata* new_metadata =
RTCEncodedVideoFrameMetadata::Create();
new_metadata->setFrameId(5);
new_metadata->setDependencies({2, 3, 4});
- new_metadata->setWidth(6);
- new_metadata->setHeight(7);
- new_metadata->setSpatialIndex(8);
- new_metadata->setTemporalIndex(9);
- new_metadata->setSynchronizationSource(10);
- new_metadata->setContributingSources({11, 12, 13});
- new_metadata->setPayloadType(14);
new_metadata->setRtpTimestamp(1);
+ if (change_all_fields) {
+ new_metadata->setWidth(6);
+ new_metadata->setHeight(7);
+ new_metadata->setSpatialIndex(8);
+ new_metadata->setTemporalIndex(9);
+ new_metadata->setSynchronizationSource(10);
+ new_metadata->setContributingSources({11, 12, 13});
+ new_metadata->setPayloadType(14);
+ } else {
+ new_metadata->setWidth(800);
+ new_metadata->setHeight(600);
+ new_metadata->setSpatialIndex(3);
+ new_metadata->setTemporalIndex(4);
+ new_metadata->setSynchronizationSource(7);
+ new_metadata->setContributingSources({6});
+ new_metadata->setPayloadType(1);
+ }
return new_metadata;
}
@@ -186,19 +197,22 @@
std::unique_ptr<MockTransformableVideoFrame> frame =
std::make_unique<NiceMock<MockTransformableVideoFrame>>();
MockVP8Metadata(frame.get());
+ EXPECT_CALL(*frame, GetPayloadType()).WillRepeatedly(Return(1));
webrtc::VideoFrameMetadata actual_metadata;
EXPECT_CALL(*frame, SetMetadata(_)).Times(0);
RTCEncodedVideoFrame encoded_frame(std::move(frame));
- RTCEncodedVideoFrameMetadata* new_metadata = CreateMetadata();
+ RTCEncodedVideoFrameMetadata* new_metadata =
+ CreateMetadata(/*change_all_fields=*/true);
DummyExceptionStateForTesting exception_state;
encoded_frame.setMetadata(new_metadata, exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(exception_state.Message(),
- "Invalid modification of RTCEncodedVideoFrameMetadata.");
+ "Cannot setMetadata: invalid modification of "
+ "RTCEncodedVideoFrameMetadata.");
}
TEST_F(RTCEncodedVideoFrameTest, SetMetadataWithFeatureAllowsModifications) {
@@ -218,7 +232,8 @@
RTCEncodedVideoFrame encoded_frame(std::move(frame));
- RTCEncodedVideoFrameMetadata* new_metadata = CreateMetadata();
+ RTCEncodedVideoFrameMetadata* new_metadata =
+ CreateMetadata(/*change_all_fields=*/true);
DummyExceptionStateForTesting exception_state;
encoded_frame.setMetadata(new_metadata, exception_state);
@@ -261,7 +276,7 @@
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(exception_state.Message(),
- "Cannot set metadata on an empty frame.");
+ "Cannot setMetadata: underlying webrtc frame is an empty frame.");
}
TEST_F(RTCEncodedVideoFrameTest, SetMetadataRejectsInvalidDependencies) {
@@ -285,7 +300,9 @@
DummyExceptionStateForTesting exception_state;
encoded_frame.setMetadata(new_metadata, exception_state);
EXPECT_TRUE(exception_state.HadException());
- EXPECT_EQ(exception_state.Message(), "Invalid frame dependency.");
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot setMetadata: new metadata has invalid frame "
+ "dependencies.");
}
TEST_F(RTCEncodedVideoFrameTest, SetMetadataRejectsTooEarlyDependencies) {
@@ -310,7 +327,9 @@
DummyExceptionStateForTesting exception_state;
encoded_frame.setMetadata(new_metadata, exception_state);
EXPECT_TRUE(exception_state.HadException());
- EXPECT_EQ(exception_state.Message(), "Invalid frame dependency.");
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot setMetadata: new metadata has invalid frame "
+ "dependencies.");
}
TEST_F(RTCEncodedVideoFrameTest, SetMetadataRejectsTooManyDependencies) {
@@ -334,7 +353,8 @@
DummyExceptionStateForTesting exception_state;
encoded_frame.setMetadata(new_metadata, exception_state);
EXPECT_TRUE(exception_state.HadException());
- EXPECT_EQ(exception_state.Message(), "Too many dependencies.");
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot setMetadata: new metadata has too many dependencies.");
}
TEST_F(RTCEncodedVideoFrameTest, SetMetadataModifiesRtpTimestamp) {
@@ -361,4 +381,331 @@
EXPECT_FALSE(exception_state.HadException()) << exception_state.Message();
}
+TEST_F(RTCEncodedVideoFrameTest, ConstructorPreservesVP9CodecSpecifics) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ webrtc::VideoFrameMetadata webrtc_metadata = MockVP9Metadata(frame.get());
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+ DummyExceptionStateForTesting exception_state;
+
+ RTCEncodedVideoFrame* new_frame =
+ RTCEncodedVideoFrame::Create(&encoded_frame, exception_state);
+ EXPECT_FALSE(exception_state.HadException()) << exception_state.Message();
+ EXPECT_EQ(new_frame->getMetadata()->frameId(), webrtc_metadata.GetFrameId());
+ EXPECT_EQ(new_frame->getMetadata()->width(), webrtc_metadata.GetWidth());
+ EXPECT_EQ(new_frame->getMetadata()->height(), webrtc_metadata.GetHeight());
+ EXPECT_EQ(new_frame->getMetadata()->spatialIndex(),
+ webrtc_metadata.GetSpatialIndex());
+ EXPECT_EQ(new_frame->getMetadata()->temporalIndex(),
+ webrtc_metadata.GetTemporalIndex());
+ EXPECT_EQ(new_frame->getMetadata()->synchronizationSource(),
+ webrtc_metadata.GetSsrc());
+ std::vector<uint32_t> actual_csrcs;
+ for (const auto& dependency :
+ new_frame->getMetadata()->contributingSources()) {
+ actual_csrcs.push_back(dependency);
+ }
+ EXPECT_EQ(actual_csrcs, webrtc_metadata.GetCsrcs());
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorMissingFieldsFails) {
+ V8TestingScope v8_scope;
+ base::test::ScopedFeatureList feature_list_;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+
+ RTCEncodedVideoFrameMetadata* empty_metadata =
+ RTCEncodedVideoFrameMetadata::Create();
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, empty_metadata, exception_state);
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame: new metadata has member(s) "
+ "missing.");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorWithoutFeatureFailsModifications) {
+ V8TestingScope v8_scope;
+ base::test::ScopedFeatureList feature_list_;
+ feature_list_.InitWithFeatures(
+ /*enabled_features=*/{},
+ /*disabled_features=*/{kAllowRTCEncodedVideoFrameSetMetadataAllFields});
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ webrtc::VideoFrameMetadata actual_metadata;
+ EXPECT_CALL(*frame, SetMetadata(_)).Times(0);
+ EXPECT_CALL(*frame, GetPayloadType()).WillRepeatedly(Return(1));
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+
+ RTCEncodedVideoFrameMetadata* new_metadata =
+ CreateMetadata(/*change_all_fields=*/true);
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, new_metadata, exception_state);
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame: invalid modification of "
+ "RTCEncodedVideoFrameMetadata.");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorWithFeatureAllowsModifications) {
+ V8TestingScope v8_scope;
+ base::test::ScopedFeatureList feature_list_;
+ feature_list_.InitWithFeatures(
+ /*enabled_features=*/{kAllowRTCEncodedVideoFrameSetMetadataAllFields},
+ /*disabled_features=*/{});
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ webrtc::VideoFrameMetadata actual_metadata;
+ EXPECT_CALL(*frame, SetMetadata(_)).Times(0);
+ EXPECT_CALL(*frame, GetPayloadType()).WillRepeatedly(Return(14));
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+
+ RTCEncodedVideoFrameMetadata* new_metadata =
+ CreateMetadata(/*change_all_fields=*/true);
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, new_metadata, exception_state);
+
+ EXPECT_FALSE(exception_state.HadException()) << exception_state.Message();
+
+ EXPECT_EQ(new_frame->getMetadata()->frameId(), new_metadata->frameId());
+ Vector<int64_t> actual_dependencies;
+ for (const auto& dependency : new_frame->getMetadata()->dependencies()) {
+ actual_dependencies.push_back(dependency);
+ }
+ EXPECT_EQ(actual_dependencies, new_metadata->dependencies());
+ EXPECT_EQ(new_frame->getMetadata()->width(), new_metadata->width());
+ EXPECT_EQ(new_frame->getMetadata()->height(), new_metadata->height());
+ EXPECT_EQ(new_frame->getMetadata()->spatialIndex(),
+ new_metadata->spatialIndex());
+ EXPECT_EQ(new_frame->getMetadata()->temporalIndex(),
+ new_metadata->temporalIndex());
+ EXPECT_EQ(new_frame->getMetadata()->synchronizationSource(),
+ new_metadata->synchronizationSource());
+ Vector<uint32_t> actual_csrcs;
+ for (const auto& dependency :
+ new_frame->getMetadata()->contributingSources()) {
+ actual_csrcs.push_back(dependency);
+ }
+ EXPECT_EQ(actual_csrcs, new_metadata->contributingSources());
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorFromNull) {
+ V8TestingScope v8_scope;
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame =
+ RTCEncodedVideoFrame::Create(nullptr, exception_state);
+
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame from an empty VideoFrame");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorOnEmptyFrameWorks) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+
+ // Move the WebRTC frame out, as if the frame had been written into
+ // an encoded insertable stream's WritableStream to be sent on.
+ encoded_frame.PassWebRtcFrame();
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame =
+ RTCEncodedVideoFrame::Create(&encoded_frame, exception_state);
+
+ EXPECT_FALSE(exception_state.HadException());
+ EXPECT_NE(new_frame, nullptr);
+ EXPECT_EQ(new_frame->type(), "empty");
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorWithMetadataOnEmptyFrameFails) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+ RTCEncodedVideoFrameMetadata* metadata = encoded_frame.getMetadata();
+
+ // Move the WebRTC frame out, as if the frame had been written into
+ // an encoded insertable stream's WritableStream to be sent on.
+ encoded_frame.PassWebRtcFrame();
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame =
+ RTCEncodedVideoFrame::Create(&encoded_frame, metadata, exception_state);
+
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame: underlying webrtc frame is "
+ "an empty frame.");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorRejectsInvalidDependencies) {
+ V8TestingScope v8_scope;
+ base::test::ScopedFeatureList feature_list_;
+ feature_list_.InitWithFeatures(
+ /*enabled_features=*/{kAllowRTCEncodedVideoFrameSetMetadataAllFields},
+ /*disabled_features=*/{});
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ EXPECT_CALL(*frame, SetMetadata(_)).Times(0);
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+ RTCEncodedVideoFrameMetadata* new_metadata = CreateMetadata();
+ // Set an invalid dependency - all deps must be less than frame id.
+ new_metadata->setDependencies({new_metadata->frameId()});
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, new_metadata, exception_state);
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame: new metadata has invalid "
+ "frame dependencies.");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorCopiesMetadata) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+ EXPECT_CALL(*frame, GetTimestamp()).WillRepeatedly(Return(1));
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame =
+ RTCEncodedVideoFrame::Create(&encoded_frame, exception_state);
+
+ EXPECT_FALSE(exception_state.HadException()) << exception_state.Message();
+
+ EXPECT_EQ(new_frame->getMetadata()->frameId(),
+ encoded_frame.getMetadata()->frameId());
+ EXPECT_EQ(new_frame->getMetadata()->dependencies(),
+ encoded_frame.getMetadata()->dependencies());
+ EXPECT_EQ(new_frame->getMetadata()->width(),
+ encoded_frame.getMetadata()->width());
+ EXPECT_EQ(new_frame->getMetadata()->height(),
+ encoded_frame.getMetadata()->height());
+ EXPECT_EQ(new_frame->getMetadata()->spatialIndex(),
+ encoded_frame.getMetadata()->spatialIndex());
+ EXPECT_EQ(new_frame->getMetadata()->temporalIndex(),
+ encoded_frame.getMetadata()->temporalIndex());
+ EXPECT_EQ(new_frame->getMetadata()->synchronizationSource(),
+ encoded_frame.getMetadata()->synchronizationSource());
+ EXPECT_EQ(new_frame->getMetadata()->contributingSources(),
+ encoded_frame.getMetadata()->contributingSources());
+ EXPECT_EQ(new_frame->getMetadata()->rtpTimestamp(),
+ encoded_frame.getMetadata()->rtpTimestamp());
+}
+
+TEST_F(RTCEncodedVideoFrameTest, ConstructorWithMetadataGetsNewMetadata) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+ EXPECT_CALL(*frame, GetPayloadType()).WillRepeatedly(Return(1));
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+ RTCEncodedVideoFrameMetadata* new_metadata = CreateMetadata();
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, new_metadata, exception_state);
+
+ EXPECT_FALSE(exception_state.HadException()) << exception_state.Message();
+
+ // |new_frame|'s metadata is same as |new_metadata|.
+ EXPECT_EQ(new_frame->getMetadata()->frameId(), new_metadata->frameId());
+ Vector<int64_t> actual_dependencies;
+ for (const auto& dependency : new_frame->getMetadata()->dependencies()) {
+ actual_dependencies.push_back(dependency);
+ }
+ EXPECT_EQ(actual_dependencies, new_metadata->dependencies());
+ EXPECT_EQ(new_frame->getMetadata()->width(), new_metadata->width());
+ EXPECT_EQ(new_frame->getMetadata()->height(), new_metadata->height());
+ EXPECT_EQ(new_frame->getMetadata()->spatialIndex(),
+ new_metadata->spatialIndex());
+ EXPECT_EQ(new_frame->getMetadata()->temporalIndex(),
+ new_metadata->temporalIndex());
+ EXPECT_EQ(new_frame->getMetadata()->synchronizationSource(),
+ new_metadata->synchronizationSource());
+ Vector<uint32_t> actual_csrcs;
+ for (const auto& dependency :
+ new_frame->getMetadata()->contributingSources()) {
+ actual_csrcs.push_back(dependency);
+ }
+ EXPECT_EQ(actual_csrcs, new_metadata->contributingSources());
+
+ // |new_frame|'s metadata is different from original |encoded_frame|'s
+ // metadata.
+ EXPECT_NE(new_frame->getMetadata()->frameId(),
+ encoded_frame.getMetadata()->frameId());
+ EXPECT_NE(new_frame->getMetadata()->dependencies(),
+ encoded_frame.getMetadata()->dependencies());
+ EXPECT_NE(new_frame->getMetadata()->rtpTimestamp(),
+ encoded_frame.getMetadata()->rtpTimestamp());
+}
+
+TEST_F(RTCEncodedVideoFrameTest,
+ ConstructorWithMetadataDoesNotAllowChangingPayloadType) {
+ V8TestingScope v8_scope;
+
+ std::unique_ptr<MockTransformableVideoFrame> frame =
+ std::make_unique<NiceMock<MockTransformableVideoFrame>>();
+ MockVP8Metadata(frame.get());
+
+ webrtc::VideoFrameMetadata actual_metadata;
+ EXPECT_CALL(*frame, SetMetadata(_)).Times(0);
+ EXPECT_CALL(*frame, GetPayloadType()).WillRepeatedly(Return(14));
+
+ RTCEncodedVideoFrame encoded_frame(std::move(frame));
+
+ RTCEncodedVideoFrameMetadata* new_metadata = CreateMetadata();
+
+ DummyExceptionStateForTesting exception_state;
+ RTCEncodedVideoFrame* new_frame = RTCEncodedVideoFrame::Create(
+ &encoded_frame, new_metadata, exception_state);
+ EXPECT_TRUE(exception_state.HadException());
+ EXPECT_EQ(exception_state.Message(),
+ "Cannot create a new VideoFrame: invalid modification of "
+ "payloadType in RTCEncodedVideoFrameMetadata.");
+ EXPECT_EQ(new_frame, nullptr);
+}
+
} // namespace blink
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/idlharness.https.window-expected.txt
index 4ffb4e7..0ea397e 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
This is a testharness.js-based test.
-Found 30 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 31 FAIL, 0 TIMEOUT, 0 NOTRUN.
[FAIL] SFrameTransform interface: existence and properties of interface object
assert_own_property: self does not have own property "SFrameTransform" expected property "SFrameTransform" missing
[FAIL] SFrameTransform interface object length
@@ -34,6 +34,8 @@
assert_own_property: self does not have own property "SFrameTransformErrorEvent" expected property "SFrameTransformErrorEvent" missing
[FAIL] SFrameTransformErrorEvent interface: attribute frame
assert_own_property: self does not have own property "SFrameTransformErrorEvent" expected property "SFrameTransformErrorEvent" missing
+[FAIL] RTCEncodedVideoFrame interface object length
+ assert_equals: wrong value for RTCEncodedVideoFrame.length expected 0 but got 1
[FAIL] RTCRtpScriptTransform interface: existence and properties of interface object
assert_own_property: self does not have own property "RTCRtpScriptTransform" expected property "RTCRtpScriptTransform" missing
[FAIL] RTCRtpScriptTransform interface object length
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/set-metadata.https.html b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/set-metadata.https.html
deleted file mode 100644
index c469959..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/set-metadata.https.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src=/resources/testdriver.js></script>
-<script src=/resources/testdriver-vendor.js></script>
-<script src='../mediacapture-streams/permission-helper.js'></script>
-<script src="../webrtc/RTCPeerConnection-helper.js"></script>
-<script src="../service-workers/service-worker/resources/test-helpers.sub.js"></script>
-<script src='./helper.js'></script>
-<script>
-"use strict";
-
-promise_test(async t => {
- const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
- const result = await senderReader.read();
- const metadata = result.value.getMetadata();
- // TODO(https://crbug.com/webrtc/14709): When RTCEncodedVideoFrame has a
- // constructor, create a new frame from scratch instead of cloning it to
- // ensure that none of the metadata was carried over via structuredClone().
- // This would allow us to be confident that setMetadata() is doing all the
- // work.
- //
- // At that point, we can refactor the structuredClone() implementation to be
- // the same as constructor() + set data + setMetadata() to ensure that
- // structuredClone() cannot do things that are not already exposed in
- // JavaScript (no secret steps!).
- const clone = structuredClone(result.value);
- clone.setMetadata(metadata);
- const cloneMetadata = clone.getMetadata();
- // Encoding related metadata.
- assert_equals(cloneMetadata.frameId, metadata.frameId, 'frameId');
- assert_array_equals(cloneMetadata.dependencies, metadata.dependencies,
- 'dependencies');
- assert_equals(cloneMetadata.width, metadata.width, 'width');
- assert_equals(cloneMetadata.height, metadata.height, 'height');
- assert_equals(cloneMetadata.spatialIndex, metadata.spatialIndex,
- 'spatialIndex');
- assert_equals(cloneMetadata.temporalIndex, metadata.temporalIndex,
- 'temporalIndex');
- // RTP related metadata.
- // TODO(https://crbug.com/webrtc/14709): This information also needs to be
- // settable but isn't - the assertions only pass because structuredClone()
- // copies them for us. It would be great if different layers didn't consider
- // different subset of the struct as "the metadata". Can we consolidate into a
- // single webrtc struct for all metadata?
- assert_equals(cloneMetadata.synchronizationSource,
- metadata.synchronizationSource, 'synchronizationSource');
- assert_array_equals(cloneMetadata.contributingSources,
- metadata.contributingSources, 'contributingSources');
- assert_equals(cloneMetadata.payloadType, metadata.payloadType, 'payloadType');
-}, "[VP8] setMetadata() carries over codec-specific properties");
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-metadata.https.html b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-metadata.https.html
new file mode 100644
index 0000000..a2c684c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-metadata.https.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src='../../mediacapture-streams/permission-helper.js'></script>
+<script src="../../webrtc/RTCPeerConnection-helper.js"></script>
+<script src="../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src='./../helper.js'></script>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
+ const result = await senderReader.read();
+ const metadata = result.value.getMetadata();
+ // TODO(https://crbug.com/webrtc/14709): When RTCEncodedVideoFrame has a
+ // constructor, create a new frame from scratch instead of cloning it to
+ // ensure that none of the metadata was carried over via structuredClone().
+ // This would allow us to be confident that setMetadata() is doing all the
+ // work.
+ //
+ // At that point, we can refactor the structuredClone() implementation to be
+ // the same as constructor() + set data + setMetadata() to ensure that
+ // structuredClone() cannot do things that are not already exposed in
+ // JavaScript (no secret steps!).
+ const clone = structuredClone(result.value);
+ clone.setMetadata(metadata);
+ const cloneMetadata = clone.getMetadata();
+ // Encoding-related metadata.
+ assert_equals(cloneMetadata.frameId, metadata.frameId, 'frameId');
+ assert_array_equals(cloneMetadata.dependencies, metadata.dependencies,
+ 'dependencies');
+ assert_equals(cloneMetadata.width, metadata.width, 'width');
+ assert_equals(cloneMetadata.height, metadata.height, 'height');
+ assert_equals(cloneMetadata.spatialIndex, metadata.spatialIndex,
+ 'spatialIndex');
+ assert_equals(cloneMetadata.temporalIndex, metadata.temporalIndex,
+ 'temporalIndex');
+ // RTP-related metadata.
+ // TODO(https://crbug.com/webrtc/14709): This information also needs to be
+ // settable but isn't - the assertions only pass because structuredClone()
+ // copies them for us. It would be great if different layers didn't consider
+ // different subset of the struct as "the metadata". Can we consolidate into a
+ // single webrtc struct for all metadata?
+ assert_equals(cloneMetadata.synchronizationSource,
+ metadata.synchronizationSource, 'synchronizationSource');
+ assert_array_equals(cloneMetadata.contributingSources,
+ metadata.contributingSources, 'contributingSources');
+ assert_equals(cloneMetadata.payloadType, metadata.payloadType, 'payloadType');
+}, "[VP8] setMetadata() carries over codec-specific properties");
+
+promise_test(async t => {
+ const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
+ const result = await senderReader.read();
+ const metadata = result.value.getMetadata();
+ const newFrame = new RTCEncodedVideoFrame(result.value);
+ const newMetadata = newFrame.getMetadata();
+
+ // Encoding-related metadata.
+ assert_equals(newMetadata.frameId, metadata.frameId, 'frameId');
+ assert_array_equals(newMetadata.dependencies, metadata.dependencies,
+ 'dependencies');
+ assert_equals(newMetadata.width, metadata.width, 'width');
+ assert_equals(newMetadata.height, metadata.height, 'height');
+ assert_equals(newMetadata.spatialIndex, metadata.spatialIndex,
+ 'spatialIndex');
+ assert_equals(newMetadata.temporalIndex, metadata.temporalIndex,
+ 'temporalIndex');
+
+ // RTP-related metadata.
+ assert_equals(newMetadata.synchronizationSource,
+ metadata.synchronizationSource, 'synchronizationSource');
+ assert_array_equals(newMetadata.contributingSources,
+ metadata.contributingSources, 'contributingSources');
+ assert_equals(newMetadata.payloadType, metadata.payloadType, 'payloadType');
+ assert_equals(newMetadata.rtpTimestamp, metadata.rtpTimestamp, 'rtpTimestamp');
+}, "[VP8] constructor carries over codec-specific properties");
+
+promise_test(async t => {
+ const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
+ const result = await senderReader.read();
+ const metadata = result.value.getMetadata();
+ metadata.rtpTimestamp = 100;
+ const newFrame = new RTCEncodedVideoFrame(result.value, metadata);
+ const newMetadata = newFrame.getMetadata();
+
+ // Encoding-related metadata.
+ assert_equals(newMetadata.frameId, metadata.frameId, 'frameId');
+ assert_array_equals(newMetadata.dependencies, metadata.dependencies,
+ 'dependencies');
+ assert_equals(newMetadata.width, metadata.width, 'width');
+ assert_equals(newMetadata.height, metadata.height, 'height');
+ assert_equals(newMetadata.spatialIndex, metadata.spatialIndex,
+ 'spatialIndex');
+ assert_equals(newMetadata.temporalIndex, metadata.temporalIndex,
+ 'temporalIndex');
+
+ // RTP-related metadata.
+ assert_equals(newMetadata.synchronizationSource,
+ metadata.synchronizationSource, 'synchronizationSource');
+ assert_array_equals(newMetadata.contributingSources,
+ metadata.contributingSources, 'contributingSources');
+ assert_equals(newMetadata.payloadType, metadata.payloadType, 'payloadType');
+ assert_equals(newMetadata.rtpTimestamp, metadata.rtpTimestamp, 'rtpTimestamp');
+ assert_not_equals(newMetadata.rtpTimestamp, result.value.getMetadata().rtpTimestamp, 'rtpTimestamp');
+}, "[VP8] constructor with metadata carries over codec-specific properties");
+
+promise_test(async t => {
+ const senderReader = await setupLoopbackWithCodecAndGetReader(t, 'VP8');
+ const result = await senderReader.read();
+ const metadata = result.value.getMetadata();
+ // Change the metadata by setting a new RTPTimestamp.
+ metadata.rtpTimestamp = 100;
+ const newFrame = new RTCEncodedVideoFrame(result.value);
+ const newMetadata = newFrame.getMetadata();
+
+ // Encoding-related metadata.
+ assert_equals(newMetadata.frameId, metadata.frameId, 'frameId');
+ assert_array_equals(newMetadata.dependencies, metadata.dependencies,
+ 'dependencies');
+ assert_equals(newMetadata.width, metadata.width, 'width');
+ assert_equals(newMetadata.height, metadata.height, 'height');
+ assert_equals(newMetadata.spatialIndex, metadata.spatialIndex,
+ 'spatialIndex');
+ assert_equals(newMetadata.temporalIndex, metadata.temporalIndex,
+ 'temporalIndex');
+
+ // RTP-related metadata.
+ assert_equals(newMetadata.synchronizationSource,
+ metadata.synchronizationSource, 'synchronizationSource');
+ assert_array_equals(newMetadata.contributingSources,
+ metadata.contributingSources, 'contributingSources');
+ assert_equals(newMetadata.payloadType, metadata.payloadType, 'payloadType');
+
+ // RTPTimestamp don't match because the constructor did not use the metadata for construction.
+ assert_not_equals(newMetadata.rtpTimestamp, metadata.rtpTimestamp, 'rtpTimestamp');
+ assert_equals(newMetadata.rtpTimestamp, result.value.getMetadata().rtpTimestamp, 'rtpTimestamp');
+}, "[VP8] constructor without metadata does not carry over modified metadata ");
+</script>
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCEncodedVideoFrame-set-metadata-expected.txt b/third_party/blink/web_tests/fast/peerconnection/RTCEncodedVideoFrame-set-metadata-expected.txt
index bce5755..5778c9a 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCEncodedVideoFrame-set-metadata-expected.txt
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCEncodedVideoFrame-set-metadata-expected.txt
@@ -1,5 +1,5 @@
This is a testharness.js-based test.
[FAIL] setMetadata() allows changes when AllowRTCEncodedVideoFrameSetMetadataAllFields is enabled
- promise_test: Unhandled rejection with value: object "InvalidModificationError: Failed to execute 'setMetadata' on 'RTCEncodedVideoFrame': Invalid modification of RTCEncodedVideoFrameMetadata."
+ promise_test: Unhandled rejection with value: object "InvalidModificationError: Failed to execute 'setMetadata' on 'RTCEncodedVideoFrame': Cannot setMetadata: invalid modification of RTCEncodedVideoFrameMetadata."
Harness: the test ran to completion.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 63f2684..56cf38b9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11304,6 +11304,7 @@
<int value="4858"
label="StorageAccessAPI_requestStorageAccess_BeyondCookies_SharedWorker_Use"/>
<int value="4859" label="V8PointerEvent_GetCoalescedEvents_Method"/>
+ <int value="4860" label="V8RTCEncodedVideoFrame_Constructor"/>
<int value="4861" label="CSSFunctions"/>
<int value="4862" label="CSSPageRule"/>
<int value="4863"