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/jni_string.h"
11#include "base/android/scoped_java_ref.h"
12#include "base/strings/string_number_conversions.h"
13#include "base/strings/stringprintf.h"
14#include "jni/VideoCapture_jni.h"
15#include "media/base/video_util.h"
16
17using base::android::AttachCurrentThread;
18using base::android::CheckException;
19using base::android::GetClass;
20using base::android::MethodID;
21using base::android::JavaRef;
22using base::android::ScopedJavaLocalRef;
23
24namespace media {
25
26// static
27void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
28  device_names->clear();
29
30  JNIEnv* env = AttachCurrentThread();
31
32  int num_cameras = Java_ChromiumCameraInfo_getNumberOfCameras(env);
33  DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
34  if (num_cameras <= 0)
35    return;
36
37  for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
38    ScopedJavaLocalRef<jobject> ci =
39        Java_ChromiumCameraInfo_getAt(env, camera_id);
40
41    Name name(
42        base::android::ConvertJavaStringToUTF8(
43            Java_ChromiumCameraInfo_getDeviceName(env, ci.obj())),
44        base::StringPrintf("%d", Java_ChromiumCameraInfo_getId(env, ci.obj())));
45    device_names->push_back(name);
46
47    DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name="
48             << name.name()
49             << ", unique_id="
50             << name.id()
51             << ", orientation "
52             << Java_ChromiumCameraInfo_getOrientation(env, ci.obj());
53  }
54}
55
56// static
57void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
58    VideoCaptureFormats* formats) {
59  NOTIMPLEMENTED();
60}
61
62const std::string VideoCaptureDevice::Name::GetModel() const {
63  // Android cameras are not typically USB devices, and this method is currently
64  // only used for USB model identifiers, so this implementation just indicates
65  // an unknown device model.
66  return "";
67}
68
69// static
70VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
71  return VideoCaptureDeviceAndroid::Create(device_name);
72}
73
74// static
75VideoCaptureDevice* VideoCaptureDeviceAndroid::Create(const Name& device_name) {
76  scoped_ptr<VideoCaptureDeviceAndroid> ret(
77      new VideoCaptureDeviceAndroid(device_name));
78  if (ret->Init())
79    return ret.release();
80  return NULL;
81}
82
83// static
84bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
85  return RegisterNativesImpl(env);
86}
87
88VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name)
89    : state_(kIdle), got_first_frame_(false), device_name_(device_name) {}
90
91VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
92  StopAndDeAllocate();
93}
94
95bool VideoCaptureDeviceAndroid::Init() {
96  int id;
97  if (!base::StringToInt(device_name_.id(), &id))
98    return false;
99
100  JNIEnv* env = AttachCurrentThread();
101
102  j_capture_.Reset(Java_VideoCapture_createVideoCapture(
103      env, base::android::GetApplicationContext(), id,
104      reinterpret_cast<intptr_t>(this)));
105
106  return true;
107}
108
109void VideoCaptureDeviceAndroid::AllocateAndStart(
110    const VideoCaptureParams& params,
111    scoped_ptr<Client> client) {
112  DVLOG(1) << "VideoCaptureDeviceAndroid::AllocateAndStart";
113  {
114    base::AutoLock lock(lock_);
115    if (state_ != kIdle)
116      return;
117    client_ = client.Pass();
118    got_first_frame_ = false;
119  }
120
121  JNIEnv* env = AttachCurrentThread();
122
123  jboolean ret =
124      Java_VideoCapture_allocate(env,
125                                 j_capture_.obj(),
126                                 params.requested_format.frame_size.width(),
127                                 params.requested_format.frame_size.height(),
128                                 params.requested_format.frame_rate);
129  if (!ret) {
130    SetErrorState("failed to allocate");
131    return;
132  }
133
134  // Store current width and height.
135  capture_format_.frame_size.SetSize(
136      Java_VideoCapture_queryWidth(env, j_capture_.obj()),
137      Java_VideoCapture_queryHeight(env, j_capture_.obj()));
138  capture_format_.frame_rate =
139      Java_VideoCapture_queryFrameRate(env, j_capture_.obj());
140  capture_format_.pixel_format = GetColorspace();
141  DCHECK_NE(capture_format_.pixel_format, media::PIXEL_FORMAT_UNKNOWN);
142  CHECK(capture_format_.frame_size.GetArea() > 0);
143  CHECK(!(capture_format_.frame_size.width() % 2));
144  CHECK(!(capture_format_.frame_size.height() % 2));
145
146  if (capture_format_.frame_rate > 0) {
147    frame_interval_ = base::TimeDelta::FromMicroseconds(
148        (base::Time::kMicrosecondsPerSecond + capture_format_.frame_rate - 1) /
149        capture_format_.frame_rate);
150  }
151
152  DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried frame_size="
153           << capture_format_.frame_size.ToString()
154           << ", frame_rate=" << capture_format_.frame_rate;
155
156  jint result = Java_VideoCapture_startCapture(env, j_capture_.obj());
157  if (result < 0) {
158    SetErrorState("failed to start capture");
159    return;
160  }
161
162  {
163    base::AutoLock lock(lock_);
164    state_ = kCapturing;
165  }
166}
167
168void VideoCaptureDeviceAndroid::StopAndDeAllocate() {
169  DVLOG(1) << "VideoCaptureDeviceAndroid::StopAndDeAllocate";
170  {
171    base::AutoLock lock(lock_);
172    if (state_ != kCapturing && state_ != kError)
173      return;
174  }
175
176  JNIEnv* env = AttachCurrentThread();
177
178  jint ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
179  if (ret < 0) {
180    SetErrorState("failed to stop capture");
181    return;
182  }
183
184  {
185    base::AutoLock lock(lock_);
186    state_ = kIdle;
187    client_.reset();
188  }
189
190  Java_VideoCapture_deallocate(env, j_capture_.obj());
191}
192
193void VideoCaptureDeviceAndroid::OnFrameAvailable(
194    JNIEnv* env,
195    jobject obj,
196    jbyteArray data,
197    jint length,
198    jint rotation) {
199  DVLOG(3) << "VideoCaptureDeviceAndroid::OnFrameAvailable: length =" << length;
200
201  base::AutoLock lock(lock_);
202  if (state_ != kCapturing || !client_.get())
203    return;
204
205  jbyte* buffer = env->GetByteArrayElements(data, NULL);
206  if (!buffer) {
207    LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
208                  "failed to GetByteArrayElements";
209    return;
210  }
211
212  base::TimeTicks current_time = base::TimeTicks::Now();
213  if (!got_first_frame_) {
214    // Set aside one frame allowance for fluctuation.
215    expected_next_frame_time_ = current_time - frame_interval_;
216    got_first_frame_ = true;
217  }
218
219  // Deliver the frame when it doesn't arrive too early.
220  if (expected_next_frame_time_ <= current_time) {
221    expected_next_frame_time_ += frame_interval_;
222
223    client_->OnIncomingCapturedFrame(reinterpret_cast<uint8*>(buffer),
224                                     length,
225                                     base::Time::Now(),
226                                     rotation,
227                                     capture_format_);
228  }
229
230  env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
231}
232
233VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
234  JNIEnv* env = AttachCurrentThread();
235  int current_capture_colorspace =
236      Java_VideoCapture_getColorspace(env, j_capture_.obj());
237  switch (current_capture_colorspace){
238  case ANDROID_IMAGEFORMAT_YV12:
239    return media::PIXEL_FORMAT_YV12;
240  case ANDROID_IMAGEFORMAT_NV21:
241    return media::PIXEL_FORMAT_NV21;
242  case ANDROID_IMAGEFORMAT_YUY2:
243    return media::PIXEL_FORMAT_YUY2;
244  case ANDROID_IMAGEFORMAT_NV16:
245  case ANDROID_IMAGEFORMAT_JPEG:
246  case ANDROID_IMAGEFORMAT_RGB_565:
247  case ANDROID_IMAGEFORMAT_UNKNOWN:
248    // NOTE(mcasas): NV16, JPEG, RGB565 not supported in VideoPixelFormat.
249  default:
250    return media::PIXEL_FORMAT_UNKNOWN;
251  }
252}
253
254void VideoCaptureDeviceAndroid::SetErrorState(const std::string& reason) {
255  LOG(ERROR) << "VideoCaptureDeviceAndroid::SetErrorState: " << reason;
256  {
257    base::AutoLock lock(lock_);
258    state_ = kError;
259  }
260  client_->OnError();
261}
262
263}  // namespace media
264