blob: ac3f01750c2b328e02bd755ac1a4b6cd6ba78a7b [file] [log] [blame]
// Copyright 2015 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 "media/renderers/video_overlay_factory.h"
#include "base/single_thread_task_runner.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "media/base/video_frame.h"
#include "media/video/gpu_video_accelerator_factories.h"
namespace media {
class VideoOverlayFactory::Texture {
public:
explicit Texture(GpuVideoAcceleratorFactories* gpu_factories)
: gpu_factories_(gpu_factories), image_id_(0), texture_id_(0) {
DCHECK(gpu_factories_);
DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
gpu::gles2::GLES2Interface* gl = gpu_factories_->ContextGL();
if (!gl)
return;
gpu_memory_buffer_ = gpu_factories_->CreateGpuMemoryBuffer(
gfx::Size(1, 1), gfx::BufferFormat::BGRA_8888,
gfx::BufferUsage::SCANOUT);
if (gpu_memory_buffer_) {
image_id_ = gl->CreateImageCHROMIUM(gpu_memory_buffer_->AsClientBuffer(),
1, 1, GL_BGRA_EXT);
}
if (image_id_) {
gl->GenTextures(1, &texture_id_);
gl->BindTexture(GL_TEXTURE_2D, texture_id_);
gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id_);
gl->ProduceTextureDirectCHROMIUM(texture_id_, mailbox_.name);
gl->GenSyncTokenCHROMIUM(sync_token_.GetData());
}
}
~Texture() {
DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
if (image_id_) {
gpu::gles2::GLES2Interface* gl = gpu_factories_->ContextGL();
if (!gl)
return;
gl->BindTexture(GL_TEXTURE_2D, texture_id_);
gl->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id_);
gl->DeleteTextures(1, &texture_id_);
gl->DestroyImageCHROMIUM(image_id_);
}
}
bool IsValid() const { return image_id_ != 0; }
private:
friend class VideoOverlayFactory;
GpuVideoAcceleratorFactories* gpu_factories_;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
GLuint image_id_;
GLuint texture_id_;
gpu::Mailbox mailbox_;
gpu::SyncToken sync_token_;
};
VideoOverlayFactory::VideoOverlayFactory(
GpuVideoAcceleratorFactories* gpu_factories)
: gpu_factories_(gpu_factories) {}
VideoOverlayFactory::~VideoOverlayFactory() = default;
scoped_refptr<VideoFrame> VideoOverlayFactory::CreateFrame(
const gfx::Size& size) {
// Frame size empty => video has one dimension = 0.
// Dimension 0 case triggers a DCHECK later on if we push through the overlay
// path.
Texture* texture = size.IsEmpty() ? nullptr : GetTexture();
if (!texture) {
DVLOG(1) << "Create black frame " << size.width() << "x" << size.height();
return VideoFrame::CreateBlackFrame(gfx::Size(1, 1));
}
DCHECK(texture);
DCHECK(texture->IsValid());
DVLOG(2) << "Create video overlay frame: " << size.ToString();
gpu::MailboxHolder holders[VideoFrame::kMaxPlanes] = {gpu::MailboxHolder(
texture->mailbox_, texture->sync_token_, GL_TEXTURE_2D)};
scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
PIXEL_FORMAT_XRGB, holders, VideoFrame::ReleaseMailboxCB(),
size, // coded_size
gfx::Rect(size), // visible rect
size, // natural size
base::TimeDelta()); // timestamp
CHECK(frame);
frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
// On platforms that use the video hole, the video never makes it in the media
// pipeline as it is side loaded straight into the hardware video plane. This
// is intended for protected video such that userspace cannot copy potentially
// protected content. While the video may not be protected, since it goes
// through the protected video path, the video always looks like a protected
// video from the perspective of VideoOverlayFactory which produces a dummy
// video frame in its place.
frame->metadata()->SetBoolean(VideoFrameMetadata::PROTECTED_VIDEO, true);
frame->metadata()->SetBoolean(VideoFrameMetadata::HW_PROTECTED, true);
return frame;
}
VideoOverlayFactory::Texture* VideoOverlayFactory::GetTexture() {
if (!gpu_factories_)
return nullptr;
// Lazily create overlay texture.
if (!texture_)
texture_.reset(new Texture(gpu_factories_));
return texture_->IsValid() ? texture_.get() : nullptr;
}
} // namespace media