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

Side by Side Diff: media/capture/service/video_capture_device_client_impl.cc

Issue 1699553002: Mojo Video Capture service in media/capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased to using MojoSharedBufferVideoFrame Created 4 years, 9 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 unified diff | Download patch
OLDNEW
(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
magjed_chromium 2016/03/07 15:36:24 Can you split out the duplicated logic into a func
mcasas 2016/03/07 20:57:49 Are you proposing I take out the common parts into
magjed_chromium 2016/03/09 14:11:18 Yes, I'm proposing that you take out the common pa
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698