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

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

Issue 11860002: Add video capture on Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: code review Created 7 years, 10 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 | Annotate | Revision Log
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/video/capture/android/video_capture_device_android.h"
6
7 #include <string>
8
9 #include "base/android/jni_android.h"
10 #include "base/android/scoped_java_ref.h"
11 #include "base/string_number_conversions.h"
12 #include "base/stringprintf.h"
13 #include "jni/VideoCapture_jni.h"
14
15 using base::android::AttachCurrentThread;
16 using base::android::CheckException;
17 using base::android::GetClass;
18 using base::android::MethodID;
19 using base::android::JavaRef;
20 using base::android::ScopedJavaLocalRef;
21
22 namespace {
23
24 void ResetBuffer(uint8* buffer, int width, int height) {
25 int y_size = width * height;
26 memset(buffer, 0, y_size);
27 buffer += y_size;
28 memset(buffer, 128, y_size / 2);
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 What about the stride!=width case? Also this is ob
wjia(left Chromium) 2013/01/30 18:25:47 We need support width!=stride case, all the way in
29 }
30
31 void RotatePlaneByPixels(uint8* src, uint8* dest, int width, int height,
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 I'm confused by your choice here; why ask Camera t
wjia(left Chromium) 2013/01/30 18:25:47 There is a TODO in VideoCapture.java to investigat
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 This explanation doesn't make sense to me. The cod
wjia(left Chromium) 2013/02/06 00:45:34 As we discussed offline, CallbackBuffer would be u
32 int rotation, bool flip_vert, bool flip_horiz) {
33 // Consolidate cases. Only 0 and 90 are left.
34 if (rotation == 180 || rotation == 270) {
35 rotation -= 180;
36 flip_vert = !flip_vert;
37 flip_horiz = !flip_horiz;
38 }
39
40 int num_rows = height;
41 int num_cols = width;
42 int src_stride = width;
43 int dest_row_step = width;
44 int dest_col_step = 1;
45
46 if (rotation == 0) {
47 if (flip_horiz) {
48 // Use pixel copying.
49 dest_col_step = -1;
50 if (flip_vert) {
51 // Rotation 180.
52 dest_row_step = -width;
53 dest += height * width - 1;
54 } else {
55 dest += width - 1;
56 }
57 } else {
58 if (flip_vert) {
59 // Fast copy by rows.
60 dest += width * (height - 1);
61 for (int row = 0; row < height; ++row) {
62 memcpy(dest, src, width);
63 src += width;
64 dest -= width;
65 }
66 } else {
67 memcpy(dest, src, width * height);
68 }
69 return;
70 }
71 } else if (rotation == 90) {
72 int offset = (width - height) / 2;
73 if (width > height) {
74 src += offset;
75 num_rows = num_cols = height;
76 } else {
77 src += width * offset;
78 num_rows = num_cols = width;
79 offset = (height - width) / 2;
80 }
81
82 dest_col_step = (flip_vert ? -width : width);
83 if (flip_horiz) {
84 dest_row_step = 1;
85 if (flip_vert) {
86 dest += (width > height ? width * (height - 1) + offset :
87 width * (height - offset - 1));
88 } else {
89 dest += (width > height ? offset : width * offset);
90 }
91 } else {
92 dest_row_step = -1;
93 if (flip_vert) {
94 dest += (width > height ? width * height - offset - 1 :
95 width * (height - offset) - 1);
96 } else {
97 dest += (width > height ? width - offset - 1 :
98 width * (offset + 1) - 1);
99 }
100 }
101 }
102
103 // Copy pixels.
104 for (int row = 0; row < num_rows; ++row) {
105 uint8* src_ptr = src;
106 uint8* dest_ptr = dest;
107 for (int col = 0; col < num_cols; ++col) {
108 *dest_ptr = *src_ptr++;
109 dest_ptr += dest_col_step;
110 }
111 src += src_stride;
112 dest += dest_row_step;
113 }
114 }
115
116 int GetIntField(JNIEnv* env,
117 const JavaRef<jclass>& clazz,
118 const JavaRef<jobject>& instance,
119 const char* field_name) {
120 jfieldID field = GetFieldID(env, clazz, field_name, "I");
121 jint int_value = env->GetIntField(instance.obj(), field);
122 return int_value;
123 }
124
125 } // namespace
126
127 namespace media {
128
129 // static
130 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
131 device_names->clear();
132
133 JNIEnv* env = AttachCurrentThread();
134 CHECK(env);
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 ACT() already DCHECKs. Why CHECK here?
wjia(left Chromium) 2013/01/30 18:25:47 I think caller wouldn't assume any implementation
135
136 std::string camera_string("android/hardware/Camera");
Yaron 2013/01/28 23:58:51 Can you use the jni generator to generate bindings
wjia(left Chromium) 2013/01/30 18:25:47 Did it for Camera. But jar_file_jni_generator.gypi
137 std::string camera_info_string = camera_string + "$CameraInfo";
138
139 ScopedJavaLocalRef<jclass> camera_class(GetClass(env, camera_string.c_str()));
140
141 jmethodID method_get_number_of_cameras = MethodID::Get<MethodID::TYPE_STATIC>(
142 env, camera_class.obj(), "getNumberOfCameras", "()I");
143
144 int num_cameras = env->CallStaticIntMethod(camera_class.obj(),
145 method_get_number_of_cameras);
146 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
147 if (num_cameras <= 0)
148 return;
149
150 // Signature of getCameraInfo: "(ILandroid/hardware/Camera$CameraInfo;)V"
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 Is this adding value?
wjia(left Chromium) 2013/01/30 18:25:47 Removed.
151 std::string signature = "(IL" + camera_info_string + ";)V";
152 jmethodID method_get_camera_info = MethodID::Get<MethodID::TYPE_STATIC>(
153 env, camera_class.obj(), "getCameraInfo", signature.c_str());
154
155 ScopedJavaLocalRef<jclass> camera_info_class(
156 GetClass(env, camera_info_string.c_str()));
157 jmethodID constructor = MethodID::Get<MethodID::TYPE_INSTANCE>(env,
158 camera_info_class.obj(), "<init>", "()V");
159
160 ScopedJavaLocalRef<jobject> object_camera_info(env,
161 env->NewObject(camera_info_class.obj(), constructor));
162
163 jfieldID field_facing = GetFieldID(env, camera_info_class, "facing", "I");
164 jfieldID field_facing_front = GetStaticFieldID(env,
165 camera_info_class, "CAMERA_FACING_FRONT", "I");
166
167 for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
168 env->CallStaticVoidMethod(camera_class.obj(),
169 method_get_camera_info,
170 camera_id,
171 object_camera_info.obj());
172 CheckException(env);
173
174 Name name;
175 name.unique_id = StringPrintf("%d", camera_id);
176 std::string facing_string;
177 if (env->GetIntField(object_camera_info.obj(), field_facing) ==
178 env->GetStaticIntField(camera_info_class.obj(), field_facing_front)) {
179 facing_string = "front";
180 } else {
181 facing_string = "back";
182 }
183 name.device_name = StringPrintf("camera %d, facing %s",
184 camera_id, facing_string.c_str());
185 device_names->push_back(name);
186 jfieldID field_orientation = GetFieldID(env, camera_info_class,
187 "orientation", "I");
188 jint orientation = env->GetIntField(object_camera_info.obj(),
189 field_orientation);
190 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name="
191 << name.device_name.c_str()
192 << ", unique_id="
193 << name.unique_id.c_str()
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 .c_str() unnecessary here and above
wjia(left Chromium) 2013/01/30 18:25:47 Done.
194 << ", orientation "
195 << orientation;
196 }
197 }
198
199 // static
200 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
201 VideoCaptureDeviceAndroid* self = new VideoCaptureDeviceAndroid(device_name);
202 if (!self || !self->Init())
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 !self is unnecessary
wjia(left Chromium) 2013/01/30 18:25:47 Done.
203 return NULL;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 Init() failure leaks here. A better idiom is: sco
wjia(left Chromium) 2013/01/30 18:25:47 Done.
204
205 return self;
206 }
207
208 // static
209 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
210 bool ret = RegisterNativesImpl(env);
211 DCHECK(g_VideoCapture_clazz);
Yaron 2013/01/28 23:58:51 No need to DCHECK. just do: return Register..
wjia(left Chromium) 2013/01/30 18:25:47 Done.
212 return ret;
213 }
214
215 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name)
216 : state_(kIdle),
217 observer_(NULL),
218 device_name_(device_name),
219 rotation_(0) {
220 }
221
222 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
223 DeAllocate();
224 }
225
226 bool VideoCaptureDeviceAndroid::Init() {
227 int id;
228 if (device_name_.device_name.find("camera") == std::string::npos)
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 why do you care about this?
wjia(left Chromium) 2013/01/30 18:25:47 Removed.
229 return false;
230
231 base::StringToInt(device_name_.unique_id, &id);
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 you should always test the return code of string-p
wjia(left Chromium) 2013/01/30 18:25:47 Done.
232
233 JNIEnv* env = AttachCurrentThread();
234 if (!env)
235 return false;
236
237 j_capture_.Reset(Java_VideoCapture_createVideoCapture(env,
238 base::android::GetApplicationContext(), id,
239 reinterpret_cast<jlong>(this)));
240
241 return true;
242 }
243
244 const VideoCaptureDevice::Name& VideoCaptureDeviceAndroid::device_name() {
245 return device_name_;
246 }
247
248 void VideoCaptureDeviceAndroid::Allocate(int width, int height, int frame_rate,
249 EventHandler* observer) {
250 {
251 base::AutoLock lock(lock_);
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 IWBN for the decl of lock_ in the .h to be accompa
wjia(left Chromium) 2013/01/30 18:25:47 Done.
252 if (state_ != kIdle)
253 return;
254 observer_ = observer;
255 state_ = kAllocated;
256 }
257
258 JNIEnv* env = AttachCurrentThread();
259 CHECK(env);
260
261 jint ret = Java_VideoCapture_allocate(env,
262 j_capture_.obj(),
263 width,
264 height,
265 frame_rate);
266 if (ret < 0) {
267 SetErrorState("failed to allocate");
268 return;
269 }
270
271 // Store current width and height.
272 current_settings_.color = VideoCaptureCapability::kYV12;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 please initialize struct fields in declaration ord
wjia(left Chromium) 2013/01/30 18:25:47 Done.
273 current_settings_.width =
274 Java_VideoCapture_queryWidth(env, j_capture_.obj());
275 current_settings_.height =
276 Java_VideoCapture_queryHeight(env, j_capture_.obj());
277 current_settings_.frame_rate =
278 Java_VideoCapture_queryFrameRate(env, j_capture_.obj());
279 DCHECK(current_settings_.width > 0 && !(current_settings_.width % 2));
280 DCHECK(current_settings_.height > 0 && !(current_settings_.height % 2));
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 Why insist on even dimensions (but not powers-of-t
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 What about the other fields of VCC? Shouldn't the
wjia(left Chromium) 2013/01/30 18:25:47 Even number would ensure buffer size calculation i
wjia(left Chromium) 2013/01/30 18:25:47 zero-init'ed current_settings_ in constructor.
281
282 DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried width="
283 << current_settings_.width
284 << ", height="
285 << current_settings_.height
286 << ", frame_rate="
287 << current_settings_.frame_rate;
288 // Report the frame size to the observer.
289 observer_->OnFrameInfo(current_settings_);
290
291 int y_size = current_settings_.width * current_settings_.height;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 Again, what about stride? See, for example, the e
wjia(left Chromium) 2013/01/30 18:25:47 Yes, I am aware of this. Need to do a refactoring
292 rotation_buffer_.reset(new uint8[y_size * 3 / 2]);
293 ResetBuffer(rotation_buffer_.get(), current_settings_.width,
294 current_settings_.height);
295 }
296
297 void VideoCaptureDeviceAndroid::Start() {
298 DVLOG(1) << "VideoCaptureDeviceAndroid::Start";
299 {
300 base::AutoLock lock(lock_);
301 if (state_ != kAllocated)
302 return;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 why isn't this a DCHECK/SetErrorState()?
wjia(left Chromium) 2013/01/30 18:25:47 changed to DCHECK.
303 }
304
305 JNIEnv* env = AttachCurrentThread();
306 CHECK(env);
307
308 jint ret = Java_VideoCapture_startCapture(env, j_capture_.obj());
309 if (ret < 0) {
310 SetErrorState("failed to start capture");
311 return;
312 }
313
314 {
315 base::AutoLock lock(lock_);
316 state_ = kCapturing;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 having two critical sections means two threads can
wjia(left Chromium) 2013/01/30 18:25:47 Comments for |lock_| have been added. |state_| can
317 }
318 }
319
320 void VideoCaptureDeviceAndroid::Stop() {
321 DVLOG(1) << "VideoCaptureDeviceAndroid::Stop";
322 {
323 base::AutoLock lock(lock_);
324 if (state_ == kIdle || state_ == kAllocated)
325 return;
326 state_ = kAllocated;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 This makes Stop() silently clear an error state, w
wjia(left Chromium) 2013/01/30 18:25:47 Done.
327 }
328
329 JNIEnv* env = AttachCurrentThread();
330 CHECK(env);
331
332 jint ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
333 if (ret < 0) {
334 SetErrorState("failed to stop capture");
335 return;
336 }
337 }
338
339 void VideoCaptureDeviceAndroid::DeAllocate() {
340 DVLOG(1) << "VideoCaptureDeviceAndroid::DeAllocate";
341 {
342 base::AutoLock lock(lock_);
343 if (state_ == kIdle)
344 return;
345
346 if (state_ == kCapturing) {
347 base::AutoUnlock unlock(lock_);
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 There is a race here if two threads can call DeAll
wjia(left Chromium) 2013/01/30 18:25:47 DeAllocate() is called from only one thread.
348 Stop();
349 }
350
351 if (state_ == kAllocated)
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 How can this fail to be true, other than the Error
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 Generally I think most of your == tests should be
wjia(left Chromium) 2013/01/30 18:25:47 In Stop(), error state is kept. Error could also h
wjia(left Chromium) 2013/01/30 18:25:47 Done.
352 state_ = kIdle;
353
354 observer_ = NULL;
355 }
356
357 JNIEnv* env = AttachCurrentThread();
358 CHECK(env);
359
360 Java_VideoCapture_deallocate(env, j_capture_.obj());
361 }
362
363 void VideoCaptureDeviceAndroid::OnFrameAvailable(JNIEnv* env, jobject obj,
364 jbyteArray data, jint length,
365 jint rotation, jboolean flip_vert, jboolean flip_horiz) {
366 DVLOG(3) << "VideoCaptureDeviceAndroid::OnFrameAvailable: length =" << length;
367
368 base::AutoLock lock(lock_);
369 if (state_ != kCapturing || !observer_)
370 return;
371
372 jbyte* buffer = env->GetByteArrayElements(data, NULL);
373 if (!buffer) {
374 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
375 "failed to GetByteArrayElements";
376 return;
377 }
378
379 // TODO(wjia): move rotation into VideoCaptureController to remove
380 // one buffer copying.
381 // Rotate the buffer when needed.
382 int width = current_settings_.width;
383 int height = current_settings_.height;
384 if (rotation_ != rotation) {
385 rotation_ = rotation;
386 ResetBuffer(rotation_buffer_.get(), width, height);
387 }
388
389 uint8* src = reinterpret_cast<uint8*>(buffer);
390 uint8* dest = rotation_buffer_.get();
391
392 RotatePlaneByPixels(src, dest, width, height, rotation, flip_vert,
393 flip_horiz);
394 int y_size = width * height;
395 src += y_size;
396 dest += y_size;
397 RotatePlaneByPixels(src, dest, width/2, height/2, rotation, flip_vert,
398 flip_horiz);
399 src += y_size/4;
400 dest += y_size/4;
401 RotatePlaneByPixels(src, dest, width/2, height/2, rotation, flip_vert,
402 flip_horiz);
403 src = rotation_buffer_.get();
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 I found this confusing; why not just use rotation_
wjia(left Chromium) 2013/01/30 18:25:47 I was kind of looking ahead. Since this rotation c
404 observer_->OnIncomingCapturedFrame(src, length, base::Time::Now());
405
406 env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
407 }
408
409 void VideoCaptureDeviceAndroid::SetErrorState(const std::string& reason) {
410 LOG(ERROR) << "VideoCaptureDeviceAndroid::SetErrorState: " << reason;
411 state_ = kError;
Ami GONE FROM CHROMIUM 2013/01/28 23:55:47 This is racy; probably you want to rename SES to S
wjia(left Chromium) 2013/01/30 18:25:47 Oops, missed this one. Accessing |state_| should a
412 observer_->OnError();
413 }
414
415 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698