这篇文章基于高通msm8936/msm8939平台,分下面几点内容展开:
一、主要硬件模块
二、Camera 预览数据流
三、Android Camera 软件架构
四、HAL层dump Yuv
五、Jepg Encoder dump YUV
一、主要硬件模块 (Major Hardware Blocks Used )
1)Camera Control Interface (CCI)
Used for dedicated I2C for camera subsystem
2)Video Frontend (VFE4)
Image processing hardware for processing camera frame data
MSM8939 VFE can support up to 21 MP
VFE4 maximum clock is 320 MHz in Normal mode and 600 MHz in Turbo mode
3)JPEG encoder/decoder
Hardware block for JPEG encoding
JPEG encoder maximum clock is 266 MHz in Normal mode
LIBJPEG software library is used for decoding
4)Camera Postprocessor (CPP)
Supports flip/rotate, denoise, smooth/sharpen, crop, and upscale features for full-size VFE output frames
--------- 我们经常说的翻转、旋转、降噪、裁剪、按比例缩放等操作就是放在cpp中进行出来。
二、Camera 预览数据量
MDP: (支持16bit总线以上LCD) lcd总线接口类型,支持高速刷屏。
三、Sortware Architechture - Android Camera
MCT :Media Controller
四、HAL层dump Yuv
上面的文档是针对8939平台的,如果在其它平台上,可能有有点差异,具体值以实际代码为准。
我这边是在8937平台上看的调试代码:
persist.vendor.camera.dumpimg
//------ hardware/qcom/camera/QCamera2/HAL3/QCamera3Channel.cpp
void QCamera3Channel::dumpYUV(mm_camera_buf_def_t *frame, cam_dimension_t dim,
cam_frame_len_offset_t offset, uint8_t dump_type)
{
char buf[FILENAME_MAX];
memset(buf, 0, sizeof(buf));
static int counter = 0;
char prop[PROPERTY_VALUE_MAX];
property_get("persist.vendor.camera.dumpimg", prop, "0");
mYUVDump = (uint32_t)atoi(prop);
if (mYUVDump & dump_type) {
mFrmNum = ((mYUVDump & 0xffff0000) >> 16);
if (mFrmNum == 0) {
mFrmNum = 10;
}
if (mFrmNum > 256) {
mFrmNum = 256;
}
mSkipMode = ((mYUVDump & 0x0000ff00) >> 8);
if (mSkipMode == 0) {
mSkipMode = 1;
}
if (mDumpSkipCnt == 0) {
mDumpSkipCnt = 1;
}
if (mDumpSkipCnt % mSkipMode == 0) {
if (mDumpFrmCnt < mFrmNum) {
/* Note that the image dimension will be the unrotated stream dimension.
* If you feel that the image would have been rotated during reprocess
* then swap the dimensions while opening the file
* */
switch (dump_type) {
case QCAMERA_DUMP_FRM_PREVIEW:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"p_%d_%d_%dx%d.yuv",
counter, frame->frame_idx, dim.width, dim.height);
break;
case QCAMERA_DUMP_FRM_VIDEO:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"v_%d_%d_%dx%d.yuv",
counter, frame->frame_idx, dim.width, dim.height);
break;
case QCAMERA_DUMP_FRM_INPUT_JPEG:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"s_%d_%d_%dx%d.yuv",
counter, frame->frame_idx, dim.width, dim.height);
break;
case QCAMERA_DUMP_FRM_INPUT_REPROCESS:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"ir_%d_%d_%dx%d.yuv",
counter, frame->frame_idx, dim.width, dim.height);
break;
case QCAMERA_DUMP_FRM_CALLBACK:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"c_%d_%d_%dx%d.yuv",
counter, frame->frame_idx, dim.width, dim.height);
break;
case QCAMERA_DUMP_FRM_OUTPUT_JPEG:
snprintf(buf, sizeof(buf), QCAMERA_DUMP_FRM_LOCATION"j_%d_%d_%dx%d.jpg",
counter, frame->frame_idx, dim.width, dim.height);
break;
default :
LOGE("dumping not enabled for stream type %d",dump_type);
break;
}
counter++;
int file_fd = open(buf, O_RDWR | O_CREAT, 0777);
ssize_t written_len = 0;
if (file_fd >= 0) {
void *data = NULL;
fchmod(file_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if( dump_type == QCAMERA_DUMP_FRM_OUTPUT_JPEG ) {
written_len = write(file_fd, frame->buffer, frame->frame_len);
}
else {
for (uint32_t i = 0; i < offset.num_planes; i++) {
uint32_t index = offset.mp[i].offset;
if (i > 0) {
index += offset.mp[i-1].len;
}
for (int j = 0; j < offset.mp[i].height; j++) {
data = (void *)((uint8_t *)frame->buffer + index);
written_len += write(file_fd, data,
(size_t)offset.mp[i].width);
index += (uint32_t)offset.mp[i].stride;
}
}
}
LOGH("written number of bytes %ld\n", written_len);
mDumpFrmCnt++;
frame->cache_flags |= CPU_HAS_READ;
close(file_fd);
} else {
LOGE("failed to open file to dump image");
}
}
} else {
mDumpSkipCnt++;
}
}
}
1)setprop persist.vendor.camera.dumping 4
值设为 1<<2,也就是4,这个dump的是送给Jepg Encode之前的yuv数据。如果需要查看拍照前的yuv数据,可以设置这个进行dump。
2) setprop persist.vendor.camera.dumping 8
值设为 1 << 3 也就是4,可以dump callback的yuv。比如第三方app 的hdr算法,拍照的时候,请求了3张不同ev值的yuv,这时就可以把3张yuv dump出来。
下面是使用第三方app,hdr模式下拍照的dump的例子。
3)setprop persist.vendor.camera.dumping 32
值设为32,这个dump的是拍照返回的jpeg,这个是Jpeg Encoder处理后返回的,dump出来的格式自然也就是jpeg格式。
五、Jepg Encoder dump YUV
按照文档说明,在mm_jpeg.c文件中添加定义宏 MM_JPEG_DUMP_INPUT即可打开dump。
这里需要注意文件路径读写权限的问题,相应的目录权限需要修改成777,如果文件夹路径不存在,需要手动创建下。
如果需要dump处理后的jpeg数据,则可以添加定义宏 MM_JPEG_DUMP_OUT_BS
*本人从事Android Camera相关开发已有5年,
*目前在深圳上班,
*小伙伴记得点我头像,看【个人介绍】进行关注哦,希望和更多的小伙伴一起交流 ~
** 为了方便大家沟通交流,我建了个wx交流群,想加入的同学,欢迎私信我加入~*
-------- 2021.08.21 深圳 10:05