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