camera摄像头采集数据帧中yuv420的planer的理解一
最近使用高通的8053的摄像头采集数据并编码成264格式。高通的官方例子给的是用回调函数处理一帧数据的。
//回调函数当有一帧数据采集回来后会进入此函数
static void mm_app_video_notify_cb(mm_camera_super_buf_t *bufs,
void *user_data)
{
char file_name[64];
mm_camera_buf_def_t *frame = bufs->bufs[0];
mm_camera_test_obj_t *pme = (mm_camera_test_obj_t *)user_data;
printf("mm_app_video_notify_cb \n");
printf("BEGIN - length=%zu, frame idx = %d\n",frame->frame_len, frame->frame_idx);
snprintf(file_name, sizeof(file_name), "V_C%d", pme->cam->camera_handle);
mm_app_dump_video(frame, file_name, "yuv", frame->frame_idx);//转存成yuv数据
encode_oneFrame(frame, file_name, "yuv", frame->frame_idx);//编码成一帧264格式的数据并保存
if (MM_CAMERA_OK != pme->cam->ops->qbuf(bufs->camera_handle,
bufs->ch_id,
frame)) {
LOGE("Failed in Preview Qbuf\n");
}
mm_app_cache_ops((mm_camera_app_meminfo_t *)frame->mem_info,
ION_IOC_INV_CACHES);
LOGD("END\n");
}
默认的使用是YUV 420的
#define DEFAULT_PREVIEW_FORMAT CAM_FORMAT_YUV_420_NV21
#define DEFAULT_PREVIEW_WIDTH 1280//预览视频大小
#define DEFAULT_PREVIEW_HEIGHT 960
#define DEFAULT_PREVIEW_PADDING CAM_PAD_TO_WORD
#define DEFAULT_VIDEO_FORMAT CAM_FORMAT_YUV_420_NV12
参考了很多H264的编码方案
1 直接调用X264的函数编码
2 使用ffmpeg和x264进行编码
3 使用openmx编码
4 ffmpeg和openmx进行编码
目前研究了第一种,但发现x264只能编码标准的yuv420的格式。
这里遇到的问题是关于yuv plane方式的理解。
Qualcomm里边的是有两个plane
for (i = 0; i < frame->planes_buf.num_planes; i++) {
printf("saving file from address: %p, data offset: %d, length: %d \n", frame->buffer,
frame->planes_buf.planes[i].data_offset, frame->planes_buf.planes[i].length);
write(file_fd,(uint8_t *)frame->buffer + offset,frame->planes_buf.planes[i].length);
offset += (int)frame->planes_buf.planes[i].length;
}
然后连续两次将plane的数据写入yuv文件,
调用x264进行编码的时候发现,不知如何提取分量了。
在x264给出的例程中
case X264_CSP_I420:{
fread(pPic_in->img.plane[0],y_size,1,fp_src); //Y
fread(pPic_in->img.plane[1],y_size/4,1,fp_src); //U
fread(pPic_in->img.plane[2],y_size/4,1,fp_src); //V
break;}
从文件里边读出来的这里是通过fread读取的,fread会自动将指针后移,如果重新从头开始读取的话需要加fseek至0处。
然后查看关于yuv420 planed的相关文章,总结如下:
一、YUV格式分为两大类:planar(平面)和packed(打包)。planar格式,先连续存储所有像素点的Y分量,紧接着存储所有像素点的U,随后存储所有像素点的V。packed格式,每个像素点的Y、U、V分量是连续存储的。
重点强调:平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量, 最后存储所有的V(Cr)分量。
NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00。
YUV420 planar数据存储, 以720×488大小图象YUV420 planar为例,
其存储格式是: 共大小为(720×480×3>>1)字节,
分为三个部分: Y分量: (720×480)个字节 U(Cb)分量: (720×480>>2)个字节 V(Cr)分量: (720×480>>2)个字节
三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储。
即YUV数据的0--720×480字节是Y分量值, 720×480--720×480×5/4字节是U分量 720×480×5/4 --720×480×3/2字节是V分量。
NV12、NV21(属于YUV420)如下图
那么根据上面x264的demo和高通回调函数数据的写入,应该这么处理
memcpy(pPic_in->img.plane[0],(uint8_t *)frame->buffer ,w*h);//就shi Y 分量
memcpy(pPic_in->img.plane[1],(uint8_t *)frame->buffer+w*h ,w*h/4);
memcpy(pPic_in->img.plane[2],(uint8_t *)frame->buffer+w*h +w*h/4 ,w*h/4);
通过比较左边的是用demo生成的,右边的是改动后的。画质比较接近
但很显然这是不正确的。因为从camera拿到的数据是nv21的,这里不管是x264的demo还是我的改动都是按yuvi420处理的,所以灰白色没有颜色。