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

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

Powered by Google App Engine
This is Rietveld 408576698