Android(安卓)Camera 二 JNI JAVA和C/CPP图像数据传输流程分析
Android Camera 一 源码路径
Android Camera 二 JNI JAVA和C/CPP图像数据传输流程分析
Android Camera 三 CameraService 和 Client 链接到 HAL
Android Camera 四 Camera HAL 分析
Android Camera 五 Camera HAL v1
Linux v4l2 一 应用层
Linux v4l2 二 驱动和 usb 摄像头
android7
源码文件列表
frameworks/base/core/java/android/hardware/Camera.java
frameworks/base/core/jni/android_hardware_Camera.cpp
frameworks/av/camera/Camera.cpp
调用关系图:
java 层
首先打开 camera ,创建 Camera 对象。
public static Camera open(int cameraId) { return new Camera(cameraId); } /** * Creates a new Camera object to access the first back-facing camera on the * device. If the device does not have a back-facing camera, this returns * null. * @see #open(int) */ public static Camera open() { int numberOfCameras = getNumberOfCameras(); CameraInfo cameraInfo = new CameraInfo(); for (int i = 0; i < numberOfCameras; i++) { getCameraInfo(i, cameraInfo); if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { return new Camera(i); } } return null; }
调用带参数的 camera 构造方法,并返回该对象;
/** used by Camera#open, Camera#open(int) */ Camera(int cameraId) { if(cameraId >= getNumberOfCameras()){ throw new RuntimeException("Unknown camera ID"); } int err = cameraInitNormal(cameraId); if (checkInitErrors(err)) { if (err == -EACCES) { throw new RuntimeException("Fail to connect to camera service"); } else if (err == -ENODEV) { throw new RuntimeException("Camera initialization failed"); } // Should never hit this. throw new RuntimeException("Unknown camera error"); } }
camera 方法中调用 cameraInitNormal → cameraInitVersion → native_setup ,进入 native 方法。
private int cameraInitVersion(int cameraId, int halVersion) { mShutterCallback = null; mRawImageCallback = null; mJpegCallback = null; mPreviewCallback = null; mPostviewCallback = null; mUsingPreviewAllocation = false; mZoomListener = null; /* ### QC ADD-ONS: START */ mCameraDataCallback = null; mCameraMetaDataCallback = null; /* ### QC ADD-ONS: END */ Looper looper; if ((looper = Looper.myLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else if ((looper = Looper.getMainLooper()) != null) { mEventHandler = new EventHandler(this, looper); } else { mEventHandler = null; } String packageName = ActivityThread.currentOpPackageName(); //Force HAL1 if the package name falls in this bucket String packageList = SystemProperties.get("camera.hal1.packagelist", ""); if (packageList.length() > 0) { TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); splitter.setString(packageList); for (String str : splitter) { if (packageName.equals(str)) { halVersion = CAMERA_HAL_API_VERSION_1_0; break; } } } return native_setup(new WeakReference(this), cameraId, halVersion, packageName); } private int cameraInitNormal(int cameraId) { return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT); }
调用流程如下图所示:
CPP 层
JNI 本地方法 android_hardware_Camera_native_setup 。
// connect to camera servicestatic jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName){ // Convert jstring to String16 const char16_t *rawClientName = reinterpret_cast( env->GetStringChars-(clientPackageName, NULL)); jsize rawClientNameLen = env->GetStringLength(clientPackageName); String16 clientName(rawClientName, rawClientNameLen); env->ReleaseStringChars(clientPackageName, reinterpret_cast(rawClientName)); sp camera; if(halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) { // Default path: hal version is don't care, do normal camera connect. // 默认路径,无关 HAL 版本,正常链接相机 camera = Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID); } else { jint status = Camera::connectLegacy(cameraId, halVersion, clientName, Camera::USE_CALLING_UID, camera); if(status != NO_ERROR) { return status; } } if(camera == NULL) { return -EACCES; } // make sure camera hardware is alive if(camera->getStatus() != NO_ERROR) { return NO_INIT; } jclass clazz = env->GetObjectClass(thiz); if(clazz == NULL) { // This should never happen jniThrowRuntimeException(env, "Can't find android/hardware/Camera"); return INVALID_OPERATION; } // We use a weak reference so the Camera object can be garbage collected. // 我们使用弱引用,以便相机对象可以被垃圾收集。 // The reference is only used as a proxy for callbacks. // 引用仅用作回调的代理。 sp context = new JNICameraContext(env, weak_this, clazz, camera); // 轻量级指针(Light Pointer),弱引用 context->incStrong((void*)android_hardware_Camera_native_setup); // camera 类继承接口类 CameraListener 的虚函数来监听获取图片/视频数据 camera->setListener(context); // save context in opaque field // 在 JAVA 层保存 context,这里是特殊处理, 把 context 类保存成一个 int 型的变量, 使用关键字reinterpret_cast<> 获取方法实例 env->SetLongField(thiz, fields.context, (jlong)context.get()); // Update default display orientation in case the sensor is reverse-landscape // 更新传感器反向横向时的默认显示方向 CameraInfo cameraInfo; status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo); if(rc != NO_ERROR) { return rc; } int defaultOrientation = 0; switch(cameraInfo.orientation) { case 0: break; case 90: if(cameraInfo.facing == CAMERA_FACING_FRONT) { defaultOrientation = 180; } break; case 180: defaultOrientation = 180; break; case 270: if(cameraInfo.facing != CAMERA_FACING_FRONT) { defaultOrientation = 180; } break; default: ALOGE("Unexpected camera orientation %d!", cameraInfo.orientation); break; } if(defaultOrientation != 0) { ALOGV("Setting default display orientation to %d", defaultOrientation); rc = camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, defaultOrientation, 0); if(rc != NO_ERROR) { ALOGE("Unable to update default orientation: %s (%d)", strerror(-rc), rc); return rc; } } return NO_ERROR;}
重点是 CameraListener 接口类的继承关系 ,camera->setListener(context);
CameraListener 接口类定义:
class CameraListener: virtual public RefBase{public: virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; virtual void postData(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata) = 0; virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr) = 0; virtual void postRecordingFrameHandleTimestamp(nsecs_t timestamp, native_handle_t* handle) = 0;};
camera 类和 JNICameraContext 类继承了 CameraListener 接口类。
class JNICameraContext: public CameraListener{ ......}/** CameraTraits 中 定义了 CameraListener */template <>struct CameraTraits{ typedef CameraListener TCamListener; typedef ::android::hardware::ICamera TCamUser; typedef ::android::hardware::ICameraClient TCamCallbacks; typedef ::android::binder::Status(::android::hardware::ICameraService::*TCamConnectService) (const sp<::android::hardware::ICameraClient>&, int, const String16&, int, int, /*out*/ sp<::android::hardware::ICamera>*); static TCamConnectService fnConnectService;};class Camera : public CameraBase, public ::android::hardware::BnCameraClient{ ......}
JNICameraContext 实现虚函数 CameraListener->postData 。
virtual void postData(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata);
然后 camera 类中注册,并调用 JNICameraContext->postData 实现 camera 图像数据传输到 Java 层。
/* 注册 CameraListener 接口监听 */void Camera::setListener(const sp& listener){ Mutex::Autolock _l(mLock); mListener = listener;}/* 帧或图像就绪时,从相机服务中回调图像数据 */void Camera::dataCallback(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata){ sp listener; { Mutex::Autolock _l(mLock); listener = mListener; } if (listener != NULL) { /* 调用的是 JNICameraContext 中实现 postData */ listener->postData(msgType, dataPtr, metadata); }}
下面分析 camera context 类:
JNICameraContext 类作为一个中间类, 实现 JAVA 和 C/CPP 的衔接;
// provides persistent context for calls from native code to Java// 为 Java 的调用 native code 提供持久的上下文class JNICameraContext: public CameraListener{public: JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp& camera); // 构造 context 上下文 ~JNICameraContext() { release(); } virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); virtual void postData(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata); // 虚函数实现获取 camera 数据 virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr); virtual void postRecordingFrameHandleTimestamp(nsecs_t timestamp, native_handle_t* handle); void postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata); void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType); void setCallbackMode(JNIEnv *env, bool installed, bool manualMode); sp getCamera() { Mutex::Autolock _l(mLock); return mCamera; } bool isRawImageCallbackBufferAvailable() const; void release();private: void copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType); // 本地回调 camera 图像数据 void clearCallbackBuffers_l(JNIEnv *env, Vector *buffers); void clearCallbackBuffers_l(JNIEnv *env); jbyteArray getCallbackBuffer(JNIEnv *env, Vector *buffers, size_t bufferSize); jobject mCameraJObjectWeak; // weak reference to java object, Java对象的弱引用 jclass mCameraJClass; // strong reference to java class, 对Java类的强引用 sp mCamera; // strong reference to native object, 对 native 对象的强引用 jclass mFaceClass; // strong reference to Face class jclass mRectClass; // strong reference to Rect class jclass mPointClass; // strong reference to Point class bool mIsExtendedFace; Mutex mLock; /* * Global reference application-managed raw image buffer queue. * 全局引用应用程序管理的原始映像缓冲区队列 * * Manual-only mode is supported for raw image callbacks, which is * set whenever method addCallbackBuffer() with msgType = * CAMERA_MSG_RAW_IMAGE is called; otherwise, null is returned * with raw image callbacks. * 原始图像回调支持仅手动模式,只要调用msgtype= CAMERA_MSG_RAW_IMAGE 的 addcallbuffer()方法, * 就会设置该模式;否则,原始图像回调将返回空值。 */ Vector mRawImageCallbackBuffers; /* * Application-managed preview buffer queue and the flags * associated with the usage of the preview buffer callback. * 应用程序管理的预览缓冲区队列以及与预览缓冲区回调的使用相关联的标志。 */ Vector mCallbackBuffers; // Global reference application managed byte[] bool mManualBufferMode; // Whether to use application managed buffers. bool mManualCameraCallbackSet; // Whether the callback has been set, used to // reduce unnecessary calls to set the callback.};
JNICameraContext 构造函数: 保存 camera 类的指针,并创建 java 方法的强引用;
JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp& camera){ mCameraJObjectWeak = env->NewGlobalRef(weak_this); mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; // 把 camera 实例保存到 mCamera jclass extendedfaceClazz = env->FindClass("org/codeaurora/camera/ExtendedFace"); if(NULL != extendedfaceClazz) { mFaceClass = (jclass) env->NewGlobalRef(extendedfaceClazz); // 创建 extendedfaceClazz 强引用 mIsExtendedFace = true; } else { env->ExceptionClear(); jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); mFaceClass = (jclass) env->NewGlobalRef(faceClazz); mIsExtendedFace = false; } jclass rectClazz = env->FindClass("android/graphics/Rect"); mRectClass = (jclass) env->NewGlobalRef(rectClazz); jclass pointClazz = env->FindClass("android/graphics/Point"); mPointClass = (jclass) env->NewGlobalRef(pointClazz); mManualBufferMode = false; mManualCameraCallbackSet = false;}
get_native_camera 方法获取 camera 的指针供 其他 native 方法调用;
sp get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext){ sp camera; Mutex::Autolock _l(sLock); // fields.context 是在 android_hardware_Camera_native_setup 里保存 context 类, // 使用 reinterpret_cast<> 获取 context 的指针 JNICameraContext* context = reinterpret_cast(env->GetLongField(thiz, fields.context)); if (context != NULL) { camera = context->getCamera(); } ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get()); if (camera == 0) { jniThrowRuntimeException(env, "Camera is being used after Camera.release() was called"); } if (pContext != NULL) *pContext = context; return camera;}
JNICameraContext->postData 调用 JNICameraContext->copyAndPost ,从 C/CPP 层 把 camera 图像数据传输到 JAVA 层;
void JNICameraContext::postData(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata){ // VM pointer will be NULL if object is released Mutex::Autolock _l(mLock); JNIEnv *env = AndroidRuntime::getJNIEnv(); if(mCameraJObjectWeak == NULL) { ALOGW("callback on dead camera object"); return; } int32_t dataMsgType = msgType & ~CAMERA_MSG_PREVIEW_METADATA; // return data based on callback type switch(dataMsgType) { case CAMERA_MSG_VIDEO_FRAME: // should never happen break; // For backward-compatibility purpose, if there is no callback // buffer for raw image, the callback returns null. case CAMERA_MSG_RAW_IMAGE: ALOGV("rawCallback"); if(mRawImageCallbackBuffers.isEmpty()) { env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, dataMsgType, 0, 0, NULL); // CPP 调用 JAVA 方法实现跨语言传递数据 } else { copyAndPost(env, dataPtr, dataMsgType); // 回调数据 } break; // There is no data. case 0: break; default: ALOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get()); copyAndPost(env, dataPtr, dataMsgType); break; } // post frame metadata to Java if(metadata && (msgType & CAMERA_MSG_PREVIEW_METADATA)) { postMetadata(env, CAMERA_MSG_PREVIEW_METADATA, metadata); }}
copyAndPost 函数调用 Java 方法把把数传输到 APP 层:
void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType){ jbyteArray obj = NULL; // allocate Java byte array and copy data// Java 分配数组和复制数据 if(dataPtr != NULL) { ssize_t offset; size_t size; sp heap = dataPtr->getMemory(&offset, &size); ALOGV("copyAndPost: off=%zd, size=%zu", offset, size); uint8_t *heapBase = (uint8_t*)heap->base(); if(heapBase != NULL) { const jbyte* data = reinterpret_cast(heapBase + offset); if(msgType == CAMERA_MSG_RAW_IMAGE) { obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size); } else if(msgType == CAMERA_MSG_PREVIEW_FRAME && mManualBufferMode) { obj = getCallbackBuffer(env, &mCallbackBuffers, size); if(mCallbackBuffers.isEmpty()) { ALOGV("Out of buffers, clearing callback!"); mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP); mManualCameraCallbackSet = false; if(obj == NULL) { return; } } } else { ALOGV("Allocating callback buffer"); obj = env->NewByteArray(size); } if(obj == NULL) { ALOGE("Couldn't allocate byte array for JPEG data"); env->ExceptionClear(); } else { env->SetByteArrayRegion(obj, 0, size, data); } } else { ALOGE("image heap is NULL"); } } // post image data to Java// 调用 JAVA 方法,把数据传递到 java 层 env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, obj); if(obj) { env->DeleteLocalRef(obj); }}
APP 通过 JNI 接口 → C/CPP 本地方法打开相机设备 → 设置相机设备参数 → 获取相机的图像数据 → C/CPP 调用 JAVA 方法把图像数据发送到 APP 。
更多相关文章
- 解决Android视频播放横竖屏切换播放问题!
- Android(安卓)官方架构组件(一)——Lifecycle
- Android基于Http协议实现文件上传功能的方法
- 倒计时效果
- SDK is not loaded yet解决方法
- 【Android】原生Progress提示
- EditText焦点自动带出软键盘问题
- Android(安卓)面试题总结之View(一)
- django返回json的几种方法以及android调用