Commit f19b1b09 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Add VideoFrame.clone()

This CL adds the ability to clone a blink::VideoFrame. Doing so creates
a new VideoFrame, with a new reference to the same underlying
media::VideoFrame. This allows users that want to keep a frame alive
to pass a clone to workers or objects which might destroy the frame.

An example use case might be a user sending cloned reference to
different workers, which can then call destroy() on the frames without
impacting each other's work. A user could also send a clone to
VideoEncoder, to keep using the frame after the VideoEncoder is done
with the clone and calls destroy on it.

Bug: 1108023
Change-Id: Ia4f22f1429e466474fb87ce9700a54b87428440b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2355092
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Auto-Submit: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798386}
parent 50e61c78
......@@ -266,9 +266,22 @@ base::Optional<uint64_t> VideoFrame::duration() const {
}
void VideoFrame::destroy() {
// TODO(tguilbert): Add a warning when destroying already destroyed frames?
handle_->Invalidate();
}
VideoFrame* VideoFrame::clone(ExceptionState& exception_state) {
auto frame = handle_->frame();
if (!frame) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot clone destroyed VideoFrame.");
return nullptr;
}
return MakeGarbageCollected<VideoFrame>(std::move(frame));
}
scoped_refptr<VideoFrame::Handle> VideoFrame::handle() {
return handle_;
}
......
......@@ -86,6 +86,11 @@ class MODULES_EXPORT VideoFrame final : public ScriptWrappable,
// This effectively "destroys" all frames sharing the same Handle.
void destroy();
// Creates a copy of |this|, with a new Handle, referencing the same
// media::VideoFrame. The cloned frame will not be destroyed when |this| is,
// and its lifetime should be independently managed.
VideoFrame* clone(ExceptionState&);
ScriptPromise createImageBitmap(ScriptState*,
const ImageBitmapOptions*,
ExceptionState&);
......
......@@ -54,6 +54,11 @@ enum VideoPixelFormat {
// TODO(sandersd): Describe how a destroyed VideoFrame acts.
void destroy();
// Creates of the copy of this VideoFrame, which needs to be independently
// destroyed.
[RaisesException]
VideoFrame clone();
// Create an ImageBitmap from the crop region, scaled to the display size.
// TODO(sandersd): Should use the global createImageBitmap() instead.
[CallWith=ScriptState, RaisesException] Promise<ImageBitmap> createImageBitmap(
......
......@@ -4,6 +4,7 @@
#include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
......@@ -104,6 +105,42 @@ TEST_F(VideoFrameTest, FramesNotSharingHandleDestruction) {
EXPECT_EQ(media_frame, frame_with_new_handle->frame());
}
TEST_F(VideoFrameTest, ClonedFrame) {
V8TestingScope scope;
scoped_refptr<media::VideoFrame> media_frame =
CreateDefaultBlackMediaVideoFrame();
VideoFrame* blink_frame = CreateBlinkVideoFrame(media_frame);
VideoFrame* cloned_frame = blink_frame->clone(scope.GetExceptionState());
// The cloned frame should be referencing the same media::VideoFrame.
EXPECT_EQ(blink_frame->frame(), cloned_frame->frame());
EXPECT_EQ(media_frame, cloned_frame->frame());
EXPECT_FALSE(scope.GetExceptionState().HadException());
blink_frame->destroy();
// Destroying the original frame should not affect the cloned frame.
EXPECT_EQ(media_frame, cloned_frame->frame());
}
TEST_F(VideoFrameTest, CloningDestroyedFrame) {
V8TestingScope scope;
scoped_refptr<media::VideoFrame> media_frame =
CreateDefaultBlackMediaVideoFrame();
VideoFrame* blink_frame = CreateBlinkVideoFrame(media_frame);
blink_frame->destroy();
VideoFrame* cloned_frame = blink_frame->clone(scope.GetExceptionState());
// No frame should have been created, and there should be an exception.
EXPECT_EQ(nullptr, cloned_frame);
EXPECT_TRUE(scope.GetExceptionState().HadException());
}
} // namespace
} // namespace blink
......@@ -1588,6 +1588,7 @@ interface VideoFrame
getter format
getter planes
getter timestamp
method clone
method constructor
method createImageBitmap
method destroy
......
......@@ -1672,6 +1672,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] getter format
[Worker] getter planes
[Worker] getter timestamp
[Worker] method clone
[Worker] method constructor
[Worker] method createImageBitmap
[Worker] method destroy
......
......@@ -8807,6 +8807,7 @@ interface VideoFrame
getter format
getter planes
getter timestamp
method clone
method constructor
method createImageBitmap
method destroy
......
......@@ -1466,6 +1466,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] getter format
[Worker] getter planes
[Worker] getter timestamp
[Worker] method clone
[Worker] method constructor
[Worker] method createImageBitmap
[Worker] method destroy
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment