OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "chrome/browser/chromeos/login/camera.h" | 5 #include "chrome/browser/chromeos/login/camera.h" |
6 | 6 |
7 #include <stdlib.h> | 7 #include <stdlib.h> |
8 #include <fcntl.h> // low-level i/o | 8 #include <fcntl.h> // low-level i/o |
9 #include <unistd.h> | 9 #include <unistd.h> |
10 #include <errno.h> | 10 #include <errno.h> |
11 #include <sys/stat.h> | 11 #include <sys/stat.h> |
12 #include <sys/types.h> | 12 #include <sys/types.h> |
13 #include <sys/time.h> | 13 #include <sys/time.h> |
14 #include <sys/mman.h> | 14 #include <sys/mman.h> |
15 #include <sys/ioctl.h> | 15 #include <sys/ioctl.h> |
16 #include <asm/types.h> // for videodev2.h | 16 #include <asm/types.h> // for videodev2.h |
17 #include <linux/videodev2.h> | 17 #include <linux/videodev2.h> |
18 | 18 |
19 #include <algorithm> | 19 #include <algorithm> |
20 #include <vector> | 20 #include <vector> |
21 | 21 |
22 #include "base/logging.h" | 22 #include "base/logging.h" |
23 #include "base/string_util.h" | 23 #include "base/string_util.h" |
24 #include "base/stringprintf.h" | 24 #include "base/stringprintf.h" |
| 25 #include "base/thread.h" |
25 #include "base/time.h" | 26 #include "base/time.h" |
26 #include "chrome/browser/browser_thread.h" | 27 #include "chrome/browser/browser_thread.h" |
27 #include "gfx/size.h" | 28 #include "gfx/size.h" |
28 #include "skia/ext/image_operations.h" | 29 #include "skia/ext/image_operations.h" |
29 #include "third_party/skia/include/core/SkBitmap.h" | 30 #include "third_party/skia/include/core/SkBitmap.h" |
30 #include "third_party/skia/include/core/SkColorPriv.h" | 31 #include "third_party/skia/include/core/SkColorPriv.h" |
31 | 32 |
32 namespace chromeos { | 33 namespace chromeos { |
33 | 34 |
34 namespace { | 35 namespace { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 if (sizes[i].GetArea() > max_area) { | 107 if (sizes[i].GetArea() > max_area) { |
107 max_size_index = i; | 108 max_size_index = i; |
108 max_area = sizes[i].GetArea(); | 109 max_area = sizes[i].GetArea(); |
109 } | 110 } |
110 } | 111 } |
111 return sizes[max_size_index]; | 112 return sizes[max_size_index]; |
112 } | 113 } |
113 | 114 |
114 // Default camera device name. | 115 // Default camera device name. |
115 const char kDeviceName[] = "/dev/video0"; | 116 const char kDeviceName[] = "/dev/video0"; |
116 // Name for camera thread. | |
117 const char kCameraThreadName[] = "Chrome_CameraThread"; | |
118 // Default width of each frame received from the camera. | 117 // Default width of each frame received from the camera. |
119 const int kFrameWidth = 640; | 118 const int kFrameWidth = 640; |
120 // Default height of each frame received from the camera. | 119 // Default height of each frame received from the camera. |
121 const int kFrameHeight = 480; | 120 const int kFrameHeight = 480; |
122 // Number of buffers to request from the device. | 121 // Number of buffers to request from the device. |
123 const int kRequestBuffersCount = 4; | 122 const int kRequestBuffersCount = 4; |
124 // Timeout for select() call in microseconds. | 123 // Timeout for select() call in microseconds. |
125 const long kSelectTimeout = 1 * base::Time::kMicrosecondsPerSecond; | 124 const long kSelectTimeout = 1 * base::Time::kMicrosecondsPerSecond; |
126 | 125 |
127 } // namespace | 126 } // namespace |
128 | 127 |
129 // static | |
130 Lock Camera::image_lock_; | |
131 | |
132 // static | |
133 Lock Camera::thread_lock_; | |
134 | |
135 /////////////////////////////////////////////////////////////////////////////// | 128 /////////////////////////////////////////////////////////////////////////////// |
136 // Camera, public members: | 129 // Camera, public members: |
137 | 130 |
138 Camera::Camera(Delegate* delegate, bool mirrored) | 131 Camera::Camera(Delegate* delegate, base::Thread* thread, bool mirrored) |
139 : delegate_(delegate), | 132 : delegate_(delegate), |
140 camera_thread_(kCameraThreadName), | 133 thread_(thread), |
141 device_name_(kDeviceName), | 134 device_name_(kDeviceName), |
142 device_descriptor_(-1), | 135 device_descriptor_(-1), |
143 is_capturing_(false), | 136 is_capturing_(false), |
144 desired_width_(kFrameWidth), | 137 desired_width_(kFrameWidth), |
145 desired_height_(kFrameHeight), | 138 desired_height_(kFrameHeight), |
146 frame_width_(kFrameWidth), | 139 frame_width_(kFrameWidth), |
147 frame_height_(kFrameHeight), | 140 frame_height_(kFrameHeight), |
148 mirrored_(mirrored) { | 141 mirrored_(mirrored) { |
149 } | 142 } |
150 | 143 |
151 Camera::~Camera() { | 144 Camera::~Camera() { |
152 DCHECK_EQ(-1, device_descriptor_) << "Don't forget to uninitialize camera."; | 145 DCHECK_EQ(-1, device_descriptor_) << "Don't forget to uninitialize camera."; |
153 } | 146 } |
154 | 147 |
155 // If this method is called there's no need to call PostCameraThreadAck(). | |
156 void Camera::ReportFailure() { | 148 void Camera::ReportFailure() { |
157 DCHECK(IsOnCameraThread()); | 149 DCHECK(IsOnCameraThread()); |
158 if (device_descriptor_ == -1) { | 150 if (device_descriptor_ == -1) { |
159 BrowserThread::PostTask( | 151 BrowserThread::PostTask( |
160 BrowserThread::UI, | 152 BrowserThread::UI, |
161 FROM_HERE, | 153 FROM_HERE, |
162 NewRunnableMethod(this, | 154 NewRunnableMethod(this, |
163 &Camera::OnInitializeFailure)); | 155 &Camera::OnInitializeFailure)); |
164 } else if (!is_capturing_) { | 156 } else if (!is_capturing_) { |
165 BrowserThread::PostTask( | 157 BrowserThread::PostTask( |
166 BrowserThread::UI, | 158 BrowserThread::UI, |
167 FROM_HERE, | 159 FROM_HERE, |
168 NewRunnableMethod(this, | 160 NewRunnableMethod(this, |
169 &Camera::OnStartCapturingFailure)); | 161 &Camera::OnStartCapturingFailure)); |
170 } else { | 162 } else { |
171 BrowserThread::PostTask( | 163 BrowserThread::PostTask( |
172 BrowserThread::UI, | 164 BrowserThread::UI, |
173 FROM_HERE, | 165 FROM_HERE, |
174 NewRunnableMethod(this, | 166 NewRunnableMethod(this, |
175 &Camera::OnCaptureFailure)); | 167 &Camera::OnCaptureFailure)); |
176 } | 168 } |
177 } | 169 } |
178 | 170 |
179 void Camera::Initialize(int desired_width, int desired_height) { | 171 void Camera::Initialize(int desired_width, int desired_height) { |
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
181 PostCameraTask( | 173 PostCameraTask( |
| 174 FROM_HERE, |
182 NewRunnableMethod(this, | 175 NewRunnableMethod(this, |
183 &Camera::DoInitialize, | 176 &Camera::DoInitialize, |
184 desired_width, | 177 desired_width, |
185 desired_height)); | 178 desired_height)); |
186 } | 179 } |
187 | 180 |
188 void Camera::DoInitialize(int desired_width, int desired_height) { | 181 void Camera::DoInitialize(int desired_width, int desired_height) { |
189 DCHECK(IsOnCameraThread()); | 182 DCHECK(IsOnCameraThread()); |
190 DCHECK(delegate_); | |
191 | 183 |
192 if (device_descriptor_ != -1) { | 184 if (device_descriptor_ != -1) { |
193 LOG(WARNING) << "Camera is initialized already."; | 185 LOG(WARNING) << "Camera is initialized already."; |
194 PostCameraThreadAck(); | |
195 return; | 186 return; |
196 } | 187 } |
197 | 188 |
198 int fd = OpenDevice(device_name_.c_str()); | 189 int fd = OpenDevice(device_name_.c_str()); |
199 if (fd == -1) { | 190 if (fd == -1) { |
200 ReportFailure(); | 191 ReportFailure(); |
201 return; | 192 return; |
202 } | 193 } |
203 | 194 |
204 v4l2_capability cap; | 195 v4l2_capability cap; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 if (!InitializeReadingMode(fd)) { | 231 if (!InitializeReadingMode(fd)) { |
241 ReportFailure(); | 232 ReportFailure(); |
242 return; | 233 return; |
243 } | 234 } |
244 | 235 |
245 device_descriptor_ = fd; | 236 device_descriptor_ = fd; |
246 frame_width_ = frame_size.width(); | 237 frame_width_ = frame_size.width(); |
247 frame_height_ = frame_size.height(); | 238 frame_height_ = frame_size.height(); |
248 desired_width_ = desired_width; | 239 desired_width_ = desired_width; |
249 desired_height_ = desired_height; | 240 desired_height_ = desired_height; |
250 // No need to call PostCameraThreadAck() back as this method | |
251 // is being posted instead. | |
252 BrowserThread::PostTask( | 241 BrowserThread::PostTask( |
253 BrowserThread::UI, | 242 BrowserThread::UI, |
254 FROM_HERE, | 243 FROM_HERE, |
255 NewRunnableMethod(this, &Camera::OnInitializeSuccess)); | 244 NewRunnableMethod(this, &Camera::OnInitializeSuccess)); |
256 } | 245 } |
257 | 246 |
258 void Camera::CameraThreadAck() { | |
259 } | |
260 | |
261 void Camera::PostCameraThreadAck() { | |
262 BrowserThread::PostTask( | |
263 BrowserThread::UI, | |
264 FROM_HERE, | |
265 NewRunnableMethod(this, &Camera::CameraThreadAck)); | |
266 } | |
267 | |
268 void Camera::Uninitialize() { | 247 void Camera::Uninitialize() { |
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
270 PostCameraTask(NewRunnableMethod(this, &Camera::DoUninitialize)); | 249 PostCameraTask(FROM_HERE, NewRunnableMethod(this, &Camera::DoUninitialize)); |
271 } | 250 } |
272 | 251 |
273 void Camera::DoUninitialize() { | 252 void Camera::DoUninitialize() { |
274 DCHECK(IsOnCameraThread()); | 253 DCHECK(IsOnCameraThread()); |
275 if (device_descriptor_ == -1) { | 254 if (device_descriptor_ == -1) { |
276 LOG(WARNING) << "Calling uninitialize for uninitialized camera."; | 255 LOG(WARNING) << "Calling uninitialize for uninitialized camera."; |
277 PostCameraThreadAck(); | |
278 return; | 256 return; |
279 } | 257 } |
280 DoStopCapturing(); | 258 DoStopCapturing(); |
281 UnmapVideoBuffers(); | 259 UnmapVideoBuffers(); |
282 if (close(device_descriptor_) == -1) | 260 if (close(device_descriptor_) == -1) |
283 log_errno("Closing the device failed."); | 261 log_errno("Closing the device failed."); |
284 device_descriptor_ = -1; | 262 device_descriptor_ = -1; |
285 // Maintain a reference so that camera object isn't deleted on wrong thread. | |
286 PostCameraThreadAck(); | |
287 } | 263 } |
288 | 264 |
289 void Camera::StartCapturing() { | 265 void Camera::StartCapturing() { |
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
291 PostCameraTask(NewRunnableMethod(this, &Camera::DoStartCapturing)); | 267 PostCameraTask(FROM_HERE, |
| 268 NewRunnableMethod(this, &Camera::DoStartCapturing)); |
292 } | 269 } |
293 | 270 |
294 void Camera::DoStartCapturing() { | 271 void Camera::DoStartCapturing() { |
295 DCHECK(IsOnCameraThread()); | 272 DCHECK(IsOnCameraThread()); |
296 if (is_capturing_) { | 273 if (is_capturing_) { |
297 LOG(WARNING) << "Capturing is already started."; | 274 LOG(WARNING) << "Capturing is already started."; |
298 PostCameraThreadAck(); | |
299 return; | 275 return; |
300 } | 276 } |
301 | 277 |
302 for (size_t i = 0; i < buffers_.size(); ++i) { | 278 for (size_t i = 0; i < buffers_.size(); ++i) { |
303 v4l2_buffer buffer = {}; | 279 v4l2_buffer buffer = {}; |
304 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 280 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
305 buffer.memory = V4L2_MEMORY_MMAP; | 281 buffer.memory = V4L2_MEMORY_MMAP; |
306 buffer.index = i; | 282 buffer.index = i; |
307 if (xioctl(device_descriptor_, VIDIOC_QBUF, &buffer) == -1) { | 283 if (xioctl(device_descriptor_, VIDIOC_QBUF, &buffer) == -1) { |
308 log_errno("VIDIOC_QBUF failed."); | 284 log_errno("VIDIOC_QBUF failed."); |
309 ReportFailure(); | 285 ReportFailure(); |
310 return; | 286 return; |
311 } | 287 } |
312 } | 288 } |
313 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 289 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
314 if (xioctl(device_descriptor_, VIDIOC_STREAMON, &type) == -1) { | 290 if (xioctl(device_descriptor_, VIDIOC_STREAMON, &type) == -1) { |
315 log_errno("VIDIOC_STREAMON failed."); | 291 log_errno("VIDIOC_STREAMON failed."); |
316 ReportFailure(); | 292 ReportFailure(); |
317 return; | 293 return; |
318 } | 294 } |
319 // No need to post DidProcessCameraThreadMethod() as this method is | 295 // No need to post DidProcessCameraThreadMethod() as this method is |
320 // being posted instead. | 296 // being posted instead. |
321 BrowserThread::PostTask( | 297 BrowserThread::PostTask( |
322 BrowserThread::UI, | 298 BrowserThread::UI, |
323 FROM_HERE, | 299 FROM_HERE, |
324 NewRunnableMethod(this, | 300 NewRunnableMethod(this, |
325 &Camera::OnStartCapturingSuccess)); | 301 &Camera::OnStartCapturingSuccess)); |
326 is_capturing_ = true; | 302 is_capturing_ = true; |
327 PostCameraTask(NewRunnableMethod(this, &Camera::OnCapture)); | 303 PostCameraTask(FROM_HERE, |
| 304 NewRunnableMethod(this, &Camera::OnCapture)); |
328 } | 305 } |
329 | 306 |
330 void Camera::StopCapturing() { | 307 void Camera::StopCapturing() { |
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
332 PostCameraTask(NewRunnableMethod(this, &Camera::DoStopCapturing)); | 309 PostCameraTask(FROM_HERE, |
| 310 NewRunnableMethod(this, &Camera::DoStopCapturing)); |
333 } | 311 } |
334 | 312 |
335 void Camera::DoStopCapturing() { | 313 void Camera::DoStopCapturing() { |
336 DCHECK(IsOnCameraThread()); | 314 DCHECK(IsOnCameraThread()); |
337 if (!is_capturing_) { | 315 if (!is_capturing_) { |
338 PostCameraThreadAck(); | |
339 LOG(WARNING) << "Calling StopCapturing when capturing is not started."; | 316 LOG(WARNING) << "Calling StopCapturing when capturing is not started."; |
340 return; | 317 return; |
341 } | 318 } |
342 // OnCapture must exit if this flag is not set. | 319 // OnCapture must exit if this flag is not set. |
343 is_capturing_ = false; | 320 is_capturing_ = false; |
344 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 321 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
345 if (xioctl(device_descriptor_, VIDIOC_STREAMOFF, &type) == -1) | 322 if (xioctl(device_descriptor_, VIDIOC_STREAMOFF, &type) == -1) |
346 log_errno("VIDIOC_STREAMOFF failed."); | 323 log_errno("VIDIOC_STREAMOFF failed."); |
347 // Maintain a reference so that camera object isn't deleted on wrong thread. | |
348 PostCameraThreadAck(); | |
349 } | 324 } |
350 | 325 |
351 void Camera::GetFrame(SkBitmap* frame) { | 326 void Camera::GetFrame(SkBitmap* frame) { |
352 AutoLock lock(image_lock_); | 327 AutoLock lock(image_lock_); |
353 frame->swap(frame_image_); | 328 frame->swap(frame_image_); |
354 } | 329 } |
355 | 330 |
356 /////////////////////////////////////////////////////////////////////////////// | 331 /////////////////////////////////////////////////////////////////////////////// |
357 // Camera, private members: | 332 // Camera, private members: |
358 | 333 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 void Camera::UnmapVideoBuffers() { | 398 void Camera::UnmapVideoBuffers() { |
424 DCHECK(IsOnCameraThread()); | 399 DCHECK(IsOnCameraThread()); |
425 for (size_t i = 0; i < buffers_.size(); ++i) { | 400 for (size_t i = 0; i < buffers_.size(); ++i) { |
426 if (munmap(buffers_[i].start, buffers_[i].length) == -1) | 401 if (munmap(buffers_[i].start, buffers_[i].length) == -1) |
427 log_errno("munmap failed."); | 402 log_errno("munmap failed."); |
428 } | 403 } |
429 } | 404 } |
430 | 405 |
431 void Camera::OnCapture() { | 406 void Camera::OnCapture() { |
432 DCHECK(IsOnCameraThread()); | 407 DCHECK(IsOnCameraThread()); |
433 if (!is_capturing_) { | 408 if (!is_capturing_) |
434 // Maintain a reference so that camera object isn't deleted on wrong thread. | |
435 PostCameraThreadAck(); | |
436 return; | 409 return; |
437 } | |
438 | 410 |
439 do { | 411 do { |
440 fd_set fds; | 412 fd_set fds; |
441 FD_ZERO(&fds); | 413 FD_ZERO(&fds); |
442 FD_SET(device_descriptor_, &fds); | 414 FD_SET(device_descriptor_, &fds); |
443 | 415 |
444 timeval tv = {}; | 416 timeval tv = {}; |
445 tv.tv_sec = kSelectTimeout / base::Time::kMicrosecondsPerSecond; | 417 tv.tv_sec = kSelectTimeout / base::Time::kMicrosecondsPerSecond; |
446 tv.tv_usec = kSelectTimeout % base::Time::kMicrosecondsPerSecond; | 418 tv.tv_usec = kSelectTimeout % base::Time::kMicrosecondsPerSecond; |
447 | 419 |
448 int result = select(device_descriptor_ + 1, &fds, NULL, NULL, &tv); | 420 int result = select(device_descriptor_ + 1, &fds, NULL, NULL, &tv); |
449 if (result == -1) { | 421 if (result == -1) { |
450 if (errno == EINTR) | 422 if (errno == EINTR) |
451 continue; | 423 continue; |
452 log_errno("select() failed."); | 424 log_errno("select() failed."); |
453 ReportFailure(); | 425 ReportFailure(); |
454 break; | 426 break; |
455 } | 427 } |
456 if (result == 0) { | 428 if (result == 0) { |
457 LOG(ERROR) << "select() timeout."; | 429 LOG(ERROR) << "select() timeout."; |
458 ReportFailure(); | 430 ReportFailure(); |
459 break; | 431 break; |
460 } | 432 } |
461 // EAGAIN - continue select loop. | 433 // EAGAIN - continue select loop. |
462 } while (!ReadFrame()); | 434 } while (!ReadFrame()); |
463 | 435 |
464 PostCameraTask(NewRunnableMethod(this, &Camera::OnCapture)); | 436 PostCameraTask(FROM_HERE, |
| 437 NewRunnableMethod(this, &Camera::OnCapture)); |
465 } | 438 } |
466 | 439 |
467 bool Camera::ReadFrame() { | 440 bool Camera::ReadFrame() { |
468 DCHECK(IsOnCameraThread()); | 441 DCHECK(IsOnCameraThread()); |
469 v4l2_buffer buffer = {}; | 442 v4l2_buffer buffer = {}; |
470 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 443 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
471 buffer.memory = V4L2_MEMORY_MMAP; | 444 buffer.memory = V4L2_MEMORY_MMAP; |
472 if (xioctl(device_descriptor_, VIDIOC_DQBUF, &buffer) == -1) { | 445 if (xioctl(device_descriptor_, VIDIOC_DQBUF, &buffer) == -1) { |
473 // Return false only in this case to try again. | 446 // Return false only in this case to try again. |
474 if (errno == EAGAIN) | 447 if (errno == EAGAIN) |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
594 } | 567 } |
595 | 568 |
596 void Camera::OnCaptureFailure() { | 569 void Camera::OnCaptureFailure() { |
597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
598 if (delegate_) | 571 if (delegate_) |
599 delegate_->OnCaptureFailure(); | 572 delegate_->OnCaptureFailure(); |
600 } | 573 } |
601 | 574 |
602 bool Camera::IsOnCameraThread() const { | 575 bool Camera::IsOnCameraThread() const { |
603 AutoLock lock(thread_lock_); | 576 AutoLock lock(thread_lock_); |
604 return MessageLoop::current() == camera_thread_.message_loop(); | 577 return thread_ && MessageLoop::current() == thread_->message_loop(); |
605 } | 578 } |
606 | 579 |
607 void Camera::PostCameraTask(Task* task) { | 580 void Camera::PostCameraTask(const tracked_objects::Location& from_here, |
| 581 Task* task) { |
608 AutoLock lock(thread_lock_); | 582 AutoLock lock(thread_lock_); |
609 if (!camera_thread_.IsRunning()) | 583 if (!thread_) |
610 camera_thread_.Start(); | 584 return; |
611 camera_thread_.message_loop()->PostTask(FROM_HERE, task); | 585 DCHECK(thread_->IsRunning()); |
| 586 thread_->message_loop()->PostTask(from_here, task); |
612 } | 587 } |
613 | 588 |
614 } // namespace chromeos | 589 } // namespace chromeos |
OLD | NEW |