ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-05-27 23:30:44  阅读:523  来源: 互联网

标签:status IPC CameraPovider hidl CameraMetadata camera 传递数据 metadata size


在学习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;
   ....
}

类图如下:
在这里插入图片描述
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->processCaptureRequest(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 ↩︎

标签:status,IPC,CameraPovider,hidl,CameraMetadata,camera,传递数据,metadata,size
来源: https://blog.csdn.net/m0_54238665/article/details/117340339

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有