摄像头V4L2获取的YUY2格式转YUV420格式
摄像头取出格式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;
}