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

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

Issue 2214533002: move //media/capture to //device/capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/capture/video/android/video_capture_device_android.h"
6
7 #include <stdint.h>
8 #include <utility>
9
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_array.h"
12 #include "base/android/jni_string.h"
13 #include "base/numerics/safe_conversions.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "jni/VideoCapture_jni.h"
16 #include "media/capture/video/android/photo_capabilities.h"
17 #include "media/capture/video/android/video_capture_device_factory_android.h"
18 #include "third_party/libyuv/include/libyuv.h"
19
20 using base::android::AttachCurrentThread;
21 using base::android::CheckException;
22 using base::android::GetClass;
23 using base::android::MethodID;
24 using base::android::JavaRef;
25 using base::android::ScopedJavaLocalRef;
26
27 namespace media {
28
29 // static
30 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
31 return RegisterNativesImpl(env);
32 }
33
34 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(
35 const VideoCaptureDeviceDescriptor& device_descriptor)
36 : state_(kIdle),
37 got_first_frame_(false),
38 device_descriptor_(device_descriptor) {}
39
40 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
41 DCHECK(thread_checker_.CalledOnValidThread());
42 StopAndDeAllocate();
43 }
44
45 bool VideoCaptureDeviceAndroid::Init() {
46 int id;
47 if (!base::StringToInt(device_descriptor_.device_id, &id))
48 return false;
49
50 j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid(
51 id, reinterpret_cast<intptr_t>(this)));
52 return true;
53 }
54
55 void VideoCaptureDeviceAndroid::AllocateAndStart(
56 const VideoCaptureParams& params,
57 std::unique_ptr<Client> client) {
58 DCHECK(thread_checker_.CalledOnValidThread());
59 {
60 base::AutoLock lock(lock_);
61 if (state_ != kIdle)
62 return;
63 client_ = std::move(client);
64 got_first_frame_ = false;
65 }
66
67 JNIEnv* env = AttachCurrentThread();
68
69 jboolean ret = Java_VideoCapture_allocate(
70 env, j_capture_.obj(), params.requested_format.frame_size.width(),
71 params.requested_format.frame_size.height(),
72 params.requested_format.frame_rate);
73 if (!ret) {
74 SetErrorState(FROM_HERE, "failed to allocate");
75 return;
76 }
77
78 capture_format_.frame_size.SetSize(
79 Java_VideoCapture_queryWidth(env, j_capture_.obj()),
80 Java_VideoCapture_queryHeight(env, j_capture_.obj()));
81 capture_format_.frame_rate =
82 Java_VideoCapture_queryFrameRate(env, j_capture_.obj());
83 capture_format_.pixel_format = GetColorspace();
84 DCHECK_NE(capture_format_.pixel_format, media::PIXEL_FORMAT_UNKNOWN);
85 CHECK(capture_format_.frame_size.GetArea() > 0);
86 CHECK(!(capture_format_.frame_size.width() % 2));
87 CHECK(!(capture_format_.frame_size.height() % 2));
88
89 if (capture_format_.frame_rate > 0) {
90 frame_interval_ = base::TimeDelta::FromMicroseconds(
91 (base::Time::kMicrosecondsPerSecond + capture_format_.frame_rate - 1) /
92 capture_format_.frame_rate);
93 }
94
95 DVLOG(1) << __FUNCTION__ << " requested ("
96 << capture_format_.frame_size.ToString() << ")@ "
97 << capture_format_.frame_rate << "fps";
98
99 ret = Java_VideoCapture_startCapture(env, j_capture_.obj());
100 if (!ret) {
101 SetErrorState(FROM_HERE, "failed to start capture");
102 return;
103 }
104
105 {
106 base::AutoLock lock(lock_);
107 state_ = kCapturing;
108 }
109 }
110
111 void VideoCaptureDeviceAndroid::StopAndDeAllocate() {
112 DCHECK(thread_checker_.CalledOnValidThread());
113 {
114 base::AutoLock lock(lock_);
115 if (state_ != kCapturing && state_ != kError)
116 return;
117 }
118
119 JNIEnv* env = AttachCurrentThread();
120
121 const jboolean ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
122 if (!ret) {
123 SetErrorState(FROM_HERE, "failed to stop capture");
124 return;
125 }
126
127 {
128 base::AutoLock lock(lock_);
129 state_ = kIdle;
130 client_.reset();
131 }
132
133 Java_VideoCapture_deallocate(env, j_capture_.obj());
134 }
135
136 void VideoCaptureDeviceAndroid::TakePhoto(TakePhotoCallback callback) {
137 DCHECK(thread_checker_.CalledOnValidThread());
138 {
139 base::AutoLock lock(lock_);
140 if (state_ != kCapturing)
141 return;
142 }
143
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 }
160
161 void VideoCaptureDeviceAndroid::GetPhotoCapabilities(
162 GetPhotoCapabilitiesCallback callback) {
163 DCHECK(thread_checker_.CalledOnValidThread());
164 JNIEnv* env = AttachCurrentThread();
165
166 PhotoCapabilities caps(
167 Java_VideoCapture_getPhotoCapabilities(env, j_capture_.obj()));
168
169 // TODO(mcasas): Manual member copying sucks, consider adding typemapping from
170 // PhotoCapabilities to mojom::PhotoCapabilitiesPtr, https://crbug.com/622002.
171 mojom::PhotoCapabilitiesPtr photo_capabilities =
172 mojom::PhotoCapabilities::New();
173 photo_capabilities->iso = mojom::Range::New();
174 photo_capabilities->iso->current = caps.getCurrentIso();
175 photo_capabilities->iso->max = caps.getMaxIso();
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 }
194
195 void VideoCaptureDeviceAndroid::SetPhotoOptions(
196 mojom::PhotoSettingsPtr settings,
197 SetPhotoOptionsCallback callback) {
198 DCHECK(thread_checker_.CalledOnValidThread());
199 JNIEnv* env = AttachCurrentThread();
200 // |width| and/or |height| are kept for the next TakePhoto()s.
201 if (settings->has_width || settings->has_height)
202 next_photo_resolution_.SetSize(0, 0);
203 if (settings->has_width) {
204 next_photo_resolution_.set_width(
205 base::saturated_cast<int>(settings->width));
206 }
207 if (settings->has_height) {
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 }
216
217 void VideoCaptureDeviceAndroid::OnFrameAvailable(
218 JNIEnv* env,
219 const JavaParamRef<jobject>& obj,
220 const JavaParamRef<jbyteArray>& data,
221 jint length,
222 jint rotation) {
223 {
224 base::AutoLock lock(lock_);
225 if (state_ != kCapturing || !client_)
226 return;
227 }
228
229 jbyte* buffer = env->GetByteArrayElements(data, NULL);
230 if (!buffer) {
231 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
232 "failed to GetByteArrayElements";
233 return;
234 }
235
236 const base::TimeTicks current_time = base::TimeTicks::Now();
237 if (!got_first_frame_) {
238 // Set aside one frame allowance for fluctuation.
239 expected_next_frame_time_ = current_time - frame_interval_;
240 first_ref_time_ = current_time;
241 got_first_frame_ = true;
242 }
243
244 // Deliver the frame when it doesn't arrive too early.
245 if (expected_next_frame_time_ <= current_time) {
246 expected_next_frame_time_ += frame_interval_;
247
248 // TODO(qiangchen): Investigate how to get raw timestamp for Android,
249 // rather than using reference time to calculate timestamp.
250 base::AutoLock lock(lock_);
251 if (!client_)
252 return;
253 client_->OnIncomingCapturedData(reinterpret_cast<uint8_t*>(buffer), length,
254 capture_format_, rotation, current_time,
255 current_time - first_ref_time_);
256 }
257
258 env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
259 }
260
261 void VideoCaptureDeviceAndroid::OnI420FrameAvailable(JNIEnv* env,
262 jobject obj,
263 jobject y_buffer,
264 jint y_stride,
265 jobject u_buffer,
266 jobject v_buffer,
267 jint uv_row_stride,
268 jint uv_pixel_stride,
269 jint width,
270 jint height,
271 jint rotation) {
272 {
273 base::AutoLock lock(lock_);
274 if (state_ != kCapturing || !client_)
275 return;
276 }
277
278 const base::TimeTicks current_time = base::TimeTicks::Now();
279 if (!got_first_frame_) {
280 // Set aside one frame allowance for fluctuation.
281 expected_next_frame_time_ = current_time - frame_interval_;
282 first_ref_time_ = current_time;
283 got_first_frame_ = true;
284 }
285
286 uint8_t* const y_src =
287 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer));
288 CHECK(y_src);
289 uint8_t* const u_src =
290 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer));
291 CHECK(u_src);
292 uint8_t* const v_src =
293 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer));
294 CHECK(v_src);
295
296 const int y_plane_length = width * height;
297 const int uv_plane_length = y_plane_length / 4;
298 const int buffer_length = y_plane_length + uv_plane_length * 2;
299 std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_length]);
300
301 libyuv::Android420ToI420(y_src, y_stride, u_src, uv_row_stride, v_src,
302 uv_row_stride, uv_pixel_stride, buffer.get(), width,
303 buffer.get() + y_plane_length, width / 2,
304 buffer.get() + y_plane_length + uv_plane_length,
305 width / 2, width, height);
306
307 // Deliver the frame when it doesn't arrive too early.
308 if (expected_next_frame_time_ <= current_time) {
309 expected_next_frame_time_ += frame_interval_;
310
311 // TODO(qiangchen): Investigate how to get raw timestamp for Android,
312 // rather than using reference time to calculate timestamp.
313 base::AutoLock lock(lock_);
314 if (!client_)
315 return;
316 client_->OnIncomingCapturedData(buffer.get(), buffer_length,
317 capture_format_, rotation, current_time,
318 current_time - first_ref_time_);
319 }
320 }
321
322 void VideoCaptureDeviceAndroid::OnError(JNIEnv* env,
323 const JavaParamRef<jobject>& obj,
324 const JavaParamRef<jstring>& message) {
325 SetErrorState(FROM_HERE,
326 base::android::ConvertJavaStringToUTF8(env, message));
327 }
328
329 void VideoCaptureDeviceAndroid::OnPhotoTaken(
330 JNIEnv* env,
331 const base::android::JavaParamRef<jobject>& obj,
332 jlong callback_id,
333 const base::android::JavaParamRef<jbyteArray>& data) {
334 DCHECK(callback_id);
335
336 base::AutoLock lock(photo_callbacks_lock_);
337
338 TakePhotoCallback* const cb =
339 reinterpret_cast<TakePhotoCallback*>(callback_id);
340 // Search for the pointer |cb| in the list of |photo_callbacks_|.
341 const auto reference_it =
342 std::find_if(photo_callbacks_.begin(), photo_callbacks_.end(),
343 [cb](const std::unique_ptr<TakePhotoCallback>& callback) {
344 return callback.get() == cb;
345 });
346 if (reference_it == photo_callbacks_.end()) {
347 NOTREACHED() << "|callback_id| not found.";
348 return;
349 }
350
351 mojom::BlobPtr blob = mojom::Blob::New();
352 base::android::JavaByteArrayToByteVector(env, data.obj(), &blob->data);
353 blob->mime_type = blob->data.empty() ? "" : "image/jpeg";
354 cb->Run(std::move(blob));
355
356 photo_callbacks_.erase(reference_it);
357 }
358
359 VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
360 JNIEnv* env = AttachCurrentThread();
361 const int current_capture_colorspace =
362 Java_VideoCapture_getColorspace(env, j_capture_.obj());
363 switch (current_capture_colorspace) {
364 case ANDROID_IMAGE_FORMAT_YV12:
365 return media::PIXEL_FORMAT_YV12;
366 case ANDROID_IMAGE_FORMAT_YUV_420_888:
367 return media::PIXEL_FORMAT_I420;
368 case ANDROID_IMAGE_FORMAT_NV21:
369 return media::PIXEL_FORMAT_NV21;
370 case ANDROID_IMAGE_FORMAT_UNKNOWN:
371 default:
372 return media::PIXEL_FORMAT_UNKNOWN;
373 }
374 }
375
376 void VideoCaptureDeviceAndroid::SetErrorState(
377 const tracked_objects::Location& from_here,
378 const std::string& reason) {
379 {
380 base::AutoLock lock(lock_);
381 state_ = kError;
382 if (!client_)
383 return;
384 client_->OnError(from_here, reason);
385 }
386 }
387
388 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698