blob: d7c8f8c6e57864ea87ff7a6859744343607336d4 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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/decoder_template.h"
#include <limits>
#include <utility>
#include <vector>
#include "base/atomic_sequence_num.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"
#include "media/base/decoder_status.h"
#include "media/base/media_util.h"
#include "media/media_buildflags.h"
#include "media/video/gpu_video_accelerator_factories.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_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_output_callback.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_init.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/execution_context/agent.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/modules/webcodecs/audio_data.h"
#include "third_party/blink/renderer/modules/webcodecs/audio_decoder.h"
#include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
#include "third_party/blink/renderer/modules/webcodecs/gpu_factories_retriever.h"
#include "third_party/blink/renderer/modules/webcodecs/video_decoder.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.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/heap/cross_thread_handle.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
namespace blink {
namespace {
constexpr const char kCategory[] = "media";
base::AtomicSequenceNumber g_sequence_num_for_counters;
} // namespace
// static
template <typename Traits>
const CodecTraceNames* DecoderTemplate<Traits>::GetTraceNames() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(CodecTraceNames, trace_names,
(Traits::GetName()));
return &trace_names;
}
template <typename Traits>
DecoderTemplate<Traits>::DecoderTemplate(ScriptState* script_state,
const InitType* init,
ExceptionState& exception_state)
: ActiveScriptWrappable<DecoderTemplate<Traits>>({}),
ReclaimableCodec(ReclaimableCodec::CodecType::kDecoder,
ExecutionContext::From(script_state)),
script_state_(script_state),
state_(V8CodecState::Enum::kUnconfigured),
trace_counter_id_(g_sequence_num_for_counters.GetNext()) {
DVLOG(1) << __func__;
DCHECK(init->hasOutput());
DCHECK(init->hasError());
ExecutionContext* context = GetExecutionContext();
DCHECK(context);
main_thread_task_runner_ =
context->GetTaskRunner(TaskType::kInternalMediaRealTime);
logger_ = std::make_unique<CodecLogger<media::DecoderStatus>>(
context, main_thread_task_runner_);
logger_->log()->SetProperty<media::MediaLogProperty::kFrameUrl>(
context->Url().GetString().Ascii());
output_cb_ = init->output();
error_cb_ = init->error();
}
template <typename Traits>
DecoderTemplate<Traits>::~DecoderTemplate() {
DVLOG(1) << __func__;
base::UmaHistogramSparse(
String::Format("Blink.WebCodecs.%s.FinalStatus", Traits::GetName())
.Ascii()
.c_str(),
static_cast<int>(logger_->status_code()));
}
template <typename Traits>
uint32_t DecoderTemplate<Traits>::decodeQueueSize() {
return num_pending_decodes_;
}
template <typename Traits>
bool DecoderTemplate<Traits>::IsClosed() {
return state_ == V8CodecState::Enum::kClosed;
}
template <typename Traits>
HardwarePreference DecoderTemplate<Traits>::GetHardwarePreference(
const ConfigType&) {
return HardwarePreference::kNoPreference;
}
template <typename Traits>
bool DecoderTemplate<Traits>::GetLowDelayPreference(const ConfigType&) {
return false;
}
template <typename Traits>
void DecoderTemplate<Traits>::SetHardwarePreference(HardwarePreference) {}
template <typename Traits>
void DecoderTemplate<Traits>::OnActiveConfigChanged(const MediaConfigType&) {}
template <typename Traits>
void DecoderTemplate<Traits>::configure(const ConfigType* config,
ExceptionState& exception_state) {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "decode", exception_state))
return;
String js_error_message;
if (!IsValidConfig(*config, &js_error_message)) {
exception_state.ThrowTypeError(js_error_message);
return;
}
std::optional<MediaConfigType> media_config =
MakeMediaConfig(*config, &js_error_message);
// Audio/VideoDecoder don't yet support encryption.
if (media_config && media_config->is_encrypted()) {
js_error_message = "Encrypted content is not supported";
media_config = std::nullopt;
}
MarkCodecActive();
state_ = V8CodecState(V8CodecState::Enum::kConfigured);
require_key_frame_ = true;
Request* request = MakeGarbageCollected<Request>();
request->type = Request::Type::kConfigure;
if (media_config.has_value()) {
request->media_config = std::make_unique<MediaConfigType>(*media_config);
} else {
request->js_error_message = js_error_message;
}
request->reset_generation = reset_generation_;
request->hw_pref = GetHardwarePreference(*config);
request->low_delay = GetLowDelayPreference(*config);
requests_.push_back(request);
ProcessRequests();
}
template <typename Traits>
void DecoderTemplate<Traits>::decode(const InputType* chunk,
ExceptionState& exception_state) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "decode", exception_state))
return;
if (ThrowIfCodecStateUnconfigured(state_, "decode", exception_state))
return;
Request* request = MakeGarbageCollected<Request>();
request->type = Request::Type::kDecode;
request->reset_generation = reset_generation_;
auto status_or_buffer = MakeInput(*chunk, require_key_frame_);
if (status_or_buffer.has_value()) {
request->decoder_buffer = std::move(status_or_buffer).value();
require_key_frame_ = false;
} else {
request->status = std::move(status_or_buffer).error();
if (request->status == media::DecoderStatus::Codes::kKeyFrameRequired) {
exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
String(request->status.message()));
return;
}
}
MarkCodecActive();
requests_.push_back(request);
++num_pending_decodes_;
ProcessRequests();
}
template <typename Traits>
ScriptPromise<IDLUndefined> DecoderTemplate<Traits>::flush(
ExceptionState& exception_state) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "flush", exception_state))
return EmptyPromise();
if (ThrowIfCodecStateUnconfigured(state_, "flush", exception_state))
return EmptyPromise();
MarkCodecActive();
require_key_frame_ = true;
Request* request = MakeGarbageCollected<Request>();
request->type = Request::Type::kFlush;
auto* resolver =
MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(script_state_);
request->resolver = resolver;
request->reset_generation = reset_generation_;
requests_.push_back(request);
ProcessRequests();
return resolver->Promise();
}
template <typename Traits>
void DecoderTemplate<Traits>::reset(ExceptionState& exception_state) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "reset", exception_state))
return;
MarkCodecActive();
ResetAlgorithm();
}
template <typename Traits>
void DecoderTemplate<Traits>::close(ExceptionState& exception_state) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ThrowIfCodecStateClosed(state_, "close", exception_state))
return;
Shutdown();
}
template <typename Traits>
void DecoderTemplate<Traits>::ProcessRequests() {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!IsClosed());
while (!pending_request_ && !requests_.empty()) {
Request* request = requests_.front();
// Skip processing for requests that are canceled by a recent reset().
if (MaybeAbortRequest(request)) {
requests_.pop_front();
continue;
}
TraceQueueSizes();
DCHECK_EQ(request->reset_generation, reset_generation_);
switch (request->type) {
case Request::Type::kConfigure:
if (!ProcessConfigureRequest(request))
return;
break;
case Request::Type::kDecode:
if (!ProcessDecodeRequest(request))
return;
break;
case Request::Type::kFlush:
if (!ProcessFlushRequest(request))
return;
break;
case Request::Type::kReset:
if (!ProcessResetRequest(request))
return;
break;
}
requests_.pop_front();
}
TraceQueueSizes();
}
template <typename Traits>
bool DecoderTemplate<Traits>::ProcessConfigureRequest(Request* request) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!IsClosed());
DCHECK(!pending_request_);
DCHECK_EQ(request->type, Request::Type::kConfigure);
if (decoder() &&
pending_decodes_.size() + 1 >
static_cast<size_t>(Traits::GetMaxDecodeRequests(*decoder()))) {
// Try again after OnDecodeDone().
return false;
}
// TODO(sandersd): Record this configuration as pending but don't apply it
// until there is a decode request.
pending_request_ = request;
pending_request_->StartTracing();
if (!request->media_config) {
main_thread_task_runner_->PostTask(
FROM_HERE,
BindOnce(&DecoderTemplate<Traits>::Shutdown, WrapWeakPersistent(this),
WrapPersistent(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError,
request->js_error_message))));
return false;
}
if (gpu_factories_.has_value()) {
ContinueConfigureWithGpuFactories(request, gpu_factories_.value());
} else if (Traits::kNeedsGpuFactories) {
RetrieveGpuFactoriesWithKnownDecoderSupport(CrossThreadBindOnce(
&DecoderTemplate<Traits>::ContinueConfigureWithGpuFactories,
MakeUnwrappingCrossThreadHandle(this),
MakeUnwrappingCrossThreadHandle(request)));
} else {
ContinueConfigureWithGpuFactories(request, nullptr);
}
return true;
}
template <typename Traits>
void DecoderTemplate<Traits>::ContinueConfigureWithGpuFactories(
Request* request,
media::GpuVideoAcceleratorFactories* gpu_factories) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(request);
DCHECK_EQ(request->type, Request::Type::kConfigure);
if (IsClosed()) {
return;
}
gpu_factories_ = gpu_factories;
if (MaybeAbortRequest(request)) {
DCHECK_EQ(request, pending_request_);
pending_request_.Release()->EndTracing();
ProcessRequests();
return;
}
if (!decoder()) {
decoder_ = Traits::CreateDecoder(*ExecutionContext::From(script_state_),
gpu_factories_.value(), logger_->log());
if (!decoder()) {
Shutdown(MakeOperationError(
"Internal error: Could not create decoder.",
media::DecoderStatus::Codes::kFailedToCreateDecoder));
return;
}
SetHardwarePreference(request->hw_pref.value());
// Processing continues in OnInitializeDone().
// Note: OnInitializeDone() must not call ProcessRequests() reentrantly,
// which can happen if InitializeDecoder() calls it synchronously.
initializing_sync_ = true;
Traits::InitializeDecoder(
*decoder(), request->low_delay.value(), *request->media_config,
BindOnce(&DecoderTemplate::OnInitializeDone, WrapWeakPersistent(this)),
blink::BindRepeating(&DecoderTemplate::OnOutput,
WrapWeakPersistent(this), reset_generation_));
initializing_sync_ = false;
return;
}
// Processing continues in OnFlushDone().
decoder()->Decode(
media::DecoderBuffer::CreateEOSBuffer(),
BindOnce(&DecoderTemplate::OnFlushDone, WrapWeakPersistent(this)));
}
template <typename Traits>
bool DecoderTemplate<Traits>::ProcessDecodeRequest(Request* request) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
DCHECK(!pending_request_);
DCHECK_EQ(request->type, Request::Type::kDecode);
DCHECK_GT(num_pending_decodes_, 0u);
if (!decoder()) {
Shutdown(MakeEncodingError("Decoding error: no decoder found.",
media::DecoderStatus::Codes::kNotInitialized));
return false;
}
if (pending_decodes_.size() + 1 >
static_cast<size_t>(Traits::GetMaxDecodeRequests(*decoder()))) {
// Try again after OnDecodeDone().
return false;
}
// The request may be invalid, if so report that now.
if (!request->decoder_buffer || request->decoder_buffer->empty()) {
if (request->status.is_ok()) {
Shutdown(MakeEncodingError("Null or empty decoder buffer.",
media::DecoderStatus::Codes::kFailed));
} else {
Shutdown(MakeEncodingError("Decoder error.", request->status));
}
return false;
}
// Submit for decoding.
//
// |pending_decode_id_| must not be 0 nor max because it HashMap reserves
// these values for "emtpy" and "deleted".
while (++pending_decode_id_ == 0 ||
pending_decode_id_ == std::numeric_limits<uint32_t>::max() ||
pending_decodes_.Contains(pending_decode_id_))
;
pending_decodes_.Set(pending_decode_id_, request);
--num_pending_decodes_;
ScheduleDequeueEvent();
if (media::MediaTraceIsEnabled()) {
request->decode_trace = std::make_unique<media::ScopedDecodeTrace>(
GetTraceNames()->decode.c_str(), *request->decoder_buffer);
}
decoder()->Decode(std::move(request->decoder_buffer),
BindOnce(&DecoderTemplate::OnDecodeDone,
WrapWeakPersistent(this), pending_decode_id_));
return true;
}
template <typename Traits>
bool DecoderTemplate<Traits>::ProcessFlushRequest(Request* request) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!IsClosed());
DCHECK(!pending_request_);
DCHECK_EQ(request->type, Request::Type::kFlush);
DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
// flush() can only be called when state = "configured", in which case we
// should always have a decoder.
DCHECK(decoder());
if (pending_decodes_.size() + 1 >
static_cast<size_t>(Traits::GetMaxDecodeRequests(*decoder()))) {
// Try again after OnDecodeDone().
return false;
}
// Processing continues in OnFlushDone().
pending_request_ = request;
pending_request_->StartTracing();
decoder()->Decode(
media::DecoderBuffer::CreateEOSBuffer(),
BindOnce(&DecoderTemplate::OnFlushDone, WrapWeakPersistent(this)));
return true;
}
template <typename Traits>
bool DecoderTemplate<Traits>::ProcessResetRequest(Request* request) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!IsClosed());
DCHECK(!pending_request_);
DCHECK_EQ(request->type, Request::Type::kReset);
DCHECK_GT(reset_generation_, 0u);
// Signal [[codec implementation]] to cease producing output for the previous
// configuration.
if (decoder()) {
pending_request_ = request;
pending_request_->StartTracing();
// Processing continues in OnResetDone().
decoder()->Reset(
BindOnce(&DecoderTemplate::OnResetDone, WrapWeakPersistent(this)));
}
return true;
}
template <typename Traits>
void DecoderTemplate<Traits>::Shutdown(DOMException* exception) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsClosed())
return;
TRACE_EVENT1(kCategory, GetTraceNames()->shutdown.c_str(), "has_exception",
!!exception);
shutting_down_ = true;
shutting_down_due_to_error_ = exception;
// Abort pending work (otherwise it will never complete)
if (pending_request_) {
if (pending_request_->resolver) {
pending_request_->resolver.Release()->Reject(
exception
? exception
: MakeGarbageCollected<DOMException>(
DOMExceptionCode::kAbortError, "Aborted due to close()"));
}
pending_request_.Release()->EndTracing(/*shutting_down=*/true);
}
// Abort all upcoming work.
ResetAlgorithm();
ReleaseCodecPressure();
// Store the error callback so that we can use it after clearing state.
V8WebCodecsErrorCallback* error_cb = error_cb_.Get();
// Prevent any new public API calls during teardown.
// This should make it safe to call into JS synchronously.
state_ = V8CodecState(V8CodecState::Enum::kClosed);
// Prevent any late callbacks running.
output_cb_.Release();
error_cb_.Release();
// Prevent any further logging from being reported.
logger_->Neuter();
// Clear decoding and JS-visible queue state. Use DeleteSoon() to avoid
// deleting decoder_ when its callback (e.g. OnDecodeDone()) may be below us
// in the stack.
main_thread_task_runner_->DeleteSoon(FROM_HERE, std::move(decoder_));
if (pending_request_) {
// This request was added as part of calling ResetAlgorithm above. However,
// OnResetDone() will never execute, since we are now in a kClosed state,
// and |decoder_| has been reset.
DCHECK_EQ(pending_request_->type, Request::Type::kReset);
pending_request_.Release()->EndTracing(/*shutting_down=*/true);
}
bool trace_enabled = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(kCategory, &trace_enabled);
if (trace_enabled) {
for (auto& pending_decode : pending_decodes_)
pending_decode.value->decode_trace.reset();
}
pending_decodes_.clear();
num_pending_decodes_ = 0;
ScheduleDequeueEvent();
if (exception) {
error_cb->InvokeAndReportException(nullptr, exception);
}
}
template <typename Traits>
void DecoderTemplate<Traits>::ResetAlgorithm() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (state_ == V8CodecState::Enum::kUnconfigured)
return;
state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
// Increment reset counter to cause older pending requests to be rejected. See
// ProcessRequests().
reset_generation_++;
// Any previous pending decode will be filtered by ProcessRequests(). Reset
// the count immediately to report the correct value in decodeQueueSize().
num_pending_decodes_ = 0;
ScheduleDequeueEvent();
// Since configure is always required after reset we can drop any cached
// configuration.
active_config_.reset();
Request* request = MakeGarbageCollected<Request>();
request->type = Request::Type::kReset;
request->reset_generation = reset_generation_;
requests_.push_back(request);
ProcessRequests();
}
template <typename Traits>
void DecoderTemplate<Traits>::OnFlushDone(media::DecoderStatus status) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsClosed())
return;
DCHECK(pending_request_);
DCHECK(pending_request_->type == Request::Type::kConfigure ||
pending_request_->type == Request::Type::kFlush);
if (!status.is_ok()) {
Shutdown(MakeEncodingError("Error during flush.", status));
return;
}
// If reset() has been called during the Flush(), we can skip reinitialization
// since the client is required to do so manually.
const bool is_flush = pending_request_->type == Request::Type::kFlush;
if (is_flush && MaybeAbortRequest(pending_request_)) {
pending_request_.Release()->EndTracing();
ProcessRequests();
return;
}
if (!is_flush)
SetHardwarePreference(pending_request_->hw_pref.value());
// Processing continues in OnInitializeDone().
Traits::InitializeDecoder(
*decoder(), is_flush ? low_delay_ : pending_request_->low_delay.value(),
is_flush ? *active_config_ : *pending_request_->media_config,
BindOnce(&DecoderTemplate::OnInitializeDone, WrapWeakPersistent(this)),
blink::BindRepeating(&DecoderTemplate::OnOutput, WrapWeakPersistent(this),
reset_generation_));
}
template <typename Traits>
void DecoderTemplate<Traits>::OnInitializeDone(media::DecoderStatus status) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsClosed())
return;
DCHECK(pending_request_);
DCHECK(pending_request_->type == Request::Type::kConfigure ||
pending_request_->type == Request::Type::kFlush);
const bool is_flush = pending_request_->type == Request::Type::kFlush;
if (!status.is_ok()) {
std::string error_message;
if (is_flush) {
error_message = "Error during initialize after flush.";
} else if (status.code() ==
media::DecoderStatus::Codes::kUnsupportedConfig) {
error_message =
"Unsupported configuration. Check isConfigSupported() prior to "
"calling configure().";
} else {
error_message = "Decoder initialization error.";
}
Shutdown(MakeOperationError(error_message, status));
return;
}
if (is_flush) {
pending_request_->resolver.Release()->Resolve();
} else {
Traits::UpdateDecoderLog(*decoder(), *pending_request_->media_config,
logger_->log());
if (decoder()->IsPlatformDecoder())
ApplyCodecPressure();
else
ReleaseCodecPressure();
low_delay_ = pending_request_->low_delay.value();
active_config_ = std::move(pending_request_->media_config);
OnActiveConfigChanged(*active_config_);
}
pending_request_.Release()->EndTracing();
if (!initializing_sync_)
ProcessRequests();
else
DCHECK(!is_flush);
}
template <typename Traits>
void DecoderTemplate<Traits>::OnDecodeDone(uint32_t id,
media::DecoderStatus status) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsClosed())
return;
auto it = pending_decodes_.find(id);
if (it != pending_decodes_.end()) {
if (it->value->decode_trace)
it->value->decode_trace->EndTrace(status);
pending_decodes_.erase(it);
}
if (!status.is_ok() &&
status.code() != media::DecoderStatus::Codes::kAborted) {
Shutdown(MakeEncodingError("Decoding error.", std::move(status)));
return;
}
ProcessRequests();
}
template <typename Traits>
void DecoderTemplate<Traits>::OnResetDone() {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsClosed())
return;
DCHECK(pending_request_);
DCHECK_EQ(pending_request_->type, Request::Type::kReset);
pending_request_.Release()->EndTracing();
ProcessRequests();
}
template <typename Traits>
void DecoderTemplate<Traits>::OnOutput(uint32_t reset_generation,
scoped_refptr<MediaOutputType> output) {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Suppress outputs belonging to an earlier reset_generation.
if (reset_generation != reset_generation_)
return;
if (state_.AsEnum() != V8CodecState::Enum::kConfigured)
return;
auto* context = GetExecutionContext();
if (!context)
return;
auto output_or_error = MakeOutput(std::move(output), context);
if (!output_or_error.has_value()) {
Shutdown(MakeEncodingError("Error creating output from decoded data",
std::move(output_or_error).error()));
return;
}
OutputType* blink_output = std::move(output_or_error).value();
TRACE_EVENT_BEGIN1(kCategory, GetTraceNames()->output.c_str(), "timestamp",
blink_output->timestamp());
output_cb_->InvokeAndReportException(nullptr, blink_output);
TRACE_EVENT_END0(kCategory, GetTraceNames()->output.c_str());
MarkCodecActive();
}
template <typename Traits>
void DecoderTemplate<Traits>::TraceQueueSizes() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_COUNTER_ID2(kCategory, GetTraceNames()->requests_counter.c_str(),
trace_counter_id_, "decodes", num_pending_decodes_, "other",
requests_.size() - num_pending_decodes_);
}
template <typename Traits>
void DecoderTemplate<Traits>::DispatchDequeueEvent(Event* event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
probe::AsyncTask async_task(GetExecutionContext(),
event->async_task_context());
dequeue_event_pending_ = false;
DispatchEvent(*event);
}
template <typename Traits>
void DecoderTemplate<Traits>::ScheduleDequeueEvent() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (dequeue_event_pending_)
return;
dequeue_event_pending_ = true;
Event* event = Event::Create(event_type_names::kDequeue);
event->SetTarget(this);
event->async_task_context()->Schedule(GetExecutionContext(), event->type());
main_thread_task_runner_->PostTask(
FROM_HERE, BindOnce(&DecoderTemplate<Traits>::DispatchDequeueEvent,
WrapWeakPersistent(this), WrapPersistent(event)));
}
template <typename Traits>
ExecutionContext* DecoderTemplate<Traits>::GetExecutionContext() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ExecutionContextLifecycleObserver::GetExecutionContext();
}
template <typename Traits>
void DecoderTemplate<Traits>::ContextDestroyed() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Deallocate resources and suppress late callbacks from media thread.
Shutdown();
}
template <typename Traits>
void DecoderTemplate<Traits>::Trace(Visitor* visitor) const {
visitor->Trace(script_state_);
visitor->Trace(output_cb_);
visitor->Trace(error_cb_);
visitor->Trace(requests_);
visitor->Trace(pending_request_);
visitor->Trace(pending_decodes_);
visitor->Trace(shutting_down_due_to_error_);
EventTarget::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
ReclaimableCodec::Trace(visitor);
}
template <typename Traits>
void DecoderTemplate<Traits>::OnCodecReclaimed(DOMException* exception) {
TRACE_EVENT0(kCategory, GetTraceNames()->reclaimed.c_str());
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(is_applying_codec_pressure());
if (state_.AsEnum() == V8CodecState::Enum::kUnconfigured) {
decoder_.reset();
// This codec isn't holding on to any resources, and doesn't need to be
// reclaimed.
ReleaseCodecPressure();
return;
}
DCHECK_EQ(state_.AsEnum(), V8CodecState::Enum::kConfigured);
Shutdown(exception);
}
template <typename Traits>
bool DecoderTemplate<Traits>::HasPendingActivity() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return pending_request_ || !requests_.empty();
}
template <typename Traits>
bool DecoderTemplate<Traits>::MaybeAbortRequest(Request* request) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (request->reset_generation == reset_generation_) {
return false;
}
if (request->resolver) {
request->resolver.Release()->Reject(
shutting_down_due_to_error_
? shutting_down_due_to_error_.Get()
: MakeGarbageCollected<DOMException>(
DOMExceptionCode::kAbortError,
shutting_down_ ? "Aborted due to close()"
: "Aborted due to reset()"));
}
return true;
}
template <typename Traits>
void DecoderTemplate<Traits>::Request::Trace(Visitor* visitor) const {
visitor->Trace(resolver);
}
template <typename Traits>
const char* DecoderTemplate<Traits>::Request::TraceNameFromType() {
using RequestType = typename DecoderTemplate<Traits>::Request::Type;
const CodecTraceNames* trace_names = DecoderTemplate<Traits>::GetTraceNames();
switch (type) {
case RequestType::kConfigure:
return trace_names->configure.c_str();
case RequestType::kDecode:
return trace_names->decode.c_str();
case RequestType::kFlush:
return trace_names->flush.c_str();
case RequestType::kReset:
return trace_names->reset.c_str();
}
return "InvalidCodecTraceName";
}
template <typename Traits>
void DecoderTemplate<Traits>::Request::StartTracing() {
#if DCHECK_IS_ON()
DCHECK(!is_tracing);
is_tracing = true;
#endif
TRACE_EVENT_BEGIN(kCategory, perfetto::DynamicString(TraceNameFromType()),
perfetto::Track::FromPointer(this));
}
template <typename Traits>
void DecoderTemplate<Traits>::Request::EndTracing(bool shutting_down) {
#if DCHECK_IS_ON()
DCHECK(is_tracing);
is_tracing = false;
#endif
TRACE_EVENT_END(kCategory, perfetto::Track::FromPointer(this), "completed",
!shutting_down);
}
template <typename Traits>
DOMException* DecoderTemplate<Traits>::MakeOperationError(
std::string error_msg,
media::DecoderStatus status) {
if (!decoder_ || decoder_->IsPlatformDecoder()) {
return logger_->MakeOperationError(std::move(error_msg), std::move(status));
}
return logger_->MakeSoftwareCodecOperationError(std::move(error_msg),
std::move(status));
}
template <typename Traits>
DOMException* DecoderTemplate<Traits>::MakeEncodingError(
std::string error_msg,
media::DecoderStatus status) {
if (!decoder_ || decoder_->IsPlatformDecoder()) {
return logger_->MakeEncodingError(std::move(error_msg), std::move(status));
}
return logger_->MakeSoftwareCodecEncodingError(std::move(error_msg),
std::move(status));
}
template class DecoderTemplate<AudioDecoderTraits>;
template class DecoderTemplate<VideoDecoderTraits>;
} // namespace blink