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

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 and rebase 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/Camera_jni.h"
14 #include "jni/VideoCapture_jni.h"
15
16 using base::android::AttachCurrentThread;
17 using base::android::CheckException;
18 using base::android::GetClass;
19 using base::android::MethodID;
20 using base::android::JavaRef;
21 using base::android::ScopedJavaLocalRef;
22
23 namespace {
24
25 // TODO(wjia): add stride as part of buffer parameter.
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 This is not enough; IMO you need to enforce the as
wjia(left Chromium) 2013/02/06 00:45:34 Since Android YV12 always does 16 byte alignment f
26 void ResetBufferI420(uint8* buffer, int width, int height) {
27 int y_size = width * height;
28 memset(buffer, 0, y_size);
29 buffer += y_size;
30 memset(buffer, 128, y_size / 2);
31 }
32
33 void RotatePlaneByPixels(uint8* src, uint8* dest, int width, int height,
34 int rotation, bool flip_vert, bool flip_horiz) {
35 // Consolidate cases. Only 0 and 90 are left.
36 if (rotation == 180 || rotation == 270) {
37 rotation -= 180;
38 flip_vert = !flip_vert;
39 flip_horiz = !flip_horiz;
40 }
41
42 int num_rows = height;
43 int num_cols = width;
44 int src_stride = width;
45 int dest_row_step = width;
46 int dest_col_step = 1;
47
48 if (rotation == 0) {
49 if (flip_horiz) {
50 // Use pixel copying.
51 dest_col_step = -1;
52 if (flip_vert) {
53 // Rotation 180.
54 dest_row_step = -width;
55 dest += height * width - 1;
56 } else {
57 dest += width - 1;
58 }
59 } else {
60 if (flip_vert) {
61 // Fast copy by rows.
62 dest += width * (height - 1);
63 for (int row = 0; row < height; ++row) {
64 memcpy(dest, src, width);
65 src += width;
66 dest -= width;
67 }
68 } else {
69 memcpy(dest, src, width * height);
70 }
71 return;
72 }
73 } else if (rotation == 90) {
74 int offset = (width - height) / 2;
75 if (width > height) {
76 src += offset;
77 num_rows = num_cols = height;
78 } else {
79 src += width * offset;
80 num_rows = num_cols = width;
81 offset = (height - width) / 2;
82 }
83
84 dest_col_step = (flip_vert ? -width : width);
85 if (flip_horiz) {
86 dest_row_step = 1;
87 if (flip_vert) {
88 dest += (width > height ? width * (height - 1) + offset :
89 width * (height - offset - 1));
90 } else {
91 dest += (width > height ? offset : width * offset);
92 }
93 } else {
94 dest_row_step = -1;
95 if (flip_vert) {
96 dest += (width > height ? width * height - offset - 1 :
97 width * (height - offset) - 1);
98 } else {
99 dest += (width > height ? width - offset - 1 :
100 width * (offset + 1) - 1);
101 }
102 }
103 }
104
105 // Copy pixels.
106 for (int row = 0; row < num_rows; ++row) {
107 uint8* src_ptr = src;
108 uint8* dest_ptr = dest;
109 for (int col = 0; col < num_cols; ++col) {
110 *dest_ptr = *src_ptr++;
111 dest_ptr += dest_col_step;
112 }
113 src += src_stride;
114 dest += dest_row_step;
115 }
116 }
117
118 int GetIntField(JNIEnv* env,
119 const JavaRef<jclass>& clazz,
120 const JavaRef<jobject>& instance,
121 const char* field_name) {
122 jfieldID field = GetFieldID(env, clazz, field_name, "I");
123 jint int_value = env->GetIntField(instance.obj(), field);
124 return int_value;
125 }
126
127 } // namespace
128
129 namespace media {
130
131 // static
132 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
133 device_names->clear();
134
135 JNIEnv* env = AttachCurrentThread();
136 CHECK(env);
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 Chromium code eschews NULL CHECKs like this that o
wjia(left Chromium) 2013/02/06 00:45:34 removed all CHECK(env).
137
138 int num_cameras = JNI_Camera::Java_Camera_getNumberOfCameras(env);
139 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
140 if (num_cameras <= 0)
141 return;
142
143 // TODO(wjia): switch to using same approach as Camera when
144 // jar_file_jni_generator.gypi supports system inner classes.
145 std::string camera_info_string("android/hardware/Camera$CameraInfo");
146
147 ScopedJavaLocalRef<jclass> camera_info_class(
148 GetClass(env, camera_info_string.c_str()));
149 jmethodID constructor = MethodID::Get<MethodID::TYPE_INSTANCE>(
150 env, camera_info_class.obj(), "<init>", "()V");
151
152 ScopedJavaLocalRef<jobject> object_camera_info(
153 env, env->NewObject(camera_info_class.obj(), constructor));
154
155 jfieldID field_facing = GetFieldID(env, camera_info_class, "facing", "I");
156 jfieldID field_facing_front = GetStaticFieldID(
157 env, camera_info_class, "CAMERA_FACING_FRONT", "I");
158
159 for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
160 JNI_Camera::Java_Camera_getCameraInfo(env, camera_id,
161 object_camera_info.obj());
162 CheckException(env);
163
164 Name name;
165 name.unique_id = StringPrintf("%d", camera_id);
166 std::string facing_string;
167 if (env->GetIntField(object_camera_info.obj(), field_facing) ==
168 env->GetStaticIntField(camera_info_class.obj(), field_facing_front)) {
169 facing_string = "front";
170 } else {
171 facing_string = "back";
172 }
173 name.device_name = StringPrintf("camera %d, facing %s",
174 camera_id, facing_string.c_str());
175 device_names->push_back(name);
176 jfieldID field_orientation = GetFieldID(env, camera_info_class,
177 "orientation", "I");
178 jint orientation = env->GetIntField(object_camera_info.obj(),
179 field_orientation);
180 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name="
181 << name.device_name
182 << ", unique_id="
183 << name.unique_id
184 << ", orientation "
185 << orientation;
186 }
187 }
188
189 // static
190 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
191 return VideoCaptureDeviceAndroid::Create(device_name);
192 }
193
194 // static
195 VideoCaptureDevice* VideoCaptureDeviceAndroid::Create(const Name& device_name) {
196 scoped_ptr<VideoCaptureDeviceAndroid> ret(
197 new VideoCaptureDeviceAndroid(device_name));
198 if (ret->Init())
199 return ret.release();
200 return NULL;
201 }
202
203 // static
204 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
205 bool ret = RegisterNativesImpl(env);
206 if (ret)
207 ret = JNI_Camera::RegisterNativesImpl(env);
208 return ret;
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 return RegisterNativesImpl(env) && JNI_Camera::Reg
wjia(left Chromium) 2013/02/06 00:45:34 Done.
209 }
210
211 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name)
212 : state_(kIdle),
213 observer_(NULL),
214 device_name_(device_name),
215 current_settings_(),
216 rotation_(0) {
217 }
218
219 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
220 DeAllocate();
221 }
222
223 bool VideoCaptureDeviceAndroid::Init() {
224 int id;
225 if (!base::StringToInt(device_name_.unique_id, &id))
226 return false;
227
228 JNIEnv* env = AttachCurrentThread();
229 if (!env)
230 return false;
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 Can never be true.
wjia(left Chromium) 2013/02/06 00:45:34 Done.
231
232 j_capture_.Reset(Java_VideoCapture_createVideoCapture(env,
233 base::android::GetApplicationContext(), id,
234 reinterpret_cast<jlong>(this)));
235
236 return true;
237 }
238
239 const VideoCaptureDevice::Name& VideoCaptureDeviceAndroid::device_name() {
240 return device_name_;
241 }
242
243 void VideoCaptureDeviceAndroid::Allocate(int width, int height, int frame_rate,
244 EventHandler* observer) {
245 {
246 base::AutoLock lock(lock_);
247 if (state_ != kIdle)
248 return;
249 observer_ = observer;
250 state_ = kAllocated;
251 }
252
253 JNIEnv* env = AttachCurrentThread();
254 CHECK(env);
255
256 jint ret = Java_VideoCapture_allocate(env,
257 j_capture_.obj(),
258 width,
259 height,
260 frame_rate);
261 if (ret < 0) {
262 SetErrorState("failed to allocate");
263 return;
264 }
265
266 // Store current width and height.
267 current_settings_.color = VideoCaptureCapability::kYV12;
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 This is still out of order (sort fields in declara
wjia(left Chromium) 2013/02/06 00:45:34 Done.
268 current_settings_.width =
269 Java_VideoCapture_queryWidth(env, j_capture_.obj());
270 current_settings_.height =
271 Java_VideoCapture_queryHeight(env, j_capture_.obj());
272 current_settings_.frame_rate =
273 Java_VideoCapture_queryFrameRate(env, j_capture_.obj());
274 DCHECK(current_settings_.width > 0 && !(current_settings_.width % 2));
275 DCHECK(current_settings_.height > 0 && !(current_settings_.height % 2));
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 The odd-size business can't be DCHECKs because oth
wjia(left Chromium) 2013/02/06 00:45:34 Done.
276
277 DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried width="
278 << current_settings_.width
279 << ", height="
280 << current_settings_.height
281 << ", frame_rate="
282 << current_settings_.frame_rate;
283 // Report the frame size to the observer.
284 observer_->OnFrameInfo(current_settings_);
285
286 int y_size = current_settings_.width * current_settings_.height;
287 rotation_buffer_.reset(new uint8[y_size * 3 / 2]);
288 ResetBufferI420(rotation_buffer_.get(), current_settings_.width,
289 current_settings_.height);
290 }
291
292 void VideoCaptureDeviceAndroid::Start() {
293 DVLOG(1) << "VideoCaptureDeviceAndroid::Start";
294 {
295 base::AutoLock lock(lock_);
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 This is now a serialization point used only for a
296 DCHECK_EQ(state_, kAllocated);
297 }
298
299 JNIEnv* env = AttachCurrentThread();
300 CHECK(env);
301
302 jint ret = Java_VideoCapture_startCapture(env, j_capture_.obj());
303 if (ret < 0) {
304 SetErrorState("failed to start capture");
305 return;
306 }
307
308 {
309 base::AutoLock lock(lock_);
310 state_ = kCapturing;
311 }
312 }
313
314 void VideoCaptureDeviceAndroid::Stop() {
315 DVLOG(1) << "VideoCaptureDeviceAndroid::Stop";
316 {
317 base::AutoLock lock(lock_);
318 if (state_ != kCapturing && state_ != kError)
319 return;
320 if (state_ == kCapturing)
321 state_ = kAllocated;
322 }
323
324 JNIEnv* env = AttachCurrentThread();
325 CHECK(env);
326
327 jint ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
328 if (ret < 0) {
329 SetErrorState("failed to stop capture");
330 return;
331 }
332 }
333
334 void VideoCaptureDeviceAndroid::DeAllocate() {
335 DVLOG(1) << "VideoCaptureDeviceAndroid::DeAllocate";
336 {
337 base::AutoLock lock(lock_);
338 if (state_ == kIdle)
339 return;
340
341 if (state_ == kCapturing) {
342 base::AutoUnlock unlock(lock_);
343 Stop();
344 }
345
346 if (state_ == kAllocated)
347 state_ = kIdle;
348
349 observer_ = NULL;
350 }
351
352 JNIEnv* env = AttachCurrentThread();
353 CHECK(env);
354
355 Java_VideoCapture_deallocate(env, j_capture_.obj());
356 }
357
358 void VideoCaptureDeviceAndroid::OnFrameAvailable(JNIEnv* env, jobject obj,
359 jbyteArray data, jint length,
360 jint rotation, jboolean flip_vert, jboolean flip_horiz) {
361 DVLOG(3) << "VideoCaptureDeviceAndroid::OnFrameAvailable: length =" << length;
362
363 base::AutoLock lock(lock_);
364 if (state_ != kCapturing || !observer_)
365 return;
366
367 jbyte* buffer = env->GetByteArrayElements(data, NULL);
368 if (!buffer) {
369 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
370 "failed to GetByteArrayElements";
371 return;
372 }
373
374 // TODO(wjia): move rotation into VideoCaptureController to remove
375 // one buffer copying.
376 // Rotate the buffer when needed.
377 int width = current_settings_.width;
378 int height = current_settings_.height;
379 if (rotation_ != rotation) {
380 rotation_ = rotation;
381 ResetBufferI420(rotation_buffer_.get(), width, height);
382 }
383
384 uint8* src = reinterpret_cast<uint8*>(buffer);
385 uint8* dest = rotation_buffer_.get();
386
387 RotatePlaneByPixels(src, dest, width, height, rotation, flip_vert,
388 flip_horiz);
389 int y_size = width * height;
390 src += y_size;
391 dest += y_size;
392 RotatePlaneByPixels(src, dest, width/2, height/2, rotation, flip_vert,
393 flip_horiz);
394 src += y_size/4;
395 dest += y_size/4;
396 RotatePlaneByPixels(src, dest, width/2, height/2, rotation, flip_vert,
397 flip_horiz);
398 observer_->OnIncomingCapturedFrame(rotation_buffer_.get(), length,
399 base::Time::Now());
400
401 env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
402 }
403
404 void VideoCaptureDeviceAndroid::SetErrorState(const std::string& reason) {
405 LOG(ERROR) << "VideoCaptureDeviceAndroid::SetErrorState: " << reason;
406 {
407 base::AutoLock lock(lock_);
408 state_ = kError;
409 }
410 observer_->OnError();
Ami GONE FROM CHROMIUM 2013/01/30 19:46:23 your .h comment promises to protect observer_ with
wjia(left Chromium) 2013/02/06 00:45:34 hrmm, I thought about the lock again. Actually, th
411 }
412
413 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698