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

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

Powered by Google App Engine
This is Rietveld 408576698