CameraService和CameraPovider CameraMetadata的定义、IPC传递数据能力及相互转换

在学习Android 0 Camera 子系统时,必然会涉及相机设置参数的IPC跨进程传递,其中会涉及两种CameraMetadata类对象

  1. CameraService中定义的CameraMetadata
  2. CameraPovider 中定义的CameraMetadata

下边会从定义、IPC传递数据能力、相互转换三个方面介绍下这两种CameraMetadata的区别和联系

1.1) CameraServic 中CameraMetadata 的定义如下:

//frameworks\av\camera\include\camera\CameraMetadata.h
/**
 3. A convenience wrapper around the C-based camera_metadata_t library.
 */
class CameraMetadata: public Parcelable {
  public:
  ....
    virtual status_t readFromParcel(const Parcel *parcel) override;
    virtual status_t writeToParcel(Parcel *parcel) const override;
    status_t updateImpl(uint32_t tag, const void *data, size_t data_count);
    ....
  private:
    camera_metadata_t *mBuffer;
   ....
}

类图如下:
CameraService和CameraPovider CameraMetadata的定义、IPC传递数据能力及相互转换
CameraServic CameraMetadata只是对camera_metadata_t1的简单封装,并且实现了Parcelable,从而能够实现跨进程传递。

1.2)CameraPovider 中CameraMetadata2的定义如下:

//out\soong\.intermediates\hardware\interfaces\camera\device\3.2\android.hardware.camera.device@3.2_genc++_headers\gen\android\hardware\camera\device\3.2\types.h
typedef ::android::hardware::hidl_vec<uint8_t> CameraMetadata;

hidl_vec的定义如下:

template<typename T>
struct hidl_vec {

    hidl_vec(hidl_vec<T> &&other) noexcept
    : mOwnsBuffer(false) {
        *this = std::move(other);
    }
....
    // Reference an existing array, optionally taking ownership. It is the
    // caller's responsibility to ensure that the underlying memory stays
    // valid for the lifetime of this hidl_vec.
    //看备注,觉得源码很不负责任啊!!!
    void setToExternal(T *data, size_t size, bool shouldOwn = false) {
        if (mOwnsBuffer) {
            delete [] mBuffer;
        }
        mBuffer = data;
        if (size > UINT32_MAX) {
            details::logAlwaysFatal("external vector size exceeds 2^32 elements.");
        }
        mSize = static_cast<uint32_t>(size);
        mOwnsBuffer = shouldOwn;
    }

    T *data() {
        return mBuffer;
    }

    const T *data() const {
        return mBuffer;
    }
....
private:
    details::hidl_pointer<T> mBuffer;
    uint32_t mSize;
    bool mOwnsBuffer;

    // copy from an array-like object, assuming my resources are freed.
    template <typename Array>
    void copyFrom(const Array &data, size_t size) {
        mSize = static_cast<uint32_t>(size);
        mOwnsBuffer = true;
        if (mSize > 0) {
            mBuffer = new T[size];
            for (size_t i = 0; i < size; ++i) {
                mBuffer[i] = data[i];
            }
        } else {
            mBuffer = NULL;
        }
    }
}

从定义上看, CameraService中的 CameraMetadata是对camera_metadata_t 的封装,且其实现了Parcelable 接口,所以其能直接通过Binder 进行IPC跨进程传递。
CameraPovider 中的CameraMetadata是hidl_vec<uint8_t>的类型定义,其并没有直接实现Parcelable,如果想需要IPC跨进程传递数据,需要通过写入Parcel进行传递。

  • 二、CameraService CameraMetadata和CameraPovider CameraMetadata IPC传递数据能力

2.1)首先分析下CameraService CameraMetadata IPC传递数据能力。
CameraMetadata实现了其父类Parcelable的writeToParcel和readFromParcel,
下边着重介绍下framework CameraMetadata的writeToParcel方法。

代码实现为:

//frameworks\av\camera\CameraMetadata.cpp
status_t CameraMetadata::writeToParcel(Parcel *parcel) const {
    ..
    //将mBuffer中的数值保存到parcel中
    return CameraMetadata::writeToParcel(*parcel, mBuffer);
}

接续分析下CameraMetadata::writeToParcel(*parcel, mBuffer);

