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