blob: cb43b6f91c88616f493aa9436fed40178e930809 [file] [log] [blame]
// 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/mediastream/video_track_signal_underlying_source.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_signal.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_track_generator.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.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/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/webrtc/api/frame_transformer_interface.h"
namespace blink {
VideoTrackSignalUnderlyingSource::VideoTrackSignalUnderlyingSource(
ScriptState* script_state,
MediaStreamTrackGenerator* generator,
wtf_size_t max_queue_size)
: UnderlyingSourceBase(script_state),
main_task_runner_(ExecutionContext::From(script_state)
->GetTaskRunner(TaskType::kInternalMediaRealTime)),
generator_(generator),
max_queue_size_(std::max(1u, max_queue_size)) {
DCHECK(generator_);
}
ScriptPromise VideoTrackSignalUnderlyingSource::pull(
ScriptState* script_state) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
if (!queue_.empty()) {
PullFromQueue();
} else {
is_pending_pull_ = true;
}
DCHECK_LT(queue_.size(), max_queue_size_);
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise VideoTrackSignalUnderlyingSource::Start(
ScriptState* script_state) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
auto* video_track = MediaStreamVideoTrack::From(generator_->Component());
if (!video_track) {
return ScriptPromise::RejectWithDOMException(
script_state,
DOMException::Create(
"No input track",
DOMException::GetErrorName(DOMExceptionCode::kInvalidStateError)));
}
video_track->SetSignalObserver(this);
auto* video_source = generator_->PushableVideoSource();
video_source->SetSignalObserver(this);
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise VideoTrackSignalUnderlyingSource::Cancel(
ScriptState* script_state,
ScriptValue reason) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
Stop();
return ScriptPromise::CastUndefined(script_state);
}
void VideoTrackSignalUnderlyingSource::Trace(Visitor* visitor) const {
visitor->Trace(generator_);
visitor->Trace(queue_);
UnderlyingSourceBase::Trace(visitor);
}
double VideoTrackSignalUnderlyingSource::DesiredSizeForTesting() const {
return Controller()->DesiredSize();
}
void VideoTrackSignalUnderlyingSource::ContextDestroyed() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
UnderlyingSourceBase::ContextDestroyed();
Stop();
}
void VideoTrackSignalUnderlyingSource::Close() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
Stop();
if (Controller())
Controller()->Close();
}
void VideoTrackSignalUnderlyingSource::SetMinimumFrameRate(double frame_rate) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
MediaStreamTrackSignal* signal = MediaStreamTrackSignal::Create();
signal->setSignalType("set-min-frame-rate");
signal->setFrameRate(frame_rate);
ProcessNewSignal(signal);
}
void VideoTrackSignalUnderlyingSource::RequestFrame() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
MediaStreamTrackSignal* signal = MediaStreamTrackSignal::Create();
signal->setSignalType("request-frame");
ProcessNewSignal(signal);
}
void VideoTrackSignalUnderlyingSource::ProcessNewSignal(
MediaStreamTrackSignal* signal) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK_LE(queue_.size(), max_queue_size_);
if (!is_active_)
return;
// If the |queue_| is empty and the consumer has signaled a pull, bypass
// |queue_| and send the frame directly to the stream controller.
if (queue_.empty() && is_pending_pull_) {
SendSignalToStream(signal);
return;
}
if (queue_.size() == max_queue_size_)
queue_.pop_front();
queue_.push_back(signal);
if (is_pending_pull_) {
PullFromQueue();
}
}
void VideoTrackSignalUnderlyingSource::PullFromQueue() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK(!queue_.empty());
DCHECK(is_active_);
SendSignalToStream(std::move(queue_.front()));
queue_.pop_front();
}
void VideoTrackSignalUnderlyingSource::SendSignalToStream(
MediaStreamTrackSignal* signal) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK(signal);
DCHECK(is_active_);
if (!Controller())
return;
Controller()->Enqueue(signal);
is_pending_pull_ = false;
}
void VideoTrackSignalUnderlyingSource::Stop() {
is_active_ = false;
queue_.clear();
}
} // namespace blink