摄像头取出格式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;
}
整个转换的核心是隔行采样 UV 数据, 而不是间隔1个UV单位采样。