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 | |
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 | |
OLD | NEW |