//frameworks\av\camera\CameraMetadata.cpp
status_t CameraMetadata::writeToParcel(Parcel& data,
                                       const camera_metadata_t* metadata) {
    status_t res = OK;

    /**
     * Below is the camera metadata parcel layout:
     *
     * |--------------------------------------------|
     * |             arg0: blobSize                 |
     * |              (length = 4)                  |
     * |--------------------------------------------|<--Skip the rest if blobSize == 0.
     * |                                            |
     * |                                            |
     * |              arg1: blob                    |
     * | (length = variable, see arg1 layout below) |
     * |                                            |
     * |                                            |
     * |--------------------------------------------|
     * |              arg2: offset                  |
     * |              (length = 4)                  |
     * |--------------------------------------------|
     */

    // arg0 = blobSize (int32)
    if (metadata == NULL) {
        // Write zero blobSize for null metadata.
        return data.writeInt32(0);
    }

    /**
     * Always make the blob size sufficiently larger, as we need put alignment
     * padding and metadata into the blob. Since we don't know the alignment
     * offset before writeBlob. Then write the metadata to aligned offset.
     */
    const size_t metadataSize = get_camera_metadata_compact_size(metadata);
    const size_t alignment = get_camera_metadata_alignment();
    const size_t blobSize = metadataSize + alignment;
    res = data.writeInt32(static_cast<int32_t>(blobSize));
    if (res != OK) {
        return res;
    }

    size_t offset = 0;
    /**
     * arg1 = metadata (blob).
     *
     * The blob size is the sum of front padding size, metadata size and back padding
     * size, which is equal to metadataSize + alignment.
     *
     * The blob layout is:
     * |------------------------------------|<----Start address of the blob (unaligned).//blob.data()
     * |           front padding            |
     * |          (size = offset)           |
     * |------------------------------------|<----Aligned start address of metadata.//metadataStart
     * |                                    |
     * |                                    |
     * |            metadata                |
     * |       (size = metadataSize)        |
     * |                                    |
     * |                                    |
     * |------------------------------------|
     * |           back padding             |
     * |     (size = alignment - offset)    |
     * |------------------------------------|<----End address of blob.
     *                                            (Blob start address + blob size).
     */
     //创建一个空的WritableBlob对象blob
    WritableBlob blob;
    do {
        //通过Parcel writeBlob 方法来初始化上一步创建的空WritableBlob对象blob
        res = data.writeBlob(blobSize, false, &blob);
        if (res != OK) {
            break;
        }
        //将起始位置对齐为alignment的整数倍
        const uintptr_t metadataStart = ALIGN_TO(blob.data(), alignment);
        //计算出对齐后的起始位置与原起始位置的差
        offset = metadataStart - reinterpret_cast<uintptr_t>(blob.data());
        ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu",
                __FUNCTION__, alignment,
                reinterpret_cast<const void *>(metadataStart), offset);
        //将数据复制到blob中
        copy_camera_metadata(reinterpret_cast<void*>(metadataStart), metadataSize, metadata);

        // Not too big of a problem since receiving side does hard validation
        // Don't check the size since the compact size could be larger
        //检查metadata有效性,我觉得应该早点检查
        if (validate_camera_metadata_structure(metadata, /*size*/NULL) != OK) {
            ALOGW("%s: Failed to validate metadata %p before writing blob",
                   __FUNCTION__, metadata);
        }

    } while(false);
    blob.release();

    // arg2 = offset (int32)
    res = data.writeInt32(static_cast<int32_t>(offset));

    return res;
}

接着分析下data.writeBlob(blobSize, false, &blob)
代码为:

//frameworks\native\libs\binder\Parcel.cpp
status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{   //很大,7FFFFFFF
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    status_t status;
    //不允许传递fds或len小于BLOB_INPLACE_LIMIT:16 * 1024(16K);不在本文的研究范围内
    if (!mAllowFds || len <= BLOB_INPLACE_LIMIT) {
        ALOGV("writeBlob: write in place");
        status = writeInt32(BLOB_INPLACE);
        if (status) return status;

        void* ptr = writeInplace(len);
        if (!ptr) return NO_MEMORY;

        outBlob->init(-1, ptr, len, false);
        return NO_ERROR;
    }
   //如果传递的数据为几兆,进入该处。
    ALOGV("writeBlob: write to ashmem");
    //创建了一个匿名共享内存,获得其文件句柄
    int fd = ashmem_create_region("Parcel Blob", len);
    if (fd < 0) return NO_MEMORY;
    //修改读写权限
    int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
    if (result < 0) {
        status = result;
    } else {
       //内存映射
        void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
            status = -errno;
        } else {
            if (!mutableCopy) {
                result = ashmem_set_prot_region(fd, PROT_READ);
            }
            if (result < 0) {
                status = result;
            } else {
                status = writeInt32(mutableCopy ? BLOB_ASHMEM_MUTABLE : BLOB_ASHMEM_IMMUTABLE);
                if (!status) {
                    status = writeFileDescriptor(fd, true /*takeOwnership*/);
                    if (!status) {
                        //初始话outBlob
                        outBlob->init(fd, ptr, len, mutableCopy);
                        return NO_ERROR;
                    }
                }
            }
        }
        ::munmap(ptr, len);
    }
    ::close(fd);
    return status;
}

