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<Camera>(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 service
static 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<const char16_t*>(
                                        env->GetStringChars-(clientPackageName, NULL));
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName,
                            reinterpret_cast<const jchar*>(rawClientName));
 
    sp<Camera> 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<JNICameraContext> 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<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata) = 0;
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& 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<Camera>
{
    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<Camera>,
    public ::android::hardware::BnCameraClient
{
    ......
}

 

JNICameraContext  实现虚函数 CameraListener->postData 。

virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata);

然后 camera 类中注册,并调用  JNICameraContext->postData 实现 camera 图像数据传输到 Java 层。

/* 注册 CameraListener 接口监听 */
void Camera::setListener(const sp<CameraListener>& listener)
{
    Mutex::Autolock _l(mLock);
    mListener = listener;
}


/* 帧或图像就绪时,从相机服务中回调图像数据 */
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata)
{
    sp<CameraListener> 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>& camera);  // 构造 context 上下文
    ~JNICameraContext() { release(); }
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata);                                 // 虚函数实现获取 camera 数据
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& 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<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
    bool isRawImageCallbackBufferAvailable() const;
    void release();

private:
    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);   // 本地回调 camera 图像数据
    void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers);
    void clearCallbackBuffers_l(JNIEnv *env);
    jbyteArray getCallbackBuffer(JNIEnv *env, Vector<jbyteArray> *buffers, size_t bufferSize);

    jobject     mCameraJObjectWeak;     // weak reference to java object, Java对象的弱引用
    jclass      mCameraJClass;          // strong reference to java class, 对Java类的强引用
    sp<Camera>  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<jbyteArray> mRawImageCallbackBuffers;

    /*
     * Application-managed preview buffer queue and the flags
     * associated with the usage of the preview buffer callback.
	 * 应用程序管理的预览缓冲区队列以及与预览缓冲区回调的使用相关联的标志。
     */
    Vector<jbyteArray> 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>& 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<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    // fields.context 是在 android_hardware_Camera_native_setup 里保存 context 类,
    // 使用 reinterpret_cast<>  获取 context 的指针
    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(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<IMemory>& 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<IMemory>& dataPtr, int msgType)
{
    jbyteArray obj = NULL;

    // allocate Java byte array and copy data
	// Java 分配数组和复制数据
    if(dataPtr != NULL)
    {
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> 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<const jbyte*>(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 。

 

 

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值