Chromium Code Reviews| 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/synchronization/waitable_event.h" | |
| 9 #include "base/trace_event/trace_event.h" | 10 #include "base/trace_event/trace_event.h" |
| 11 #include "content/browser/gpu/browser_gpu_channel_host_factory.h" | |
| 10 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" | 12 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" |
| 11 #include "content/browser/renderer_host/media/video_capture_controller.h" | 13 #include "content/browser/renderer_host/media/video_capture_controller.h" |
| 14 #include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h" | |
| 12 #include "content/public/browser/browser_thread.h" | 15 #include "content/public/browser/browser_thread.h" |
| 13 #include "media/base/bind_to_current_loop.h" | 16 #include "media/base/bind_to_current_loop.h" |
| 14 #include "media/base/video_capture_types.h" | 17 #include "media/base/video_capture_types.h" |
| 15 #include "media/base/video_frame.h" | 18 #include "media/base/video_frame.h" |
| 19 #include "media/base/video_util.h" | |
| 20 #include "media/base/yuv_convert.h" | |
| 16 #include "third_party/libyuv/include/libyuv.h" | 21 #include "third_party/libyuv/include/libyuv.h" |
| 17 | 22 |
| 18 using media::VideoCaptureFormat; | 23 using media::VideoCaptureFormat; |
| 19 using media::VideoFrame; | 24 using media::VideoFrame; |
| 20 | 25 |
| 21 namespace content { | 26 namespace content { |
| 22 | 27 |
| 23 // Class combining a Client::Buffer interface implementation and a pool buffer | 28 // Class combining a Client::Buffer interface implementation and a pool buffer |
| 24 // implementation to guarantee proper cleanup on destruction on our side. | 29 // implementation to guarantee proper cleanup on destruction on our side. |
| 25 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { | 30 class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 44 const scoped_refptr<VideoCaptureBufferPool> pool_; | 49 const scoped_refptr<VideoCaptureBufferPool> pool_; |
| 45 const int id_; | 50 const int id_; |
| 46 void* const data_; | 51 void* const data_; |
| 47 const size_t size_; | 52 const size_t size_; |
| 48 }; | 53 }; |
| 49 | 54 |
| 50 VideoCaptureDeviceClient::VideoCaptureDeviceClient( | 55 VideoCaptureDeviceClient::VideoCaptureDeviceClient( |
| 51 const base::WeakPtr<VideoCaptureController>& controller, | 56 const base::WeakPtr<VideoCaptureController>& controller, |
| 52 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) | 57 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) |
| 53 : controller_(controller), | 58 : controller_(controller), |
| 59 jpeg_decode_event_(nullptr), | |
| 54 buffer_pool_(buffer_pool), | 60 buffer_pool_(buffer_pool), |
| 55 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN) {} | 61 last_captured_pixel_format_(media::PIXEL_FORMAT_UNKNOWN), |
| 62 next_bitstream_buffer_id_(0) {} | |
| 56 | 63 |
| 57 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} | 64 VideoCaptureDeviceClient::~VideoCaptureDeviceClient() { |
| 65 if (jpeg_decoder_.get()) | |
|
mcasas
2015/03/23 19:29:51
If |jpeg_decoder_| is created from the VCD, then i
kcwu
2015/03/30 18:12:14
jpeg_decoder_ is scoped_ptr(with DefaultDeleter),
| |
| 66 jpeg_decoder_->Destroy(); | |
| 67 } | |
| 68 | |
| 69 bool VideoCaptureDeviceClient::InitializeJpegDecoder() { | |
|
mcasas
2015/03/23 19:29:51
This method looks convoluted and exposes too much
kcwu
2015/03/30 18:12:14
Using MJPEG or YUV formats is determined in VideoC
| |
| 70 LOG(ERROR) << __func__; | |
|
mcasas
2015/03/23 19:29:51
Suggest s/LOG(ERROR)/DVLOG(1)/
kcwu
2015/04/16 14:38:26
Done.
| |
| 71 GpuChannelHost* host = | |
|
mcasas
2015/03/23 19:29:51
GpuChannelHost* const
kcwu
2015/04/14 20:02:34
Done.
| |
| 72 BrowserGpuChannelHostFactory::instance()->GetGpuChannel(); | |
| 73 LOG(ERROR) << "host =" << host; | |
| 74 GpuJpegDecodeAcceleratorHost* decoder = host->CreateJpegDecoder(); | |
|
mcasas
2015/03/23 19:29:50
* const
kcwu
2015/04/14 20:02:33
Done.
| |
| 75 if (!decoder->Initialize(this)) { | |
| 76 decoder->Destroy(); | |
| 77 return false; | |
| 78 } | |
| 79 jpeg_decoder_.reset(decoder); | |
| 80 | |
| 81 return true; | |
| 82 } | |
| 83 | |
| 84 void VideoCaptureDeviceClient::VideoFrameReady( | |
| 85 int32_t bitstream_buffer_id) { | |
|
wuchengli
2015/03/23 06:30:14
Add DCHECK to make sure this runs on IO thread. Th
kcwu
2015/03/30 18:12:14
DCHECK added. But still PostTask because I want to
| |
| 86 auto map_it = jpeg_decode_task_map_.find(bitstream_buffer_id); | |
|
wuchengli
2015/03/23 06:30:14
Like you said, since you are blocking the v4l2 thr
kcwu
2015/03/30 18:12:14
This is refactored to async code.
| |
| 87 if (map_it == jpeg_decode_task_map_.end()) { | |
| 88 LOG(ERROR) << "VideoFrameReady unknown bitstream_buffer_id " | |
| 89 << bitstream_buffer_id; | |
| 90 return; | |
| 91 } | |
| 92 auto captured_data = map_it->second; | |
| 93 auto frame_format = captured_data.frame_format; | |
| 94 | |
| 95 BrowserThread::PostTask( | |
| 96 BrowserThread::IO, | |
| 97 FROM_HERE, | |
| 98 base::Bind( | |
| 99 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread, | |
| 100 controller_, | |
| 101 captured_data.buffer, | |
| 102 captured_data.frame, | |
| 103 captured_data.timestamp)); | |
| 104 | |
| 105 jpeg_decode_task_map_.erase(map_it); | |
| 106 | |
| 107 CHECK(jpeg_decode_event_); | |
| 108 jpeg_decode_event_->Signal(); | |
| 109 } | |
| 110 | |
| 111 void VideoCaptureDeviceClient::NotifyError( | |
| 112 int32_t bitstream_buffer_id, | |
| 113 media::JpegDecodeAccelerator::Error error) { | |
| 114 LOG(ERROR) << "NotifyError"; | |
| 115 | |
| 116 auto map_it = jpeg_decode_task_map_.find(bitstream_buffer_id); | |
| 117 if (map_it != jpeg_decode_task_map_.end()) { | |
| 118 auto captured_data = map_it->second; | |
| 119 LOG(ERROR) << "fallback to software decode *******************************"; | |
| 120 OnIncomingCapturedData2(captured_data); | |
| 121 jpeg_decode_task_map_.erase(map_it); | |
| 122 CHECK(jpeg_decode_event_); | |
| 123 jpeg_decode_event_->Signal(); | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 // TODO(kcwu) | |
| 128 LOG(ERROR) << "give up???"; | |
| 129 CHECK(jpeg_decode_event_); | |
| 130 jpeg_decode_event_->Signal(); | |
| 131 } | |
| 58 | 132 |
| 59 void VideoCaptureDeviceClient::OnIncomingCapturedData( | 133 void VideoCaptureDeviceClient::OnIncomingCapturedData( |
| 60 const uint8* data, | 134 const uint8* data, |
| 61 int length, | 135 int length, |
| 62 const VideoCaptureFormat& frame_format, | 136 const VideoCaptureFormat& frame_format, |
| 63 int rotation, | 137 int rotation, |
| 64 const base::TimeTicks& timestamp) { | 138 const base::TimeTicks& timestamp) { |
| 65 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData"); | 139 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData"); |
| 66 | 140 |
| 141 CapturedData captured_data; | |
| 142 captured_data.data = data; | |
| 143 captured_data.length = length; | |
| 144 captured_data.frame_format = frame_format; | |
| 145 captured_data.rotation = rotation; | |
| 146 captured_data.timestamp = timestamp; | |
| 147 | |
| 67 if (last_captured_pixel_format_ != frame_format.pixel_format) { | 148 if (last_captured_pixel_format_ != frame_format.pixel_format) { |
| 68 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( | 149 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( |
| 69 frame_format.pixel_format)); | 150 frame_format.pixel_format)); |
| 70 last_captured_pixel_format_ = frame_format.pixel_format; | 151 last_captured_pixel_format_ = frame_format.pixel_format; |
| 71 } | 152 } |
| 72 | 153 |
| 73 if (!frame_format.IsValid()) | 154 if (!frame_format.IsValid()) |
| 74 return; | 155 return; |
| 75 | 156 |
| 157 const gfx::Size size = frame_format.frame_size; | |
| 158 if (rotation != 0 || frame_format.pixel_format != media::PIXEL_FORMAT_MJPEG || | |
| 159 size.width() % 2 != 0 || size.height() % 2 != 0) { | |
| 160 OnIncomingCapturedData2(captured_data); | |
|
wuchengli
2015/03/23 06:30:14
Please measure the decode time of both cases and p
kcwu
2015/04/14 20:02:34
Acknowledged.
| |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 base::SharedMemory in_memory; | |
| 165 gfx::Size dimensions = frame_format.frame_size; | |
| 166 size_t in_buffer_size = length; | |
| 167 scoped_refptr<Buffer> out_buffer = | |
| 168 ReserveOutputBuffer(media::VideoFrame::I420, dimensions); | |
| 169 captured_data.buffer = out_buffer; | |
| 170 | |
| 171 if (in_memory.CreateAnonymous(in_buffer_size) && | |
| 172 in_memory.Map(in_buffer_size)) { | |
| 173 media::BitstreamBuffer in_buffer(next_bitstream_buffer_id_, | |
| 174 in_memory.handle(), | |
| 175 in_buffer_size); | |
| 176 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. | |
| 177 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF; | |
| 178 | |
| 179 base::SharedMemoryHandle buffer_handle = | |
| 180 buffer_pool_->GetBufferSharedMemory(out_buffer->id()); | |
| 181 | |
| 182 uint8* shm_memory = reinterpret_cast<uint8*>(in_memory.memory()); | |
| 183 memcpy(shm_memory, data, length); | |
| 184 | |
|
mcasas
2015/03/23 19:29:50
There's a lot of extra code to just maintain the i
kcwu
2015/03/30 18:12:14
Done.
| |
| 185 scoped_refptr<media::VideoFrame> out_frame = | |
| 186 media::VideoFrame::WrapExternalPackedMemory( | |
| 187 media::VideoFrame::I420, | |
| 188 dimensions, | |
| 189 gfx::Rect(dimensions), | |
| 190 dimensions, | |
| 191 reinterpret_cast<uint8*>(out_buffer->data()), | |
| 192 out_buffer->size(), | |
| 193 buffer_handle, | |
| 194 0, | |
| 195 base::TimeDelta(), | |
| 196 base::Closure()); | |
|
wuchengli
2015/03/23 06:30:14
Set framerate like line 387.
frame->metadata()->Se
kcwu
2015/03/30 18:12:14
Done.
| |
| 197 DCHECK(out_frame.get()); | |
| 198 | |
| 199 captured_data.frame = out_frame; | |
| 200 jpeg_decode_task_map_[in_buffer.id()] = captured_data; | |
| 201 | |
| 202 CHECK(!jpeg_decode_event_); | |
| 203 jpeg_decode_event_ = new base::WaitableEvent(false, false); | |
|
wuchengli
2015/03/23 06:30:14
Use scoped_ptr
kcwu
2015/03/30 18:12:14
Code removed.
| |
| 204 | |
| 205 BrowserThread::PostTask( | |
| 206 BrowserThread::IO, | |
| 207 FROM_HERE, | |
| 208 base::Bind( | |
| 209 &VideoCaptureDeviceClient::DecodeJpegOnIOThread, | |
|
wuchengli
2015/03/23 06:30:14
Post to JpegDecodeAccelerator::Decode directly.
kcwu
2015/03/30 18:12:14
Modified to call Decode() directly.
| |
| 210 base::Unretained(this), | |
| 211 in_buffer, | |
| 212 out_frame)); | |
| 213 | |
| 214 jpeg_decode_event_->Wait(); | |
| 215 delete jpeg_decode_event_; | |
| 216 jpeg_decode_event_ = nullptr; | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 void VideoCaptureDeviceClient::DecodeJpegOnIOThread( | |
| 221 media::BitstreamBuffer in_buffer, | |
| 222 scoped_refptr<media::VideoFrame> out_frame) { | |
| 223 DCHECK(jpeg_decoder_); | |
| 224 | |
| 225 jpeg_decoder_->Decode(in_buffer, out_frame); | |
| 226 } | |
| 227 | |
| 228 // TODO(kcwu): need better name | |
| 229 void VideoCaptureDeviceClient::OnIncomingCapturedData2( | |
| 230 const CapturedData& captured_data) { | |
| 231 | |
| 232 const uint8* data = captured_data.data; | |
| 233 int length = captured_data.length; | |
| 234 const VideoCaptureFormat& frame_format = captured_data.frame_format; | |
| 235 int rotation = captured_data.rotation; | |
| 236 base::TimeTicks timestamp = captured_data.timestamp; | |
| 237 | |
| 238 if (last_captured_pixel_format_ != frame_format.pixel_format) { | |
| 239 OnLog("Pixel format: " + media::VideoCaptureFormat::PixelFormatToString( | |
| 240 frame_format.pixel_format)); | |
| 241 last_captured_pixel_format_ = frame_format.pixel_format; | |
| 242 } | |
| 243 | |
| 76 // Chopped pixels in width/height in case video capture device has odd | 244 // Chopped pixels in width/height in case video capture device has odd |
| 77 // numbers for width/height. | 245 // numbers for width/height. |
| 246 // XXX kcwu: why crop?? | |
| 78 int chopped_width = 0; | 247 int chopped_width = 0; |
| 79 int chopped_height = 0; | 248 int chopped_height = 0; |
| 80 int new_unrotated_width = frame_format.frame_size.width(); | 249 int new_unrotated_width = frame_format.frame_size.width(); |
| 81 int new_unrotated_height = frame_format.frame_size.height(); | 250 int new_unrotated_height = frame_format.frame_size.height(); |
| 82 | 251 |
| 83 if (new_unrotated_width & 1) { | 252 if (new_unrotated_width & 1) { |
| 84 --new_unrotated_width; | 253 --new_unrotated_width; |
| 85 chopped_width = 1; | 254 chopped_width = 1; |
| 86 } | 255 } |
| 87 if (new_unrotated_height & 1) { | 256 if (new_unrotated_height & 1) { |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 } | 462 } |
| 294 | 463 |
| 295 void VideoCaptureDeviceClient::OnLog( | 464 void VideoCaptureDeviceClient::OnLog( |
| 296 const std::string& message) { | 465 const std::string& message) { |
| 297 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 466 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 298 base::Bind(&VideoCaptureController::DoLogOnIOThread, | 467 base::Bind(&VideoCaptureController::DoLogOnIOThread, |
| 299 controller_, message)); | 468 controller_, message)); |
| 300 } | 469 } |
| 301 | 470 |
| 302 } // namespace content | 471 } // namespace content |
| OLD | NEW |