通过上面的分析,可以得出的结论是 CameraService CameraMetadata通过借助ashmen可以实现跨进程传递很大的数据3

2.2)接着分析下CameraPovider CameraMetadata IPC传递数据能力

通过上边的分析,CameraPovider CameraMetadata其实就是hidl_vec<uint8_t>,其并没有直接实现Parcelable ,在进行跨进程IPC传递数据时,需要将其写入Parcel4 的方式是(以getCameraCharacteristics为例)

代码为:

//CameraDeviceAll.cpp
::android::status_t BnHwCameraDevice::_hidl_getCameraCharacteristics(
        ::android::hidl::base::V1_0::BnHwBase* _hidl_this,
        const ::android::hardware::Parcel &_hidl_data,
        ::android::hardware::Parcel *_hidl_reply,
        TransactCallback _hidl_cb) {


    ::android::status_t _hidl_err = ::android::OK;



    bool _hidl_callbackCalled = false;

    static_cast<BnHwCameraDevice*>(_hidl_this)->_hidl_mImpl->getCameraCharacteristics([&](const auto &_hidl_out_status, const auto &_hidl_out_cameraCharacteristics) {

        _hidl_callbackCalled = true;

        ::android::hardware::writeToParcel(::android::hardware::Status::ok(), _hidl_reply);

        _hidl_err = _hidl_reply->writeUint32((uint32_t)_hidl_out_status);
        /* _hidl_err ignored! */

        size_t _hidl__hidl_out_cameraCharacteristics_parent;
        //此处可见,调用的是parcel的writeBuffer方法
        //这种方法IPC传递数据量是非常有限的
        //qcom 平台大致为900K左右
        _hidl_err = _hidl_reply->writeBuffer(&_hidl_out_cameraCharacteristics, sizeof(_hidl_out_cameraCharacteristics), &_hidl__hidl_out_cameraCharacteristics_parent);
        /* _hidl_err ignored! */
       ....

        _hidl_cb(*_hidl_reply);
    });

   return _hidl_err;
}

接着分析下_hidl_reply->writeBuffer(…)方法

//system\libhwbinder\Parcel.cpp
status_t Parcel::writeBuffer(const void *buffer, size_t length, size_t *handle)
{
    LOG_BUFFER("writeBuffer(%p, %zu) -> %zu",
        buffer, length, mObjectsSize);
    binder_buffer_object obj;
    obj.hdr.type = BINDER_TYPE_PTR;
    obj.buffer = reinterpret_cast<binder_uintptr_t>(buffer);
    obj.length = length;
    obj.flags = 0;
    if (handle != nullptr) {
        // We use an index into mObjects as a handle
        *handle = mObjectsSize;
    }
    return writeObject(obj);
}

接续分析下writeObject(obj)

//system\libhwbinder\Parcel.cpp
template <typename T>
status_t Parcel::writeObject(const T& val)
{   //如果传递的是几兆级别的数据enoughData不成立
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;
    if (enoughData && enoughObjects) {
restart_write:
        *reinterpret_cast<T*>(mData+mDataPos) = val;

        const binder_object_header* hdr = reinterpret_cast<binder_object_header*>(mData+mDataPos);
        switch (hdr->type) {
....
            case BINDER_TYPE_PTR: {
                const binder_buffer_object *buffer_obj = reinterpret_cast<
                    const binder_buffer_object*>(hdr);
                if ((void *)buffer_obj->buffer != nullptr) {
                  //修改mDataPos索引值
                    mObjects[mObjectsSize++] = mDataPos;
                }
                break;
            }
....
        }
        return finishWrite(sizeof(val));
    }
    //分配新的内存
    if (!enoughData) {
        const status_t err = growData(sizeof(val));
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) {
        size_t newSize = ((mObjectsSize+2)*3)/2;
        if (newSize * sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;   // overflow
        //重新分配内存
        binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize;
    }

    goto restart_write;
}

通过上边的分析:writeBuffer只是将数据写入到Parcel中的mObjects中,内存不够后,重新malloc
该方法IPC跨进程传递数据量非常有限,原因是binder在传递数据是,传递的数据上限是
define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)5

总结下:

  1. 从定义上,CameraService CameraMetadata其实就是对camera_metadata的封装;CameraPovider CameraMetadata其实就是hidl_vec<uint8_t> 。
  2. framework CameraMetadata继承了Parcelable,能做直接跨进程传递,其通过借助Ashmem,其IPC 跨进程传递数据量非常大。
  3. CameraPovider CameraMetadata 其实就是hidl_vec<uint8_t>,在进行IPC跨进程传递参数时,需要将其写入parcel,其IPC传递的数据量最多900K左右。
  • 三、CameraService CameraMetadata和CameraPovider CameraMetadata相互转换

