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

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

Powered by Google App Engine
This is Rietveld 408576698