VideoCaptureAndroid: rewrote the (standalone) implementation of video capture on Android.
Besides being ~40% the size of the previous implementation, this makes it so
that VideoCaptureAndroid can stop and restart capture, which is necessary to
support onPause/onResume reasonably on Android.
BUG=1407
R=henrike@webrtc.org, wu@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/2334004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@4915 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/audio_device/android/audio_manager_jni.cc b/modules/audio_device/android/audio_manager_jni.cc
index b04b744..17320ef 100644
--- a/modules/audio_device/android/audio_manager_jni.cc
+++ b/modules/audio_device/android/audio_manager_jni.cc
@@ -12,39 +12,9 @@
#include <assert.h>
+#include "webrtc/modules/utility/interface/helpers_android.h"
#include "webrtc/system_wrappers/interface/trace.h"
-namespace {
-
-class AttachThreadScoped {
- public:
- explicit AttachThreadScoped(JavaVM* jvm)
- : attached_(false), jvm_(jvm), env_(NULL) {
- jint ret_val = jvm->GetEnv(reinterpret_cast<void**>(&env_),
- REQUIRED_JNI_VERSION);
- if (ret_val == JNI_EDETACHED) {
- // Attach the thread to the Java VM.
- ret_val = jvm_->AttachCurrentThread(&env_, NULL);
- attached_ = ret_val == JNI_OK;
- assert(attached_);
- }
- }
- ~AttachThreadScoped() {
- if (attached_ && (jvm_->DetachCurrentThread() < 0)) {
- assert(false);
- }
- }
-
- JNIEnv* env() { return env_; }
-
- private:
- bool attached_;
- JavaVM* jvm_;
- JNIEnv* env_;
-};
-
-} // namespace
-
namespace webrtc {
static JavaVM* g_jvm_ = NULL;
diff --git a/modules/audio_device/android/audio_manager_jni.h b/modules/audio_device/android/audio_manager_jni.h
index 298890e..7deef03 100644
--- a/modules/audio_device/android/audio_manager_jni.h
+++ b/modules/audio_device/android/audio_manager_jni.h
@@ -18,8 +18,6 @@
namespace webrtc {
-#define REQUIRED_JNI_VERSION JNI_VERSION_1_4
-
class AudioManagerJni {
public:
AudioManagerJni();
diff --git a/modules/utility/interface/helpers_android.h b/modules/utility/interface/helpers_android.h
new file mode 100644
index 0000000..d0796ec
--- /dev/null
+++ b/modules/utility/interface/helpers_android.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_HELPERS_ANDROID_H_
+#define WEBRTC_MODULES_UTILITY_INTERFACE_HELPERS_ANDROID_H_
+
+#include <jni.h>
+
+namespace webrtc {
+
+// Attach thread to JVM if necessary and detach at scope end if originally
+// attached.
+class AttachThreadScoped {
+ public:
+ explicit AttachThreadScoped(JavaVM* jvm);
+ ~AttachThreadScoped();
+ JNIEnv* env();
+
+ private:
+ bool attached_;
+ JavaVM* jvm_;
+ JNIEnv* env_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_UTILITY_INTERFACE_HELPERS_ANDROID_H_
diff --git a/modules/utility/source/helpers_android.cc b/modules/utility/source/helpers_android.cc
new file mode 100644
index 0000000..6acc77e
--- /dev/null
+++ b/modules/utility/source/helpers_android.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/utility/interface/helpers_android.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+namespace webrtc {
+
+AttachThreadScoped::AttachThreadScoped(JavaVM* jvm)
+ : attached_(false), jvm_(jvm), env_(NULL) {
+ jint ret_val = jvm->GetEnv(reinterpret_cast<void**>(&env_), JNI_VERSION_1_4);
+ if (ret_val == JNI_EDETACHED) {
+ // Attach the thread to the Java VM.
+ ret_val = jvm_->AttachCurrentThread(&env_, NULL);
+ attached_ = ret_val == JNI_OK;
+ assert(attached_);
+ }
+}
+
+AttachThreadScoped::~AttachThreadScoped() {
+ if (attached_ && (jvm_->DetachCurrentThread() < 0)) {
+ assert(false);
+ }
+}
+
+JNIEnv* AttachThreadScoped::env() { return env_; }
+
+} // namespace webrtc
diff --git a/modules/utility/source/utility.gypi b/modules/utility/source/utility.gypi
index 9ba639e..1c97680 100644
--- a/modules/utility/source/utility.gypi
+++ b/modules/utility/source/utility.gypi
@@ -21,6 +21,7 @@
'../interface/audio_frame_operations.h',
'../interface/file_player.h',
'../interface/file_recorder.h',
+ '../interface/helpers_android.h',
'../interface/process_thread.h',
'../interface/rtp_dump.h',
'audio_frame_operations.cc',
@@ -30,6 +31,7 @@
'file_player_impl.h',
'file_recorder_impl.cc',
'file_recorder_impl.h',
+ 'helpers_android.cc',
'process_thread_impl.cc',
'process_thread_impl.h',
'rtp_dump_impl.cc',
diff --git a/modules/video_capture/android/device_info_android.cc b/modules/video_capture/android/device_info_android.cc
index b540564..10c277e 100644
--- a/modules/video_capture/android/device_info_android.cc
+++ b/modules/video_capture/android/device_info_android.cc
@@ -10,9 +10,14 @@
#include "webrtc/modules/video_capture/android/device_info_android.h"
-#include <stdio.h>
+#include <algorithm>
+#include <sstream>
+#include <vector>
+#include "json/json.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
#include "webrtc/modules/video_capture/android/video_capture_android.h"
+#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/ref_count.h"
#include "webrtc/system_wrappers/interface/trace.h"
@@ -22,65 +27,136 @@
namespace videocapturemodule
{
-static jclass g_capabilityClass = NULL;
-
-// static
-void DeviceInfoAndroid::SetAndroidCaptureClasses(jclass capabilityClass) {
- g_capabilityClass = capabilityClass;
+static std::string ResolutionsToString(
+ const std::vector<std::pair<int, int> >& pairs) {
+ std::stringstream stream;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ if (i > 0)
+ stream << ", ";
+ stream << "(" << pairs[i].first << "x" << pairs[i].second << ")";
+ }
+ return stream.str();
}
-VideoCaptureModule::DeviceInfo*
-VideoCaptureImpl::CreateDeviceInfo (const int32_t id) {
- videocapturemodule::DeviceInfoAndroid *deviceInfo =
- new videocapturemodule::DeviceInfoAndroid(id);
- if (deviceInfo && deviceInfo->Init() != 0) {
- delete deviceInfo;
- deviceInfo = NULL;
+struct AndroidCameraInfo {
+ std::string name;
+ int min_mfps, max_mfps; // FPS*1000.
+ bool front_facing;
+ int orientation;
+ std::vector<std::pair<int, int> > resolutions; // Pairs are: (width,height).
+
+ std::string ToString() {
+ std::stringstream stream;
+ stream << "Name: [" << name << "], mfps: [" << min_mfps << ":" << max_mfps
+ << "], front_facing: " << front_facing
+ << ", orientation: " << orientation << ", resolutions: ["
+ << ResolutionsToString(resolutions) << "]";
+ return stream.str();
}
- return deviceInfo;
+};
+
+// Camera info; populated during DeviceInfoAndroid::Initialize() and immutable
+// thereafter.
+static std::vector<AndroidCameraInfo>* g_camera_info = NULL;
+
+// Set |*index| to the index of |name| in g_camera_info or return false if no
+// match found.
+static bool FindCameraIndexByName(const std::string& name, size_t* index) {
+ for (size_t i = 0; i < g_camera_info->size(); ++i) {
+ if (g_camera_info->at(i).name == name) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns a pointer to the named member of g_camera_info, or NULL if no match
+// is found.
+static AndroidCameraInfo* FindCameraInfoByName(const std::string& name) {
+ size_t index = 0;
+ if (FindCameraIndexByName(name, &index))
+ return &g_camera_info->at(index);
+ return NULL;
+}
+
+// static
+void DeviceInfoAndroid::Initialize(JNIEnv* jni) {
+ // TODO(henrike): this "if" would make a lot more sense as an assert, but
+ // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetVideoEngine() and
+ // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Terminate() conspire to
+ // prevent this. Once that code is made to only
+ // VideoEngine::SetAndroidObjects() once per process, this can turn into an
+ // assert.
+ if (g_camera_info)
+ return;
+
+ g_camera_info = new std::vector<AndroidCameraInfo>();
+ jclass j_info_class =
+ jni->FindClass("org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid");
+ assert(j_info_class);
+ jmethodID j_initialize = jni->GetStaticMethodID(
+ j_info_class, "getDeviceInfo", "()Ljava/lang/String;");
+ jstring j_json_info = static_cast<jstring>(
+ jni->CallStaticObjectMethod(j_info_class, j_initialize));
+
+ const jchar* jchars = jni->GetStringChars(j_json_info, NULL);
+ icu::UnicodeString ustr(jchars, jni->GetStringLength(j_json_info));
+ jni->ReleaseStringChars(j_json_info, jchars);
+ std::string json_info;
+ ustr.toUTF8String(json_info);
+
+ Json::Value cameras;
+ Json::Reader reader(Json::Features::strictMode());
+ bool parsed = reader.parse(json_info, cameras);
+ if (!parsed) {
+ std::stringstream stream;
+ stream << "Failed to parse configuration:\n"
+ << reader.getFormattedErrorMessages();
+ assert(false);
+ return;
+ }
+ for (Json::ArrayIndex i = 0; i < cameras.size(); ++i) {
+ const Json::Value& camera = cameras[i];
+ AndroidCameraInfo info;
+ info.name = camera["name"].asString();
+ info.min_mfps = camera["min_mfps"].asInt();
+ info.max_mfps = camera["max_mfps"].asInt();
+ info.front_facing = camera["front_facing"].asBool();
+ info.orientation = camera["orientation"].asInt();
+ Json::Value sizes = camera["sizes"];
+ for (Json::ArrayIndex j = 0; j < sizes.size(); ++j) {
+ const Json::Value& size = sizes[j];
+ info.resolutions.push_back(std::make_pair(
+ size["width"].asInt(), size["height"].asInt()));
+ }
+ g_camera_info->push_back(info);
+ }
+}
+
+VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo(
+ const int32_t id) {
+ return new videocapturemodule::DeviceInfoAndroid(id);
}
DeviceInfoAndroid::DeviceInfoAndroid(const int32_t id) :
DeviceInfoImpl(id) {
}
+DeviceInfoAndroid::~DeviceInfoAndroid() {
+}
+
+bool DeviceInfoAndroid::FindCameraIndex(const char* deviceUniqueIdUTF8,
+ size_t* index) {
+ return FindCameraIndexByName(deviceUniqueIdUTF8, index);
+}
+
int32_t DeviceInfoAndroid::Init() {
return 0;
}
-DeviceInfoAndroid::~DeviceInfoAndroid() {
-}
-
uint32_t DeviceInfoAndroid::NumberOfDevices() {
- JNIEnv *env;
- jclass javaCmDevInfoClass;
- jobject javaCmDevInfoObject;
- bool attached = false;
- if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
- env,
- javaCmDevInfoClass,
- javaCmDevInfoObject,
- attached) != 0)
- return 0;
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
- "%s GetMethodId", __FUNCTION__);
- // get the method ID for the Android Java GetDeviceUniqueName name.
- jmethodID cid = env->GetMethodID(javaCmDevInfoClass,
- "NumberOfDevices",
- "()I");
-
- jint numberOfDevices = 0;
- if (cid != NULL) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
- "%s Calling Number of devices", __FUNCTION__);
- numberOfDevices = env->CallIntMethod(javaCmDevInfoObject, cid);
- }
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
-
- if (numberOfDevices > 0)
- return numberOfDevices;
- return 0;
+ return g_camera_info->size();
}
int32_t DeviceInfoAndroid::GetDeviceName(
@@ -91,235 +167,56 @@
uint32_t deviceUniqueIdUTF8Length,
char* /*productUniqueIdUTF8*/,
uint32_t /*productUniqueIdUTF8Length*/) {
-
- JNIEnv *env;
- jclass javaCmDevInfoClass;
- jobject javaCmDevInfoObject;
- int32_t result = 0;
- bool attached = false;
- if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
- env,
- javaCmDevInfoClass,
- javaCmDevInfoObject,
- attached)!= 0)
+ if (deviceNumber >= g_camera_info->size())
return -1;
-
- // get the method ID for the Android Java GetDeviceUniqueName name.
- jmethodID cid = env->GetMethodID(javaCmDevInfoClass, "GetDeviceUniqueName",
- "(I)Ljava/lang/String;");
- if (cid != NULL) {
- jobject javaDeviceNameObj = env->CallObjectMethod(javaCmDevInfoObject,
- cid, deviceNumber);
- if (javaDeviceNameObj == NULL) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Failed to get device name for device %d.",
- __FUNCTION__, (int) deviceNumber);
- result = -1;
- } else {
- jboolean isCopy;
- const char* javaDeviceNameChar = env->GetStringUTFChars(
- (jstring) javaDeviceNameObj
- ,&isCopy);
- const jsize javaDeviceNameCharLength =
- env->GetStringUTFLength((jstring) javaDeviceNameObj);
- if ((uint32_t) javaDeviceNameCharLength <
- deviceUniqueIdUTF8Length) {
- memcpy(deviceUniqueIdUTF8,
- javaDeviceNameChar,
- javaDeviceNameCharLength + 1);
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
- _id, "%s: deviceUniqueIdUTF8 to short.",
- __FUNCTION__);
- result = -1;
- }
- if ((uint32_t) javaDeviceNameCharLength < deviceNameLength) {
- memcpy(deviceNameUTF8,
- javaDeviceNameChar,
- javaDeviceNameCharLength + 1);
- }
- env->ReleaseStringUTFChars((jstring) javaDeviceNameObj,
- javaDeviceNameChar);
- } // javaDeviceNameObj == NULL
-
+ const AndroidCameraInfo& info = g_camera_info->at(deviceNumber);
+ if (info.name.length() + 1 > deviceNameLength ||
+ info.name.length() + 1 > deviceUniqueIdUTF8Length) {
+ return -1;
}
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Failed to find GetDeviceUniqueName function id",
- __FUNCTION__);
- result = -1;
- }
-
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
-
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: result %d", __FUNCTION__, (int) result);
- return result;
-
+ memcpy(deviceNameUTF8, info.name.c_str(), info.name.length() + 1);
+ memcpy(deviceUniqueIdUTF8, info.name.c_str(), info.name.length() + 1);
+ return 0;
}
int32_t DeviceInfoAndroid::CreateCapabilityMap(
const char* deviceUniqueIdUTF8) {
_captureCapabilities.clear();
-
- JNIEnv *env;
- jclass javaCmDevInfoClass;
- jobject javaCmDevInfoObject;
- bool attached = false;
- if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
- env,
- javaCmDevInfoClass,
- javaCmDevInfoObject,
- attached) != 0)
+ const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8);
+ if (info == NULL)
return -1;
- // Find the capability class
- jclass javaCapClass = g_capabilityClass;
- if (javaCapClass == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: SetAndroidCaptureClasses must be called first!",
- __FUNCTION__);
- return -1;
- }
-
- // get the method ID for the Android Java GetCapabilityArray .
- jmethodID cid = env->GetMethodID(
- javaCmDevInfoClass,
- "GetCapabilityArray",
- "(Ljava/lang/String;)[Lorg/webrtc/videoengine/CaptureCapabilityAndroid;");
- if (cid == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Can't find method GetCapabilityArray.", __FUNCTION__);
- return -1;
- }
- // Create a jstring so we can pass the deviceUniquName to the java method.
- jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8);
-
- if (capureIdString == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Can't create string for method GetCapabilityArray.",
- __FUNCTION__);
- return -1;
- }
- // Call the java class and get an array with capabilities back.
- jobject javaCapabilitiesObj = env->CallObjectMethod(javaCmDevInfoObject,
- cid, capureIdString);
- if (!javaCapabilitiesObj) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Failed to call java GetCapabilityArray.",
- __FUNCTION__);
- return -1;
- }
-
- jfieldID widthField = env->GetFieldID(javaCapClass, "width", "I");
- jfieldID heigtField = env->GetFieldID(javaCapClass, "height", "I");
- jfieldID maxFpsField = env->GetFieldID(javaCapClass, "maxFPS", "I");
- if (widthField == NULL || heigtField == NULL || maxFpsField == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Failed to get field Id.", __FUNCTION__);
- return -1;
- }
-
- const jsize numberOfCapabilities =
- env->GetArrayLength((jarray) javaCapabilitiesObj);
-
- for (jsize i = 0; i < numberOfCapabilities; ++i) {
+ for (size_t i = 0; i < info->resolutions.size(); ++i) {
+ const std::pair<int, int>& size = info->resolutions[i];
VideoCaptureCapability cap;
- jobject capabilityElement = env->GetObjectArrayElement(
- (jobjectArray) javaCapabilitiesObj,
- i);
-
- cap.width = env->GetIntField(capabilityElement, widthField);
- cap.height = env->GetIntField(capabilityElement, heigtField);
- cap.expectedCaptureDelay = _expectedCaptureDelay;
+ cap.width = size.first;
+ cap.height = size.second;
+ cap.maxFPS = info->max_mfps / 1000;
+ cap.expectedCaptureDelay = kExpectedCaptureDelay;
cap.rawType = kVideoNV21;
- cap.maxFPS = env->GetIntField(capabilityElement, maxFpsField);
- WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
- "%s: Cap width %d, height %d, fps %d", __FUNCTION__,
- cap.width, cap.height, cap.maxFPS);
_captureCapabilities.push_back(cap);
}
-
- _lastUsedDeviceNameLength = strlen((char*) deviceUniqueIdUTF8);
- _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
- _lastUsedDeviceNameLength + 1);
- memcpy(_lastUsedDeviceName,
- deviceUniqueIdUTF8,
- _lastUsedDeviceNameLength + 1);
-
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
- "CreateCapabilityMap %d", _captureCapabilities.size());
-
return _captureCapabilities.size();
}
int32_t DeviceInfoAndroid::GetOrientation(
const char* deviceUniqueIdUTF8,
VideoCaptureRotation& orientation) {
- JNIEnv *env;
- jclass javaCmDevInfoClass;
- jobject javaCmDevInfoObject;
- bool attached = false;
- if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
- env,
- javaCmDevInfoClass,
- javaCmDevInfoObject,
- attached) != 0)
+ const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8);
+ if (info == NULL ||
+ !VideoCaptureImpl::RotationFromDegrees(info->orientation, &orientation)) {
return -1;
+ }
+ return 0;
+}
- // get the method ID for the Android Java GetOrientation .
- jmethodID cid = env->GetMethodID(javaCmDevInfoClass, "GetOrientation",
- "(Ljava/lang/String;)I");
- if (cid == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Can't find method GetOrientation.", __FUNCTION__);
- return -1;
- }
- // Create a jstring so we can pass the deviceUniquName to the java method.
- jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8);
- if (capureIdString == NULL) {
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Can't create string for method GetCapabilityArray.",
- __FUNCTION__);
- return -1;
- }
- // Call the java class and get the orientation.
- jint jorientation = env->CallIntMethod(javaCmDevInfoObject, cid,
- capureIdString);
- VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached);
-
- int32_t retValue = 0;
- switch (jorientation) {
- case -1: // Error
- orientation = kCameraRotate0;
- retValue = -1;
- break;
- case 0:
- orientation = kCameraRotate0;
- break;
- case 90:
- orientation = kCameraRotate90;
- break;
- case 180:
- orientation = kCameraRotate180;
- break;
- case 270:
- orientation = kCameraRotate270;
- break;
- case 360:
- orientation = kCameraRotate0;
- break;
- }
- return retValue;
+void DeviceInfoAndroid::GetFpsRange(const char* deviceUniqueIdUTF8,
+ int* min_mfps, int* max_mfps) {
+ const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8);
+ if (info == NULL)
+ return;
+ *min_mfps = info->min_mfps;
+ *max_mfps = info->max_mfps;
}
} // namespace videocapturemodule
diff --git a/modules/video_capture/android/device_info_android.h b/modules/video_capture/android/device_info_android.h
index 6d985c3..d277113 100644
--- a/modules/video_capture/android/device_info_android.h
+++ b/modules/video_capture/android/device_info_android.h
@@ -21,19 +21,18 @@
namespace videocapturemodule
{
-// Android logging, uncomment to print trace to
-// logcat instead of trace file/callback
-// #include <android/log.h>
-// #define WEBRTC_TRACE(a,b,c,...)
-// __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__)
-
class DeviceInfoAndroid : public DeviceInfoImpl {
-
public:
- static void SetAndroidCaptureClasses(jclass capabilityClass);
- DeviceInfoAndroid(const int32_t id);
- int32_t Init();
+ static void Initialize(JNIEnv* env);
+
+ DeviceInfoAndroid(int32_t id);
virtual ~DeviceInfoAndroid();
+
+ // Set |*index| to the index of the camera matching |deviceUniqueIdUTF8|, or
+ // return false if no match.
+ bool FindCameraIndex(const char* deviceUniqueIdUTF8, size_t* index);
+
+ virtual int32_t Init();
virtual uint32_t NumberOfDevices();
virtual int32_t GetDeviceName(
uint32_t deviceNumber,
@@ -53,9 +52,14 @@
uint32_t /*positionY*/) { return -1; }
virtual int32_t GetOrientation(const char* deviceUniqueIdUTF8,
VideoCaptureRotation& orientation);
+
+ // Populate |min_mfps| and |max_mfps| with the supported range of the device.
+ void GetFpsRange(const char* deviceUniqueIdUTF8,
+ int* min_mfps,
+ int* max_mfps);
+
private:
- bool IsDeviceNameMatches(const char* name, const char* deviceUniqueIdUTF8);
- enum {_expectedCaptureDelay = 190};
+ enum { kExpectedCaptureDelay = 190};
};
} // namespace videocapturemodule
diff --git a/modules/video_capture/android/java/src/org/webrtc/videoengine/CaptureCapabilityAndroid.java b/modules/video_capture/android/java/src/org/webrtc/videoengine/CaptureCapabilityAndroid.java
deleted file mode 100644
index 33c9927..0000000
--- a/modules/video_capture/android/java/src/org/webrtc/videoengine/CaptureCapabilityAndroid.java
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-package org.webrtc.videoengine;
-
-public class CaptureCapabilityAndroid {
- public int width = 0;
- public int height = 0;
- public int maxFPS = 0;
-}
diff --git a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
index 5cdf37f..2c99682 100644
--- a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -14,9 +14,6 @@
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
-import org.webrtc.videoengine.CaptureCapabilityAndroid;
-import org.webrtc.videoengine.VideoCaptureDeviceInfoAndroid.AndroidVideoCaptureDevice;
-
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -28,240 +25,177 @@
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
+// Wrapper for android Camera, with support for direct local preview rendering.
+// Threading notes: this class is called from ViE C++ code, and from Camera &
+// SurfaceHolder Java callbacks. Since these calls happen on different threads,
+// the entry points to this class are all synchronized. This shouldn't present
+// a performance bottleneck because only onPreviewFrame() is called more than
+// once (and is called serially on a single thread), so the lock should be
+// uncontended.
public class VideoCaptureAndroid implements PreviewCallback, Callback {
+ private final static String TAG = "WEBRTC-JC";
- private final static String TAG = "WEBRTC-JC";
+ private Camera camera; // Only non-null while capturing.
+ private final int id;
+ private final Camera.CameraInfo info;
+ private final long native_capturer; // |VideoCaptureAndroid*| in C++.
+ private SurfaceHolder localPreview;
+ private SurfaceTexture dummySurfaceTexture;
+ // Arbitrary queue depth. Higher number means more memory allocated & held,
+ // lower number means more sensitivity to processing time in the client (and
+ // potentially stalling the capturer if it runs out of buffers to write to).
+ private final int numCaptureBuffers = 3;
- private Camera camera;
- private AndroidVideoCaptureDevice currentDevice = null;
- public ReentrantLock previewBufferLock = new ReentrantLock();
- // This lock takes sync with StartCapture and SurfaceChanged
- private ReentrantLock captureLock = new ReentrantLock();
- private int PIXEL_FORMAT = ImageFormat.NV21;
- PixelFormat pixelFormat = new PixelFormat();
- // True when the C++ layer has ordered the camera to be started.
- private boolean isCaptureStarted = false;
- private boolean isCaptureRunning = false;
- private boolean isSurfaceReady = false;
+ public VideoCaptureAndroid(int id, long native_capturer) {
+ this.id = id;
+ this.native_capturer = native_capturer;
+ this.info = new Camera.CameraInfo();
+ Camera.getCameraInfo(id, info);
+ }
- private final int numCaptureBuffers = 3;
- private int expectedFrameSize = 0;
- private int orientation = 0;
- private int id = 0;
- // C++ callback context variable.
- private long context = 0;
- private SurfaceHolder localPreview = null;
- private SurfaceTexture dummySurfaceTexture = null;
- // True if this class owns the preview video buffers.
- private boolean ownsBuffers = false;
+ // Called by native code. Returns true if capturer is started.
+ //
+ // Note that this actually opens the camera, which can be a slow operation and
+ // thus might be done on a background thread, but ViE API needs a
+ // synchronous success return value so we can't do that.
+ private synchronized boolean startCapture(
+ int width, int height, int min_mfps, int max_mfps) {
+ Log.d(TAG, "startCapture: " + width + "x" + height + "@" +
+ min_mfps + ":" + max_mfps);
+ Throwable error = null;
+ try {
+ camera = Camera.open(id);
- private int mCaptureWidth = -1;
- private int mCaptureHeight = -1;
- private int mCaptureFPS = -1;
-
- public static
- void DeleteVideoCaptureAndroid(VideoCaptureAndroid captureAndroid) {
- Log.d(TAG, "DeleteVideoCaptureAndroid");
- if (captureAndroid.camera == null) {
- return;
+ localPreview = ViERenderer.GetLocalRenderer();
+ if (localPreview != null) {
+ localPreview.addCallback(this);
+ if (localPreview.getSurface() != null &&
+ localPreview.getSurface().isValid()) {
+ camera.setPreviewDisplay(localPreview);
}
-
- captureAndroid.StopCapture();
- captureAndroid.camera.release();
- captureAndroid.camera = null;
- captureAndroid.context = 0;
- }
-
- public VideoCaptureAndroid(int in_id, long in_context, Camera in_camera,
- AndroidVideoCaptureDevice in_device) {
- id = in_id;
- context = in_context;
- camera = in_camera;
- currentDevice = in_device;
- }
-
- private int tryStartCapture(int width, int height, int frameRate) {
- if (camera == null) {
- Log.e(TAG, "Camera not initialized %d" + id);
- return -1;
- }
-
- Log.d(TAG, "tryStartCapture: " + width +
- "x" + height +", frameRate: " + frameRate +
- ", isCaptureRunning: " + isCaptureRunning +
- ", isSurfaceReady: " + isSurfaceReady +
- ", isCaptureStarted: " + isCaptureStarted);
-
- if (isCaptureRunning || !isCaptureStarted) {
- return 0;
- }
-
- CaptureCapabilityAndroid currentCapability =
- new CaptureCapabilityAndroid();
- currentCapability.width = width;
- currentCapability.height = height;
- currentCapability.maxFPS = frameRate;
- PixelFormat.getPixelFormatInfo(PIXEL_FORMAT, pixelFormat);
-
- Camera.Parameters parameters = camera.getParameters();
- parameters.setPreviewSize(currentCapability.width,
- currentCapability.height);
- parameters.setPreviewFormat(PIXEL_FORMAT);
- parameters.setPreviewFrameRate(currentCapability.maxFPS);
+ } else {
+ // No local renderer (we only care about onPreviewFrame() buffers, not a
+ // directly-displayed UI element). Camera won't capture without
+ // setPreview{Texture,Display}, so we create a dummy SurfaceTexture and
+ // hand it over to Camera, but never listen for frame-ready callbacks,
+ // and never call updateTexImage on it.
try {
- camera.setParameters(parameters);
- } catch (RuntimeException e) {
- Log.e(TAG, "setParameters failed", e);
- return -1;
- }
-
- int bufSize = width * height * pixelFormat.bitsPerPixel / 8;
- byte[] buffer = null;
- for (int i = 0; i < numCaptureBuffers; i++) {
- buffer = new byte[bufSize];
- camera.addCallbackBuffer(buffer);
- }
- camera.setPreviewCallbackWithBuffer(this);
- ownsBuffers = true;
-
- camera.startPreview();
- previewBufferLock.lock();
- expectedFrameSize = bufSize;
- isCaptureRunning = true;
- previewBufferLock.unlock();
-
- return 0;
- }
-
- public int StartCapture(int width, int height, int frameRate) {
- Log.d(TAG, "StartCapture width " + width +
- " height " + height +" frame rate " + frameRate);
- // Get the local preview SurfaceHolder from the static render class
- localPreview = ViERenderer.GetLocalRenderer();
- if (localPreview != null) {
- if (localPreview.getSurface() != null &&
- localPreview.getSurface().isValid()) {
- surfaceCreated(localPreview);
- }
- localPreview.addCallback(this);
- } else {
- // No local renderer. Camera won't capture without
- // setPreview{Texture,Display}, so we create a dummy SurfaceTexture
- // and hand it over to Camera, but never listen for frame-ready
- // callbacks, and never call updateTexImage on it.
- captureLock.lock();
- try {
- dummySurfaceTexture = new SurfaceTexture(42);
- camera.setPreviewTexture(dummySurfaceTexture);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- captureLock.unlock();
- }
-
- captureLock.lock();
- isCaptureStarted = true;
- mCaptureWidth = width;
- mCaptureHeight = height;
- mCaptureFPS = frameRate;
-
- int res = tryStartCapture(mCaptureWidth, mCaptureHeight, mCaptureFPS);
-
- captureLock.unlock();
- return res;
- }
-
- public int StopCapture() {
- Log.d(TAG, "StopCapture");
- try {
- previewBufferLock.lock();
- isCaptureRunning = false;
- previewBufferLock.unlock();
- camera.stopPreview();
- camera.setPreviewCallbackWithBuffer(null);
- } catch (RuntimeException e) {
- Log.e(TAG, "Failed to stop camera", e);
- return -1;
- }
-
- isCaptureStarted = false;
- return 0;
- }
-
- native void ProvideCameraFrame(byte[] data, int length, long captureObject);
-
- public void onPreviewFrame(byte[] data, Camera camera) {
- previewBufferLock.lock();
-
- // The following line is for debug only
- // Log.v(TAG, "preview frame length " + data.length +
- // " context" + context);
- if (isCaptureRunning) {
- // If StartCapture has been called but not StopCapture
- // Call the C++ layer with the captured frame
- if (data.length == expectedFrameSize) {
- ProvideCameraFrame(data, expectedFrameSize, context);
- if (ownsBuffers) {
- // Give the video buffer to the camera service again.
- camera.addCallbackBuffer(data);
- }
- }
- }
- previewBufferLock.unlock();
- }
-
- // Sets the rotation of the preview render window.
- // Does not affect the captured video image.
- public void SetPreviewRotation(int rotation) {
- Log.v(TAG, "SetPreviewRotation:" + rotation);
-
- if (camera == null) {
- return;
- }
-
- int resultRotation = 0;
- if (currentDevice.frontCameraType ==
- VideoCaptureDeviceInfoAndroid.FrontFacingCameraType.Android23) {
- // this is a 2.3 or later front facing camera.
- // SetDisplayOrientation will flip the image horizontally
- // before doing the rotation.
- resultRotation = ( 360 - rotation ) % 360; // compensate the mirror
- }
- else {
- // Back facing or 2.2 or previous front camera
- resultRotation = rotation;
- }
- camera.setDisplayOrientation(resultRotation);
- }
-
- public void surfaceChanged(SurfaceHolder holder,
- int format, int width, int height) {
- Log.d(TAG, "VideoCaptureAndroid::surfaceChanged");
- }
-
- public void surfaceCreated(SurfaceHolder holder) {
- Log.d(TAG, "VideoCaptureAndroid::surfaceCreated");
- captureLock.lock();
- try {
- if (camera != null) {
- camera.setPreviewDisplay(holder);
- }
+ // "42" because http://goo.gl/KaEn8
+ dummySurfaceTexture = new SurfaceTexture(42);
+ camera.setPreviewTexture(dummySurfaceTexture);
} catch (IOException e) {
- Log.e(TAG, "Failed to set preview surface!", e);
+ throw new RuntimeException(e);
}
- captureLock.unlock();
+ }
+
+ Camera.Parameters parameters = camera.getParameters();
+ parameters.setPreviewSize(width, height);
+ parameters.setPreviewFpsRange(min_mfps, max_mfps);
+ int format = ImageFormat.NV21;
+ parameters.setPreviewFormat(format);
+ camera.setParameters(parameters);
+ int bufSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+ for (int i = 0; i < numCaptureBuffers; i++) {
+ camera.addCallbackBuffer(new byte[bufSize]);
+ }
+ camera.setPreviewCallbackWithBuffer(this);
+ camera.startPreview();
+ return true;
+ } catch (IOException e) {
+ error = e;
+ } catch (RuntimeException e) {
+ error = e;
+ }
+ Log.e(TAG, "startCapture failed", error);
+ if (camera != null) {
+ stopCapture();
+ }
+ return false;
+ }
+
+ // Called by native code. Returns true when camera is known to be stopped.
+ private synchronized boolean stopCapture() {
+ Log.d(TAG, "stopCapture");
+ if (camera == null) {
+ throw new RuntimeException("Camera is already stopped!");
+ }
+ Throwable error = null;
+ try {
+ if (localPreview != null) {
+ localPreview.removeCallback(this);
+ camera.setPreviewDisplay(null);
+ } else {
+ camera.setPreviewTexture(null);
+ }
+ camera.setPreviewCallbackWithBuffer(null);
+ camera.stopPreview();
+ camera.release();
+ camera = null;
+ return true;
+ } catch (IOException e) {
+ error = e;
+ } catch (RuntimeException e) {
+ error = e;
+ }
+ Log.e(TAG, "Failed to stop camera", error);
+ return false;
+ }
+
+ private native void ProvideCameraFrame(
+ byte[] data, int length, long captureObject);
+
+ public synchronized void onPreviewFrame(byte[] data, Camera camera) {
+ ProvideCameraFrame(data, data.length, native_capturer);
+ camera.addCallbackBuffer(data);
+ }
+
+ // Sets the rotation of the preview render window.
+ // Does not affect the captured video image.
+ // Called by native code.
+ private synchronized void setPreviewRotation(int rotation) {
+ Log.v(TAG, "setPreviewRotation:" + rotation);
+
+ if (camera == null) {
+ return;
}
- public void surfaceDestroyed(SurfaceHolder holder) {
- Log.d(TAG, "VideoCaptureAndroid::surfaceDestroyed");
- captureLock.lock();
- try {
- if (camera != null) {
- camera.setPreviewDisplay(null);
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to clear preview surface!", e);
- }
- captureLock.unlock();
+ int resultRotation = 0;
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ // This is a front facing camera. SetDisplayOrientation will flip
+ // the image horizontally before doing the rotation.
+ resultRotation = ( 360 - rotation ) % 360; // Compensate for the mirror.
+ } else {
+ // Back-facing camera.
+ resultRotation = rotation;
}
+ camera.setDisplayOrientation(resultRotation);
+ }
+
+ public synchronized void surfaceChanged(
+ SurfaceHolder holder, int format, int width, int height) {
+ Log.d(TAG, "VideoCaptureAndroid::surfaceChanged ignored: " +
+ format + ": " + width + "x" + height);
+ }
+
+ public synchronized void surfaceCreated(SurfaceHolder holder) {
+ Log.d(TAG, "VideoCaptureAndroid::surfaceCreated");
+ try {
+ if (camera != null) {
+ camera.setPreviewDisplay(holder);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public synchronized void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(TAG, "VideoCaptureAndroid::surfaceDestroyed");
+ try {
+ if (camera != null) {
+ camera.setPreviewDisplay(null);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
index d1a744f..f23e9a8 100644
--- a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
+++ b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
@@ -17,377 +17,79 @@
import java.util.List;
import java.util.Locale;
-import dalvik.system.DexClassLoader;
-
import android.content.Context;
-import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
+import android.hardware.Camera;
import android.util.Log;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
public class VideoCaptureDeviceInfoAndroid {
+ private final static String TAG = "WEBRTC-JC";
- //Context
- Context context;
+ private static boolean isFrontFacing(CameraInfo info) {
+ return info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT;
+ }
- // Set VERBOSE as the default logging level because camera device info
- // is very useful information and doesn't degrade performance normally
- private final static String TAG = "WEBRTC";
+ private static String deviceUniqueName(int index, CameraInfo info) {
+ return "Camera " + index +", Facing " +
+ (isFrontFacing(info) ? "front" : "back") +
+ ", Orientation "+ info.orientation;
+ }
- // Private class with info about all available cameras and the capabilities
- public class AndroidVideoCaptureDevice {
- AndroidVideoCaptureDevice() {
- frontCameraType = FrontFacingCameraType.None;
- index = 0;
- }
-
- public String deviceUniqueName;
- public CaptureCapabilityAndroid captureCapabilies[];
- public FrontFacingCameraType frontCameraType;
-
- // Orientation of camera as described in
- // android.hardware.Camera.CameraInfo.Orientation
- public int orientation;
- // Camera index used in Camera.Open on Android 2.3 and onwards
- public int index;
- }
-
- public enum FrontFacingCameraType {
- None, // This is not a front facing camera
- GalaxyS, // Galaxy S front facing camera.
- HTCEvo, // HTC Evo front facing camera
- Android23, // Android 2.3 front facing camera.
- }
-
- String currentDeviceUniqueId;
- int id;
- List<AndroidVideoCaptureDevice> deviceList;
-
- public static VideoCaptureDeviceInfoAndroid
- CreateVideoCaptureDeviceInfoAndroid(int in_id, Context in_context) {
- Log.d(TAG,
- String.format(Locale.US, "VideoCaptureDeviceInfoAndroid"));
-
- VideoCaptureDeviceInfoAndroid self =
- new VideoCaptureDeviceInfoAndroid(in_id, in_context);
- if(self != null && self.Init() == 0) {
- return self;
- }
- else {
- Log.d(TAG, "Failed to create VideoCaptureDeviceInfoAndroid.");
- }
- return null;
- }
-
- private VideoCaptureDeviceInfoAndroid(int in_id,
- Context in_context) {
- id = in_id;
- context = in_context;
- deviceList = new ArrayList<AndroidVideoCaptureDevice>();
- }
-
- private int Init() {
- // Populate the deviceList with available cameras and their capabilities.
- Camera camera = null;
- if(android.os.Build.VERSION.SDK_INT > 8) {
- // From Android 2.3 and onwards
- for(int i = 0; i < Camera.getNumberOfCameras(); ++i) {
- AndroidVideoCaptureDevice newDevice = new AndroidVideoCaptureDevice();
-
- Camera.CameraInfo info = new Camera.CameraInfo();
- Camera.getCameraInfo(i, info);
- newDevice.index = i;
- newDevice.orientation=info.orientation;
- if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
- newDevice.deviceUniqueName =
- "Camera " + i +", Facing back, Orientation "+ info.orientation;
- Log.d(TAG, "Camera " + i +", Facing back, Orientation "+ info.orientation);
-
- }
- else {
- newDevice.deviceUniqueName =
- "Camera " + i +", Facing front, Orientation "+ info.orientation;
- newDevice.frontCameraType = FrontFacingCameraType.Android23;
- Log.d(TAG, "Camera " + i +", Facing front, Orientation "+ info.orientation);
- }
-
- camera = Camera.open(i);
- Camera.Parameters parameters = camera.getParameters();
- AddDeviceInfo(newDevice, parameters);
- camera.release();
- camera = null;
- deviceList.add(newDevice);
- }
- }
- VerifyCapabilities();
- return 0;
- }
-
- // Adds the capture capabilities of the currently opened device
- private void AddDeviceInfo(AndroidVideoCaptureDevice newDevice,
- Camera.Parameters parameters) {
-
- List<Size> sizes = parameters.getSupportedPreviewSizes();
- List<Integer> frameRates = parameters.getSupportedPreviewFrameRates();
- int maxFPS = 0;
- if (frameRates != null) {
- for(Integer frameRate:frameRates) {
- if(frameRate > maxFPS) {
- maxFPS = frameRate;
- }
- }
- }
-
- newDevice.captureCapabilies = new CaptureCapabilityAndroid[sizes.size()];
- for(int i = 0; i < sizes.size(); ++i) {
- Size s = sizes.get(i);
- newDevice.captureCapabilies[i] = new CaptureCapabilityAndroid();
- newDevice.captureCapabilies[i].height = s.height;
- newDevice.captureCapabilies[i].width = s.width;
- newDevice.captureCapabilies[i].maxFPS = maxFPS;
- Log.v(TAG, "VideoCaptureDeviceInfo " + ", maxFPS: " + maxFPS +
- ", width: " + s.width + ", height: " + s.height);
- }
- }
-
- // Function that make sure device specific capabilities are
- // in the capability list.
- // Ie Galaxy S supports CIF but does not list CIF as a supported capability.
- // Motorola Droid Camera does not work with frame rate above 15fps.
- // http://code.google.com/p/android/issues/detail?id=5514#c0
- private void VerifyCapabilities() {
- // Nexus S or Galaxy S
- if(android.os.Build.DEVICE.equals("GT-I9000") ||
- android.os.Build.DEVICE.equals("crespo")) {
- CaptureCapabilityAndroid specificCapability =
- new CaptureCapabilityAndroid();
- specificCapability.width = 352;
- specificCapability.height = 288;
- specificCapability.maxFPS = 15;
- AddDeviceSpecificCapability(specificCapability);
-
- specificCapability = new CaptureCapabilityAndroid();
- specificCapability.width = 176;
- specificCapability.height = 144;
- specificCapability.maxFPS = 15;
- AddDeviceSpecificCapability(specificCapability);
-
- specificCapability = new CaptureCapabilityAndroid();
- specificCapability.width = 320;
- specificCapability.height = 240;
- specificCapability.maxFPS = 15;
- AddDeviceSpecificCapability(specificCapability);
- }
- // Motorola Milestone Camera server does not work at 30fps
- // even though it reports that it can
- if(android.os.Build.MANUFACTURER.equals("motorola") &&
- android.os.Build.DEVICE.equals("umts_sholes")) {
- for (AndroidVideoCaptureDevice device : deviceList) {
- for (CaptureCapabilityAndroid capability : device.captureCapabilies) {
- capability.maxFPS = 15;
- }
- }
- }
- }
-
- private void AddDeviceSpecificCapability(
- CaptureCapabilityAndroid specificCapability) {
- for(AndroidVideoCaptureDevice device:deviceList) {
- boolean foundCapability = false;
- for(CaptureCapabilityAndroid capability:device.captureCapabilies) {
- if(capability.width == specificCapability.width &&
- capability.height == specificCapability.height) {
- foundCapability = true;
- break;
- }
- }
- if(foundCapability==false) {
- CaptureCapabilityAndroid newCaptureCapabilies[]=
- new CaptureCapabilityAndroid[device.captureCapabilies.length+1];
- for(int i = 0; i < device.captureCapabilies.length; ++i) {
- newCaptureCapabilies[i+1] = device.captureCapabilies[i];
- }
- newCaptureCapabilies[0] = specificCapability;
- device.captureCapabilies = newCaptureCapabilies;
- }
- }
- }
-
- // Returns the number of Capture devices that is supported
- public int NumberOfDevices() {
- return deviceList.size();
- }
-
- public String GetDeviceUniqueName(int deviceNumber) {
- if(deviceNumber < 0 || deviceNumber >= deviceList.size()) {
- return null;
- }
- return deviceList.get(deviceNumber).deviceUniqueName;
- }
-
- public CaptureCapabilityAndroid[] GetCapabilityArray (String deviceUniqueId)
- {
- for (AndroidVideoCaptureDevice device: deviceList) {
- if(device.deviceUniqueName.equals(deviceUniqueId)) {
- return (CaptureCapabilityAndroid[]) device.captureCapabilies;
- }
- }
- return null;
- }
-
- // Returns the camera orientation as described by
- // android.hardware.Camera.CameraInfo.orientation
- public int GetOrientation(String deviceUniqueId) {
- for (AndroidVideoCaptureDevice device: deviceList) {
- if(device.deviceUniqueName.equals(deviceUniqueId)) {
- return device.orientation;
- }
- }
- return -1;
- }
-
- // Returns an instance of VideoCaptureAndroid.
- public VideoCaptureAndroid AllocateCamera(int id, long context,
- String deviceUniqueId) {
+ // Returns information about all cameras on the device as a serialized JSON
+ // array of dictionaries encoding information about a single device. Since
+ // this reflects static information about the hardware present, there is no
+ // need to call this function more than once in a single process. It is
+ // marked "private" as it is only called by native code.
+ private static String getDeviceInfo() {
+ try {
+ JSONArray devices = new JSONArray();
+ for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
+ CameraInfo info = new CameraInfo();
+ Camera.getCameraInfo(i, info);
+ String uniqueName = deviceUniqueName(i, info);
+ JSONObject cameraDict = new JSONObject();
+ devices.put(cameraDict);
+ List<Size> supportedSizes;
+ List<int[]> supportedFpsRanges;
try {
- Log.d(TAG, "AllocateCamera " + deviceUniqueId);
-
- Camera camera = null;
- AndroidVideoCaptureDevice deviceToUse = null;
- for (AndroidVideoCaptureDevice device: deviceList) {
- if(device.deviceUniqueName.equals(deviceUniqueId)) {
- // Found the wanted camera
- deviceToUse = device;
- switch(device.frontCameraType) {
- case GalaxyS:
- camera = AllocateGalaxySFrontCamera();
- break;
- case HTCEvo:
- camera = AllocateEVOFrontFacingCamera();
- break;
- default:
- // From Android 2.3 and onwards)
- if(android.os.Build.VERSION.SDK_INT>8)
- camera=Camera.open(device.index);
- else
- camera=Camera.open(); // Default camera
- }
- }
- }
-
- if(camera == null) {
- return null;
- }
- Log.v(TAG, "AllocateCamera - creating VideoCaptureAndroid");
-
- return new VideoCaptureAndroid(id, context, camera, deviceToUse);
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "AllocateCamera Failed to open camera", e);
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "AllocateCamera Failed to open camera", e);
- } catch (InvocationTargetException e) {
- Log.e(TAG, "AllocateCamera Failed to open camera", e);
- } catch (IllegalAccessException e) {
- Log.e(TAG, "AllocateCamera Failed to open camera", e);
+ Camera camera = Camera.open(i);
+ Parameters parameters = camera.getParameters();
+ supportedSizes = parameters.getSupportedPreviewSizes();
+ supportedFpsRanges = parameters.getSupportedPreviewFpsRange();
+ camera.release();
+ Log.d(TAG, uniqueName);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to open " + uniqueName + ", skipping");
+ continue;
}
- return null;
+ JSONArray sizes = new JSONArray();
+ for (Size supportedSize : supportedSizes) {
+ JSONObject size = new JSONObject();
+ size.put("width", supportedSize.width);
+ size.put("height", supportedSize.height);
+ sizes.put(size);
+ }
+ // Android SDK deals in integral "milliframes per second"
+ // (i.e. fps*1000, instead of floating-point frames-per-second) so we
+ // preserve that through the Java->C++->Java round-trip.
+ int[] mfps = supportedFpsRanges.get(supportedFpsRanges.size() - 1);
+ cameraDict.put("name", uniqueName);
+ cameraDict.put("front_facing", isFrontFacing(info))
+ .put("orientation", info.orientation)
+ .put("sizes", sizes)
+ .put("min_mfps", mfps[Parameters.PREVIEW_FPS_MIN_INDEX])
+ .put("max_mfps", mfps[Parameters.PREVIEW_FPS_MAX_INDEX]);
+ }
+ String ret = devices.toString(2);
+ return ret;
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
}
-
- // Searches for a front facing camera device. This is device specific code.
- private Camera.Parameters
- SearchOldFrontFacingCameras(AndroidVideoCaptureDevice newDevice)
- throws SecurityException, IllegalArgumentException,
- NoSuchMethodException, ClassNotFoundException,
- IllegalAccessException, InvocationTargetException {
- // Check the id of the opened camera device
- // Returns null on X10 and 1 on Samsung Galaxy S.
- Camera camera = Camera.open();
- Camera.Parameters parameters = camera.getParameters();
- String cameraId = parameters.get("camera-id");
- if(cameraId != null && cameraId.equals("1")) {
- // This might be a Samsung Galaxy S with a front facing camera.
- parameters.set("camera-id", 2);
- camera.setParameters(parameters);
- parameters = camera.getParameters();
- newDevice.frontCameraType = FrontFacingCameraType.GalaxyS;
- newDevice.orientation = 0;
- camera.release();
- return parameters;
- }
- camera.release();
-
- // Check for Evo front facing camera
- File file =
- new File("/system/framework/com.htc.hardware.twinCamDevice.jar");
- boolean exists = file.exists();
- if (!exists) {
- file =
- new File("/system/framework/com.sprint.hardware.twinCamDevice.jar");
- exists = file.exists();
- }
- if(exists) {
- newDevice.frontCameraType = FrontFacingCameraType.HTCEvo;
- newDevice.orientation = 0;
- Camera evCamera = AllocateEVOFrontFacingCamera();
- parameters = evCamera.getParameters();
- evCamera.release();
- return parameters;
- }
- return null;
- }
-
- // Returns a handle to HTC front facing camera.
- // The caller is responsible to release it on completion.
- private Camera AllocateEVOFrontFacingCamera()
- throws SecurityException, NoSuchMethodException,
- ClassNotFoundException, IllegalArgumentException,
- IllegalAccessException, InvocationTargetException {
- String classPath = null;
- File file =
- new File("/system/framework/com.htc.hardware.twinCamDevice.jar");
- classPath = "com.htc.hardware.twinCamDevice.FrontFacingCamera";
- boolean exists = file.exists();
- if (!exists){
- file =
- new File("/system/framework/com.sprint.hardware.twinCamDevice.jar");
- classPath = "com.sprint.hardware.twinCamDevice.FrontFacingCamera";
- exists = file.exists();
- }
- if(!exists) {
- return null;
- }
-
- String dexOutputDir = "";
- if(context != null) {
- dexOutputDir = context.getFilesDir().getAbsolutePath();
- File mFilesDir = new File(dexOutputDir, "dexfiles");
- if(!mFilesDir.exists()){
- // Log.e("*WEBRTCN*", "Directory doesn't exists");
- if(!mFilesDir.mkdirs()) {
- // Log.e("*WEBRTCN*", "Unable to create files directory");
- }
- }
- }
-
- dexOutputDir += "/dexfiles";
-
- DexClassLoader loader =
- new DexClassLoader(file.getAbsolutePath(), dexOutputDir,
- null, ClassLoader.getSystemClassLoader());
-
- Method method = loader.loadClass(classPath).getDeclaredMethod(
- "getFrontFacingCamera", (Class[]) null);
- Camera camera = (Camera) method.invoke((Object[])null,(Object[]) null);
- return camera;
- }
-
- // Returns a handle to Galaxy S front camera.
- // The caller is responsible to release it on completion.
- private Camera AllocateGalaxySFrontCamera() {
- Camera camera = Camera.open();
- Camera.Parameters parameters = camera.getParameters();
- parameters.set("camera-id",2);
- camera.setParameters(parameters);
- return camera;
- }
-
+ }
}
diff --git a/modules/video_capture/android/video_capture_android.cc b/modules/video_capture/android/video_capture_android.cc
index a66ce5c..2b6d606 100644
--- a/modules/video_capture/android/video_capture_android.cc
+++ b/modules/video_capture/android/video_capture_android.cc
@@ -10,599 +10,173 @@
#include "webrtc/modules/video_capture/android/video_capture_android.h"
-#include <stdio.h>
-
+#include "webrtc/modules/utility/interface/helpers_android.h"
+#include "webrtc/modules/video_capture/android/device_info_android.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
#include "webrtc/system_wrappers/interface/ref_count.h"
#include "webrtc/system_wrappers/interface/trace.h"
-namespace webrtc
-{
-#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD)
-// TODO(leozwang) These SetAndroidVM apis will be refactored, thus we only
-// keep and reference java vm.
-int32_t SetCaptureAndroidVM(void* javaVM, void* javaContext) {
- return videocapturemodule::VideoCaptureAndroid::SetAndroidObjects(
- javaVM,
- javaContext);
-}
-#endif
+static JavaVM* g_jvm = NULL;
+static jclass g_java_capturer_class = NULL; // VideoCaptureAndroid.class.
-namespace videocapturemodule
-{
+namespace webrtc {
+
+// Called by Java when the camera has a new frame to deliver.
+void JNICALL ProvideCameraFrame(
+ JNIEnv* env,
+ jobject,
+ jbyteArray javaCameraFrame,
+ jint length,
+ jlong context) {
+ webrtc::videocapturemodule::VideoCaptureAndroid* captureModule =
+ reinterpret_cast<webrtc::videocapturemodule::VideoCaptureAndroid*>(
+ context);
+ jbyte* cameraFrame = env->GetByteArrayElements(javaCameraFrame, NULL);
+ captureModule->OnIncomingFrame(
+ reinterpret_cast<uint8_t*>(cameraFrame), length, 0);
+ env->ReleaseByteArrayElements(javaCameraFrame, cameraFrame, JNI_ABORT);
+}
+
+int32_t SetCaptureAndroidVM(JavaVM* javaVM) {
+ g_jvm = javaVM;
+ AttachThreadScoped ats(g_jvm);
+
+ videocapturemodule::DeviceInfoAndroid::Initialize(ats.env());
+
+ jclass j_capture_class =
+ ats.env()->FindClass("org/webrtc/videoengine/VideoCaptureAndroid");
+ assert(j_capture_class);
+ g_java_capturer_class =
+ reinterpret_cast<jclass>(ats.env()->NewGlobalRef(j_capture_class));
+ assert(g_java_capturer_class);
+
+ JNINativeMethod native_method = {
+ "ProvideCameraFrame", "([BIJ)V",
+ reinterpret_cast<void*>(&ProvideCameraFrame)
+ };
+ if (ats.env()->RegisterNatives(g_java_capturer_class, &native_method, 1) != 0)
+ assert(false);
+
+ return 0;
+}
+
+namespace videocapturemodule {
VideoCaptureModule* VideoCaptureImpl::Create(
const int32_t id,
const char* deviceUniqueIdUTF8) {
-
RefCountImpl<videocapturemodule::VideoCaptureAndroid>* implementation =
new RefCountImpl<videocapturemodule::VideoCaptureAndroid>(id);
-
- if (!implementation || implementation->Init(id, deviceUniqueIdUTF8) != 0) {
+ if (implementation->Init(id, deviceUniqueIdUTF8) != 0) {
delete implementation;
implementation = NULL;
}
return implementation;
}
-// Android logging, uncomment to print trace to
-// logcat instead of trace file/callback
-// #include <android/log.h>
-// #undef WEBRTC_TRACE
-// #define WEBRTC_TRACE(a,b,c,...)
-// __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__)
-
-JavaVM* VideoCaptureAndroid::g_jvm = NULL;
-//VideoCaptureAndroid.java
-jclass VideoCaptureAndroid::g_javaCmClass = NULL;
-//VideoCaptureDeviceInfoAndroid.java
-jclass VideoCaptureAndroid::g_javaCmDevInfoClass = NULL;
-//static instance of VideoCaptureDeviceInfoAndroid.java
-jobject VideoCaptureAndroid::g_javaCmDevInfoObject = NULL;
-jobject VideoCaptureAndroid::g_javaContext = NULL;
-
-/*
- * Register references to Java Capture class.
- */
-int32_t VideoCaptureAndroid::SetAndroidObjects(void* javaVM,
- void* javaContext) {
-
- g_jvm = static_cast<JavaVM*> (javaVM);
- g_javaContext = static_cast<jobject> (javaContext);
-
- if (javaVM) {
- JNIEnv* env = NULL;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not get Java environment", __FUNCTION__);
- return -1;
- }
- // get java capture class type (note path to class packet)
- jclass javaCmClassLocal = env->FindClass(AndroidJavaCaptureClass);
- if (!javaCmClassLocal) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not find java class", __FUNCTION__);
- return -1;
- }
- // create a global reference to the class
- // (to tell JNI that we are referencing it
- // after this function has returned)
- g_javaCmClass = static_cast<jclass>
- (env->NewGlobalRef(javaCmClassLocal));
- if (!g_javaCmClass) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: InitVideoEngineJava(): could not create"
- " Java Camera class reference",
- __FUNCTION__);
- return -1;
- }
- // Delete local class ref, we only use the global ref
- env->DeleteLocalRef(javaCmClassLocal);
- JNINativeMethod nativeFunctions =
- { "ProvideCameraFrame", "([BIJ)V",
- (void*) &VideoCaptureAndroid::ProvideCameraFrame };
- if (env->RegisterNatives(g_javaCmClass, &nativeFunctions, 1) == 0) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: Registered native functions", __FUNCTION__);
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Failed to register native functions",
- __FUNCTION__);
- return -1;
- }
-
- jclass capabilityClassLocal = env->FindClass(
- "org/webrtc/videoengine/CaptureCapabilityAndroid");
- if (!capabilityClassLocal) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not find java class", __FUNCTION__);
- return -1;
- }
- jclass capabilityClassGlobal = reinterpret_cast<jclass>(env->NewGlobalRef(
- capabilityClassLocal));
- DeviceInfoAndroid::SetAndroidCaptureClasses(capabilityClassGlobal);
-
- // get java capture class type (note path to class packet)
- jclass javaCmDevInfoClassLocal = env->FindClass(
- "org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid");
- if (!javaCmDevInfoClassLocal) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not find java class", __FUNCTION__);
- return -1;
- }
-
- // create a global reference to the class
- // (to tell JNI that we are referencing it
- // after this function has returned)
- g_javaCmDevInfoClass = static_cast<jclass>
- (env->NewGlobalRef(javaCmDevInfoClassLocal));
- if (!g_javaCmDevInfoClass) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: InitVideoEngineJava(): could not create Java "
- "Camera Device info class reference",
- __FUNCTION__);
- return -1;
- }
- // Delete local class ref, we only use the global ref
- env->DeleteLocalRef(javaCmDevInfoClassLocal);
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "VideoCaptureDeviceInfoAndroid get method id");
-
- // get the method ID for the Android Java CaptureClass static
- //CreateVideoCaptureAndroid factory method.
- jmethodID cid = env->GetStaticMethodID(
- g_javaCmDevInfoClass,
- "CreateVideoCaptureDeviceInfoAndroid",
- "(ILandroid/content/Context;)"
- "Lorg/webrtc/videoengine/VideoCaptureDeviceInfoAndroid;");
- if (cid == NULL) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not get java"
- "VideoCaptureDeviceInfoAndroid constructor ID",
- __FUNCTION__);
- return -1;
- }
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: construct static java device object", __FUNCTION__);
-
- // construct the object by calling the static constructor object
- jobject javaCameraDeviceInfoObjLocal =
- env->CallStaticObjectMethod(g_javaCmDevInfoClass,
- cid, (int) -1,
- g_javaContext);
- if (!javaCameraDeviceInfoObjLocal) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
- "%s: could not create Java Capture Device info object",
- __FUNCTION__);
- return -1;
- }
- // create a reference to the object (to tell JNI that
- // we are referencing it after this function has returned)
- g_javaCmDevInfoObject = env->NewGlobalRef(javaCameraDeviceInfoObjLocal);
- if (!g_javaCmDevInfoObject) {
- WEBRTC_TRACE(webrtc::kTraceError,
- webrtc::kTraceAudioDevice,
- -1,
- "%s: could not create Java"
- "cameradevinceinfo object reference",
- __FUNCTION__);
- return -1;
- }
- // Delete local object ref, we only use the global ref
- env->DeleteLocalRef(javaCameraDeviceInfoObjLocal);
- return 0;
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: JVM is NULL, assuming deinit", __FUNCTION__);
- if (!g_jvm) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: SetAndroidObjects not called with a valid JVM.",
- __FUNCTION__);
- return -1;
- }
- JNIEnv* env = NULL;
- bool attached = false;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
- -1, "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- return -1;
- }
- attached = true;
- }
- env->DeleteGlobalRef(g_javaCmDevInfoObject);
- env->DeleteGlobalRef(g_javaCmDevInfoClass);
- env->DeleteGlobalRef(g_javaCmClass);
- if (attached && g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
- "%s: Could not detach thread from JVM", __FUNCTION__);
- return -1;
- }
- return 0;
- env = (JNIEnv *) NULL;
- }
- return 0;
+int32_t VideoCaptureAndroid::OnIncomingFrame(uint8_t* videoFrame,
+ int32_t videoFrameLength,
+ int64_t captureTime) {
+ return IncomingFrame(
+ videoFrame, videoFrameLength, _captureCapability, captureTime);
}
-int32_t VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
- JNIEnv*& env,
- jclass& javaCmDevInfoClass,
- jobject& javaCmDevInfoObject,
- bool& attached) {
- // get the JNI env for this thread
- if (!g_jvm) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: SetAndroidObjects not called with a valid JVM.",
- __FUNCTION__);
- return -1;
- }
- attached = false;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- return -1;
- }
- attached = true;
- }
- javaCmDevInfoClass = g_javaCmDevInfoClass;
- javaCmDevInfoObject = g_javaCmDevInfoObject;
- return 0;
-
-}
-
-int32_t VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(
- bool attached) {
- if (attached && g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
- "%s: Could not detach thread from JVM", __FUNCTION__);
- return -1;
- }
- return 0;
-}
-
-/*
- * JNI callback from Java class. Called
- * when the camera has a new frame to deliver
- * Class: org_webrtc_capturemodule_VideoCaptureAndroid
- * Method: ProvideCameraFrame
- * Signature: ([BIJ)V
- */
-void JNICALL VideoCaptureAndroid::ProvideCameraFrame(JNIEnv * env,
- jobject,
- jbyteArray javaCameraFrame,
- jint length,
- jlong context) {
- VideoCaptureAndroid* captureModule =
- reinterpret_cast<VideoCaptureAndroid*>(context);
- WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture,
- -1, "%s: IncomingFrame %d", __FUNCTION__,length);
- jbyte* cameraFrame= env->GetByteArrayElements(javaCameraFrame,NULL);
- captureModule->IncomingFrame((uint8_t*) cameraFrame,
- length,captureModule->_frameInfo,0);
- env->ReleaseByteArrayElements(javaCameraFrame,cameraFrame,JNI_ABORT);
-}
-
-
-
VideoCaptureAndroid::VideoCaptureAndroid(const int32_t id)
- : VideoCaptureImpl(id), _capInfo(id), _javaCaptureObj(NULL),
+ : VideoCaptureImpl(id),
+ _deviceInfo(id),
+ _jCapturer(NULL),
_captureStarted(false) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: context %x", __FUNCTION__, (int) this);
}
-// ----------------------------------------------------------------------------
-// Init
-//
-// Initializes needed Java resources like the JNI interface to
-// VideoCaptureAndroid.java
-// ----------------------------------------------------------------------------
int32_t VideoCaptureAndroid::Init(const int32_t id,
const char* deviceUniqueIdUTF8) {
const int nameLength = strlen(deviceUniqueIdUTF8);
- if (nameLength >= kVideoCaptureUniqueNameLength) {
+ if (nameLength >= kVideoCaptureUniqueNameLength)
return -1;
- }
// Store the device name
_deviceUniqueId = new char[nameLength + 1];
memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
- if (_capInfo.Init() != 0) {
- WEBRTC_TRACE(webrtc::kTraceError,
- webrtc::kTraceVideoCapture,
- _id,
- "%s: Failed to initialize CaptureDeviceInfo",
- __FUNCTION__);
+ AttachThreadScoped ats(g_jvm);
+ JNIEnv* env = ats.env();
+
+ jmethodID ctor = env->GetMethodID(g_java_capturer_class, "<init>", "(IJ)V");
+ assert(ctor);
+ jlong j_this = reinterpret_cast<intptr_t>(this);
+ size_t camera_id = 0;
+ if (!_deviceInfo.FindCameraIndex(deviceUniqueIdUTF8, &camera_id))
return -1;
- }
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:",
- __FUNCTION__);
- // use the jvm that has been set
- if (!g_jvm) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Not a valid Java VM pointer", __FUNCTION__);
- return -1;
- }
- // get the JNI env for this thread
- JNIEnv *env;
- bool isAttached = false;
-
- // get the JNI env for this thread
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- return -1;
- }
- isAttached = true;
- }
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
- "get method id");
-
- // get the method ID for the Android Java
- // CaptureDeviceInfoClass AllocateCamera factory method.
- char signature[256];
- sprintf(signature, "(IJLjava/lang/String;)L%s;", AndroidJavaCaptureClass);
-
- jmethodID cid = env->GetMethodID(g_javaCmDevInfoClass, "AllocateCamera",
- signature);
- if (cid == NULL) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: could not get constructor ID", __FUNCTION__);
- return -1; /* exception thrown */
- }
-
- jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8);
- // construct the object by calling the static constructor object
- jobject javaCameraObjLocal = env->CallObjectMethod(g_javaCmDevInfoObject,
- cid, (jint) id,
- (jlong) this,
- capureIdString);
- if (!javaCameraObjLocal) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
- "%s: could not create Java Capture object", __FUNCTION__);
- return -1;
- }
-
- // create a reference to the object (to tell JNI that we are referencing it
- // after this function has returned)
- _javaCaptureObj = env->NewGlobalRef(javaCameraObjLocal);
- if (!_javaCaptureObj) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioDevice, _id,
- "%s: could not create Java camera object reference",
- __FUNCTION__);
- return -1;
- }
-
- // Delete local object ref, we only use the global ref
- env->DeleteLocalRef(javaCameraObjLocal);
-
- // Detach this thread if it was attached
- if (isAttached) {
- if (g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
- "%s: Could not detach thread from JVM", __FUNCTION__);
- }
- }
-
+ _jCapturer = env->NewGlobalRef(
+ env->NewObject(g_java_capturer_class, ctor, camera_id, j_this));
+ assert(_jCapturer);
return 0;
}
VideoCaptureAndroid::~VideoCaptureAndroid() {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:",
- __FUNCTION__);
- if (_javaCaptureObj == NULL || g_jvm == NULL) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Nothing to clean", __FUNCTION__);
- }
- else {
- bool isAttached = false;
- // get the JNI env for this thread
- JNIEnv *env;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
- _id,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- }
- else {
- isAttached = true;
- }
- }
-
- // get the method ID for the Android Java CaptureClass static
- // DeleteVideoCaptureAndroid method. Call this to release the camera so
- // another application can use it.
- jmethodID cid = env->GetStaticMethodID(
- g_javaCmClass,
- "DeleteVideoCaptureAndroid",
- "(Lorg/webrtc/videoengine/VideoCaptureAndroid;)V");
- if (cid != NULL) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: Call DeleteVideoCaptureAndroid", __FUNCTION__);
- // Close the camera by calling the static destruct function.
- env->CallStaticVoidMethod(g_javaCmClass, cid, _javaCaptureObj);
-
- // Delete global object ref to the camera.
- env->DeleteGlobalRef(_javaCaptureObj);
- _javaCaptureObj = NULL;
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Failed to find DeleteVideoCaptureAndroid id",
- __FUNCTION__);
- }
-
- // Detach this thread if it was attached
- if (isAttached) {
- if (g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice,
- _id, "%s: Could not detach thread from JVM",
- __FUNCTION__);
- }
- }
- }
+ // Ensure Java camera is released even if our caller didn't explicitly Stop.
+ if (_captureStarted)
+ StopCapture();
+ AttachThreadScoped ats(g_jvm);
+ ats.env()->DeleteGlobalRef(_jCapturer);
}
int32_t VideoCaptureAndroid::StartCapture(
const VideoCaptureCapability& capability) {
CriticalSectionScoped cs(&_apiCs);
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: ", __FUNCTION__);
+ AttachThreadScoped ats(g_jvm);
+ JNIEnv* env = ats.env();
- bool isAttached = false;
- int32_t result = 0;
- // get the JNI env for this thread
- JNIEnv *env;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- }
- else {
- isAttached = true;
- }
- }
-
- if (_capInfo.GetBestMatchedCapability(_deviceUniqueId, capability,
- _frameInfo) < 0) {
+ if (_deviceInfo.GetBestMatchedCapability(
+ _deviceUniqueId, capability, _captureCapability) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: GetBestMatchedCapability failed. Req cap w%d h%d",
+ "%s: GetBestMatchedCapability failed: %dx%d",
__FUNCTION__, capability.width, capability.height);
return -1;
}
- // Store the new expected capture delay
- _captureDelay = _frameInfo.expectedCaptureDelay;
+ _captureDelay = _captureCapability.expectedCaptureDelay;
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: _frameInfo w%d h%d", __FUNCTION__, _frameInfo.width,
- _frameInfo.height);
-
- // get the method ID for the Android Java
- // CaptureClass static StartCapture method.
- jmethodID cid = env->GetMethodID(g_javaCmClass, "StartCapture", "(III)I");
- if (cid != NULL) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: Call StartCapture", __FUNCTION__);
- // Close the camera by calling the static destruct function.
- result = env->CallIntMethod(_javaCaptureObj, cid, _frameInfo.width,
- _frameInfo.height, _frameInfo.maxFPS);
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Failed to find StartCapture id", __FUNCTION__);
- }
-
- // Detach this thread if it was attached
- if (isAttached) {
- if (g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
- "%s: Could not detach thread from JVM", __FUNCTION__);
- }
- }
- if (result == 0) {
+ jmethodID j_start =
+ env->GetMethodID(g_java_capturer_class, "startCapture", "(IIII)Z");
+ assert(j_start);
+ int min_mfps = 0;
+ int max_mfps = 0;
+ _deviceInfo.GetFpsRange(_deviceUniqueId, &min_mfps, &max_mfps);
+ bool started = env->CallBooleanMethod(_jCapturer, j_start,
+ _captureCapability.width,
+ _captureCapability.height,
+ min_mfps, max_mfps);
+ if (started) {
_requestedCapability = capability;
_captureStarted = true;
}
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: result %d", __FUNCTION__, result);
- return result;
+ return started ? 0 : -1;
}
int32_t VideoCaptureAndroid::StopCapture() {
CriticalSectionScoped cs(&_apiCs);
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: ", __FUNCTION__);
-
- bool isAttached = false;
- int32_t result = 0;
- // get the JNI env for this thread
- JNIEnv *env = NULL;
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- }
- else {
- isAttached = true;
- }
- }
+ AttachThreadScoped ats(g_jvm);
+ JNIEnv* env = ats.env();
memset(&_requestedCapability, 0, sizeof(_requestedCapability));
- memset(&_frameInfo, 0, sizeof(_frameInfo));
-
- // get the method ID for the Android Java CaptureClass StopCapture method.
- jmethodID cid = env->GetMethodID(g_javaCmClass, "StopCapture", "()I");
- if (cid != NULL) {
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
- "%s: Call StopCapture", __FUNCTION__);
- // Close the camera by calling the static destruct function.
- result = env->CallIntMethod(_javaCaptureObj, cid);
- }
- else {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: Failed to find StopCapture id", __FUNCTION__);
- }
-
- // Detach this thread if it was attached
- if (isAttached) {
- if (g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
- "%s: Could not detach thread from JVM", __FUNCTION__);
- }
- }
+ memset(&_captureCapability, 0, sizeof(_captureCapability));
_captureStarted = false;
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: result %d", __FUNCTION__, result);
- return result;
+ jmethodID j_stop =
+ env->GetMethodID(g_java_capturer_class, "stopCapture", "()Z");
+ return env->CallBooleanMethod(_jCapturer, j_stop) ? 0 : -1;
}
bool VideoCaptureAndroid::CaptureStarted() {
CriticalSectionScoped cs(&_apiCs);
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: ", __FUNCTION__);
return _captureStarted;
}
int32_t VideoCaptureAndroid::CaptureSettings(
VideoCaptureCapability& settings) {
CriticalSectionScoped cs(&_apiCs);
- WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
- "%s: ", __FUNCTION__);
settings = _requestedCapability;
return 0;
}
@@ -610,64 +184,20 @@
int32_t VideoCaptureAndroid::SetCaptureRotation(
VideoCaptureRotation rotation) {
CriticalSectionScoped cs(&_apiCs);
- if (VideoCaptureImpl::SetCaptureRotation(rotation) == 0) {
- if (!g_jvm)
- return -1;
+ if (VideoCaptureImpl::SetCaptureRotation(rotation) != 0)
+ return 0;
- // get the JNI env for this thread
- JNIEnv *env;
- bool isAttached = false;
+ AttachThreadScoped ats(g_jvm);
+ JNIEnv* env = ats.env();
- // get the JNI env for this thread
- if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // try to attach the thread and get the env
- // Attach this thread to JVM
- jint res = g_jvm->AttachCurrentThread(&env, NULL);
- if ((res < 0) || !env) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
- _id,
- "%s: Could not attach thread to JVM (%d, %p)",
- __FUNCTION__, res, env);
- return -1;
- }
- isAttached = true;
- }
-
- jmethodID cid = env->GetMethodID(g_javaCmClass, "SetPreviewRotation",
- "(I)V");
- if (cid == NULL) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
- "%s: could not get java SetPreviewRotation ID",
- __FUNCTION__);
- return -1;
- }
- jint rotateFrame = 0;
- switch (rotation) {
- case kCameraRotate0:
- rotateFrame = 0;
- break;
- case kCameraRotate90:
- rotateFrame = 90;
- break;
- case kCameraRotate180:
- rotateFrame = 180;
- break;
- case kCameraRotate270:
- rotateFrame = 270;
- break;
- }
- env->CallVoidMethod(_javaCaptureObj, cid, rotateFrame);
-
- // Detach this thread if it was attached
- if (isAttached) {
- if (g_jvm->DetachCurrentThread() < 0) {
- WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice,
- _id, "%s: Could not detach thread from JVM",
- __FUNCTION__);
- }
- }
-
+ jmethodID j_spr =
+ env->GetMethodID(g_java_capturer_class, "setPreviewRotation", "(I)V");
+ assert(j_spr);
+ int rotation_degrees;
+ if (RotationInDegrees(rotation, &rotation_degrees) != 0) {
+ assert(false);
}
+ env->CallVoidMethod(_jCapturer, j_spr, rotation_degrees);
return 0;
}
diff --git a/modules/video_capture/android/video_capture_android.h b/modules/video_capture/android/video_capture_android.h
index 31cf76d..3ab7189 100644
--- a/modules/video_capture/android/video_capture_android.h
+++ b/modules/video_capture/android/video_capture_android.h
@@ -16,49 +16,31 @@
#include "webrtc/modules/video_capture/android/device_info_android.h"
#include "webrtc/modules/video_capture/video_capture_impl.h"
-#define AndroidJavaCaptureClass "org/webrtc/videoengine/VideoCaptureAndroid"
-
namespace webrtc {
namespace videocapturemodule {
class VideoCaptureAndroid : public VideoCaptureImpl {
public:
- static int32_t SetAndroidObjects(void* javaVM, void* javaContext);
- static int32_t AttachAndUseAndroidDeviceInfoObjects(
- JNIEnv*& env,
- jclass& javaCmDevInfoClass,
- jobject& javaCmDevInfoObject,
- bool& attached);
- static int32_t ReleaseAndroidDeviceInfoObjects(bool attached);
-
VideoCaptureAndroid(const int32_t id);
virtual int32_t Init(const int32_t id, const char* deviceUniqueIdUTF8);
-
- virtual int32_t StartCapture(
- const VideoCaptureCapability& capability);
+ virtual int32_t StartCapture(const VideoCaptureCapability& capability);
virtual int32_t StopCapture();
virtual bool CaptureStarted();
virtual int32_t CaptureSettings(VideoCaptureCapability& settings);
virtual int32_t SetCaptureRotation(VideoCaptureRotation rotation);
+ int32_t OnIncomingFrame(uint8_t* videoFrame,
+ int32_t videoFrameLength,
+ int64_t captureTime = 0);
+
protected:
virtual ~VideoCaptureAndroid();
- static void JNICALL ProvideCameraFrame (JNIEnv * env,
- jobject,
- jbyteArray javaCameraFrame,
- jint length, jlong context);
- DeviceInfoAndroid _capInfo;
- jobject _javaCaptureObj; // Java Camera object.
- VideoCaptureCapability _frameInfo;
- bool _captureStarted;
- static JavaVM* g_jvm;
- static jclass g_javaCmClass;
- static jclass g_javaCmDevInfoClass;
- //Static java object implementing the needed device info functions;
- static jobject g_javaCmDevInfoObject;
- static jobject g_javaContext; // Java Application context
+ DeviceInfoAndroid _deviceInfo;
+ jobject _jCapturer; // Global ref to Java VideoCaptureAndroid object.
+ VideoCaptureCapability _captureCapability;
+ bool _captureStarted;
};
} // namespace videocapturemodule
diff --git a/modules/video_capture/include/video_capture.h b/modules/video_capture/include/video_capture.h
index e3d674b..6b6247c 100644
--- a/modules/video_capture/include/video_capture.h
+++ b/modules/video_capture/include/video_capture.h
@@ -14,10 +14,14 @@
#include "webrtc/modules/interface/module.h"
#include "webrtc/modules/video_capture/include/video_capture_defines.h"
+#ifdef ANDROID
+#include <jni.h>
+#endif
+
namespace webrtc {
-#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD)
-int32_t SetCaptureAndroidVM(void* javaVM, void* javaContext);
+#if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD)
+int32_t SetCaptureAndroidVM(JavaVM* javaVM);
#endif
class VideoCaptureModule: public RefCountedModule {
diff --git a/modules/video_capture/video_capture.gypi b/modules/video_capture/video_capture.gypi
index ea6d04b..b8a63c9 100644
--- a/modules/video_capture/video_capture.gypi
+++ b/modules/video_capture/video_capture.gypi
@@ -91,6 +91,10 @@
},
}], # win
['OS=="android"', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
+ '<(DEPTH)/third_party/jsoncpp/jsoncpp.gyp:jsoncpp',
+ ],
'sources': [
'android/device_info_android.cc',
'android/device_info_android.h',
diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
index 56d6d18..f4abecb 100644
--- a/modules/video_capture/video_capture_impl.cc
+++ b/modules/video_capture/video_capture_impl.cc
@@ -41,6 +41,47 @@
return _deviceUniqueId;
}
+// static
+int32_t VideoCaptureImpl::RotationFromDegrees(int degrees,
+ VideoCaptureRotation* rotation) {
+ switch (degrees) {
+ case 0:
+ *rotation = kCameraRotate0;
+ return 0;
+ case 90:
+ *rotation = kCameraRotate90;
+ return 0;
+ case 180:
+ *rotation = kCameraRotate180;
+ return 0;
+ case 270:
+ *rotation = kCameraRotate270;
+ return 0;
+ default:
+ return -1;;
+ }
+}
+
+// static
+int32_t VideoCaptureImpl::RotationInDegrees(VideoCaptureRotation rotation,
+ int* degrees) {
+ switch (rotation) {
+ case kCameraRotate0:
+ *degrees = 0;
+ return 0;
+ case kCameraRotate90:
+ *degrees = 90;
+ return 0;
+ case kCameraRotate180:
+ *degrees = 180;
+ return 0;
+ case kCameraRotate270:
+ *degrees = 270;
+ return 0;
+ }
+ return -1;
+}
+
int32_t VideoCaptureImpl::ChangeUniqueId(const int32_t id)
{
_id = id;
@@ -358,6 +399,8 @@
case kCameraRotate270:
_rotateFrame = kRotate270;
break;
+ default:
+ return -1;
}
return 0;
}
diff --git a/modules/video_capture/video_capture_impl.h b/modules/video_capture/video_capture_impl.h
index 6560506..41f555c 100644
--- a/modules/video_capture/video_capture_impl.h
+++ b/modules/video_capture/video_capture_impl.h
@@ -51,6 +51,13 @@
static DeviceInfo* CreateDeviceInfo(const int32_t id);
+ // Helpers for converting between (integral) degrees and
+ // VideoCaptureRotation values. Return 0 on success.
+ static int32_t RotationFromDegrees(int degrees,
+ VideoCaptureRotation* rotation);
+ static int32_t RotationInDegrees(VideoCaptureRotation rotation,
+ int* degrees);
+
// Implements Module declared functions.
virtual int32_t ChangeUniqueId(const int32_t id);