1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/video_capture/android/video_capture_android.h"
12
13#include "webrtc/base/common.h"
14#include "webrtc/modules/utility/interface/helpers_android.h"
15#include "webrtc/modules/video_capture/android/device_info_android.h"
16#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
17#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
18#include "webrtc/system_wrappers/interface/logging.h"
19#include "webrtc/system_wrappers/interface/ref_count.h"
20#include "webrtc/system_wrappers/interface/trace.h"
21
22static JavaVM* g_jvm = NULL;
23static jclass g_java_capturer_class = NULL;  // VideoCaptureAndroid.class.
24static jobject g_context = NULL;  // Owned android.content.Context.
25
26namespace webrtc {
27
28// Called by Java to get the global application context.
29jobject JNICALL GetContext(JNIEnv* env, jclass) {
30  assert(g_context);
31  return g_context;
32}
33
34// Called by Java when the camera has a new frame to deliver.
35void JNICALL ProvideCameraFrame(
36    JNIEnv* env,
37    jobject,
38    jbyteArray javaCameraFrame,
39    jint length,
40    jlong timeStamp,
41    jlong context) {
42  webrtc::videocapturemodule::VideoCaptureAndroid* captureModule =
43      reinterpret_cast<webrtc::videocapturemodule::VideoCaptureAndroid*>(
44          context);
45  jbyte* cameraFrame = env->GetByteArrayElements(javaCameraFrame, NULL);
46  captureModule->OnIncomingFrame(
47      reinterpret_cast<uint8_t*>(cameraFrame), length, 0);
48  env->ReleaseByteArrayElements(javaCameraFrame, cameraFrame, JNI_ABORT);
49}
50
51// Called by Java when the device orientation has changed.
52void JNICALL OnOrientationChanged(
53    JNIEnv* env, jobject, jlong context, jint degrees) {
54  webrtc::videocapturemodule::VideoCaptureAndroid* captureModule =
55      reinterpret_cast<webrtc::videocapturemodule::VideoCaptureAndroid*>(
56          context);
57  degrees = (360 + degrees) % 360;
58  assert(degrees >= 0 && degrees < 360);
59  VideoCaptureRotation rotation =
60      (degrees <= 45 || degrees > 315) ? kCameraRotate0 :
61      (degrees > 45 && degrees <= 135) ? kCameraRotate90 :
62      (degrees > 135 && degrees <= 225) ? kCameraRotate180 :
63      (degrees > 225 && degrees <= 315) ? kCameraRotate270 :
64      kCameraRotate0;  // Impossible.
65  int32_t status =
66      captureModule->VideoCaptureImpl::SetCaptureRotation(rotation);
67  RTC_UNUSED(status);
68  assert(status == 0);
69}
70
71int32_t SetCaptureAndroidVM(JavaVM* javaVM, jobject context) {
72  if (javaVM) {
73    assert(!g_jvm);
74    g_jvm = javaVM;
75    AttachThreadScoped ats(g_jvm);
76    g_context = ats.env()->NewGlobalRef(context);
77
78    videocapturemodule::DeviceInfoAndroid::Initialize(ats.env());
79
80    jclass j_capture_class =
81        ats.env()->FindClass("org/webrtc/videoengine/VideoCaptureAndroid");
82    assert(j_capture_class);
83    g_java_capturer_class =
84        reinterpret_cast<jclass>(ats.env()->NewGlobalRef(j_capture_class));
85    assert(g_java_capturer_class);
86
87    JNINativeMethod native_methods[] = {
88        {"GetContext",
89         "()Landroid/content/Context;",
90         reinterpret_cast<void*>(&GetContext)},
91        {"OnOrientationChanged",
92         "(JI)V",
93         reinterpret_cast<void*>(&OnOrientationChanged)},
94        {"ProvideCameraFrame",
95         "([BIJJ)V",
96         reinterpret_cast<void*>(&ProvideCameraFrame)}};
97    if (ats.env()->RegisterNatives(g_java_capturer_class,
98                                   native_methods, 3) != 0)
99      assert(false);
100  } else {
101    if (g_jvm) {
102      AttachThreadScoped ats(g_jvm);
103      ats.env()->UnregisterNatives(g_java_capturer_class);
104      ats.env()->DeleteGlobalRef(g_java_capturer_class);
105      g_java_capturer_class = NULL;
106      ats.env()->DeleteGlobalRef(g_context);
107      g_context = NULL;
108      videocapturemodule::DeviceInfoAndroid::DeInitialize();
109      g_jvm = NULL;
110    }
111  }
112
113  return 0;
114}
115
116namespace videocapturemodule {
117
118VideoCaptureModule* VideoCaptureImpl::Create(
119    const int32_t id,
120    const char* deviceUniqueIdUTF8) {
121  RefCountImpl<videocapturemodule::VideoCaptureAndroid>* implementation =
122      new RefCountImpl<videocapturemodule::VideoCaptureAndroid>(id);
123  if (implementation->Init(id, deviceUniqueIdUTF8) != 0) {
124    delete implementation;
125    implementation = NULL;
126  }
127  return implementation;
128}
129
130int32_t VideoCaptureAndroid::OnIncomingFrame(uint8_t* videoFrame,
131                                             int32_t videoFrameLength,
132                                             int64_t captureTime) {
133  if (!_captureStarted)
134    return 0;
135  return IncomingFrame(
136      videoFrame, videoFrameLength, _captureCapability, captureTime);
137}
138
139VideoCaptureAndroid::VideoCaptureAndroid(const int32_t id)
140    : VideoCaptureImpl(id),
141      _deviceInfo(id),
142      _jCapturer(NULL),
143      _captureStarted(false) {
144}
145
146int32_t VideoCaptureAndroid::Init(const int32_t id,
147                                  const char* deviceUniqueIdUTF8) {
148  const int nameLength = strlen(deviceUniqueIdUTF8);
149  if (nameLength >= kVideoCaptureUniqueNameLength)
150    return -1;
151
152  // Store the device name
153  LOG(LS_INFO) << "VideoCaptureAndroid::Init: " << deviceUniqueIdUTF8;
154  size_t camera_id = 0;
155  if (!_deviceInfo.FindCameraIndex(deviceUniqueIdUTF8, &camera_id))
156    return -1;
157  _deviceUniqueId = new char[nameLength + 1];
158  memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
159
160  AttachThreadScoped ats(g_jvm);
161  JNIEnv* env = ats.env();
162  jmethodID ctor = env->GetMethodID(g_java_capturer_class, "<init>", "(IJ)V");
163  assert(ctor);
164  jlong j_this = reinterpret_cast<intptr_t>(this);
165  _jCapturer = env->NewGlobalRef(
166      env->NewObject(g_java_capturer_class, ctor, camera_id, j_this));
167  assert(_jCapturer);
168  return 0;
169}
170
171VideoCaptureAndroid::~VideoCaptureAndroid() {
172  // Ensure Java camera is released even if our caller didn't explicitly Stop.
173  if (_captureStarted)
174    StopCapture();
175  AttachThreadScoped ats(g_jvm);
176  ats.env()->DeleteGlobalRef(_jCapturer);
177}
178
179int32_t VideoCaptureAndroid::StartCapture(
180    const VideoCaptureCapability& capability) {
181  CriticalSectionScoped cs(&_apiCs);
182  AttachThreadScoped ats(g_jvm);
183  JNIEnv* env = ats.env();
184
185  if (_deviceInfo.GetBestMatchedCapability(
186          _deviceUniqueId, capability, _captureCapability) < 0) {
187    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
188                 "%s: GetBestMatchedCapability failed: %dx%d",
189                 __FUNCTION__, capability.width, capability.height);
190    return -1;
191  }
192
193  _captureDelay = _captureCapability.expectedCaptureDelay;
194
195  jmethodID j_start =
196      env->GetMethodID(g_java_capturer_class, "startCapture", "(IIII)Z");
197  assert(j_start);
198  int min_mfps = 0;
199  int max_mfps = 0;
200  _deviceInfo.GetMFpsRange(_deviceUniqueId, _captureCapability.maxFPS,
201                           &min_mfps, &max_mfps);
202  bool started = env->CallBooleanMethod(_jCapturer, j_start,
203                                        _captureCapability.width,
204                                        _captureCapability.height,
205                                        min_mfps, max_mfps);
206  if (started) {
207    _requestedCapability = capability;
208    _captureStarted = true;
209  }
210  return started ? 0 : -1;
211}
212
213int32_t VideoCaptureAndroid::StopCapture() {
214  _apiCs.Enter();
215  AttachThreadScoped ats(g_jvm);
216  JNIEnv* env = ats.env();
217
218  memset(&_requestedCapability, 0, sizeof(_requestedCapability));
219  memset(&_captureCapability, 0, sizeof(_captureCapability));
220  _captureStarted = false;
221  // Exit critical section to avoid blocking camera thread inside
222  // onIncomingFrame() call.
223  _apiCs.Leave();
224
225  jmethodID j_stop =
226      env->GetMethodID(g_java_capturer_class, "stopCapture", "()Z");
227  return env->CallBooleanMethod(_jCapturer, j_stop) ? 0 : -1;
228}
229
230bool VideoCaptureAndroid::CaptureStarted() {
231  CriticalSectionScoped cs(&_apiCs);
232  return _captureStarted;
233}
234
235int32_t VideoCaptureAndroid::CaptureSettings(
236    VideoCaptureCapability& settings) {
237  CriticalSectionScoped cs(&_apiCs);
238  settings = _requestedCapability;
239  return 0;
240}
241
242int32_t VideoCaptureAndroid::SetCaptureRotation(
243    VideoCaptureRotation rotation) {
244  int32_t status = VideoCaptureImpl::SetCaptureRotation(rotation);
245  if (status != 0)
246    return status;
247
248  AttachThreadScoped ats(g_jvm);
249  JNIEnv* env = ats.env();
250
251  jmethodID j_spr =
252      env->GetMethodID(g_java_capturer_class, "setPreviewRotation", "(I)V");
253  assert(j_spr);
254  int rotation_degrees;
255  if (RotationInDegrees(rotation, &rotation_degrees) != 0) {
256    assert(false);
257  }
258  env->CallVoidMethod(_jCapturer, j_spr, rotation_degrees);
259  return 0;
260}
261
262}  // namespace videocapturemodule
263}  // namespace webrtc
264