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生成的,右边的是改动后的。画质比较接近
   
 微信截图_20190823171558.png

但很显然这是不正确的。因为从camera拿到的数据是nv21的,这里不管是x264的demo还是我的改动都是按yuvi420处理的,所以灰白色没有颜色。


sitemap