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

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

Powered by Google App Engine
This is Rietveld 408576698