3.1)CameraService CameraMetadata转换为CameraPovider CameraMetadata
在CameraService向CameraProvider提交申请时,需要将CameraService CameraMetadata转换为HIDL CameraMetadata
转换方法如下:

status_t Camera3Device::HalInterface::processBatchCaptureRequests(
        std::vector<camera3_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) {
....

    // Write metadata to FMQ.
    for (size_t i = 0; i < batchSize; i++) {
        camera3_capture_request_t* request = requests[i];
        device::V3_2::CaptureRequest* captureRequest = &captureRequests[i];

        if (request->settings != nullptr) {

            } else {
              //转换方法
                captureRequest->settings.setToExternal(
                        reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(request->settings)),
                        get_camera_metadata_size(request->settings));
                captureRequest->fmqSettingsSize = 0u;
            }
        } else {
            // A null request settings maps to a size-0 CameraMetadata
            captureRequest->settings.resize(0);
            captureRequest->fmqSettingsSize = 0u;
        }
    }
    auto err = mHidlSession->proce*tureRequest(captureRequests, cachesToRemove,
            [&status, &numRequestProcessed] (auto s, uint32_t n) {
                status = s;
                *numRequestProcessed = n;
            });

}

通过上边的分析,CameraService CameraMetadata转换为CameraPovider CameraMetadata调用的是hidl_vec<uint8_t>的setToExternal方法。代码如下:

//system\libhidl\base\include\hidl\HidlSupport.h
    // Reference an existing array, optionally taking ownership. It is the
    // caller's responsibility to ensure that the underlying memory stays
    // valid for the lifetime of this hidl_vec.
    void setToExternal(T *data, size_t size, bool shouldOwn = false) {
        if (mOwnsBuffer) {
            delete [] mBuffer;
        }
        mBuffer = data;
        if (size > UINT32_MAX) {
            details::logAlwaysFatal("external vector size exceeds 2^32 elements.");
        }
        mSize = static_cast<uint32_t>(size);
        mOwnsBuffer = shouldOwn;
    }

3.2)CameraPovider CameraMetadata转换为CameraService CameraMetadata
在CameraProvider向CameraProvider返回结果时,需要将CameraProviderCameraMetadata转换为CameraService CameraMetadata
转换方法如下:

//frameworks\av\services\camera\libcameraservice\device3\Camera3Device.cpp
void Camera3Device::processOneCaptureResultLocked(
        const hardware::camera::device::V3_2::CaptureResult& result) {
     //首先将hardware::camera::device::V3_2::CaptureResult类型result转换为camera3_capture_result类型r
    camera3_capture_result r;
    status_t res;
    r.frame_number = result.frameNumber;

    hardware::camera::device::V3_2::CameraMetadata resultMetadata;
    //result.fmqResultSize为0
    if (result.fmqResultSize > 0) {
      ....
    } else {
        //将result.result转换为 hardware::camera::device::V3_2::CameraMetadata类型resultMetadata
        resultMetadata.setToExternal(const_cast<uint8_t *>(result.result.data()),
                result.result.size());
    }
    //将CameraPovider CameraMetadata 转换为camera_metadata_t类型
    if (resultMetadata.size() != 0) {
        r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
        size_t expected_metadata_size = resultMetadata.size();
        if ((res = validate_camera_metadata_structure(r.result, &expected_metadata_size)) != OK) {
            ALOGE("%s: Frame %d: Invalid camera metadata received by camera service from HAL: %s (%d)",
                    __FUNCTION__, result.frameNumber, strerror(-res), res);
            return;
        }
    }

至此分析完成了本文的所有分析。


  1. camera_metadata_t定义位typedef struct camera_metadata camera_metadata_t;camera_metadata的定义在/system/media/camera/include/system/camera_metadata.h ↩︎

  2. 由HIDL文件编译生成,HIDL定义如下:
    //hardware\interfaces\camera\device\3.2\types.hal
    typedef vec<uint8_t> CameraMetadata; ↩︎

  3. 通过上边的分析,可以看出要想跨进程传递几兆级别的数据,只要封装到实现了Parcelable writeToParcel和readFromParcel的类中,然后借助WritableBlob(其实就是借助匿名共享内存)就可以实现。 ↩︎

  4. HIDL专门设计了一套自己的parcel,位置在system\libhwbinder\Parcel.cpp ↩︎

  5. 定义在system\libhwbinder\ProcessState.cpp ↩︎

上一篇:HIDL示例-C++服务创建Client验证


下一篇:D365: 笔记(VS无法打开表浏览器)