blob: 6f459ff15f30dbfb9eafa1b4a1f5a314381dfb09 [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_track_reader.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/video_frame.h"
#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.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/wtf/cross_thread_functional.h"
namespace blink {
VideoTrackReader::VideoTrackReader(ScriptState* script_state,
MediaStreamTrack* track)
: ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
started_(false),
real_time_media_task_runner_(
ExecutionContext::From(script_state)
->GetTaskRunner(TaskType::kInternalMediaRealTime)),
track_(track) {
UseCounter::Count(ExecutionContext::From(script_state),
WebFeature::kWebCodecs);
ExecutionContext::From(script_state)
->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kDeprecation,
mojom::blink::ConsoleMessageLevel::kWarning,
"VideoTrackReader is deprecated; use MediaStreamTrackProcessor "
"instead."));
}
void VideoTrackReader::start(V8VideoFrameOutputCallback* callback,
ExceptionState& exception_state) {
DCHECK(real_time_media_task_runner_->BelongsToCurrentThread());
if (started_) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"The VideoTrackReader has already been started.");
return;
}
started_ = true;
callback_ = callback;
ConnectToTrack(WebMediaStreamTrack(track_->Component()),
ConvertToBaseRepeatingCallback(CrossThreadBindRepeating(
&VideoTrackReader::OnFrameFromVideoTrack,
WrapCrossThreadPersistent(this))),
false /* is_sink_secure */);
}
void VideoTrackReader::stop(ExceptionState& exception_state) {
DCHECK(real_time_media_task_runner_->BelongsToCurrentThread());
if (!started_) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"The VideoTrackReader has already been stopped.");
return;
}
StopInternal();
}
void VideoTrackReader::StopInternal() {
DCHECK(real_time_media_task_runner_->BelongsToCurrentThread());
started_ = false;
callback_ = nullptr;
DisconnectFromTrack();
}
void VideoTrackReader::OnFrameFromVideoTrack(
scoped_refptr<media::VideoFrame> media_frame,
std::vector<scoped_refptr<media::VideoFrame>> scaled_media_frames,
base::TimeTicks estimated_capture_time) {
// The value of estimated_capture_time here seems to almost always be the
// system clock and most implementations of this callback ignore it.
// So, we will also ignore it.
DCHECK(media_frame);
PostCrossThreadTask(
*real_time_media_task_runner_.get(), FROM_HERE,
CrossThreadBindOnce(&VideoTrackReader::ExecuteCallbackOnMainThread,
WrapCrossThreadPersistent(this),
std::move(media_frame),
std::move(scaled_media_frames)));
}
void VideoTrackReader::ExecuteCallbackOnMainThread(
scoped_refptr<media::VideoFrame> media_frame,
std::vector<scoped_refptr<media::VideoFrame>> /*scaled_media_frames*/) {
DCHECK(real_time_media_task_runner_->BelongsToCurrentThread());
if (!callback_) {
// We may have already been stopped.
return;
}
// If |track_|'s constraints changed (e.g. the resolution changed from a call
// to MediaStreamTrack.applyConstraints() in JS), this |media_frame| might
// still have the old constraints, due to the thread hop.
// We may want to invalidate |media_frames| when constraints change, but it's
// unclear whether this is a problem for now.
auto* context = GetExecutionContext();
if (!context)
return;
// Scaled media frames are currently ignored.
callback_->InvokeAndReportException(
nullptr,
MakeGarbageCollected<VideoFrame>(std::move(media_frame), context));
}
void VideoTrackReader::OnReadyStateChanged(
WebMediaStreamSource::ReadyState state) {
if (state == WebMediaStreamSource::kReadyStateEnded)
StopInternal();
}
VideoTrackReader* VideoTrackReader::Create(ScriptState* script_state,
MediaStreamTrack* track,
ExceptionState& exception_state) {
if (track->kind() != "video") {
exception_state.ThrowTypeError(
"Can only read video frames from video tracks.");
return nullptr;
}
if (!script_state->ContextIsValid()) { // when the context is detached
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"The context has been destroyed");
return nullptr;
}
return MakeGarbageCollected<VideoTrackReader>(script_state, track);
}
void VideoTrackReader::Trace(Visitor* visitor) const {
visitor->Trace(track_);
visitor->Trace(callback_);
ScriptWrappable::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
}
} // namespace blink