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

Side by Side Diff: media/capture/video/android/video_capture_device_android.cc

Issue 2193213003: ImageCapture: Queue up requests while device not ready (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Reduce requested getUserMedia() resolution Created 4 years, 4 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
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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 "media/capture/video/android/video_capture_device_android.h" 5 #include "media/capture/video/android/video_capture_device_android.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/android/jni_android.h" 10 #include "base/android/jni_android.h"
11 #include "base/android/jni_array.h" 11 #include "base/android/jni_array.h"
12 #include "base/android/jni_string.h" 12 #include "base/android/jni_string.h"
13 #include "base/numerics/safe_conversions.h" 13 #include "base/numerics/safe_conversions.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/thread_task_runner_handle.h"
15 #include "jni/VideoCapture_jni.h" 16 #include "jni/VideoCapture_jni.h"
16 #include "media/capture/video/android/photo_capabilities.h" 17 #include "media/capture/video/android/photo_capabilities.h"
17 #include "media/capture/video/android/video_capture_device_factory_android.h" 18 #include "media/capture/video/android/video_capture_device_factory_android.h"
18 #include "third_party/libyuv/include/libyuv.h" 19 #include "third_party/libyuv/include/libyuv.h"
19 20
20 using base::android::AttachCurrentThread; 21 using base::android::AttachCurrentThread;
21 using base::android::CheckException; 22 using base::android::CheckException;
22 using base::android::GetClass; 23 using base::android::GetClass;
23 using base::android::MethodID; 24 using base::android::MethodID;
24 using base::android::JavaRef; 25 using base::android::JavaRef;
25 using base::android::ScopedJavaLocalRef; 26 using base::android::ScopedJavaLocalRef;
26 27
27 namespace media { 28 namespace media {
28 29
29 // static 30 // static
30 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) { 31 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
31 return RegisterNativesImpl(env); 32 return RegisterNativesImpl(env);
32 } 33 }
33 34
34 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid( 35 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(
35 const VideoCaptureDeviceDescriptor& device_descriptor) 36 const VideoCaptureDeviceDescriptor& device_descriptor)
36 : state_(kIdle), 37 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
38 state_(kIdle),
37 got_first_frame_(false), 39 got_first_frame_(false),
38 device_descriptor_(device_descriptor) {} 40 device_descriptor_(device_descriptor),
41 weak_ptr_factory_(this) {}
39 42
40 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() { 43 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
41 DCHECK(thread_checker_.CalledOnValidThread()); 44 DCHECK(main_task_runner_->BelongsToCurrentThread());
42 StopAndDeAllocate(); 45 StopAndDeAllocate();
43 } 46 }
44 47
45 bool VideoCaptureDeviceAndroid::Init() { 48 bool VideoCaptureDeviceAndroid::Init() {
46 int id; 49 int id;
47 if (!base::StringToInt(device_descriptor_.device_id, &id)) 50 if (!base::StringToInt(device_descriptor_.device_id, &id))
48 return false; 51 return false;
49 52
50 j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid( 53 j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid(
51 id, reinterpret_cast<intptr_t>(this))); 54 id, reinterpret_cast<intptr_t>(this)));
52 return true; 55 return true;
53 } 56 }
54 57
55 void VideoCaptureDeviceAndroid::AllocateAndStart( 58 void VideoCaptureDeviceAndroid::AllocateAndStart(
56 const VideoCaptureParams& params, 59 const VideoCaptureParams& params,
57 std::unique_ptr<Client> client) { 60 std::unique_ptr<Client> client) {
58 DCHECK(thread_checker_.CalledOnValidThread()); 61 DCHECK(main_task_runner_->BelongsToCurrentThread());
59 { 62 {
60 base::AutoLock lock(lock_); 63 base::AutoLock lock(lock_);
61 if (state_ != kIdle) 64 if (state_ != kIdle)
62 return; 65 return;
63 client_ = std::move(client); 66 client_ = std::move(client);
64 got_first_frame_ = false; 67 got_first_frame_ = false;
65 } 68 }
66 69
67 JNIEnv* env = AttachCurrentThread(); 70 JNIEnv* env = AttachCurrentThread();
68 71
(...skipping 28 matching lines...) Expand all
97 << capture_format_.frame_rate << "fps"; 100 << capture_format_.frame_rate << "fps";
98 101
99 ret = Java_VideoCapture_startCapture(env, j_capture_.obj()); 102 ret = Java_VideoCapture_startCapture(env, j_capture_.obj());
100 if (!ret) { 103 if (!ret) {
101 SetErrorState(FROM_HERE, "failed to start capture"); 104 SetErrorState(FROM_HERE, "failed to start capture");
102 return; 105 return;
103 } 106 }
104 107
105 { 108 {
106 base::AutoLock lock(lock_); 109 base::AutoLock lock(lock_);
107 state_ = kCapturing; 110 state_ = kConfigured;
108 } 111 }
109 } 112 }
110 113
111 void VideoCaptureDeviceAndroid::StopAndDeAllocate() { 114 void VideoCaptureDeviceAndroid::StopAndDeAllocate() {
112 DCHECK(thread_checker_.CalledOnValidThread()); 115 DCHECK(main_task_runner_->BelongsToCurrentThread());
113 { 116 {
114 base::AutoLock lock(lock_); 117 base::AutoLock lock(lock_);
115 if (state_ != kCapturing && state_ != kError) 118 if (state_ != kConfigured && state_ != kError)
116 return; 119 return;
117 } 120 }
118 121
119 JNIEnv* env = AttachCurrentThread(); 122 JNIEnv* env = AttachCurrentThread();
120 123
121 const jboolean ret = Java_VideoCapture_stopCapture(env, j_capture_.obj()); 124 const jboolean ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
122 if (!ret) { 125 if (!ret) {
123 SetErrorState(FROM_HERE, "failed to stop capture"); 126 SetErrorState(FROM_HERE, "failed to stop capture");
124 return; 127 return;
125 } 128 }
126 129
127 { 130 {
128 base::AutoLock lock(lock_); 131 base::AutoLock lock(lock_);
129 state_ = kIdle; 132 state_ = kIdle;
130 client_.reset(); 133 client_.reset();
131 } 134 }
132 135
133 Java_VideoCapture_deallocate(env, j_capture_.obj()); 136 Java_VideoCapture_deallocate(env, j_capture_.obj());
134 } 137 }
135 138
136 void VideoCaptureDeviceAndroid::TakePhoto(TakePhotoCallback callback) { 139 void VideoCaptureDeviceAndroid::TakePhoto(TakePhotoCallback callback) {
137 DCHECK(thread_checker_.CalledOnValidThread()); 140 DCHECK(main_task_runner_->BelongsToCurrentThread());
138 { 141 {
139 base::AutoLock lock(lock_); 142 base::AutoLock lock(lock_);
140 if (state_ != kCapturing) 143 if (state_ != kConfigured)
141 return; 144 return;
145 if (!got_first_frame_) { // We have to wait until we get the first frame.
146 photo_requests_queue_.push_back(
147 base::Bind(&VideoCaptureDeviceAndroid::DoTakePhoto,
148 weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback)));
149 return;
150 }
142 } 151 }
143 152 DoTakePhoto(std::move(callback));
144 JNIEnv* env = AttachCurrentThread();
145
146 // Make copy on the heap so we can pass the pointer through JNI.
147 std::unique_ptr<TakePhotoCallback> heap_callback(
148 new TakePhotoCallback(std::move(callback)));
149 const intptr_t callback_id = reinterpret_cast<intptr_t>(heap_callback.get());
150 if (!Java_VideoCapture_takePhoto(env, j_capture_.obj(), callback_id,
151 next_photo_resolution_.width(),
152 next_photo_resolution_.height()))
153 return;
154
155 {
156 base::AutoLock lock(photo_callbacks_lock_);
157 photo_callbacks_.push_back(std::move(heap_callback));
158 }
159 } 153 }
160 154
161 void VideoCaptureDeviceAndroid::GetPhotoCapabilities( 155 void VideoCaptureDeviceAndroid::GetPhotoCapabilities(
162 GetPhotoCapabilitiesCallback callback) { 156 GetPhotoCapabilitiesCallback callback) {
163 DCHECK(thread_checker_.CalledOnValidThread()); 157 DCHECK(main_task_runner_->BelongsToCurrentThread());
164 JNIEnv* env = AttachCurrentThread(); 158 {
165 159 base::AutoLock lock(lock_);
166 PhotoCapabilities caps( 160 if (state_ != kConfigured)
167 Java_VideoCapture_getPhotoCapabilities(env, j_capture_.obj())); 161 return;
168 162 if (!got_first_frame_) { // We have to wait until we get the first frame.
169 // TODO(mcasas): Manual member copying sucks, consider adding typemapping from 163 photo_requests_queue_.push_back(
170 // PhotoCapabilities to mojom::PhotoCapabilitiesPtr, https://crbug.com/622002. 164 base::Bind(&VideoCaptureDeviceAndroid::DoGetPhotoCapabilities,
171 mojom::PhotoCapabilitiesPtr photo_capabilities = 165 weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback)));
172 mojom::PhotoCapabilities::New(); 166 return;
173 photo_capabilities->iso = mojom::Range::New(); 167 }
174 photo_capabilities->iso->current = caps.getCurrentIso(); 168 }
175 photo_capabilities->iso->max = caps.getMaxIso(); 169 DoGetPhotoCapabilities(std::move(callback));
176 photo_capabilities->iso->min = caps.getMinIso();
177 photo_capabilities->height = mojom::Range::New();
178 photo_capabilities->height->current = caps.getCurrentHeight();
179 photo_capabilities->height->max = caps.getMaxHeight();
180 photo_capabilities->height->min = caps.getMinHeight();
181 photo_capabilities->width = mojom::Range::New();
182 photo_capabilities->width->current = caps.getCurrentWidth();
183 photo_capabilities->width->max = caps.getMaxWidth();
184 photo_capabilities->width->min = caps.getMinWidth();
185 photo_capabilities->zoom = mojom::Range::New();
186 photo_capabilities->zoom->current = caps.getCurrentZoom();
187 photo_capabilities->zoom->max = caps.getMaxZoom();
188 photo_capabilities->zoom->min = caps.getMinZoom();
189 photo_capabilities->focus_mode = caps.getAutoFocusInUse()
190 ? mojom::FocusMode::AUTO
191 : mojom::FocusMode::MANUAL;
192 callback.Run(std::move(photo_capabilities));
193 } 170 }
194 171
195 void VideoCaptureDeviceAndroid::SetPhotoOptions( 172 void VideoCaptureDeviceAndroid::SetPhotoOptions(
196 mojom::PhotoSettingsPtr settings, 173 mojom::PhotoSettingsPtr settings,
197 SetPhotoOptionsCallback callback) { 174 SetPhotoOptionsCallback callback) {
198 DCHECK(thread_checker_.CalledOnValidThread()); 175 DCHECK(main_task_runner_->BelongsToCurrentThread());
199 JNIEnv* env = AttachCurrentThread(); 176 {
200 // |width| and/or |height| are kept for the next TakePhoto()s. 177 base::AutoLock lock(lock_);
201 if (settings->has_width || settings->has_height) 178 if (state_ != kConfigured)
202 next_photo_resolution_.SetSize(0, 0); 179 return;
203 if (settings->has_width) { 180 if (!got_first_frame_) { // We have to wait until we get the first frame.
204 next_photo_resolution_.set_width( 181 photo_requests_queue_.push_back(
205 base::saturated_cast<int>(settings->width)); 182 base::Bind(&VideoCaptureDeviceAndroid::DoSetPhotoOptions,
183 weak_ptr_factory_.GetWeakPtr(), base::Passed(&settings),
184 base::Passed(&callback)));
185 return;
186 }
206 } 187 }
207 if (settings->has_height) { 188 DoSetPhotoOptions(std::move(settings), std::move(callback));
208 next_photo_resolution_.set_height(
209 base::saturated_cast<int>(settings->height));
210 }
211
212 if (settings->has_zoom)
213 Java_VideoCapture_setZoom(env, j_capture_.obj(), settings->zoom);
214 callback.Run(true);
215 } 189 }
216 190
217 void VideoCaptureDeviceAndroid::OnFrameAvailable( 191 void VideoCaptureDeviceAndroid::OnFrameAvailable(
218 JNIEnv* env, 192 JNIEnv* env,
219 const JavaParamRef<jobject>& obj, 193 const JavaParamRef<jobject>& obj,
220 const JavaParamRef<jbyteArray>& data, 194 const JavaParamRef<jbyteArray>& data,
221 jint length, 195 jint length,
222 jint rotation) { 196 jint rotation) {
223 { 197 {
224 base::AutoLock lock(lock_); 198 base::AutoLock lock(lock_);
225 if (state_ != kCapturing || !client_) 199 if (state_ != kConfigured || !client_)
226 return; 200 return;
227 } 201 }
228 202
229 jbyte* buffer = env->GetByteArrayElements(data, NULL); 203 jbyte* buffer = env->GetByteArrayElements(data, NULL);
230 if (!buffer) { 204 if (!buffer) {
231 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: " 205 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
232 "failed to GetByteArrayElements"; 206 "failed to GetByteArrayElements";
233 return; 207 return;
234 } 208 }
235 209
236 const base::TimeTicks current_time = base::TimeTicks::Now(); 210 const base::TimeTicks current_time = base::TimeTicks::Now();
237 if (!got_first_frame_) { 211 {
238 // Set aside one frame allowance for fluctuation. 212 base::AutoLock lock(lock_);
239 expected_next_frame_time_ = current_time - frame_interval_; 213 if (!got_first_frame_) {
240 first_ref_time_ = current_time; 214 // Set aside one frame allowance for fluctuation.
241 got_first_frame_ = true; 215 expected_next_frame_time_ = current_time - frame_interval_;
216 first_ref_time_ = current_time;
217 got_first_frame_ = true;
218
219 for (const auto& request : photo_requests_queue_)
220 main_task_runner_->PostTask(FROM_HERE, request);
221 photo_requests_queue_.clear();
222 }
242 } 223 }
243 224
244 // Deliver the frame when it doesn't arrive too early. 225 // Deliver the frame when it doesn't arrive too early.
245 if (expected_next_frame_time_ <= current_time) { 226 if (expected_next_frame_time_ <= current_time) {
246 expected_next_frame_time_ += frame_interval_; 227 expected_next_frame_time_ += frame_interval_;
247 228
248 // TODO(qiangchen): Investigate how to get raw timestamp for Android, 229 // TODO(qiangchen): Investigate how to get raw timestamp for Android,
249 // rather than using reference time to calculate timestamp. 230 // rather than using reference time to calculate timestamp.
250 base::AutoLock lock(lock_); 231 base::AutoLock lock(lock_);
251 if (!client_) 232 if (!client_)
(...skipping 12 matching lines...) Expand all
264 jint y_stride, 245 jint y_stride,
265 jobject u_buffer, 246 jobject u_buffer,
266 jobject v_buffer, 247 jobject v_buffer,
267 jint uv_row_stride, 248 jint uv_row_stride,
268 jint uv_pixel_stride, 249 jint uv_pixel_stride,
269 jint width, 250 jint width,
270 jint height, 251 jint height,
271 jint rotation) { 252 jint rotation) {
272 { 253 {
273 base::AutoLock lock(lock_); 254 base::AutoLock lock(lock_);
274 if (state_ != kCapturing || !client_) 255 if (state_ != kConfigured || !client_)
275 return; 256 return;
276 } 257 }
277 258
278 const base::TimeTicks current_time = base::TimeTicks::Now(); 259 const base::TimeTicks current_time = base::TimeTicks::Now();
279 if (!got_first_frame_) { 260 {
280 // Set aside one frame allowance for fluctuation. 261 base::AutoLock lock(lock_);
281 expected_next_frame_time_ = current_time - frame_interval_; 262 if (!got_first_frame_) {
282 first_ref_time_ = current_time; 263 // Set aside one frame allowance for fluctuation.
283 got_first_frame_ = true; 264 expected_next_frame_time_ = current_time - frame_interval_;
265 first_ref_time_ = current_time;
266 got_first_frame_ = true;
267
268 for (const auto& request : photo_requests_queue_)
269 main_task_runner_->PostTask(FROM_HERE, request);
270 photo_requests_queue_.clear();
271 }
284 } 272 }
285 273
286 uint8_t* const y_src = 274 uint8_t* const y_src =
287 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer)); 275 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer));
288 CHECK(y_src); 276 CHECK(y_src);
289 uint8_t* const u_src = 277 uint8_t* const u_src =
290 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer)); 278 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer));
291 CHECK(u_src); 279 CHECK(u_src);
292 uint8_t* const v_src = 280 uint8_t* const v_src =
293 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer)); 281 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer));
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
378 const std::string& reason) { 366 const std::string& reason) {
379 { 367 {
380 base::AutoLock lock(lock_); 368 base::AutoLock lock(lock_);
381 state_ = kError; 369 state_ = kError;
382 if (!client_) 370 if (!client_)
383 return; 371 return;
384 client_->OnError(from_here, reason); 372 client_->OnError(from_here, reason);
385 } 373 }
386 } 374 }
387 375
376 void VideoCaptureDeviceAndroid::DoTakePhoto(TakePhotoCallback callback) {
377 DCHECK(main_task_runner_->BelongsToCurrentThread());
378 #if DCHECK_IS_ON()
379 {
380 base::AutoLock lock(lock_);
381 DCHECK_EQ(kConfigured, state_);
382 DCHECK(got_first_frame_);
383 }
384 #endif
385 JNIEnv* env = AttachCurrentThread();
386
387 // Make copy on the heap so we can pass the pointer through JNI.
388 std::unique_ptr<TakePhotoCallback> heap_callback(
389 new TakePhotoCallback(std::move(callback)));
390 const intptr_t callback_id = reinterpret_cast<intptr_t>(heap_callback.get());
391 if (!Java_VideoCapture_takePhoto(env, j_capture_.obj(), callback_id,
392 next_photo_resolution_.width(),
393 next_photo_resolution_.height()))
394 return;
395
396 {
397 base::AutoLock lock(photo_callbacks_lock_);
398 photo_callbacks_.push_back(std::move(heap_callback));
399 }
400 }
401
402 void VideoCaptureDeviceAndroid::DoGetPhotoCapabilities(
403 GetPhotoCapabilitiesCallback callback) {
404 DCHECK(main_task_runner_->BelongsToCurrentThread());
405 #if DCHECK_IS_ON()
406 {
407 base::AutoLock lock(lock_);
408 DCHECK_EQ(kConfigured, state_);
409 DCHECK(got_first_frame_);
410 }
411 #endif
412 JNIEnv* env = AttachCurrentThread();
413
414 PhotoCapabilities caps(
415 Java_VideoCapture_getPhotoCapabilities(env, j_capture_.obj()));
416
417 // TODO(mcasas): Manual member copying sucks, consider adding typemapping from
418 // PhotoCapabilities to mojom::PhotoCapabilitiesPtr, https://crbug.com/622002.
419 mojom::PhotoCapabilitiesPtr photo_capabilities =
420 mojom::PhotoCapabilities::New();
421 photo_capabilities->iso = mojom::Range::New();
422 photo_capabilities->iso->current = caps.getCurrentIso();
423 photo_capabilities->iso->max = caps.getMaxIso();
424 photo_capabilities->iso->min = caps.getMinIso();
425 photo_capabilities->height = mojom::Range::New();
426 photo_capabilities->height->current = caps.getCurrentHeight();
427 photo_capabilities->height->max = caps.getMaxHeight();
428 photo_capabilities->height->min = caps.getMinHeight();
429 photo_capabilities->width = mojom::Range::New();
430 photo_capabilities->width->current = caps.getCurrentWidth();
431 photo_capabilities->width->max = caps.getMaxWidth();
432 photo_capabilities->width->min = caps.getMinWidth();
433 photo_capabilities->zoom = mojom::Range::New();
434 photo_capabilities->zoom->current = caps.getCurrentZoom();
435 photo_capabilities->zoom->max = caps.getMaxZoom();
436 photo_capabilities->zoom->min = caps.getMinZoom();
437 photo_capabilities->focus_mode = caps.getAutoFocusInUse()
438 ? mojom::FocusMode::AUTO
439 : mojom::FocusMode::MANUAL;
440 callback.Run(std::move(photo_capabilities));
441 }
442
443 void VideoCaptureDeviceAndroid::DoSetPhotoOptions(
444 mojom::PhotoSettingsPtr settings,
445 SetPhotoOptionsCallback callback) {
446 DCHECK(main_task_runner_->BelongsToCurrentThread());
447 #if DCHECK_IS_ON()
448 {
449 base::AutoLock lock(lock_);
450 DCHECK_EQ(kConfigured, state_);
451 DCHECK(got_first_frame_);
452 }
453 #endif
454 JNIEnv* env = AttachCurrentThread();
455
456 // |width| and/or |height| are kept for the next TakePhoto()s.
457 if (settings->has_width || settings->has_height)
458 next_photo_resolution_.SetSize(0, 0);
459 if (settings->has_width) {
460 next_photo_resolution_.set_width(
461 base::saturated_cast<int>(settings->width));
462 }
463 if (settings->has_height) {
464 next_photo_resolution_.set_height(
465 base::saturated_cast<int>(settings->height));
466 }
467
468 if (settings->has_zoom)
469 Java_VideoCapture_setZoom(env, j_capture_.obj(), settings->zoom);
470
471 callback.Run(true);
472 }
473
388 } // namespace media 474 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video/android/video_capture_device_android.h ('k') | media/capture/video/fake_video_capture_device.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698