摄像头V4L2获取的YUY2格式转YUV420格式

发布时间 2023-08-16 11:02:35作者: 阿风小子

摄像头取出格式YUY2(YUYV)

Y       U00    Y        V00    Y       U01    Y        V01    Y       U02    Y       V02    Y       U03    Y       V03
Y    U10    Y    V10    Y    U11    Y    V11    Y    U12    Y    V12    Y    U13    Y    V13
Y    U20    Y    V20    Y    U21    Y    V21    Y    U22    Y    V22    Y    U23    Y    V23
Y    U30    Y    V30    Y    U31    Y    V31    Y    U32    Y    V32    Y    U33    Y    V33
转为 NV12(YUV420) 

这里的转化主要是我的电脑软件pyuv 只支持yuv420格式的预览

另一方面主要是因为 ffmpeg 中进行编码时,采用的是 AV_PIX_FMT_YUV420P.

也是需要把摄像头中的YUYV转化为 ffmpeg 支持的YUV420P格式

Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y      Y  
Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y    Y
U00    U01    U02    U03    U20    U21    U22    U23    V00    V01    V02    V03    V20    V21    V22    V23
这里转换主要为需要隔1整行取出1整行的UV数据,而不是间隔一个一个取。

可以看出丢失了一部分UV数据,只要Y数据没有丢失,图像会依然保持完整呈现。

转换代码:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>

#define CAM_W 640
#define CAM_H 480

typedef struct{
    char y1;
    char u;
    char y2;
    char v;
}TAG_YUY2;

static int YUYV_TO_NV12(unsigned char yuyv_in[], unsigned char* out){
    TAG_YUY2 *p_yuy2;
    int yuy2_size = CAM_W*CAM_H;
    int i = 0;
    int j = 0;
    int total = CAM_W*CAM_H*2;
    int tag_size = CAM_W*CAM_H/2;
    p_yuy2 = (TAG_YUY2 *)malloc(total);
    memcpy(p_yuy2, yuyv_in, total);
    
    i=0;
    j=0;
    int n = 0;
    for(i=0; i<tag_size; i++){
        out[j++] = p_yuy2[i].y1;
        out[j++] = p_yuy2[i].y2;
    }
    int pos = 0;
    int x = 0;
    int y = 0;
    int len_x = CAM_W/2;
    for(y = 0; y < CAM_H; y+=2){ // 每2行取一次值 u
        for(x = 0; x < len_x; x++){
            out[j++] = p_yuy2[y*len_x + x].u;
        }
    }
    for(y = 0; y < CAM_H; y+=2){ // 每2行取一次值 v
        for(x = 0; x < len_x; x++){
            out[j++] = p_yuy2[y*len_x + x].v;
        }
    }
    free(p_yuy2);
    return (CAM_W*CAM_H*3/2);
}

int main(){
    int result;
    FILE *fp_yuyv = fopen("frame.yuv", "rb");
    FILE *fp_nv21 = fopen("nv21.yuv", "wb");
    
    unsigned char* buf_yuv = (unsigned char*)malloc(CAM_W*CAM_H*2);
    unsigned char* nv21 = (unsigned char*)malloc(CAM_W*CAM_H*2);

    fread(buf_yuv, 1, CAM_W*CAM_H*2, fp_yuyv);

    memset(nv21, 0, CAM_W*CAM_H*2);
    result = YUYV_TO_NV12(buf_yuv, nv21);

    fwrite(nv21, 1, result, fp_nv21);
    
    free(buf_yuv);
    free(nv21);
    
    fclose(fp_yuyv);
    fclose(fp_nv21);
    return 0;
}