| // 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) |
| : overlay_plane_id_(base::UnguessableToken::Create()), |
| 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); |
| frame->metadata()->SetUnguessableToken(VideoFrameMetadata::OVERLAY_PLANE_ID, |
| overlay_plane_id_); |
| 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 |