blob: 6574e4668ec48614340d70f66e59efcd0ca3b3f7 [file] [log] [blame]
// Copyright 2016 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 "content/renderer/media/android/stream_texture_wrapper_impl.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "cc/layers/video_frame_provider.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "media/base/bind_to_current_loop.h"
namespace {
// Non-member function to allow it to run even after this class is deleted.
void OnReleaseVideoFrame(scoped_refptr<content::StreamTextureFactory> factories,
gpu::Mailbox mailbox,
const gpu::SyncToken& sync_token) {
gpu::SharedImageInterface* sii = factories->SharedImageInterface();
sii->DestroySharedImage(sync_token, mailbox);
sii->Flush();
}
}
namespace content {
StreamTextureWrapperImpl::StreamTextureWrapperImpl(
bool enable_texture_copy,
scoped_refptr<StreamTextureFactory> factory,
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
: enable_texture_copy_(enable_texture_copy),
factory_(factory),
main_task_runner_(main_task_runner) {}
StreamTextureWrapperImpl::~StreamTextureWrapperImpl() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
SetCurrentFrameInternal(nullptr);
}
media::ScopedStreamTextureWrapper StreamTextureWrapperImpl::Create(
bool enable_texture_copy,
scoped_refptr<StreamTextureFactory> factory,
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) {
return media::ScopedStreamTextureWrapper(new StreamTextureWrapperImpl(
enable_texture_copy, factory, main_task_runner));
}
scoped_refptr<media::VideoFrame> StreamTextureWrapperImpl::GetCurrentFrame() {
base::AutoLock auto_lock(current_frame_lock_);
return current_frame_;
}
void StreamTextureWrapperImpl::CreateVideoFrame(
const gpu::Mailbox& mailbox,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info) {
// This message comes from GPU process when the SharedImage is already
// created, so we don't need to wait on any synctoken, mailbox is ready to
// use.
gpu::MailboxHolder holders[media::VideoFrame::kMaxPlanes] = {
gpu::MailboxHolder(mailbox, gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES)};
gpu::SharedImageInterface* sii = factory_->SharedImageInterface();
sii->NotifyMailboxAdded(mailbox, gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_RASTER);
// The pixel format doesn't matter here as long as it's valid for texture
// frames. But SkiaRenderer wants to ensure that the format of the resource
// used here which will eventually create a promise image must match the
// format of the resource(SharedImageVideo) used to create fulfill image.
// crbug.com/1028746. Since we create all the textures/abstract textures as
// well as shared images for video to be of format RGBA, we need to use the
// pixel format as ABGR here(which corresponds to 32bpp RGBA).
scoped_refptr<media::VideoFrame> new_frame =
media::VideoFrame::WrapNativeTextures(
media::PIXEL_FORMAT_ABGR, holders,
media::BindToLoop(
main_task_runner_,
base::BindOnce(&OnReleaseVideoFrame, factory_, mailbox)),
coded_size, visible_rect, visible_rect.size(), base::TimeDelta());
new_frame->set_ycbcr_info(ycbcr_info);
if (enable_texture_copy_) {
new_frame->metadata()->copy_mode =
media::VideoFrameMetadata::CopyMode::kCopyToNewTexture;
}
SetCurrentFrameInternal(new_frame);
}
void StreamTextureWrapperImpl::ForwardStreamTextureForSurfaceRequest(
const base::UnguessableToken& request_token) {
stream_texture_proxy_->ForwardStreamTextureForSurfaceRequest(request_token);
}
void StreamTextureWrapperImpl::ClearReceivedFrameCBOnAnyThread() {
// Safely stop StreamTextureProxy from signaling the arrival of new frames.
if (stream_texture_proxy_)
stream_texture_proxy_->ClearReceivedFrameCB();
}
void StreamTextureWrapperImpl::SetCurrentFrameInternal(
scoped_refptr<media::VideoFrame> video_frame) {
base::AutoLock auto_lock(current_frame_lock_);
current_frame_ = std::move(video_frame);
}
void StreamTextureWrapperImpl::UpdateTextureSize(const gfx::Size& new_size) {
DVLOG(2) << __func__;
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&StreamTextureWrapperImpl::UpdateTextureSize,
weak_factory_.GetWeakPtr(), new_size));
return;
}
// InitializeOnMainThread() hasn't run, or failed.
if (!stream_texture_proxy_)
return;
if (rotated_visible_size_ == new_size)
return;
rotated_visible_size_ = new_size;
stream_texture_proxy_->UpdateRotatedVisibleSize(rotated_visible_size_);
}
void StreamTextureWrapperImpl::Initialize(
const base::RepeatingClosure& received_frame_cb,
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
StreamTextureWrapperInitCB init_cb) {
DVLOG(2) << __func__;
compositor_task_runner_ = compositor_task_runner;
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&StreamTextureWrapperImpl::InitializeOnMainThread,
weak_factory_.GetWeakPtr(), received_frame_cb,
media::BindToCurrentLoop(std::move(init_cb))));
}
void StreamTextureWrapperImpl::InitializeOnMainThread(
const base::RepeatingClosure& received_frame_cb,
StreamTextureWrapperInitCB init_cb) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DVLOG(2) << __func__;
// Normally, we have a factory. However, if the gpu process is restarting,
// then we might not.
if (!factory_) {
std::move(init_cb).Run(false);
return;
}
stream_texture_proxy_ = factory_->CreateProxy();
if (!stream_texture_proxy_) {
std::move(init_cb).Run(false);
return;
}
// Unretained is safe here since |stream_texture_proxy_| is a scoped member of
// the this StreamTextureWrapperImpl class which clears/resets this callback
// before |this| is destroyed.
stream_texture_proxy_->BindToTaskRunner(
received_frame_cb,
base::BindRepeating(&StreamTextureWrapperImpl::CreateVideoFrame,
base::Unretained(this)),
compositor_task_runner_);
std::move(init_cb).Run(true);
}
void StreamTextureWrapperImpl::Destroy() {
if (!main_task_runner_->BelongsToCurrentThread()) {
// base::Unretained is safe here because this function is the only one that
// can call delete.
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&StreamTextureWrapperImpl::Destroy,
base::Unretained(this)));
return;
}
delete this;
}
} // namespace content