Commit ab53a091 authored by Eugene Zemtsov's avatar Eugene Zemtsov Committed by Chromium LUCI CQ

webcodecs: Extracting parts of VideoEncoder into EncoderBase

to be reused in AudioEncoder

Bug: 1094179
Change-Id: I3e693a3da7727bd827440c391e2ababca5681b19
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2623950
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: default avatarChrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842837}
parent fa5b85b9
......@@ -32,6 +32,8 @@ blink_modules_sources("webcodecs") {
"encoded_video_chunk.cc",
"encoded_video_chunk.h",
"encoded_video_metadata.h",
"encoder_base.cc",
"encoder_base.h",
"image_decoder_external.cc",
"image_decoder_external.h",
"plane.cc",
......
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/webcodecs/encoder_base.h"
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_encode_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_init.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
#include "third_party/blink/renderer/modules/webcodecs/encoded_video_metadata.h"
#include "third_party/blink/renderer/modules/webcodecs/video_encoder.h"
#include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace blink {
template <typename Traits>
EncoderBase<Traits>::EncoderBase(ScriptState* script_state,
const InitType* init,
ExceptionState& exception_state)
: ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
state_(V8CodecState::Enum::kUnconfigured),
script_state_(script_state) {
// TODO(crbug.com/1151005): Use a real MediaLog in worker contexts too.
if (IsMainThread()) {
logger_ = std::make_unique<CodecLogger>(
GetExecutionContext(), Thread::MainThread()->GetTaskRunner());
} else {
// This will create a logger backed by a NullMediaLog, which does nothing.
logger_ = std::make_unique<CodecLogger>();
}
media::MediaLog* log = logger_->log();
log->SetProperty<media::MediaLogProperty::kFrameTitle>(
std::string(Traits::GetNameForDevTools()));
log->SetProperty<media::MediaLogProperty::kFrameUrl>(
GetExecutionContext()->Url().GetString().Ascii());
output_callback_ = init->output();
if (init->hasError())
error_callback_ = init->error();
}
template <typename Traits>
EncoderBase<Traits>::~EncoderBase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
template <typename Traits>
int32_t EncoderBase<Traits>::encodeQueueSize() {
return requested_encodes_;
}
template <typename Traits>
void EncoderBase<Traits>::configure(const ConfigType* config,
ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "configure", exception_state))
return;
InternalConfigType* parsed_config = ParseConfig(config, exception_state);
if (!parsed_config) {
DCHECK(exception_state.HadException());
return;
}
if (!VerifyCodecSupport(parsed_config, exception_state)) {
DCHECK(exception_state.HadException());
return;
}
Request* request = MakeGarbageCollected<Request>();
request->reset_count = reset_count_;
if (media_encoder_ && active_config_ &&
state_.AsEnum() == V8CodecState::Enum::kConfigured &&
CanReconfigure(*active_config_, *parsed_config)) {
request->type = Request::Type::kReconfigure;
} else {
state_ = V8CodecState(V8CodecState::Enum::kConfigured);
request->type = Request::Type::kConfigure;
}
active_config_ = parsed_config;
EnqueueRequest(request);
}
template <typename Traits>
void EncoderBase<Traits>::encode(FrameType* frame,
const EncodeOptionsType* opts,
ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "encode", exception_state))
return;
if (ThrowIfCodecStateUnconfigured(state_, "encode", exception_state))
return;
DCHECK(active_config_);
auto* context = GetExecutionContext();
if (!context) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Context is destroyed.");
return;
}
// This will fail if |frame| is already destroyed.
auto* internal_frame = CloneFrame(frame, context);
if (!internal_frame) {
exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
"Cannot encode destroyed frame.");
return;
}
// At this point, we have "consumed" the frame, and will destroy the clone
// in ProcessEncode().
frame->destroy();
Request* request = MakeGarbageCollected<Request>();
request->reset_count = reset_count_;
request->type = Request::Type::kEncode;
request->frame = internal_frame;
request->encodeOpts = opts;
++requested_encodes_;
EnqueueRequest(request);
}
template <typename Traits>
void EncoderBase<Traits>::close(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "close", exception_state))
return;
state_ = V8CodecState(V8CodecState::Enum::kClosed);
ResetInternal();
media_encoder_.reset();
output_callback_.Clear();
error_callback_.Clear();
}
template <typename Traits>
ScriptPromise EncoderBase<Traits>::flush(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "flush", exception_state))
return ScriptPromise();
if (ThrowIfCodecStateUnconfigured(state_, "flush", exception_state))
return ScriptPromise();
Request* request = MakeGarbageCollected<Request>();
request->resolver =
MakeGarbageCollected<ScriptPromiseResolver>(script_state_);
request->reset_count = reset_count_;
request->type = Request::Type::kFlush;
EnqueueRequest(request);
return request->resolver->Promise();
}
template <typename Traits>
void EncoderBase<Traits>::reset(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "reset", exception_state))
return;
state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
ResetInternal();
media_encoder_.reset();
}
template <typename Traits>
void EncoderBase<Traits>::ResetInternal() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
reset_count_++;
while (!requests_.empty()) {
Request* pending_req = requests_.TakeFirst();
DCHECK(pending_req);
if (pending_req->resolver)
pending_req->resolver.Release()->Resolve();
if (pending_req->frame)
pending_req->frame.Release()->destroy();
}
stall_request_processing_ = false;
}
template <typename Traits>
void EncoderBase<Traits>::HandleError(DOMException* ex) {
if (state_.AsEnum() == V8CodecState::Enum::kClosed)
return;
// Save a temp before we clear the callback.
V8WebCodecsErrorCallback* error_callback = error_callback_.Get();
state_ = V8CodecState(V8CodecState::Enum::kClosed);
ResetInternal();
// Errors are permanent. Shut everything down.
error_callback_.Clear();
media_encoder_.reset();
output_callback_.Clear();
// Prevent further logging.
logger_->Neuter();
if (!script_state_->ContextIsValid() || !error_callback)
return;
ScriptState::Scope scope(script_state_);
error_callback->InvokeAndReportException(nullptr, ex);
}
template <typename Traits>
void EncoderBase<Traits>::EnqueueRequest(Request* request) {
requests_.push_back(request);
ProcessRequests();
}
template <typename Traits>
void EncoderBase<Traits>::ProcessRequests() {
while (!requests_.empty() && !stall_request_processing_) {
Request* request = requests_.TakeFirst();
DCHECK(request);
switch (request->type) {
case Request::Type::kConfigure:
ProcessConfigure(request);
break;
case Request::Type::kReconfigure:
ProcessReconfigure(request);
break;
case Request::Type::kEncode:
ProcessEncode(request);
break;
case Request::Type::kFlush:
ProcessFlush(request);
break;
default:
NOTREACHED();
}
}
}
template <typename Traits>
void EncoderBase<Traits>::ContextDestroyed() {
logger_->Neuter();
}
template <typename Traits>
bool EncoderBase<Traits>::HasPendingActivity() const {
return stall_request_processing_ || !requests_.empty();
}
template <typename Traits>
void EncoderBase<Traits>::Trace(Visitor* visitor) const {
visitor->Trace(active_config_);
visitor->Trace(script_state_);
visitor->Trace(output_callback_);
visitor->Trace(error_callback_);
visitor->Trace(requests_);
ScriptWrappable::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
}
template <typename Traits>
void EncoderBase<Traits>::Request::Trace(Visitor* visitor) const {
visitor->Trace(frame);
visitor->Trace(encodeOpts);
visitor->Trace(resolver);
}
template class EncoderBase<VideoEncoderTraits>;
} // namespace blink
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ENCODER_BASE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ENCODER_BASE_H_
#include <memory>
#include "media/base/media_log.h"
#include "media/base/status.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_webcodecs_error_callback.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_logger.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/context_lifecycle_observer.h"
namespace blink {
class ExceptionState;
enum class DOMExceptionCode;
class Visitor;
template <typename Traits>
class MODULES_EXPORT EncoderBase
: public ScriptWrappable,
public ActiveScriptWrappable<EncoderBase<Traits>>,
public ExecutionContextLifecycleObserver {
public:
using InitType = typename Traits::Init;
using ConfigType = typename Traits::Config;
using InternalConfigType = typename Traits::InternalConfig;
using FrameType = typename Traits::Frame;
using EncodeOptionsType = typename Traits::EncodeOptions;
using OutputChunkType = typename Traits::OutputChunk;
using OutputCallbackType = typename Traits::OutputCallback;
using MediaEncoderType = typename Traits::MediaEncoder;
EncoderBase(ScriptState*, const InitType*, ExceptionState&);
~EncoderBase() override;
// *_encoder.idl implementation.
virtual int32_t encodeQueueSize();
virtual void configure(const ConfigType*, ExceptionState&);
virtual void encode(FrameType* frame,
const EncodeOptionsType* opts,
ExceptionState& exception_state);
virtual ScriptPromise flush(ExceptionState&);
virtual void reset(ExceptionState&);
virtual void close(ExceptionState&);
virtual String state() { return state_; }
// ExecutionContextLifecycleObserver override.
void ContextDestroyed() override;
// ScriptWrappable override.
bool HasPendingActivity() const override;
// GarbageCollected override.
void Trace(Visitor*) const override;
protected:
struct Request final : public GarbageCollected<Request> {
enum class Type {
// Configure an encoder from scratch, possibly replacing the existing one.
kConfigure,
// Adjust options in the already configured encoder.
kReconfigure,
kEncode,
kFlush,
};
void Trace(Visitor*) const;
Type type;
// Current value of EncoderBase.reset_count_ when request was created.
uint32_t reset_count = 0;
Member<FrameType> frame; // used by kEncode
Member<const EncodeOptionsType> encodeOpts; // used by kEncode
Member<ScriptPromiseResolver> resolver; // used by kFlush
};
virtual void HandleError(DOMException* ex);
virtual void EnqueueRequest(Request* request);
virtual void ProcessRequests();
virtual void ProcessEncode(Request* request) = 0;
virtual void ProcessConfigure(Request* request) = 0;
virtual void ProcessReconfigure(Request* request) = 0;
virtual void ProcessFlush(Request* request) = 0;
virtual void ResetInternal();
virtual bool CanReconfigure(InternalConfigType& original_config,
InternalConfigType& new_config) = 0;
virtual InternalConfigType* ParseConfig(const ConfigType*,
ExceptionState&) = 0;
virtual bool VerifyCodecSupport(InternalConfigType*, ExceptionState&) = 0;
virtual FrameType* CloneFrame(FrameType*, ExecutionContext*) = 0;
std::unique_ptr<CodecLogger> logger_;
std::unique_ptr<MediaEncoderType> media_encoder_;
V8CodecState state_;
Member<InternalConfigType> active_config_;
Member<ScriptState> script_state_;
Member<OutputCallbackType> output_callback_;
Member<V8WebCodecsErrorCallback> error_callback_;
HeapDeque<Member<Request>> requests_;
int32_t requested_encodes_ = 0;
// How many times reset() was called on the encoder. It's used to decide
// when a callback needs to be dismissed because reset() was called between
// an operation and its callback.
uint32_t reset_count_ = 0;
// Some kConfigure and kFlush requests can't be executed in parallel with
// kEncode. This flag stops processing of new requests in the requests_ queue
// till the current requests are finished.
bool stall_request_processing_ = false;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_ENCODER_BASE_H_
......@@ -176,6 +176,11 @@ std::pair<SkColorType, GrGLenum> GetSkiaAndGlColorTypesForPlane(
} // namespace
// static
const char* VideoEncoderTraits::GetNameForDevTools() {
return "VideoEncoder(WebCodecs)";
}
// static
VideoEncoder* VideoEncoder::Create(ScriptState* script_state,
const VideoEncoderInit* init,
......@@ -187,40 +192,12 @@ VideoEncoder* VideoEncoder::Create(ScriptState* script_state,
VideoEncoder::VideoEncoder(ScriptState* script_state,
const VideoEncoderInit* init,
ExceptionState& exception_state)
: ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
state_(V8CodecState::Enum::kUnconfigured),
script_state_(script_state) {
: Base(script_state, init, exception_state) {
UseCounter::Count(ExecutionContext::From(script_state),
WebFeature::kWebCodecs);
// TODO(crbug.com/1151005): Use a real MediaLog in worker contexts too.
if (IsMainThread()) {
logger_ = std::make_unique<CodecLogger>(
GetExecutionContext(), Thread::MainThread()->GetTaskRunner());
} else {
// This will create a logger backed by a NullMediaLog, which does nothing.
logger_ = std::make_unique<CodecLogger>();
}
media::MediaLog* log = logger_->log();
log->SetProperty<media::MediaLogProperty::kFrameTitle>(
std::string("VideoEncoder(WebCodecs)"));
log->SetProperty<media::MediaLogProperty::kFrameUrl>(
GetExecutionContext()->Url().GetString().Ascii());
output_callback_ = init->output();
if (init->hasError())
error_callback_ = init->error();
}
VideoEncoder::~VideoEncoder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
int32_t VideoEncoder::encodeQueueSize() {
return requested_encodes_;
}
VideoEncoder::~VideoEncoder() = default;
VideoEncoder::ParsedConfig* VideoEncoder::ParseConfig(
const VideoEncoderConfig* config,
......@@ -336,6 +313,11 @@ bool VideoEncoder::VerifyCodecSupport(ParsedConfig* config,
return true;
}
VideoFrame* VideoEncoder::CloneFrame(VideoFrame* frame,
ExecutionContext* context) {
return frame->CloneFromNative(context);
}
void VideoEncoder::UpdateEncoderLog(std::string encoder_name,
bool is_hw_accelerated) {
// TODO(https://crbug.com/1139089) : Add encoder properties.
......@@ -453,188 +435,6 @@ bool VideoEncoder::CanReconfigure(ParsedConfig& original_config,
original_config.acc_pref == new_config.acc_pref;
}
void VideoEncoder::configure(const VideoEncoderConfig* config,
ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "configure", exception_state))
return;
auto* parsed_config = ParseConfig(config, exception_state);
if (!parsed_config) {
DCHECK(exception_state.HadException());
return;
}
if (!VerifyCodecSupport(parsed_config, exception_state)) {
DCHECK(exception_state.HadException());
return;
}
Request* request = MakeGarbageCollected<Request>();
request->reset_count = reset_count_;
if (media_encoder_ && active_config_ &&
state_.AsEnum() == V8CodecState::Enum::kConfigured &&
CanReconfigure(*active_config_, *parsed_config)) {
request->type = Request::Type::kReconfigure;
} else {
state_ = V8CodecState(V8CodecState::Enum::kConfigured);
request->type = Request::Type::kConfigure;
}
active_config_ = parsed_config;
EnqueueRequest(request);
}
void VideoEncoder::encode(VideoFrame* frame,
const VideoEncoderEncodeOptions* opts,
ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "encode", exception_state))
return;
if (ThrowIfCodecStateUnconfigured(state_, "encode", exception_state))
return;
DCHECK(active_config_);
auto* context = GetExecutionContext();
if (!context) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Context is destroyed.");
return;
}
// This will fail if |frame| is already destroyed.
auto* internal_frame = frame->CloneFromNative(context);
if (!internal_frame) {
exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
"Cannot encode destroyed frame.");
return;
}
// At this point, we have "consumed" the frame, and will destroy the clone
// in ProcessEncode().
frame->destroy();
Request* request = MakeGarbageCollected<Request>();
request->reset_count = reset_count_;
request->type = Request::Type::kEncode;
request->frame = internal_frame;
request->encodeOpts = opts;
++requested_encodes_;
EnqueueRequest(request);
}
void VideoEncoder::close(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "close", exception_state))
return;
state_ = V8CodecState(V8CodecState::Enum::kClosed);
ResetInternal();
media_encoder_.reset();
output_callback_.Clear();
error_callback_.Clear();
}
ScriptPromise VideoEncoder::flush(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "flush", exception_state))
return ScriptPromise();
if (ThrowIfCodecStateUnconfigured(state_, "flush", exception_state))
return ScriptPromise();
Request* request = MakeGarbageCollected<Request>();
request->resolver =
MakeGarbageCollected<ScriptPromiseResolver>(script_state_);
request->reset_count = reset_count_;
request->type = Request::Type::kFlush;
EnqueueRequest(request);
return request->resolver->Promise();
}
void VideoEncoder::reset(ExceptionState& exception_state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "reset", exception_state))
return;
state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
ResetInternal();
media_encoder_.reset();
}
void VideoEncoder::ResetInternal() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
reset_count_++;
while (!requests_.empty()) {
Request* pending_req = requests_.TakeFirst();
DCHECK(pending_req);
if (pending_req->resolver)
pending_req->resolver.Release()->Resolve();
if (pending_req->frame)
pending_req->frame.Release()->destroy();
}
stall_request_processing_ = false;
}
void VideoEncoder::HandleError(DOMException* ex) {
if (state_.AsEnum() == V8CodecState::Enum::kClosed)
return;
// Save a temp before we clear the callback.
V8WebCodecsErrorCallback* error_callback = error_callback_.Get();
state_ = V8CodecState(V8CodecState::Enum::kClosed);
ResetInternal();
// Errors are permanent. Shut everything down.
error_callback_.Clear();
media_encoder_.reset();
output_callback_.Clear();
// Prevent further logging.
logger_->Neuter();
if (!script_state_->ContextIsValid() || !error_callback)
return;
ScriptState::Scope scope(script_state_);
error_callback->InvokeAndReportException(nullptr, ex);
}
void VideoEncoder::EnqueueRequest(Request* request) {
requests_.push_back(request);
ProcessRequests();
}
void VideoEncoder::ProcessRequests() {
while (!requests_.empty() && !stall_request_processing_) {
Request* request = requests_.TakeFirst();
DCHECK(request);
switch (request->type) {
case Request::Type::kConfigure:
ProcessConfigure(request);
break;
case Request::Type::kReconfigure:
ProcessReconfigure(request);
break;
case Request::Type::kEncode:
ProcessEncode(request);
break;
case Request::Type::kFlush:
ProcessFlush(request);
break;
default:
NOTREACHED();
}
}
}
void VideoEncoder::ProcessEncode(Request* request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
......@@ -859,14 +659,6 @@ void VideoEncoder::CallOutputCallback(
output_callback_->InvokeAndReportException(nullptr, chunk, decoder_config);
}
void VideoEncoder::ContextDestroyed() {
logger_->Neuter();
}
bool VideoEncoder::HasPendingActivity() const {
return stall_request_processing_ || !requests_.empty();
}
// This function reads pixel data from textures associated with |txt_frame|
// and creates a new CPU memory backed frame. It's needed because
// existing video encoders can't handle texture backed frames.
......@@ -973,19 +765,4 @@ VideoEncoder::ReadbackTextureBackedFrameToMemory(
return result;
}
void VideoEncoder::Trace(Visitor* visitor) const {
visitor->Trace(active_config_);
visitor->Trace(script_state_);
visitor->Trace(output_callback_);
visitor->Trace(error_callback_);
visitor->Trace(requests_);
ScriptWrappable::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
}
void VideoEncoder::Request::Trace(Visitor* visitor) const {
visitor->Trace(frame);
visitor->Trace(encodeOpts);
visitor->Trace(resolver);
}
} // namespace blink
......@@ -8,22 +8,13 @@
#include <memory>
#include "base/optional.h"
#include "media/base/media_log.h"
#include "media/base/status.h"
#include "media/base/video_codecs.h"
#include "media/base/video_color_space.h"
#include "media/base/video_encoder.h"
#include "media/base/video_frame_pool.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_output_callback.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_webcodecs_error_callback.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_logger.h"
#include "third_party/blink/renderer/modules/webcodecs/encoder_base.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/context_lifecycle_observer.h"
namespace media {
class GpuVideoAcceleratorFactories;
......@@ -33,56 +24,13 @@ struct VideoEncoderOutput;
namespace blink {
class ExceptionState;
enum class DOMExceptionCode;
class VideoEncoderConfig;
class VideoEncoderInit;
class VideoEncoderEncodeOptions;
class Visitor;
class MODULES_EXPORT VideoEncoder final
: public ScriptWrappable,
public ActiveScriptWrappable<VideoEncoder>,
public ExecutionContextLifecycleObserver {
DEFINE_WRAPPERTYPEINFO();
class MODULES_EXPORT VideoEncoderTraits {
public:
static VideoEncoder* Create(ScriptState*,
const VideoEncoderInit*,
ExceptionState&);
VideoEncoder(ScriptState*, const VideoEncoderInit*, ExceptionState&);
~VideoEncoder() override;
// video_encoder.idl implementation.
int32_t encodeQueueSize();
void encode(VideoFrame* frame,
const VideoEncoderEncodeOptions*,
ExceptionState&);
void configure(const VideoEncoderConfig*, ExceptionState&);
ScriptPromise flush(ExceptionState&);
void reset(ExceptionState&);
void close(ExceptionState&);
String state() { return state_; }
// ExecutionContextLifecycleObserver override.
void ContextDestroyed() override;
// ScriptWrappable override.
bool HasPendingActivity() const override;
// GarbageCollected override.
void Trace(Visitor*) const override;
private:
enum class AccelerationPreference { kAllow, kDeny, kRequire };
// TODO(ezemtsov): Replace this with a {Audio|Video}EncoderConfig.
struct ParsedConfig final : public GarbageCollected<ParsedConfig> {
media::VideoCodec codec;
media::VideoCodecProfile profile;
......@@ -97,48 +45,54 @@ class MODULES_EXPORT VideoEncoder final
void Trace(Visitor*) const {}
};
struct Request final : public GarbageCollected<Request> {
enum class Type {
// Configure an encoder from scratch, possibly replacing the existing one.
kConfigure,
// Adjust options in the already configured encoder.
kReconfigure,
kEncode,
kFlush,
};
void Trace(Visitor*) const;
Type type;
// Current value of VideoEncoder.reset_count_ when request was created.
uint32_t reset_count = 0;
Member<VideoFrame> frame; // used by kEncode
Member<const VideoEncoderEncodeOptions> encodeOpts; // used by kEncode
Member<ScriptPromiseResolver> resolver; // used by kFlush
};
using Init = VideoEncoderInit;
using Config = VideoEncoderConfig;
using InternalConfig = ParsedConfig;
using Frame = VideoFrame;
using EncodeOptions = VideoEncoderEncodeOptions;
using OutputChunk = EncodedVideoChunk;
using OutputCallback = V8VideoEncoderOutputCallback;
using MediaEncoder = media::VideoEncoder;
// Can't be a virtual method, because it's used from base ctor.
static const char* GetNameForDevTools();
};
class MODULES_EXPORT VideoEncoder final
: public EncoderBase<VideoEncoderTraits> {
DEFINE_WRAPPERTYPEINFO();
public:
static VideoEncoder* Create(ScriptState*,
const VideoEncoderInit*,
ExceptionState&);
VideoEncoder(ScriptState*, const VideoEncoderInit*, ExceptionState&);
~VideoEncoder() override;
private:
using Base = EncoderBase<VideoEncoderTraits>;
using AccelerationPreference = VideoEncoderTraits::AccelerationPreference;
using ParsedConfig = VideoEncoderTraits::ParsedConfig;
void CallOutputCallback(
ParsedConfig* active_config,
uint32_t reset_count,
media::VideoEncoderOutput output,
base::Optional<media::VideoEncoder::CodecDescription> codec_desc);
void HandleError(DOMException* ex);
void EnqueueRequest(Request* request);
void ProcessRequests();
void ProcessEncode(Request* request);
void ProcessConfigure(Request* request);
void ProcessReconfigure(Request* request);
void ProcessFlush(Request* request);
void ProcessEncode(Request* request) override;
void ProcessConfigure(Request* request) override;
void ProcessReconfigure(Request* request) override;
void ProcessFlush(Request* request) override;
void UpdateEncoderLog(std::string encoder_name, bool is_hw_accelerated);
void ResetInternal();
ScriptPromiseResolver* MakePromise();
void OnReceivedGpuFactories(Request*, media::GpuVideoAcceleratorFactories*);
ParsedConfig* ParseConfig(const VideoEncoderConfig*, ExceptionState&);
bool VerifyCodecSupport(ParsedConfig*, ExceptionState&);
ParsedConfig* ParseConfig(const VideoEncoderConfig*,
ExceptionState&) override;
bool VerifyCodecSupport(ParsedConfig*, ExceptionState&) override;
VideoFrame* CloneFrame(VideoFrame*, ExecutionContext*) override;
void CreateAndInitializeEncoderWithoutAcceleration(Request* request);
void CreateAndInitializeEncoderOnEncoderSupportKnown(
Request* request,
......@@ -146,34 +100,12 @@ class MODULES_EXPORT VideoEncoder final
std::unique_ptr<media::VideoEncoder> CreateMediaVideoEncoder(
const ParsedConfig& config,
media::GpuVideoAcceleratorFactories* gpu_factories);
bool CanReconfigure(ParsedConfig& original_config, ParsedConfig& new_config);
bool CanReconfigure(ParsedConfig& original_config,
ParsedConfig& new_config) override;
scoped_refptr<media::VideoFrame> ReadbackTextureBackedFrameToMemory(
scoped_refptr<media::VideoFrame> txt_frame);
std::unique_ptr<CodecLogger> logger_;
std::unique_ptr<media::VideoEncoder> media_encoder_;
V8CodecState state_;
Member<ParsedConfig> active_config_;
Member<ScriptState> script_state_;
Member<V8VideoEncoderOutputCallback> output_callback_;
Member<V8WebCodecsErrorCallback> error_callback_;
HeapDeque<Member<Request>> requests_;
int32_t requested_encodes_ = 0;
// How many times reset() was called on the encoder. It's used to decide
// when a callback needs to be dismissed because reset() was called between
// an operation and its callback.
uint32_t reset_count_ = 0;
// Some kConfigure and kFlush requests can't be executed in parallel with
// kEncode. This flag stops processing of new requests in the requests_ queue
// till the current requests are finished.
bool stall_request_processing_ = false;
media::VideoFramePool readback_frame_pool_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace blink
......
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