blob: 119f1c76fbf860c11833a7de0ce8c85f5d032c2d [file] [log] [blame]
// Copyright 2020 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/video_frame_logger.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
namespace blink {
// How frequently we check for leaks.
constexpr base::TimeDelta kTimerInterval = base::TimeDelta::FromSeconds(10);
// How long we wait before stopping the timer when there is no activity.
constexpr base::TimeDelta kTimerShutdownDelay =
base::TimeDelta::FromSeconds(60);
void VideoFrameLogger::VideoFrameCloseAuditor::ReportUnclosedFrame() {
were_frames_not_closed_ = true;
}
void VideoFrameLogger::VideoFrameCloseAuditor::Clear() {
were_frames_not_closed_ = false;
}
VideoFrameLogger::VideoFrameLogger(ExecutionContext& context)
: Supplement<ExecutionContext>(context),
close_auditor_(base::MakeRefCounted<VideoFrameCloseAuditor>()),
timer_(context.GetTaskRunner(TaskType::kInternalMedia),
this,
&VideoFrameLogger::LogCloseErrors) {}
// static
VideoFrameLogger& VideoFrameLogger::From(ExecutionContext& context) {
VideoFrameLogger* supplement =
Supplement<ExecutionContext>::From<VideoFrameLogger>(context);
if (!supplement) {
supplement = MakeGarbageCollected<VideoFrameLogger>(context);
Supplement<ExecutionContext>::ProvideTo(context, supplement);
}
return *supplement;
}
scoped_refptr<VideoFrameLogger::VideoFrameCloseAuditor>
VideoFrameLogger::GetCloseAuditor() {
// We cannot directly log close errors: they are detected during garbage
// collection, and it would be unsafe to access GC'ed objects from a GC'ed
// object's destructor. Instead, start a timer here to periodically poll for
// these errors. The timer should stop itself after a period of inactivity.
if (!timer_.IsActive())
timer_.StartRepeating(kTimerInterval, FROM_HERE);
last_auditor_access_ = base::TimeTicks::Now();
return close_auditor_;
}
void VideoFrameLogger::LogCreateImageBitmapDeprecationNotice() {
if (already_logged_create_image_bitmap_deprecation_)
return;
already_logged_create_image_bitmap_deprecation_ = true;
GetSupplementable()->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kDeprecation,
mojom::blink::ConsoleMessageLevel::kWarning,
"VideoFrame.createImageBitmap() is deprecated; please use "
"createImageBitmap(VideoFrame)."));
}
void VideoFrameLogger::LogCloseErrors(TimerBase*) {
// If it's been a while since this class was used and there are not other
// references to |leak_status_|, stop the timer.
if (base::TimeTicks::Now() - last_auditor_access_ > kTimerShutdownDelay &&
close_auditor_->HasOneRef()) {
timer_.Stop();
}
if (!close_auditor_->were_frames_not_closed())
return;
auto* execution_context = GetSupplementable();
if (!execution_context->IsContextDestroyed()) {
execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kJavaScript,
mojom::blink::ConsoleMessageLevel::kError,
"A VideoFrame was garbage collected without being closed. "
"Applications should call close() on frames when done with them to "
"prevent stalls."));
}
close_auditor_->Clear();
}
void VideoFrameLogger::Trace(Visitor* visitor) const {
visitor->Trace(timer_);
Supplement<ExecutionContext>::Trace(visitor);
}
// static
const char VideoFrameLogger::kSupplementName[] = "VideoFrameLogger";
} // namespace blink