| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/video_capture_device_client.h" | 5 #include "content/browser/renderer_host/media/video_capture_device_client.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
| 9 #include "base/trace_event/trace_event.h" | 9 #include "base/trace_event/trace_event.h" |
| 10 #include "content/browser/compositor/image_transport_factory.h" |
| 11 #include "content/browser/gpu/browser_gpu_channel_host_factory.h" |
| 12 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" |
| 13 #include "content/browser/gpu/gpu_data_manager_impl.h" |
| 10 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" | 14 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" |
| 11 #include "content/browser/renderer_host/media/video_capture_controller.h" | 15 #include "content/browser/renderer_host/media/video_capture_controller.h" |
| 16 #include "content/common/gpu/client/context_provider_command_buffer.h" |
| 17 #include "content/common/gpu/client/gl_helper.h" |
| 18 #include "content/common/gpu/client/gpu_channel_host.h" |
| 19 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" |
| 20 #include "content/common/gpu/gpu_process_launch_causes.h" |
| 12 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
| 22 #include "gpu/command_buffer/common/mailbox_holder.h" |
| 13 #include "media/base/bind_to_current_loop.h" | 23 #include "media/base/bind_to_current_loop.h" |
| 14 #include "media/base/video_capture_types.h" | 24 #include "media/base/video_capture_types.h" |
| 15 #include "media/base/video_frame.h" | 25 #include "media/base/video_frame.h" |
| 26 #include "third_party/khronos/GLES2/gl2ext.h" |
| 16 #include "third_party/libyuv/include/libyuv.h" | 27 #include "third_party/libyuv/include/libyuv.h" |
| 17 | 28 |
| 18 using media::VideoCaptureFormat; | 29 using media::VideoCaptureFormat; |
| 19 using media::VideoFrame; | 30 using media::VideoFrame; |
| 20 | 31 |
| 21 namespace content { | 32 namespace content { |
| 22 | 33 |
| 34 namespace { |
| 35 |
| 36 #if !defined(OS_ANDROID) |
| 37 // Modelled after GpuProcessTransportFactory::CreateContextCommon(). |
| 38 scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> CreateContextCommon( |
| 39 scoped_refptr<content::GpuChannelHost> gpu_channel_host, |
| 40 int surface_id) { |
| 41 if (!content::GpuDataManagerImpl::GetInstance()-> |
| 42 CanUseGpuBrowserCompositor()) { |
| 43 DLOG(ERROR) << "No accelerated graphics found. Check chrome://gpu"; |
| 44 return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); |
| 45 } |
| 46 blink::WebGraphicsContext3D::Attributes attrs; |
| 47 attrs.shareResources = true; |
| 48 attrs.depth = false; |
| 49 attrs.stencil = false; |
| 50 attrs.antialias = false; |
| 51 attrs.noAutomaticFlushes = true; |
| 52 |
| 53 if (!gpu_channel_host.get()) { |
| 54 DLOG(ERROR) << "Failed to establish GPU channel."; |
| 55 return scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl>(); |
| 56 } |
| 57 GURL url("chrome://gpu/GpuProcessTransportFactory::CreateCaptureContext"); |
| 58 return make_scoped_ptr( |
| 59 new WebGraphicsContext3DCommandBufferImpl( |
| 60 surface_id, |
| 61 url, |
| 62 gpu_channel_host.get(), |
| 63 attrs, |
| 64 true /* lose_context_when_out_of_memory */, |
| 65 content::WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), |
| 66 NULL)); |
| 67 } |
| 68 |
| 69 // Modelled after |
| 70 // GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(). |
| 71 scoped_ptr<content::WebGraphicsContext3DCommandBufferImpl> |
| 72 CreateOffscreenCommandBufferContext() { |
| 73 content::CauseForGpuLaunch cause = content::CAUSE_FOR_GPU_LAUNCH_CANVAS_2D; |
| 74 // Android does not support synchronous opening of GPU channels. Should use |
| 75 // EstablishGpuChannel() instead. |
| 76 scoped_refptr<content::GpuChannelHost> gpu_channel_host( |
| 77 content::BrowserGpuChannelHostFactory::instance()-> |
| 78 EstablishGpuChannelSync(cause)); |
| 79 DCHECK(gpu_channel_host); |
| 80 return CreateContextCommon(gpu_channel_host, 0); |
| 81 } |
| 82 #endif |
| 83 |
| 84 typedef base::Callback<void(scoped_refptr<ContextProviderCommandBuffer>)> |
| 85 ProcessContextCallback; |
| 86 |
| 87 void CreateContextOnUIThread(ProcessContextCallback bottom_half) { |
| 88 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 89 #if !defined(OS_ANDROID) |
| 90 bottom_half.Run(ContextProviderCommandBuffer::Create( |
| 91 CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread")); |
| 92 return; |
| 93 #endif |
| 94 } |
| 95 |
| 96 void ResetLostContextCallback( |
| 97 const scoped_refptr<ContextProviderCommandBuffer>& capture_thread_context) { |
| 98 capture_thread_context->SetLostContextCallback( |
| 99 cc::ContextProvider::LostContextCallback()); |
| 100 } |
| 101 |
| 102 } // anonymous namespace |
| 103 |
| 23 // Class combining a Client::Buffer interface implementation and a pool buffer | 104 // Class combining a Client::Buffer interface implementation and a pool buffer |
| 24 // implementation to guarantee proper cleanup on destruction on our side. | 105 // implementation to guarantee proper cleanup on destruction on our side. |
| 25 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { | 106 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { |
| 26 public: | 107 public: |
| 27 AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, | 108 AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool, |
| 28 int buffer_id, | 109 int buffer_id) |
| 29 void* data, | |
| 30 size_t size) | |
| 31 : pool_(pool), | 110 : pool_(pool), |
| 32 id_(buffer_id), | 111 id_(buffer_id), |
| 33 data_(data), | 112 buffer_handle_(pool_->GetBufferHandle(buffer_id).Pass()) { |
| 34 size_(size) { | |
| 35 DCHECK(pool_.get()); | 113 DCHECK(pool_.get()); |
| 36 } | 114 } |
| 37 int id() const override { return id_; } | 115 int id() const override { return id_; } |
| 38 void* data() const override { return data_; } | 116 size_t size() const override { return buffer_handle_->size(); } |
| 39 size_t size() const override { return size_; } | 117 scoped_ptr<media::DataHandle> GetDataHandle() override { |
| 118 return buffer_handle_->GetDataHandle(); |
| 119 } |
| 120 ClientBuffer AsClientBuffer() override { |
| 121 return buffer_handle_->AsClientBuffer(); |
| 122 } |
| 40 | 123 |
| 41 private: | 124 private: |
| 42 ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } | 125 ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); } |
| 43 | 126 |
| 44 const scoped_refptr<VideoCaptureBufferPool> pool_; | 127 const scoped_refptr<VideoCaptureBufferPool> pool_; |
| 45 const int id_; | 128 const int id_; |
| 46 void* const data_; | 129 const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_; |
| 47 const size_t size_; | 130 }; |
| 131 |
| 132 // Internal ref-counted class wrap an incoming GpuMemoryBuffer into a Texture |
| 133 // backed VideoFrame. This VideoFrame creation is balanced by a waiting on the |
| 134 // associated |sync_point|. After VideoFrame consumption the inserted |
| 135 // ReleaseCallback() will be called, where the GpuMemoryBuffer is recycled. |
| 136 // |
| 137 // This class jumps between threads due to GPU-related thread limitations, i.e. |
| 138 // some objects cannot be accessed from IO Thread, where we are constructed, |
| 139 // others need to be constructed on UI Thread. For this reason most of the |
| 140 // operations are carried out on Capture Thread (|capture_task_runner_|). |
| 141 class VideoCaptureDeviceClient::TextureWrapperDelegate final |
| 142 : public base::RefCountedThreadSafe<TextureWrapperDelegate> { |
| 143 public: |
| 144 TextureWrapperDelegate( |
| 145 const base::WeakPtr<VideoCaptureController>& controller, |
| 146 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, |
| 147 const media::VideoCaptureFormat& capture_format); |
| 148 |
| 149 // Wraps the GpuMemoryBuffer-backed |buffer| into a Texture, and sends it to |
| 150 // |controller_| wrapped in a VideoFrame. |
| 151 void OnIncomingCapturedGpuMemoryBuffer( |
| 152 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, |
| 153 const gfx::Size& frame_size, |
| 154 const base::TimeTicks& timestamp); |
| 155 |
| 156 private: |
| 157 friend class base::RefCountedThreadSafe<TextureWrapperDelegate>; |
| 158 ~TextureWrapperDelegate(); |
| 159 |
| 160 // Creates some necessary members in |capture_task_runner_|. |
| 161 void Init(const media::VideoCaptureFormat& capture_format); |
| 162 // Runs the bottom half of the GlHelper creation. |
| 163 void CreateGlHelper( |
| 164 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context); |
| 165 |
| 166 // Recycles |memory_buffer|, deletes Image and Texture on VideoFrame release. |
| 167 void ReleaseCallback(GLuint image_id, |
| 168 GLuint texture_id, |
| 169 //linked_ptr<gfx::GpuMemoryBuffer> memory_buffer, |
| 170 uint32 sync_point); |
| 171 |
| 172 // The Command Buffer lost the GL context, f.i. GPU process crashed. Signal |
| 173 // error to our owner so the capture can be torn down. |
| 174 void LostContextCallback(); |
| 175 |
| 176 // Prints the error |message| and notifies |controller_| of an error. |
| 177 void OnError(const std::string& message); |
| 178 |
| 179 const base::WeakPtr<VideoCaptureController> controller_; |
| 180 const scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; |
| 181 |
| 182 // Command buffer reference, needs to be destroyed when unused. It is created |
| 183 // on UI Thread and bound to Capture Thread. In particular, it cannot be used |
| 184 // from IO Thread. |
| 185 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context_; |
| 186 // Created and used from Capture Thread. Cannot be used from IO Thread. |
| 187 scoped_ptr<GLHelper> gl_helper_; |
| 188 |
| 189 DISALLOW_COPY_AND_ASSIGN(TextureWrapperDelegate); |
| 48 }; | 190 }; |
| 49 | 191 |
| 50 VideoCaptureDeviceClient::VideoCaptureDeviceClient( | 192 VideoCaptureDeviceClient::VideoCaptureDeviceClient( |
| 51 const base::WeakPtr<VideoCaptureController>& controller, | 193 const base::WeakPtr<VideoCaptureController>& controller, |
| 52 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) | 194 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool, |
| 195 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, |
| 196 const media::VideoCaptureFormat& capture_format) |
| 53 : controller_(controller), | 197 : controller_(controller), |
| 54 buffer_pool_(buffer_pool), | 198 buffer_pool_(buffer_pool), |
| 55 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {} | 199 wrapper_delegate_( |
| 200 (capture_format.pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER) |
| 201 ? new TextureWrapperDelegate(controller, |
| 202 capture_task_runner, |
| 203 capture_format) |
| 204 : nullptr), |
| 205 capture_task_runner_(capture_task_runner), |
| 206 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) { |
| 207 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 208 } |
| 56 | 209 |
| 57 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} | 210 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} |
| 58 | 211 |
| 59 void VideoCaptureDeviceClient::OnIncomingCapturedData( | 212 void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| 60 const uint8* data, | 213 const uint8* data, |
| 61 int length, | 214 int length, |
| 62 const VideoCaptureFormat& frame_format, | 215 const VideoCaptureFormat& frame_format, |
| 63 int rotation, | 216 int rotation, |
| 64 const base::TimeTicks& timestamp) { | 217 const base::TimeTicks& timestamp) { |
| 65 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData"); | 218 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedData"); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 gfx::Rect(dimensions), | 256 gfx::Rect(dimensions), |
| 104 dimensions)) { | 257 dimensions)) { |
| 105 return; | 258 return; |
| 106 } | 259 } |
| 107 | 260 |
| 108 scoped_refptr<Buffer> buffer = | 261 scoped_refptr<Buffer> buffer = |
| 109 ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dimensions); | 262 ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dimensions); |
| 110 if (!buffer.get()) | 263 if (!buffer.get()) |
| 111 return; | 264 return; |
| 112 | 265 |
| 113 uint8* const yplane = reinterpret_cast<uint8*>(buffer->data()); | 266 const scoped_ptr<media::DataHandle> scoped_data_handle = |
| 267 buffer->GetDataHandle().Pass(); |
| 268 uint8* const yplane = reinterpret_cast<uint8*>(scoped_data_handle->data()); |
| 114 uint8* const uplane = | 269 uint8* const uplane = |
| 115 yplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, | 270 yplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, |
| 116 VideoFrame::kYPlane, dimensions); | 271 VideoFrame::kYPlane, dimensions); |
| 117 uint8* const vplane = | 272 uint8* const vplane = |
| 118 uplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, | 273 uplane + VideoFrame::PlaneAllocationSize(VideoFrame::I420, |
| 119 VideoFrame::kUPlane, dimensions); | 274 VideoFrame::kUPlane, dimensions); |
| 120 int yplane_stride = dimensions.width(); | 275 int yplane_stride = dimensions.width(); |
| 121 int uv_plane_stride = yplane_stride / 2; | 276 int uv_plane_stride = yplane_stride / 2; |
| 122 int crop_x = 0; | 277 int crop_x = 0; |
| 123 int crop_y = 0; | 278 int crop_y = 0; |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 194 (flip ? -1 : 1) * frame_format.frame_size.height(), | 349 (flip ? -1 : 1) * frame_format.frame_size.height(), |
| 195 new_unrotated_width, | 350 new_unrotated_width, |
| 196 new_unrotated_height, | 351 new_unrotated_height, |
| 197 rotation_mode, | 352 rotation_mode, |
| 198 origin_colorspace) != 0) { | 353 origin_colorspace) != 0) { |
| 199 DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from " | 354 DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from " |
| 200 << media::VideoCaptureFormat::PixelFormatToString( | 355 << media::VideoCaptureFormat::PixelFormatToString( |
| 201 frame_format.pixel_format); | 356 frame_format.pixel_format); |
| 202 return; | 357 return; |
| 203 } | 358 } |
| 204 scoped_refptr<VideoFrame> frame = | |
| 205 VideoFrame::WrapExternalPackedMemory( | |
| 206 VideoFrame::I420, | |
| 207 dimensions, | |
| 208 gfx::Rect(dimensions), | |
| 209 dimensions, | |
| 210 yplane, | |
| 211 VideoFrame::AllocationSize(VideoFrame::I420, dimensions), | |
| 212 base::SharedMemory::NULLHandle(), | |
| 213 0, | |
| 214 base::TimeDelta(), | |
| 215 base::Closure()); | |
| 216 DCHECK(frame.get()); | |
| 217 frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, | |
| 218 frame_format.frame_rate); | |
| 219 | 359 |
| 220 BrowserThread::PostTask( | 360 OnIncomingCapturedBuffer(buffer, |
| 221 BrowserThread::IO, | 361 media::VideoCaptureFormat(dimensions, |
| 222 FROM_HERE, | 362 frame_format.frame_rate, |
| 223 base::Bind( | 363 media::PIXEL_FORMAT_I420), |
| 224 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, | 364 timestamp); |
| 225 controller_, | |
| 226 buffer, | |
| 227 frame, | |
| 228 timestamp)); | |
| 229 } | 365 } |
| 230 | 366 |
| 231 void | 367 void |
| 232 VideoCaptureDeviceClient::OnIncomingCapturedYuvData( | 368 VideoCaptureDeviceClient::OnIncomingCapturedYuvData( |
| 233 const uint8* y_data, | 369 const uint8* y_data, |
| 234 const uint8* u_data, | 370 const uint8* u_data, |
| 235 const uint8* v_data, | 371 const uint8* v_data, |
| 236 size_t y_stride, | 372 size_t y_stride, |
| 237 size_t u_stride, | 373 size_t u_stride, |
| 238 size_t v_stride, | 374 size_t v_stride, |
| 239 const VideoCaptureFormat& frame_format, | 375 const VideoCaptureFormat& frame_format, |
| 240 int clockwise_rotation, | 376 int clockwise_rotation, |
| 241 const base::TimeTicks& timestamp) { | 377 const base::TimeTicks& timestamp) { |
| 242 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedYuvData"); | 378 TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedYuvData"); |
| 243 DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_I420); | 379 DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_I420); |
| 244 DCHECK_EQ(clockwise_rotation, 0) << "Rotation not supported"; | 380 DCHECK_EQ(clockwise_rotation, 0) << "Rotation not supported"; |
| 245 | 381 |
| 246 scoped_refptr<Buffer> buffer = | 382 scoped_refptr<Buffer> buffer = |
| 247 ReserveOutputBuffer(frame_format.pixel_format, frame_format.frame_size); | 383 ReserveOutputBuffer(frame_format.pixel_format, frame_format.frame_size); |
| 248 if (!buffer.get()) | 384 if (!buffer.get()) |
| 249 return; | 385 return; |
| 386 const scoped_ptr<media::DataHandle> scoped_data_handle = |
| 387 buffer->GetDataHandle().Pass(); |
| 250 | 388 |
| 251 // Blit (copy) here from y,u,v into buffer.data()). Needed so we can return | 389 // Blit (copy) here from y,u,v into buffer.data()). Needed so we can return |
| 252 // the parameter buffer synchronously to the driver. | 390 // the parameter buffer synchronously to the driver. |
| 253 const size_t y_plane_size = VideoFrame::PlaneAllocationSize(VideoFrame::I420, | 391 const size_t y_plane_size = VideoFrame::PlaneAllocationSize(VideoFrame::I420, |
| 254 VideoFrame::kYPlane, frame_format.frame_size); | 392 VideoFrame::kYPlane, frame_format.frame_size); |
| 255 const size_t u_plane_size = VideoFrame::PlaneAllocationSize( | 393 const size_t u_plane_size = VideoFrame::PlaneAllocationSize( |
| 256 VideoFrame::I420, VideoFrame::kUPlane, frame_format.frame_size); | 394 VideoFrame::I420, VideoFrame::kUPlane, frame_format.frame_size); |
| 257 uint8* const dst_y = reinterpret_cast<uint8*>(buffer->data()); | 395 uint8* const dst_y = reinterpret_cast<uint8*>(scoped_data_handle->data()); |
| 258 uint8* const dst_u = dst_y + y_plane_size; | 396 uint8* const dst_u = dst_y + y_plane_size; |
| 259 uint8* const dst_v = dst_u + u_plane_size; | 397 uint8* const dst_v = dst_u + u_plane_size; |
| 260 | 398 |
| 261 const size_t dst_y_stride = VideoFrame::RowBytes( | 399 const size_t dst_y_stride = VideoFrame::RowBytes( |
| 262 VideoFrame::kYPlane, VideoFrame::I420, frame_format.frame_size.width()); | 400 VideoFrame::kYPlane, VideoFrame::I420, frame_format.frame_size.width()); |
| 263 const size_t dst_u_stride = VideoFrame::RowBytes( | 401 const size_t dst_u_stride = VideoFrame::RowBytes( |
| 264 VideoFrame::kUPlane, VideoFrame::I420, frame_format.frame_size.width()); | 402 VideoFrame::kUPlane, VideoFrame::I420, frame_format.frame_size.width()); |
| 265 const size_t dst_v_stride = VideoFrame::RowBytes( | 403 const size_t dst_v_stride = VideoFrame::RowBytes( |
| 266 VideoFrame::kVPlane, VideoFrame::I420, frame_format.frame_size.width()); | 404 VideoFrame::kVPlane, VideoFrame::I420, frame_format.frame_size.width()); |
| 267 DCHECK_GE(y_stride, dst_y_stride); | 405 DCHECK_GE(y_stride, dst_y_stride); |
| 268 DCHECK_GE(u_stride, dst_u_stride); | 406 DCHECK_GE(u_stride, dst_u_stride); |
| 269 DCHECK_GE(v_stride, dst_v_stride); | 407 DCHECK_GE(v_stride, dst_v_stride); |
| 270 | 408 |
| 271 if (libyuv::I420Copy(y_data, y_stride, | 409 if (libyuv::I420Copy(y_data, y_stride, |
| 272 u_data, u_stride, | 410 u_data, u_stride, |
| 273 v_data, v_stride, | 411 v_data, v_stride, |
| 274 dst_y, dst_y_stride, | 412 dst_y, dst_y_stride, |
| 275 dst_u, dst_u_stride, | 413 dst_u, dst_u_stride, |
| 276 dst_v, dst_v_stride, | 414 dst_v, dst_v_stride, |
| 277 frame_format.frame_size.width(), | 415 frame_format.frame_size.width(), |
| 278 frame_format.frame_size.height())) { | 416 frame_format.frame_size.height())) { |
| 279 DLOG(WARNING) << "Failed to copy buffer"; | 417 DLOG(WARNING) << "Failed to copy buffer"; |
| 280 return; | 418 return; |
| 281 } | 419 } |
| 282 | 420 |
| 283 scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapExternalYuvData( | 421 OnIncomingCapturedBuffer(buffer, frame_format, timestamp); |
| 284 VideoFrame::I420, frame_format.frame_size, | |
| 285 gfx::Rect(frame_format.frame_size), frame_format.frame_size, y_stride, | |
| 286 u_stride, v_stride, dst_y, dst_u, dst_v, base::TimeDelta(), | |
| 287 base::Closure()); | |
| 288 DCHECK(video_frame.get()); | |
| 289 video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, | |
| 290 frame_format.frame_rate); | |
| 291 | |
| 292 BrowserThread::PostTask( | |
| 293 BrowserThread::IO, | |
| 294 FROM_HERE, | |
| 295 base::Bind( | |
| 296 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, | |
| 297 controller_, | |
| 298 buffer, | |
| 299 video_frame, | |
| 300 timestamp)); | |
| 301 }; | 422 }; |
| 302 | 423 |
| 303 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> | 424 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> |
| 304 VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format, | 425 VideoCaptureDeviceClient::ReserveOutputBuffer(media::VideoPixelFormat format, |
| 305 const gfx::Size& dimensions) { | 426 const gfx::Size& dimensions) { |
| 306 DCHECK(format == media::PIXEL_FORMAT_TEXTURE || | 427 DCHECK(format == media::PIXEL_FORMAT_I420 || |
| 307 format == media::PIXEL_FORMAT_I420 || | 428 format == media::PIXEL_FORMAT_TEXTURE || |
| 308 format == media::PIXEL_FORMAT_ARGB); | 429 format == media::PIXEL_FORMAT_GPUMEMORYBUFFER); |
| 309 DCHECK_GT(dimensions.width(), 0); | 430 DCHECK_GT(dimensions.width(), 0); |
| 310 DCHECK_GT(dimensions.height(), 0); | 431 DCHECK_GT(dimensions.height(), 0); |
| 311 | 432 |
| 312 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; | 433 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId; |
| 313 const int buffer_id = | 434 const int buffer_id = |
| 314 buffer_pool_->ReserveForProducer(format, dimensions, &buffer_id_to_drop); | 435 buffer_pool_->ReserveForProducer(format, dimensions, &buffer_id_to_drop); |
| 315 if (buffer_id == VideoCaptureBufferPool::kInvalidId) | 436 if (buffer_id == VideoCaptureBufferPool::kInvalidId) |
| 316 return NULL; | 437 return NULL; |
| 317 void* data; | |
| 318 size_t size; | |
| 319 buffer_pool_->GetBufferInfo(buffer_id, &data, &size); | |
| 320 | 438 |
| 321 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( | 439 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer( |
| 322 new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size)); | 440 new AutoReleaseBuffer(buffer_pool_, buffer_id)); |
| 323 | 441 |
| 324 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { | 442 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) { |
| 325 BrowserThread::PostTask(BrowserThread::IO, | 443 BrowserThread::PostTask(BrowserThread::IO, |
| 326 FROM_HERE, | 444 FROM_HERE, |
| 327 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread, | 445 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread, |
| 328 controller_, buffer_id_to_drop)); | 446 controller_, buffer_id_to_drop)); |
| 329 } | 447 } |
| 330 | 448 |
| 331 return output_buffer; | 449 return output_buffer; |
| 332 } | 450 } |
| 333 | 451 |
| 334 void | 452 void VideoCaptureDeviceClient::OnIncomingCapturedBuffer( |
| 335 VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( | 453 const scoped_refptr<Buffer>& buffer, |
| 454 const media::VideoCaptureFormat& frame_format, |
| 455 const base::TimeTicks& timestamp) { |
| 456 if (frame_format.pixel_format == media::PIXEL_FORMAT_GPUMEMORYBUFFER) { |
| 457 capture_task_runner_->PostTask( |
| 458 FROM_HERE, |
| 459 base::Bind( |
| 460 &TextureWrapperDelegate::OnIncomingCapturedGpuMemoryBuffer, |
| 461 wrapper_delegate_, |
| 462 buffer, |
| 463 frame_format.frame_size, |
| 464 timestamp)); |
| 465 } else { |
| 466 const scoped_ptr<media::DataHandle> scoped_data_handle = |
| 467 buffer->GetDataHandle().Pass(); |
| 468 |
| 469 scoped_refptr<VideoFrame> video_frame = |
| 470 VideoFrame::WrapExternalPackedMemory( |
| 471 VideoFrame::I420, |
| 472 frame_format.frame_size, |
| 473 gfx::Rect(frame_format.frame_size), |
| 474 frame_format.frame_size, |
| 475 reinterpret_cast<uint8*>(scoped_data_handle->data()), |
| 476 VideoFrame::AllocationSize(VideoFrame::I420, |
| 477 frame_format.frame_size), |
| 478 base::SharedMemory::NULLHandle(), |
| 479 0 /* shared_memory_offset */, |
| 480 base::TimeDelta(), |
| 481 base::Closure()); |
| 482 DCHECK(video_frame.get()); |
| 483 video_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, |
| 484 frame_format.frame_rate); |
| 485 |
| 486 OnIncomingCapturedVideoFrame(buffer, |
| 487 video_frame, |
| 488 timestamp); |
| 489 } |
| 490 } |
| 491 |
| 492 void VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( |
| 336 const scoped_refptr<Buffer>& buffer, | 493 const scoped_refptr<Buffer>& buffer, |
| 337 const scoped_refptr<VideoFrame>& frame, | 494 const scoped_refptr<VideoFrame>& frame, |
| 338 const base::TimeTicks& timestamp) { | 495 const base::TimeTicks& timestamp) { |
| 339 BrowserThread::PostTask( | 496 BrowserThread::PostTask( |
| 340 BrowserThread::IO, | 497 BrowserThread::IO, |
| 341 FROM_HERE, | 498 FROM_HERE, |
| 342 base::Bind( | 499 base::Bind( |
| 343 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, | 500 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, |
| 344 controller_, | 501 controller_, |
| 345 buffer, | 502 buffer, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 361 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); | 518 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); |
| 362 } | 519 } |
| 363 | 520 |
| 364 void VideoCaptureDeviceClient::OnLog( | 521 void VideoCaptureDeviceClient::OnLog( |
| 365 const std::string& message) { | 522 const std::string& message) { |
| 366 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 523 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 367 base::Bind(&VideoCaptureController::DoLogOnIOThread, | 524 base::Bind(&VideoCaptureController::DoLogOnIOThread, |
| 368 controller_, message)); | 525 controller_, message)); |
| 369 } | 526 } |
| 370 | 527 |
| 528 VideoCaptureDeviceClient::TextureWrapperDelegate::TextureWrapperDelegate( |
| 529 const base::WeakPtr<VideoCaptureController>& controller, |
| 530 const scoped_refptr<base::SingleThreadTaskRunner>& capture_task_runner, |
| 531 const media::VideoCaptureFormat& capture_format) |
| 532 : controller_(controller), |
| 533 capture_task_runner_(capture_task_runner) { |
| 534 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 535 capture_task_runner_->PostTask(FROM_HERE, |
| 536 base::Bind(&TextureWrapperDelegate::Init, this, capture_format)); |
| 537 } |
| 538 |
| 539 void VideoCaptureDeviceClient::TextureWrapperDelegate:: |
| 540 OnIncomingCapturedGpuMemoryBuffer( |
| 541 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer, |
| 542 const gfx::Size& frame_size, |
| 543 const base::TimeTicks& timestamp) { |
| 544 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 545 DVLOG_IF(1, !gl_helper_) << " Skipping ingress frame, no GL context."; |
| 546 if (!gl_helper_) |
| 547 return; |
| 548 |
| 549 gpu::gles2::GLES2Interface* gl = capture_thread_context_->ContextGL(); |
| 550 GLuint image_id = gl->CreateImageCHROMIUM(buffer->AsClientBuffer(), |
| 551 frame_size.width(), |
| 552 frame_size.height(), GL_BGRA_EXT); |
| 553 DCHECK(image_id); |
| 554 |
| 555 GLuint texture_id = gl_helper_->CreateTexture(); |
| 556 DCHECK(texture_id); |
| 557 { |
| 558 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_id); |
| 559 gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id); |
| 560 } |
| 561 |
| 562 scoped_ptr<gpu::MailboxHolder> mailbox_holder(new gpu::MailboxHolder( |
| 563 gl_helper_->ProduceMailboxHolderFromTexture(texture_id))); |
| 564 DCHECK(!mailbox_holder->mailbox.IsZero()); |
| 565 DCHECK(mailbox_holder->mailbox.Verify()); |
| 566 DCHECK(mailbox_holder->texture_target); |
| 567 DCHECK(mailbox_holder->sync_point); |
| 568 |
| 569 scoped_refptr<media::VideoFrame> video_frame = |
| 570 media::VideoFrame::WrapNativeTexture( |
| 571 mailbox_holder.Pass(), |
| 572 media::BindToCurrentLoop( |
| 573 base::Bind(&VideoCaptureDeviceClient::TextureWrapperDelegate:: |
| 574 ReleaseCallback, |
| 575 this, image_id, texture_id /* gpu_memory_buffer */ )), |
| 576 frame_size, |
| 577 gfx::Rect(frame_size), |
| 578 frame_size, |
| 579 base::TimeDelta(), |
| 580 true /* allow_overlay */); |
| 581 |
| 582 BrowserThread::PostTask( |
| 583 BrowserThread::IO, FROM_HERE, |
| 584 base::Bind( |
| 585 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, |
| 586 controller_, buffer, video_frame, timestamp)); |
| 587 } |
| 588 |
| 589 VideoCaptureDeviceClient::TextureWrapperDelegate::~TextureWrapperDelegate() { |
| 590 // Might not be running on capture_task_runner_'s thread. Ensure owned objects |
| 591 // are destroyed on the correct threads. |
| 592 if (gl_helper_) |
| 593 capture_task_runner_->DeleteSoon(FROM_HERE, gl_helper_.release()); |
| 594 |
| 595 if (capture_thread_context_) { |
| 596 capture_task_runner_->PostTask( |
| 597 FROM_HERE, |
| 598 base::Bind(&ResetLostContextCallback, capture_thread_context_)); |
| 599 capture_thread_context_->AddRef(); |
| 600 ContextProviderCommandBuffer* raw_capture_thread_context = |
| 601 capture_thread_context_.get(); |
| 602 capture_thread_context_ = nullptr; |
| 603 capture_task_runner_->ReleaseSoon(FROM_HERE, raw_capture_thread_context); |
| 604 } |
| 605 } |
| 606 |
| 607 void VideoCaptureDeviceClient::TextureWrapperDelegate::Init( |
| 608 const media::VideoCaptureFormat& capture_format) { |
| 609 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 610 |
| 611 // In threaded compositing mode, we have to create our own context for Capture |
| 612 // to avoid using the GPU command queue from multiple threads. Context |
| 613 // creation must happen on UI thread; then the context needs to be bound to |
| 614 // the appropriate thread, which is done in CreateGlHelper(). |
| 615 BrowserThread::PostTask( |
| 616 BrowserThread::UI, FROM_HERE, |
| 617 base::Bind(&CreateContextOnUIThread, |
| 618 media::BindToCurrentLoop( |
| 619 base::Bind(&VideoCaptureDeviceClient:: |
| 620 TextureWrapperDelegate::CreateGlHelper, |
| 621 this)))); |
| 622 } |
| 623 |
| 624 void VideoCaptureDeviceClient::TextureWrapperDelegate::CreateGlHelper( |
| 625 scoped_refptr<ContextProviderCommandBuffer> capture_thread_context) { |
| 626 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 627 |
| 628 if (!capture_thread_context.get()) { |
| 629 DLOG(ERROR) << "No offscreen GL Context!"; |
| 630 return; |
| 631 } |
| 632 // This may not happen in IO Thread. The destructor resets the context lost |
| 633 // callback, so base::Unretained is safe; otherwise it'd be a circular ref |
| 634 // counted dependency. |
| 635 capture_thread_context->SetLostContextCallback(media::BindToCurrentLoop( |
| 636 base::Bind( |
| 637 &VideoCaptureDeviceClient::TextureWrapperDelegate:: |
| 638 LostContextCallback, |
| 639 base::Unretained(this)))); |
| 640 if (!capture_thread_context->BindToCurrentThread()) { |
| 641 capture_thread_context = NULL; |
| 642 DLOG(ERROR) << "Couldn't bind the Capture Context to the Capture Thread."; |
| 643 return; |
| 644 } |
| 645 DCHECK(capture_thread_context); |
| 646 capture_thread_context_ = capture_thread_context; |
| 647 |
| 648 // At this point, |capture_thread_context| is a cc::ContextProvider. Creation |
| 649 // of our GLHelper should happen on Capture Thread. |
| 650 gl_helper_.reset(new GLHelper(capture_thread_context->ContextGL(), |
| 651 capture_thread_context->ContextSupport())); |
| 652 DCHECK(gl_helper_); |
| 653 } |
| 654 |
| 655 void VideoCaptureDeviceClient::TextureWrapperDelegate::ReleaseCallback( |
| 656 GLuint image_id, |
| 657 GLuint texture_id, |
| 658 uint32 sync_point) { |
| 659 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 660 |
| 661 if (gl_helper_) { |
| 662 gl_helper_->DeleteTexture(texture_id); |
| 663 capture_thread_context_->ContextGL()->DestroyImageCHROMIUM(image_id); |
| 664 } |
| 665 } |
| 666 |
| 667 void VideoCaptureDeviceClient::TextureWrapperDelegate::LostContextCallback() { |
| 668 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 669 // Prevent incoming frames from being processed while OnError gets groked. |
| 670 gl_helper_.reset(); |
| 671 OnError("GLContext lost"); |
| 672 } |
| 673 |
| 674 void VideoCaptureDeviceClient::TextureWrapperDelegate::OnError( |
| 675 const std::string& message) { |
| 676 DCHECK(capture_task_runner_->BelongsToCurrentThread()); |
| 677 DLOG(ERROR) << message; |
| 678 BrowserThread::PostTask( |
| 679 BrowserThread::IO, FROM_HERE, |
| 680 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); |
| 681 } |
| 682 |
| 371 } // namespace content | 683 } // namespace content |
| OLD | NEW |