OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/capture/service/video_capture_device_client_impl.h" |
| 6 |
| 7 #include "base/time/time.h" |
| 8 #include "media/base/bind_to_current_loop.h" |
| 9 #include "media/mojo/common/mojo_shared_buffer_video_frame.h" |
| 10 #include "third_party/libyuv/include/libyuv.h" |
| 11 #include "ui/gfx/geometry/rect.h" |
| 12 #include "ui/gfx/geometry/size.h" |
| 13 |
| 14 namespace media { |
| 15 |
| 16 namespace { |
| 17 |
| 18 // This implements a VideoCaptureDevice::Client::Buffer as a lightweight wrapper |
| 19 // around a MojoSharedBufferVideoFrame. This Buffer is not meant to be used |
| 20 // beyond the VideoCaptureDevice and its Client implementation. |
| 21 class BufferImpl : public media::VideoCaptureDevice::Client::Buffer { |
| 22 public: |
| 23 BufferImpl(scoped_refptr<MojoSharedBufferVideoFrame> video_frame) |
| 24 : video_frame_(video_frame) {} |
| 25 |
| 26 ~BufferImpl() override {} |
| 27 |
| 28 // media::VideoCaptureDevice::Client::Buffer: |
| 29 int id() const override { // Not needed. |
| 30 NOTREACHED(); |
| 31 return 0; |
| 32 } |
| 33 gfx::Size dimensions() const override { return video_frame_->coded_size(); } |
| 34 size_t mapped_size() const override { return video_frame_->mapped_size(); } |
| 35 void* data(int plane) override { return video_frame_->data(plane); } |
| 36 ClientBuffer AsClientBuffer(int plane) override { |
| 37 NOTREACHED(); |
| 38 return nullptr; |
| 39 } |
| 40 #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) |
| 41 base::FileDescriptor AsPlatformFile() override { |
| 42 NOTREACHED(); |
| 43 return base::FileDescriptor(); |
| 44 } |
| 45 #endif |
| 46 |
| 47 const scoped_refptr<MojoSharedBufferVideoFrame> video_frame() { |
| 48 return video_frame_; |
| 49 } |
| 50 |
| 51 private: |
| 52 const scoped_refptr<MojoSharedBufferVideoFrame> video_frame_; |
| 53 |
| 54 DISALLOW_COPY_AND_ASSIGN(BufferImpl); |
| 55 }; |
| 56 |
| 57 } // namespace |
| 58 |
| 59 // A pool of -possibly heterogenous- MojoSharedBufferVideoFrames, provides |
| 60 // MojoSharedBufferVideoFrames |
| 61 // if available and of the appropriate format, otherwise allocates it. The MVF |
| 62 // are returned via registration of DestructionObserver. This class is used from |
| 63 // the mojo component main thread and from a number of device threads. |
| 64 class VideoCaptureDeviceClientImpl::VideoFramePool |
| 65 : public base::RefCountedThreadSafe< |
| 66 VideoCaptureDeviceClientImpl::VideoFramePool> { |
| 67 public: |
| 68 VideoFramePool(); |
| 69 |
| 70 // Produces a frame matching the specified |dimensions|, either by grabbing it |
| 71 // from the pool or creating a new frame if no suitable one is found. |
| 72 scoped_refptr<MojoSharedBufferVideoFrame> CreateFrame( |
| 73 const gfx::Size& dimensions, |
| 74 base::TimeDelta timestamp); |
| 75 |
| 76 // Once this is called frames will no longer be inserted into |frames_|. |
| 77 void Shutdown(); |
| 78 |
| 79 private: |
| 80 // Can't use a WeakPtr since this class is used by multiple threads. |
| 81 friend class base::RefCountedThreadSafe< |
| 82 VideoCaptureDeviceClientImpl::VideoFramePool>; |
| 83 ~VideoFramePool(); |
| 84 |
| 85 // Called when the frame wrapper gets destroyed. |frame| is the actual frame |
| 86 // that was wrapped and is re inserted in |frames_| by this function. |
| 87 void FrameReleased(const scoped_refptr<MojoSharedBufferVideoFrame>& frame); |
| 88 |
| 89 // For mutual exclusion when accessing from multiple threads. |
| 90 base::Lock lock_; |
| 91 bool is_shutdown_; |
| 92 |
| 93 std::list<scoped_refptr<MojoSharedBufferVideoFrame>> frames_; |
| 94 |
| 95 DISALLOW_COPY_AND_ASSIGN(VideoFramePool); |
| 96 }; |
| 97 |
| 98 VideoCaptureDeviceClientImpl::VideoCaptureDeviceClientImpl(Delegate* delegate) |
| 99 : delegate_(delegate), |
| 100 video_frame_pool_(new VideoFramePool()), |
| 101 weak_factory_(this) { |
| 102 DCHECK(delegate_); |
| 103 // |this| is used exclusively by internal video capture device code which may, |
| 104 // and ususally will, operate on a different thread from the constructor. |
| 105 thread_checker_.DetachFromThread(); |
| 106 } |
| 107 |
| 108 VideoCaptureDeviceClientImpl::~VideoCaptureDeviceClientImpl() { |
| 109 // This happens on an OS thread. |
| 110 video_frame_pool_->Shutdown(); |
| 111 } |
| 112 |
| 113 void VideoCaptureDeviceClientImpl::OnIncomingCapturedData( |
| 114 const uint8_t* data, |
| 115 int length, |
| 116 const media::VideoCaptureFormat& frame_format, |
| 117 int clockwise_rotation, |
| 118 const base::TimeTicks& timestamp) { |
| 119 DVLOG(1) << __FUNCTION__; |
| 120 // N.B.: All of the logic throughout this method is lifted directly from |
| 121 // content::VideoCaptureDeviceClient and should generally continue to mirror |
| 122 // that implementation until this one becomes the source of truth. |
| 123 DCHECK(thread_checker_.CalledOnValidThread()); |
| 124 DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage); |
| 125 |
| 126 if (!frame_format.IsValid()) |
| 127 return; |
| 128 |
| 129 const int width_parity = frame_format.frame_size.width() & 1; |
| 130 const int height_parity = frame_format.frame_size.height() & 1; |
| 131 const int even_width = frame_format.frame_size.width() & ~1; |
| 132 const int even_height = frame_format.frame_size.height() & ~1; |
| 133 |
| 134 int destination_width = even_width; |
| 135 int destination_height = even_height; |
| 136 |
| 137 if (clockwise_rotation == 90 || clockwise_rotation == 270) |
| 138 std::swap(destination_width, destination_height); |
| 139 |
| 140 DCHECK_EQ(clockwise_rotation % 90, 0) |
| 141 << "Rotation must be a multiple of 90, now: " << clockwise_rotation; |
| 142 libyuv::RotationMode rotation_mode = libyuv::kRotate0; |
| 143 if (clockwise_rotation == 90) |
| 144 rotation_mode = libyuv::kRotate90; |
| 145 else if (clockwise_rotation == 180) |
| 146 rotation_mode = libyuv::kRotate180; |
| 147 else if (clockwise_rotation == 270) |
| 148 rotation_mode = libyuv::kRotate270; |
| 149 |
| 150 libyuv::FourCC colorspace = libyuv::FOURCC_ANY; |
| 151 bool flip = false; |
| 152 switch (frame_format.pixel_format) { |
| 153 case media::PIXEL_FORMAT_UNKNOWN: |
| 154 break; |
| 155 case media::PIXEL_FORMAT_I420: |
| 156 DCHECK(!width_parity && !height_parity); |
| 157 colorspace = libyuv::FOURCC_I420; |
| 158 break; |
| 159 case media::PIXEL_FORMAT_YV12: |
| 160 DCHECK(!width_parity && !height_parity); |
| 161 colorspace = libyuv::FOURCC_YV12; |
| 162 break; |
| 163 case media::PIXEL_FORMAT_NV12: |
| 164 DCHECK(!width_parity && !height_parity); |
| 165 colorspace = libyuv::FOURCC_NV12; |
| 166 break; |
| 167 case media::PIXEL_FORMAT_NV21: |
| 168 DCHECK(!width_parity && !height_parity); |
| 169 colorspace = libyuv::FOURCC_NV21; |
| 170 break; |
| 171 case media::PIXEL_FORMAT_YUY2: |
| 172 DCHECK(!width_parity && !height_parity); |
| 173 colorspace = libyuv::FOURCC_YUY2; |
| 174 break; |
| 175 case media::PIXEL_FORMAT_UYVY: |
| 176 DCHECK(!width_parity && !height_parity); |
| 177 colorspace = libyuv::FOURCC_UYVY; |
| 178 break; |
| 179 case media::PIXEL_FORMAT_RGB24: |
| 180 // Linux RGB24 defines red at lowest byte address, |
| 181 // see http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html. |
| 182 // Windows RGB24 defines blue at lowest byte, |
| 183 // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd407253 |
| 184 #if defined(OS_LINUX) |
| 185 colorspace = libyuv::FOURCC_RAW; |
| 186 #elif defined(OS_WIN) |
| 187 colorspace = libyuv::FOURCC_24BG; |
| 188 #else |
| 189 NOTREACHED() << "RGB24 is only available in Linux and Windows platforms"; |
| 190 #endif |
| 191 #if defined(OS_WIN) |
| 192 // TODO(wjia): Currently, for RGB24 on WIN, capture device always |
| 193 // passes in positive src_width and src_height. Remove this hardcoded |
| 194 // value when nagative src_height is supported. The negative src_height |
| 195 // indicates that vertical flipping is needed. |
| 196 flip = true; |
| 197 #endif |
| 198 break; |
| 199 case media::PIXEL_FORMAT_RGB32: |
| 200 // Fallback to PIXEL_FORMAT_ARGB setting |flip| in Windows |
| 201 // platforms. |
| 202 #if defined(OS_WIN) |
| 203 flip = true; |
| 204 #endif |
| 205 case media::PIXEL_FORMAT_ARGB: |
| 206 colorspace = libyuv::FOURCC_ARGB; |
| 207 break; |
| 208 case media::PIXEL_FORMAT_MJPEG: |
| 209 colorspace = libyuv::FOURCC_MJPG; |
| 210 break; |
| 211 default: |
| 212 NOTREACHED() << media::VideoPixelFormatToString( |
| 213 frame_format.pixel_format); |
| 214 } |
| 215 |
| 216 const gfx::Size dimensions(destination_width, destination_height); |
| 217 scoped_ptr<Buffer> buffer = ReserveOutputBuffer( |
| 218 dimensions, media::PIXEL_FORMAT_I420, media::PIXEL_STORAGE_CPU); |
| 219 if (!buffer) |
| 220 return; |
| 221 |
| 222 int y_stride = dimensions.width(); |
| 223 int uv_stride = y_stride / 2; |
| 224 if (libyuv::ConvertToI420( |
| 225 data, length, |
| 226 static_cast<uint8_t*>(buffer->data(media::VideoFrame::kYPlane)), |
| 227 y_stride, |
| 228 static_cast<uint8_t*>(buffer->data(media::VideoFrame::kUPlane)), |
| 229 uv_stride, |
| 230 static_cast<uint8_t*>(buffer->data(media::VideoFrame::kVPlane)), |
| 231 uv_stride, 0 /* crop_x */, 0 /* crop_y */, |
| 232 frame_format.frame_size.width(), |
| 233 (flip ? -1 : 1) * frame_format.frame_size.height(), even_width, |
| 234 even_height, rotation_mode, colorspace)) { |
| 235 DLOG(ERROR) << "Failed to convert frame pixels to I420"; |
| 236 return; |
| 237 } |
| 238 |
| 239 OnIncomingCapturedBuffer(std::move(buffer), media::VideoCaptureFormat( |
| 240 frame_format.frame_size, 0.0f, |
| 241 media::PIXEL_FORMAT_I420), |
| 242 timestamp); |
| 243 } |
| 244 |
| 245 scoped_ptr<media::VideoCaptureDevice::Client::Buffer> |
| 246 VideoCaptureDeviceClientImpl::ReserveOutputBuffer( |
| 247 const gfx::Size& dimensions, |
| 248 media::VideoPixelFormat pixel_format, |
| 249 media::VideoPixelStorage /* pixel_storage */) { |
| 250 DVLOG(1) << __FUNCTION__; |
| 251 DCHECK(thread_checker_.CalledOnValidThread()); |
| 252 DCHECK_EQ(media::PIXEL_FORMAT_I420, pixel_format); |
| 253 DCHECK(!dimensions.IsEmpty()); |
| 254 |
| 255 return make_scoped_ptr<Buffer>(new BufferImpl(video_frame_pool_->CreateFrame( |
| 256 dimensions, base::TimeTicks::Now() - base::TimeTicks()))); |
| 257 } |
| 258 |
| 259 void VideoCaptureDeviceClientImpl::OnIncomingCapturedBuffer( |
| 260 scoped_ptr<Buffer> buffer, |
| 261 const media::VideoCaptureFormat& frame_format, |
| 262 const base::TimeTicks& timestamp) { |
| 263 DVLOG(1) << __FUNCTION__; |
| 264 DCHECK(thread_checker_.CalledOnValidThread()); |
| 265 DCHECK(buffer); |
| 266 DCHECK_EQ(frame_format.pixel_format, media::PIXEL_FORMAT_I420); |
| 267 DCHECK_EQ(frame_format.pixel_storage, media::PIXEL_STORAGE_CPU); |
| 268 |
| 269 // |buffer| can get out of scope since we have the important frame. |
| 270 delegate_->OnFrame(static_cast<BufferImpl*>(buffer.get())->video_frame(), |
| 271 timestamp); |
| 272 } |
| 273 |
| 274 void VideoCaptureDeviceClientImpl::OnIncomingCapturedVideoFrame( |
| 275 scoped_ptr<Buffer> buffer, |
| 276 const scoped_refptr<media::VideoFrame>& frame, |
| 277 const base::TimeTicks& timestamp) { |
| 278 DCHECK(thread_checker_.CalledOnValidThread()); |
| 279 DCHECK(buffer); |
| 280 |
| 281 // TODO(mcasas): Contents capture calls here. Wire this path by wrapping |
| 282 // |frame| in a MojoSharedBufferVideoFrame. |
| 283 NOTIMPLEMENTED(); |
| 284 } |
| 285 |
| 286 void VideoCaptureDeviceClientImpl::OnError( |
| 287 const tracked_objects::Location& from_here, |
| 288 const std::string& reason) { |
| 289 DVLOG(1) << __FUNCTION__ << " " << reason; |
| 290 DCHECK(thread_checker_.CalledOnValidThread()); |
| 291 delegate_->OnError(reason); |
| 292 } |
| 293 |
| 294 double VideoCaptureDeviceClientImpl::GetBufferPoolUtilization() const { |
| 295 DCHECK(thread_checker_.CalledOnValidThread()); |
| 296 NOTIMPLEMENTED(); |
| 297 // TODO(mcasas): In content/, the frame pool did have a maximum. Consider |
| 298 // doing the same in this VideoFramePool, and return here the amount in use |
| 299 // there. |
| 300 return 0.0; |
| 301 } |
| 302 |
| 303 VideoCaptureDeviceClientImpl::VideoFramePool::VideoFramePool() |
| 304 : is_shutdown_(false) {} |
| 305 |
| 306 scoped_refptr<MojoSharedBufferVideoFrame> |
| 307 VideoCaptureDeviceClientImpl::VideoFramePool::CreateFrame( |
| 308 const gfx::Size& dimensions, |
| 309 base::TimeDelta timestamp) { |
| 310 DVLOG(1) << __FUNCTION__ << " " << dimensions.ToString(); |
| 311 base::AutoLock auto_lock(lock_); |
| 312 DCHECK(!is_shutdown_); |
| 313 |
| 314 std::list<scoped_refptr<MojoSharedBufferVideoFrame>>::iterator |
| 315 suitable_frame = std::find_if( |
| 316 frames_.begin(), frames_.end(), |
| 317 [dimensions](const scoped_refptr<MojoSharedBufferVideoFrame>& frame) { |
| 318 return frame->coded_size() == dimensions; |
| 319 }); |
| 320 |
| 321 scoped_refptr<MojoSharedBufferVideoFrame> frame; |
| 322 if (suitable_frame != frames_.end()) { |
| 323 frame = *suitable_frame; |
| 324 frames_.erase(suitable_frame); |
| 325 DCHECK(frame->handle().is_valid()); |
| 326 frame->set_timestamp(timestamp); |
| 327 frame->metadata()->Clear(); |
| 328 } else { |
| 329 frame = |
| 330 MojoSharedBufferVideoFrame::CreateDefaultI420(dimensions, timestamp); |
| 331 } |
| 332 LOG_IF(ERROR, !frame.get()) << "Failed to get/create a video frame"; |
| 333 |
| 334 // Make a copy of |frame| to register a Destruction Observer. |
| 335 scoped_refptr<MojoSharedBufferVideoFrame> wrapped_frame = frame; |
| 336 wrapped_frame->AddDestructionObserver( |
| 337 base::Bind(&VideoFramePool::FrameReleased, this, frame)); |
| 338 return wrapped_frame; |
| 339 } |
| 340 |
| 341 void VideoCaptureDeviceClientImpl::VideoFramePool::Shutdown() { |
| 342 base::AutoLock auto_lock(lock_); |
| 343 is_shutdown_ = true; |
| 344 frames_.clear(); |
| 345 } |
| 346 |
| 347 VideoCaptureDeviceClientImpl::VideoFramePool::~VideoFramePool() { |
| 348 DCHECK(is_shutdown_); |
| 349 } |
| 350 |
| 351 void VideoCaptureDeviceClientImpl::VideoFramePool::FrameReleased( |
| 352 const scoped_refptr<MojoSharedBufferVideoFrame>& frame) { |
| 353 DVLOG(1) << __FUNCTION__; |
| 354 if (is_shutdown_) |
| 355 return; |
| 356 frames_.push_back(frame); |
| 357 } |
| 358 |
| 359 } // namespace media |
OLD | NEW |