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

Unified Diff: content/browser/renderer_host/media/video_capture_device_client.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_device_client.cc
diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/content/browser/renderer_host/media/video_capture_device_client.cc
index 66b7fdb3d5d8064233e13585558b4a3894daac4e..55bb8cc5431b467eb0426b1bc23b29e31a063dfa 100644
--- a/content/browser/renderer_host/media/video_capture_device_client.cc
+++ b/content/browser/renderer_host/media/video_capture_device_client.cc
@@ -7,12 +7,23 @@
#include "base/bind.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.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_buffer_pool.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"
using media::VideoCaptureFormat;
@@ -20,39 +31,181 @@ using media::VideoFrame;
namespace content {
+namespace {
+
+#if !defined(OS_ANDROID)
+// 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;
+ // Android does not support synchronous opening of GPU channels. Should use
+ // EstablishGpuChannel() instead.
+ scoped_refptr<content::GpuChannelHost> gpu_channel_host(
+ content::BrowserGpuChannelHostFactory::instance()->
+ EstablishGpuChannelSync(cause));
+ DCHECK(gpu_channel_host);
+ return CreateContextCommon(gpu_channel_host, 0);
+}
+#endif
+
+typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)>
+ ProcessContextCallback;
+
+void CreateContextOnUIThread(ProcessContextCallback bottom_half) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+#if !defined(OS_ANDROID)
+ bottom_half.Run(ContextProviderCommandBuffer::Create(
+ CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread"));
+ return;
+#endif
+}
+
+void ResetLostContextCallback(
+ const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) {
+ capture_thread_context->SetLostContextCallback(
+ cc::ContextProvider::LostContextCallback());
+}
+
+} // anonymous namespace
+
// Class combining a Client::Buffer interface implementation and a pool buffer
// implementation to guarantee proper cleanup on destruction on our side.
class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
public:
AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
- int buffer_id,
- void* data,
- size_t size)
+ int buffer_id)
: pool_(pool),
id_(buffer_id),
- data_(data),
- size_(size) {
+ buffer_handle_(pool_->GetBufferHandle(buffer_id).Pass()) {
DCHECK(pool_.get());
}
int id() const override { return id_; }
- void* data() const override { return data_; }
- size_t size() const override { return size_; }
+ size_t size() const override { return buffer_handle_->size(); }
+ scoped_ptr<media::DataHandle> GetDataHandle() override {
+ return buffer_handle_->GetDataHandle();
+ }
+ ClientBuffer AsClientBuffer() override {
+ return buffer_handle_->AsClientBuffer();
+ }
private:
~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
const scoped_refptr<VideoCaptureBufferPool> pool_;
const int id_;
- void* const data_;
- const size_t size_;
+ const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_;
+};
+
+// Internal ref-counted class wrap an incoming GpuMemoryBuffer into a Texture
+// backed 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_|).
+class VideoCaptureDeviceClient::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);
+
+ // Wraps the GpuMemoryBuffer-backed |buffer| into a Texture, and sends it to
+ // |controller_| wrapped in a VideoFrame.
+ void OnIncomingCapturedGpuMemoryBuffer(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextureWrapperDelegate);
};
VideoCaptureDeviceClient::VideoCaptureDeviceClient(
const base::WeakPtr<VideoCaptureController>& controller,
- const scoped_refptr<VideoCaptureBufferPool>& buffer_pool)
+ const scoped_refptr<VideoCaptureBufferPool>& buffer_pool,
+ const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner,
+ const media::VideoCaptureFormat& capture_format)
: controller_(controller),
buffer_pool_(buffer_pool),
- last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {}
+ wrapper_delegate_(
+ (capture_format.pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER)
+ ? new TextureWrapperDelegate(controller,
+ capture_task_runner,
+ capture_format)
+ : nullptr),
+ capture_task_runner_(capture_task_runner),
+ last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
@@ -110,7 +263,9 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData(
if (!buffer.get())
return;
- uint8* const yplane = reinterpret_cast<uint8*>(buffer->data());
+ const scoped_ptr<media::DataHandle> scoped_data_handle =
+ buffer->GetDataHandle().Pass();
+ uint8* const yplane = reinterpret_cast<uint8*>(scoped_data_handle->data());
uint8* const uplane =
yplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420,
VideoFrame::kYPlane, dimensions);
@@ -201,31 +356,12 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData(
frame_format.pixel_format);
return;
}
- scoped_refptr<VideoFrame> frame =
- VideoFrame::WrapExternalPackedMemory(
- VideoFrame::I420,
- dimensions,
- gfx::Rect(dimensions),
- dimensions,
- yplane,
- VideoFrame::AllocationSize(VideoFrame::I420, dimensions),
- base::SharedMemory::NULLHandle(),
- 0,
- base::TimeDelta(),
- base::Closure());
- DCHECK(frame.get());
- frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
- frame_format.frame_rate);
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(
- &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
- controller_,
- buffer,
- frame,
- timestamp));
+ OnIncomingCapturedBuffer(buffer,
+ media::VideoCaptureFormat(dimensions,
+ frame_format.frame_rate,
+ media::PIXEL_FORMAT_I420),
+ timestamp);
}
void
@@ -247,6 +383,8 @@ VideoCaptureDeviceClient::OnIncomingCapturedYuvData(
ReserveOutputBuffer(frame_format.pixel_format, frame_format.frame_size);
if (!buffer.get())
return;
+ const scoped_ptr<media::DataHandle> scoped_data_handle =
+ buffer->GetDataHandle().Pass();
// Blit (copy) here from y,u,v into buffer.data()). Needed so we can return
// the parameter buffer synchronously to the driver.
@@ -254,7 +392,7 @@ VideoCaptureDeviceClient::OnIncomingCapturedYuvData(
VideoFrame::kYPlane, frame_format.frame_size);
const size_t u_plane_size = VideoFrame::PlaneAllocationSize(
VideoFrame::I420, VideoFrame::kUPlane, frame_format.frame_size);
- uint8* const dst_y = reinterpret_cast<uint8*>(buffer->data());
+ uint8* const dst_y = reinterpret_cast<uint8*>(scoped_data_handle->data());
uint8* const dst_u = dst_y + y_plane_size;
uint8* const dst_v = dst_u + u_plane_size;
@@ -280,32 +418,15 @@ VideoCaptureDeviceClient::OnIncomingCapturedYuvData(
return;
}
- scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData(
- VideoFrame::I420, frame_format.frame_size,
- gfx::Rect(frame_format.frame_size), frame_format.frame_size, y_stride,
- u_stride, v_stride, dst_y, dst_u, dst_v, base::TimeDelta(),
- base::Closure());
- DCHECK(video_frame.get());
- video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
- frame_format.frame_rate);
-
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(
- &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
- controller_,
- buffer,
- video_frame,
- timestamp));
+ OnIncomingCapturedBuffer(buffer, frame_format, timestamp);
};
scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format,
const gfx::Size& dimensions) {
- DCHECK(format == media::PIXEL_FORMAT_TEXTURE ||
- format == media::PIXEL_FORMAT_I420 ||
- format == media::PIXEL_FORMAT_ARGB);
+ DCHECK(format == media::PIXEL_FORMAT_I420 ||
+ format == media::PIXEL_FORMAT_TEXTURE ||
+ format == media::PIXEL_FORMAT_GPUMEMORYBUFFER);
DCHECK_GT(dimensions.width(), 0);
DCHECK_GT(dimensions.height(), 0);
@@ -314,12 +435,9 @@ VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format,
buffer_pool_->ReserveForProducer(format, dimensions, &buffer_id_to_drop);
if (buffer_id == VideoCaptureBufferPool::kInvalidId)
return NULL;
- void* data;
- size_t size;
- buffer_pool_->GetBufferInfo(buffer_id, &data, &size);
scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
- new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size));
+ new AutoReleaseBuffer(buffer_pool_, buffer_id));
if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
BrowserThread::PostTask(BrowserThread::IO,
@@ -331,8 +449,47 @@ VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format,
return output_buffer;
}
-void
-VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
+void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
+ const scoped_refptr<Buffer>& buffer,
+ const media::VideoCaptureFormat& frame_format,
+ const base::TimeTicks& timestamp) {
+ if (frame_format.pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER) {
+ capture_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &TextureWrapperDelegate::OnIncomingCapturedGpuMemoryBuffer,
+ wrapper_delegate_,
+ buffer,
+ frame_format.frame_size,
+ timestamp));
+ } else {
+ const scoped_ptr<media::DataHandle> scoped_data_handle =
+ buffer->GetDataHandle().Pass();
+
+ scoped_refptr<VideoFrame> video_frame =
+ VideoFrame::WrapExternalPackedMemory(
+ VideoFrame::I420,
+ frame_format.frame_size,
+ gfx::Rect(frame_format.frame_size),
+ frame_format.frame_size,
+ reinterpret_cast<uint8*>(scoped_data_handle->data()),
+ VideoFrame::AllocationSize(VideoFrame::I420,
+ frame_format.frame_size),
+ base::SharedMemory::NULLHandle(),
+ 0 /* shared_memory_offset */,
+ base::TimeDelta(),
+ base::Closure());
+ DCHECK(video_frame.get());
+ video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
+ frame_format.frame_rate);
+
+ OnIncomingCapturedVideoFrame(buffer,
+ video_frame,
+ timestamp);
+ }
+}
+
+void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
const scoped_refptr<Buffer>& buffer,
const scoped_refptr<VideoFrame>& frame,
const base::TimeTicks& timestamp) {
@@ -368,4 +525,159 @@ void VideoCaptureDeviceClient::OnLog(
controller_, message));
}
+VideoCaptureDeviceClient::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 VideoCaptureDeviceClient::TextureWrapperDelegate::
+ OnIncomingCapturedGpuMemoryBuffer(
+ const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& 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;
+
+ gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL();
+ GLuint image_id = gl->CreateImageCHROMIUM(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(&VideoCaptureDeviceClient::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_, buffer, video_frame, timestamp));
+}
+
+VideoCaptureDeviceClient::TextureWrapperDelegate::~TextureWrapperDelegate() {
+ // Might not be running on capture_task_runner_'s thread. Ensure owned objects
+ // are destroyed on the correct threads.
+ 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 VideoCaptureDeviceClient::TextureWrapperDelegate::Init(
+ const media::VideoCaptureFormat& capture_format) {
+ DCHECK(capture_task_runner_->BelongsToCurrentThread());
+
+ // 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(&VideoCaptureDeviceClient::
+ TextureWrapperDelegate::CreateGlHelper,
+ this))));
+}
+
+void VideoCaptureDeviceClient::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(
+ &VideoCaptureDeviceClient::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 VideoCaptureDeviceClient::TextureWrapperDelegate::ReleaseCallback(
+ GLuint image_id,
+ GLuint texture_id,
+ uint32 sync_point) {
+ DCHECK(capture_task_runner_->BelongsToCurrentThread());
+
+ if (gl_helper_) {
+ gl_helper_->DeleteTexture(texture_id);
+ capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id);
+ }
+}
+
+void VideoCaptureDeviceClient::TextureWrapperDelegate::LostContextCallback() {
+ DCHECK(capture_task_runner_->BelongsToCurrentThread());
+ // Prevent incoming frames from being processed while OnError gets groked.
+ gl_helper_.reset();
+ OnError("GLContext lost");
+}
+
+void VideoCaptureDeviceClient::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