blob: 906e20ed6e37a45e0d001d02e22fe9b698b5fc62 [file] [log] [blame]
// Copyright 2017 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/platform/graphics/video_frame_submitter.h"
#include <vector>
#include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/filter_operations.h"
#include "cc/scheduler/video_frame_controller.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/returned_resource.h"
#include "media/base/video_frame.h"
#include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
#include "third_party/blink/public/platform/interface_provider.h"
#include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
namespace blink {
namespace {
// Delay to retry getting the context_provider.
constexpr int kGetContextProviderRetryMS = 150;
} // namespace
VideoFrameSubmitter::VideoFrameSubmitter(
WebContextProviderCallback context_provider_callback,
std::unique_ptr<VideoFrameResourceProvider> resource_provider)
: binding_(this),
context_provider_callback_(context_provider_callback),
resource_provider_(std::move(resource_provider)),
is_rendering_(false),
weak_ptr_factory_(this) {
DETACH_FROM_THREAD(media_thread_checker_);
}
VideoFrameSubmitter::~VideoFrameSubmitter() {
if (context_provider_)
context_provider_->RemoveObserver(this);
}
void VideoFrameSubmitter::SetRotation(media::VideoRotation rotation) {
rotation_ = rotation;
}
void VideoFrameSubmitter::EnableSubmission(
viz::SurfaceId id,
WebFrameSinkDestroyedCallback frame_sink_destroyed_callback) {
// TODO(lethalantidote): Set these fields earlier in the constructor. Will
// need to construct VideoFrameSubmitter later in order to do this.
surface_id_ = id;
frame_sink_destroyed_callback_ = frame_sink_destroyed_callback;
if (resource_provider_->IsInitialized())
StartSubmitting();
}
void VideoFrameSubmitter::UpdateSubmissionState(bool should_submit) {
should_submit_ = should_submit;
UpdateSubmissionStateInternal();
}
void VideoFrameSubmitter::UpdateSubmissionStateInternal() {
if (compositor_frame_sink_) {
compositor_frame_sink_->SetNeedsBeginFrame(is_rendering_ && should_submit_);
if (should_submit_)
SubmitSingleFrame();
}
}
void VideoFrameSubmitter::StopUsingProvider() {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
if (is_rendering_)
StopRendering();
provider_ = nullptr;
}
void VideoFrameSubmitter::StopRendering() {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
DCHECK(is_rendering_);
DCHECK(provider_);
is_rendering_ = false;
UpdateSubmissionStateInternal();
}
void VideoFrameSubmitter::SubmitSingleFrame() {
// If we haven't gotten a valid result yet from |context_provider_callback_|
// |resource_provider_| will remain uninitalized.
if (!resource_provider_->IsInitialized())
return;
viz::BeginFrameAck current_begin_frame_ack =
viz::BeginFrameAck::CreateManualAckWithDamage();
scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
if (video_frame) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
weak_ptr_factory_.GetWeakPtr(),
current_begin_frame_ack, video_frame));
provider_->PutCurrentFrame();
}
}
void VideoFrameSubmitter::DidReceiveFrame() {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
DCHECK(provider_);
// DidReceiveFrame is called before renderering has started, as a part of
// PaintSingleFrame.
if (!is_rendering_) {
SubmitSingleFrame();
}
}
void VideoFrameSubmitter::StartRendering() {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
DCHECK(!is_rendering_);
is_rendering_ = true;
if (compositor_frame_sink_)
compositor_frame_sink_->SetNeedsBeginFrame(is_rendering_ && should_submit_);
}
void VideoFrameSubmitter::Initialize(cc::VideoFrameProvider* provider) {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
if (provider) {
DCHECK(!provider_);
provider_ = provider;
context_provider_callback_.Run(
base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
weak_ptr_factory_.GetWeakPtr()));
}
}
void VideoFrameSubmitter::OnReceivedContextProvider(
bool use_gpu_compositing,
scoped_refptr<ui::ContextProviderCommandBuffer> context_provider) {
// We could get a null |context_provider| back if the context is still lost.
if (!context_provider && use_gpu_compositing) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
context_provider_callback_,
base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
weak_ptr_factory_.GetWeakPtr())),
base::TimeDelta::FromMilliseconds(kGetContextProviderRetryMS));
return;
}
context_provider_ = std::move(context_provider);
if (use_gpu_compositing) {
context_provider_->AddObserver(this);
resource_provider_->Initialize(context_provider_.get(), nullptr);
} else {
resource_provider_->Initialize(nullptr, this);
}
if (surface_id_.is_valid())
StartSubmitting();
}
void VideoFrameSubmitter::StartSubmitting() {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
DCHECK(surface_id_.is_valid());
mojom::blink::EmbeddedFrameSinkProviderPtr provider;
Platform::Current()->GetInterfaceProvider()->GetInterface(
mojo::MakeRequest(&provider));
viz::mojom::blink::CompositorFrameSinkClientPtr client;
binding_.Bind(mojo::MakeRequest(&client));
provider->CreateCompositorFrameSink(
surface_id_.frame_sink_id(), std::move(client),
mojo::MakeRequest(&compositor_frame_sink_));
compositor_frame_sink_.set_connection_error_handler(base::BindOnce(
&VideoFrameSubmitter::OnContextLost, base::Unretained(this)));
UpdateSubmissionStateInternal();
}
void VideoFrameSubmitter::SubmitFrame(
const viz::BeginFrameAck& begin_frame_ack,
scoped_refptr<media::VideoFrame> video_frame) {
TRACE_EVENT0("media", "VideoFrameSubmitter::SubmitFrame");
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
if (!compositor_frame_sink_ || !should_submit_)
return;
viz::CompositorFrame compositor_frame;
std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create();
render_pass->SetNew(1, gfx::Rect(video_frame->coded_size()),
gfx::Rect(video_frame->coded_size()), gfx::Transform());
render_pass->filters = cc::FilterOperations();
resource_provider_->AppendQuads(render_pass.get(), video_frame, rotation_);
compositor_frame.metadata.begin_frame_ack = begin_frame_ack;
compositor_frame.metadata.device_scale_factor = 1;
compositor_frame.metadata.may_contain_video = true;
std::vector<viz::ResourceId> resources;
DCHECK_LE(render_pass->quad_list.size(), 1u);
if (!render_pass->quad_list.empty()) {
for (viz::ResourceId resource_id :
render_pass->quad_list.front()->resources) {
resources.push_back(resource_id);
}
}
resource_provider_->PrepareSendToParent(resources,
&compositor_frame.resource_list);
compositor_frame.render_pass_list.push_back(std::move(render_pass));
// TODO(lethalantidote): Address third/fourth arg in SubmitCompositorFrame.
compositor_frame_sink_->SubmitCompositorFrame(
surface_id_.local_surface_id(), std::move(compositor_frame), nullptr, 0);
resource_provider_->ReleaseFrameResources();
waiting_for_compositor_ack_ = true;
}
void VideoFrameSubmitter::OnBeginFrame(const viz::BeginFrameArgs& args) {
TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
viz::BeginFrameAck current_begin_frame_ack =
viz::BeginFrameAck(args.source_id, args.sequence_number, false);
if (args.type == viz::BeginFrameArgs::MISSED) {
compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
return;
}
// This block is separate from the one below to avoid updating the frame by
// mistake.
if (!is_rendering_ || waiting_for_compositor_ack_) {
compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
return;
}
if (!provider_ ||
!provider_->UpdateCurrentFrame(args.frame_time + args.interval,
args.frame_time + 2 * args.interval)) {
compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
return;
}
current_begin_frame_ack.has_damage = true;
scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
SubmitFrame(current_begin_frame_ack, video_frame);
provider_->PutCurrentFrame();
}
void VideoFrameSubmitter::OnContextLost() {
// TODO(lethalantidote): This check will be obsolete once other TODO to move
// field initialization earlier is fulfilled.
if (frame_sink_destroyed_callback_)
frame_sink_destroyed_callback_.Run();
if (binding_.is_bound())
binding_.Unbind();
if (context_provider_) {
context_provider_->RemoveObserver(this);
context_provider_ = nullptr;
}
waiting_for_compositor_ack_ = false;
// |compositor_frame_sink_| should be reset last.
compositor_frame_sink_.reset();
resource_provider_->OnContextLost();
context_provider_callback_.Run(
base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
weak_ptr_factory_.GetWeakPtr()));
// We need to trigger another submit so that surface_id's get propagated
// correctly. If we don't, we don't get any more signals to update the
// submission state.
should_submit_ = true;
}
void VideoFrameSubmitter::DidReceiveCompositorFrameAck(
const WTF::Vector<viz::ReturnedResource>& resources) {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
ReclaimResources(resources);
waiting_for_compositor_ack_ = false;
}
void VideoFrameSubmitter::ReclaimResources(
const WTF::Vector<viz::ReturnedResource>& resources) {
DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
WebVector<viz::ReturnedResource> temp_resources = resources;
std::vector<viz::ReturnedResource> std_resources =
temp_resources.ReleaseVector();
resource_provider_->ReceiveReturnsFromParent(std_resources);
}
void VideoFrameSubmitter::DidPresentCompositorFrame(
uint32_t presentation_token,
::gfx::mojom::blink::PresentationFeedbackPtr feedback) {}
void VideoFrameSubmitter::DidAllocateSharedBitmap(
mojo::ScopedSharedBufferHandle buffer,
const viz::SharedBitmapId& id) {
DCHECK(compositor_frame_sink_);
compositor_frame_sink_->DidAllocateSharedBitmap(
std::move(buffer), SharedBitmapIdToGpuMailboxPtr(id));
}
void VideoFrameSubmitter::DidDeleteSharedBitmap(const viz::SharedBitmapId& id) {
DCHECK(compositor_frame_sink_);
compositor_frame_sink_->DidDeleteSharedBitmap(
SharedBitmapIdToGpuMailboxPtr(id));
}
} // namespace blink