在学习Android 0 Camera 子系统时,必然会涉及相机设置参数的IPC跨进程传递,其中会涉及两种CameraMetadata类对象
- CameraService中定义的CameraMetadata
- 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
总结下:
- 从定义上,CameraService CameraMetadata其实就是对camera_metadata的封装;CameraPovider CameraMetadata其实就是hidl_vec<uint8_t> 。
- framework CameraMetadata继承了Parcelable,能做直接跨进程传递,其通过借助Ashmem,其IPC 跨进程传递数据量非常大。
- 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;
}
}
至此分析完成了本文的所有分析。
-
camera_metadata_t定义位typedef struct camera_metadata camera_metadata_t;camera_metadata的定义在/system/media/camera/include/system/camera_metadata.h ↩︎
-
由HIDL文件编译生成,HIDL定义如下:
//hardware\interfaces\camera\device\3.2\types.hal
typedef vec<uint8_t> CameraMetadata; ↩︎ -
通过上边的分析,可以看出要想跨进程传递几兆级别的数据,只要封装到实现了Parcelable writeToParcel和readFromParcel的类中,然后借助WritableBlob(其实就是借助匿名共享内存)就可以实现。 ↩︎
-
HIDL专门设计了一套自己的parcel,位置在system\libhwbinder\Parcel.cpp ↩︎
-
定义在system\libhwbinder\ProcessState.cpp ↩︎