Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1074)

Unified Diff: content/browser/renderer_host/media/video_capture_texture_wrapper.cc

Issue 1064963002: VideoCapture: add support for GpuMemoryBuffer allocation and lifetime mgmt in VideoCaptureBufferPool (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/media/video_capture_texture_wrapper.cc
diff --git a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc b/content/browser/renderer_host/media/video_capture_texture_wrapper.cc
deleted file mode 100644
index 7c14a1501e88a41e2b6d06d7cdfba1866e3c2e5c..0000000000000000000000000000000000000000
--- a/content/browser/renderer_host/media/video_capture_texture_wrapper.cc
+++ /dev/null
@@ -1,463 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
miu 2015/04/15 19:12:40 This file is marked "M" instead of "D" in the patc
mcasas 2015/04/16 03:11:21 It's deleted in my local repo, and the diff shows
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/media/video_capture_texture_wrapper.h"
-
-#include "base/bind.h"
-#include "content/browser/compositor/image_transport_factory.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
-#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
-#include "content/browser/gpu/gpu_data_manager_impl.h"
-#include "content/browser/renderer_host/media/video_capture_controller.h"
-#include "content/common/gpu/client/context_provider_command_buffer.h"
-#include "content/common/gpu/client/gl_helper.h"
-#include "content/common/gpu/client/gpu_channel_host.h"
-#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
-#include "content/common/gpu/gpu_process_launch_causes.h"
-#include "content/public/browser/browser_thread.h"
-#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "media/base/bind_to_current_loop.h"
-#include "media/base/video_capture_types.h"
-#include "media/base/video_frame.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
-#include "third_party/libyuv/include/libyuv.h"
-
-namespace content {
-
-namespace {
-
-// VideoCaptureController has at most 3 capture frames in flight.
-const size_t kNumGpuMemoryBuffers = 3;
-
-uint32 VideoPixelFormatToFourCC(media::VideoPixelFormat pixel_format) {
- switch (pixel_format) {
- // I420 is needed by Fake and FileVideoCaptureDevice
- case media::PIXEL_FORMAT_I420:
- return libyuv::FOURCC_I420;
- case media::PIXEL_FORMAT_UYVY:
- return libyuv::FOURCC_UYVY;
- case media::PIXEL_FORMAT_YUY2:
- return libyuv::FOURCC_YUY2;
- case media::PIXEL_FORMAT_MJPEG:
- return libyuv::FOURCC_MJPG;
- default:
- NOTREACHED() << "Bad captured pixel format: "
- << media::VideoCaptureFormat::PixelFormatToString(pixel_format);
- }
- return libyuv::FOURCC_ANY;
-}
-
-// Modelled after GpuProcessTransportFactory::CreateContextCommon().
-scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> CreateContextCommon(
- scoped_refptr<content::GpuChannelHost> gpu_channel_host,
- int surface_id) {
- if (!content::GpuDataManagerImpl::GetInstance()->
- CanUseGpuBrowserCompositor()) {
- DLOG(ERROR) << "No accelerated graphics found. Check chrome://gpu";
- return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>();
- }
- blink::WebGraphicsContext3D::Attributes attrs;
- attrs.shareResources = true;
- attrs.depth = false;
- attrs.stencil = false;
- attrs.antialias = false;
- attrs.noAutomaticFlushes = true;
-
- if (!gpu_channel_host.get()) {
- DLOG(ERROR) << "Failed to establish GPU channel.";
- return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>();
- }
- GURL url("chrome://gpu/GpuProcessTransportFactory::CreateCaptureContext");
- return make_scoped_ptr(
- new WebGraphicsContext3DCommandBufferImpl(
- surface_id,
- url,
- gpu_channel_host.get(),
- attrs,
- true /* lose_context_when_out_of_memory */,
- content::WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
- NULL));
-}
-
-// Modelled after
-// GpuProcessTransportFactory::CreateOffscreenCommandBufferContext().
-scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>
-CreateOffscreenCommandBufferContext() {
- content::CauseForGpuLaunch cause = content::CAUSE_FOR_GPU_LAUNCH_CANVAS_2D;
- scoped_refptr<content::GpuChannelHost> gpu_channel_host(
- content::BrowserGpuChannelHostFactory::instance()->
- EstablishGpuChannelSync(cause));
- DCHECK(gpu_channel_host);
- return CreateContextCommon(gpu_channel_host, 0);
-}
-
-typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)>
- ProcessContextCallback;
-
-void CreateContextOnUIThread(ProcessContextCallback bottom_half) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- bottom_half.Run(ContextProviderCommandBuffer::Create(
- CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread"));
- return;
-}
-
-void ResetLostContextCallback(
- const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) {
- capture_thread_context->SetLostContextCallback(
- cc::ContextProvider::LostContextCallback());
-}
-
-} // anonymous namespace
-
-// Internal ref-counted class to manage a pool of GpuMemoryBuffers. The contents
-// of an incoming captured frame are_copied_ into the first available buffer
-// from the pool and sent to our client ultimately wrapped into a VideoFrame.
-// This VideoFrame creation is balanced by a waiting on the associated
-// |sync_point|. After VideoFrame consumption the inserted ReleaseCallback()
-// will be called, where the GpuMemoryBuffer is recycled.
-//
-// This class jumps between threads due to GPU-related thread limitations, i.e.
-// some objects cannot be accessed from IO Thread, where we are constructed,
-// others need to be constructed on UI Thread. For this reason most of the
-// operations are carried out on Capture Thread (|capture_task_runner_|).
-//
-// TODO(mcasas): ctor |frame_format| is used for early GpuMemoryBuffer pool
-// allocation, but VideoCaptureDevices might provide a different resolution when
-// calling OnIncomingCapturedData(), be that due to driver preferences or to
-// its ResolutionChangePolicy. Make the GpuMemoryBuffer allocated on demand.
-class VideoCaptureTextureWrapper::TextureWrapperDelegate final
- : public base::RefCountedThreadSafe<TextureWrapperDelegate> {
- public:
- TextureWrapperDelegate(
- const base::WeakPtr<VideoCaptureController>& controller,
- const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner,
- const media::VideoCaptureFormat& capture_format);
-
- // Copy-converts the incoming data into a GpuMemoruBuffer backed Texture, and
- // sends it to |controller_| wrapped in a VideoFrame, with |buffer| as storage
- // backend.
- void OnIncomingCapturedData(
- const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>&
- texture_buffer,
- const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>&
- argb_buffer,
- const gfx::Size& frame_size,
- const base::TimeTicks& timestamp);
-
- private:
- friend class base::RefCountedThreadSafe<TextureWrapperDelegate>;
- ~TextureWrapperDelegate();
-
- // Creates some necessary members in |capture_task_runner_|.
- void Init(const media::VideoCaptureFormat& capture_format);
- // Runs the bottom half of the GlHelper creation.
- void CreateGlHelper(
- scoped_refptr<ContextProviderCommandBuffer> capture_thread_context);
-
- // Recycles |memory_buffer|, deletes Image and Texture on VideoFrame release.
- void ReleaseCallback(GLuint image_id,
- GLuint texture_id,
- linked_ptr<gfx::GpuMemoryBuffer> memory_buffer,
- uint32 sync_point);
-
- // The Command Buffer lost the GL context, f.i. GPU process crashed. Signal
- // error to our owner so the capture can be torn down.
- void LostContextCallback();
-
- // Prints the error |message| and notifies |controller_| of an error.
- void OnError(const std::string& message);
-
- const base::WeakPtr<VideoCaptureController> controller_;
- const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_;
-
- // Command buffer reference, needs to be destroyed when unused. It is created
- // on UI Thread and bound to Capture Thread. In particular, it cannot be used
- // from IO Thread.
- scoped_refptr<ContextProviderCommandBuffer> capture_thread_context_;
- // Created and used from Capture Thread. Cannot be used from IO Thread.
- scoped_ptr<GLHelper> gl_helper_;
-
- // A pool of GpuMemoryBuffers that are used to wrap incoming captured frames;
- // recycled via ReleaseCallback().
- std::queue<linked_ptr<gfx::GpuMemoryBuffer>> gpu_memory_buffers_;
-
- DISALLOW_COPY_AND_ASSIGN(TextureWrapperDelegate);
-};
-
-VideoCaptureTextureWrapper::VideoCaptureTextureWrapper(
- const base::WeakPtr<VideoCaptureController>& controller,
- const scoped_refptr<VideoCaptureBufferPool>& buffer_pool,
- const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner,
- const media::VideoCaptureFormat& capture_format)
- : VideoCaptureDeviceClient(controller, buffer_pool),
- wrapper_delegate_(new TextureWrapperDelegate(controller,
- capture_task_runner,
- capture_format)),
- capture_task_runner_(capture_task_runner) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
-}
-
-VideoCaptureTextureWrapper::~VideoCaptureTextureWrapper() {
-}
-
-void VideoCaptureTextureWrapper::OnIncomingCapturedData(
- const uint8* data,
- int length,
- const media::VideoCaptureFormat& frame_format,
- int clockwise_rotation,
- const base::TimeTicks& timestamp) {
-
- // Reserve a temporary Buffer for conversion to ARGB.
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> argb_buffer =
- ReserveOutputBuffer(media::PIXEL_FORMAT_ARGB, frame_format.frame_size);
- DVLOG_IF(1, !argb_buffer) << "Couldn't allocate ARGB Buffer";
- if (!argb_buffer)
- return;
-
- DCHECK(argb_buffer->data());
- // TODO(mcasas): Take |rotation| into acount.
- int ret = libyuv::ConvertToARGB(data,
- length,
- reinterpret_cast<uint8*>(argb_buffer->data()),
- frame_format.frame_size.width() * 4,
- 0,
- 0,
- frame_format.frame_size.width(),
- frame_format.frame_size.height(),
- frame_format.frame_size.width(),
- frame_format.frame_size.height(),
- libyuv::kRotate0,
- VideoPixelFormatToFourCC(
- frame_format.pixel_format));
- DLOG_IF(ERROR, ret != 0) << "Error converting incoming frame";
- if (ret != 0)
- return;
-
- // Reserve output buffer for the texture on the IPC borderlands.
- scoped_refptr<media::VideoCaptureDevice::Client::Buffer> texture_buffer =
- ReserveOutputBuffer(media::PIXEL_FORMAT_TEXTURE, frame_format.frame_size);
- DVLOG_IF(1, !texture_buffer) << "Couldn't allocate Texture Buffer";
- if (!texture_buffer)
- return;
-
- capture_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &TextureWrapperDelegate::OnIncomingCapturedData,
- wrapper_delegate_,
- texture_buffer,
- argb_buffer,
- frame_format.frame_size,
- timestamp));
-}
-
-VideoCaptureTextureWrapper::TextureWrapperDelegate::TextureWrapperDelegate(
- const base::WeakPtr<VideoCaptureController>& controller,
- const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner,
- const media::VideoCaptureFormat& capture_format)
- : controller_(controller),
- capture_task_runner_(capture_task_runner) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- capture_task_runner_->PostTask(FROM_HERE,
- base::Bind(&TextureWrapperDelegate::Init, this, capture_format));
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::OnIncomingCapturedData(
- const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>&
- texture_buffer,
- const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& argb_buffer,
- const gfx::Size& frame_size,
- const base::TimeTicks& timestamp) {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
-
- DVLOG_IF(1, !gl_helper_) << " Skipping ingress frame, no GL context.";
- if (!gl_helper_)
- return;
-
- DVLOG_IF(1, gpu_memory_buffers_.empty()) << " Skipping ingress frame, 0 GMBs";
- if (gpu_memory_buffers_.empty())
- return;
-
- linked_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
- gpu_memory_buffers_.front();
- gpu_memory_buffers_.pop();
- DCHECK(gpu_memory_buffer.get());
-
- void* data = NULL;
- bool rv = gpu_memory_buffer->Map(&data);
- DCHECK(rv);
- uint32 stride;
- gpu_memory_buffer->GetStride(&stride);
-
- uint8* mapped_buffer = static_cast<uint8*>(data);
- DCHECK(mapped_buffer);
- libyuv::ARGBCopy(
- reinterpret_cast<uint8*>(argb_buffer->data()), frame_size.width() * 4,
- mapped_buffer, stride,
- frame_size.width(), frame_size.height());
- gpu_memory_buffer->Unmap();
-
- gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL();
- GLuint image_id = gl->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(),
- frame_size.width(),
- frame_size.height(), GL_BGRA_EXT);
- DCHECK(image_id);
-
- GLuint texture_id = gl_helper_->CreateTexture();
- DCHECK(texture_id);
- {
- content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_id);
- gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id);
- }
-
- scoped_ptr<gpu::MailboxHolder> mailbox_holder(new gpu::MailboxHolder(
- gl_helper_->ProduceMailboxHolderFromTexture(texture_id)));
- DCHECK(!mailbox_holder->mailbox.IsZero());
- DCHECK(mailbox_holder->mailbox.Verify());
- DCHECK(mailbox_holder->texture_target);
- DCHECK(mailbox_holder->sync_point);
-
- scoped_refptr<media::VideoFrame> video_frame =
- media::VideoFrame::WrapNativeTexture(
- mailbox_holder.Pass(),
- media::BindToCurrentLoop(
- base::Bind(&VideoCaptureTextureWrapper::TextureWrapperDelegate::
- ReleaseCallback,
- this, image_id, texture_id, gpu_memory_buffer)),
- frame_size,
- gfx::Rect(frame_size),
- frame_size,
- base::TimeDelta(),
- true /* allow_overlay */);
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(
- &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
- controller_, texture_buffer, video_frame, timestamp));
-}
-
-VideoCaptureTextureWrapper::TextureWrapperDelegate::~TextureWrapperDelegate() {
- // Might not be running on capture_task_runner_'s thread. Ensure owned objects
- // are destroyed on the correct threads.
- while (!gpu_memory_buffers_.empty()) {
- capture_task_runner_->DeleteSoon(FROM_HERE,
- gpu_memory_buffers_.front().release());
- gpu_memory_buffers_.pop();
- }
- if (gl_helper_)
- capture_task_runner_->DeleteSoon(FROM_HERE, gl_helper_.release());
-
- if (capture_thread_context_) {
- capture_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&ResetLostContextCallback, capture_thread_context_));
- capture_thread_context_->AddRef();
- ContextProviderCommandBuffer* raw_capture_thread_context =
- capture_thread_context_.get();
- capture_thread_context_ = nullptr;
- capture_task_runner_->ReleaseSoon(FROM_HERE, raw_capture_thread_context);
- }
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::Init(
- const media::VideoCaptureFormat& capture_format) {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
-
- // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
- // TODO(mcasas): At this point |frame_format| represents the format we want to
- // get from the VCDevice, but this might send another format.
- for (size_t i = 0; i < kNumGpuMemoryBuffers; ++i) {
- linked_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer(
- BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
- capture_format.frame_size,
- gfx::GpuMemoryBuffer::BGRA_8888,
- gfx::GpuMemoryBuffer::MAP).release());
- if (!gpu_memory_buffer.get()) {
- OnError("Could not allocate GpuMemoryBuffer");
- while(!gpu_memory_buffers_.empty())
- gpu_memory_buffers_.pop();
- return;
- }
- gpu_memory_buffers_.push(gpu_memory_buffer);
- }
-
- // In threaded compositing mode, we have to create our own context for Capture
- // to avoid using the GPU command queue from multiple threads. Context
- // creation must happen on UI thread; then the context needs to be bound to
- // the appropriate thread, which is done in CreateGlHelper().
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&CreateContextOnUIThread,
- media::BindToCurrentLoop(
- base::Bind(&VideoCaptureTextureWrapper::
- TextureWrapperDelegate::CreateGlHelper,
- this))));
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::CreateGlHelper(
- scoped_refptr<ContextProviderCommandBuffer> capture_thread_context) {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
-
- if (!capture_thread_context.get()) {
- DLOG(ERROR) << "No offscreen GL Context!";
- return;
- }
- // This may not happen in IO Thread. The destructor resets the context lost
- // callback, so base::Unretained is safe; otherwise it'd be a circular ref
- // counted dependency.
- capture_thread_context->SetLostContextCallback(media::BindToCurrentLoop(
- base::Bind(
- &VideoCaptureTextureWrapper::TextureWrapperDelegate::
- LostContextCallback,
- base::Unretained(this))));
- if (!capture_thread_context->BindToCurrentThread()) {
- capture_thread_context = NULL;
- DLOG(ERROR) << "Couldn't bind the Capture Context to the Capture Thread.";
- return;
- }
- DCHECK(capture_thread_context);
- capture_thread_context_ = capture_thread_context;
-
- // At this point, |capture_thread_context| is a cc::ContextProvider. Creation
- // of our GLHelper should happen on Capture Thread.
- gl_helper_.reset(new GLHelper(capture_thread_context->ContextGL(),
- capture_thread_context->ContextSupport()));
- DCHECK(gl_helper_);
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::ReleaseCallback(
- GLuint image_id,
- GLuint texture_id,
- linked_ptr<gfx::GpuMemoryBuffer> memory_buffer,
- uint32 sync_point) {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
-
- // TODO(mcasas): Before recycling |memory_buffer| we have to make sure it has
- // been consumed and fully used.
- gpu_memory_buffers_.push(memory_buffer);
-
- if (gl_helper_) {
- gl_helper_->DeleteTexture(texture_id);
- capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id);
- }
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::LostContextCallback() {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
- // Prevent incoming frames from being processed while OnError gets groked.
- gl_helper_.reset();
- OnError("GLContext lost");
-}
-
-void VideoCaptureTextureWrapper::TextureWrapperDelegate::OnError(
- const std::string& message) {
- DCHECK(capture_task_runner_->BelongsToCurrentThread());
- DLOG(ERROR) << message;
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
-}
-
-} // namespace content

Powered by Google App Engine
This is Rietveld 408576698