烦
V1
2022/08/28阅读:66主题:橙心
linux下 使用FFmpeg 采集摄像头YUV数据

从一个多媒体文件提取YUV数据
a)FFmpeg命令行
要得到原始YUV数据,需要解码操作,也就是编码的反向操作。
ffmpeg -i ./test.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv
-i:要提取的多媒体文件
-an:audio not 过滤掉音频,只要视频数据
-c:v:选择解码器
-pix_fmt:输出YUV数据格式

比对文件大小,观察下解码后的原始数据大小。

多媒体文件大小是1.8M,解码得到的YUV原始数据是538M,为什么会相差那么多?因为得到MP4多媒体文件,引用了H264压缩技术。可以想象到H264编码能力十分强。关于H264知识,我下期会介绍。
b)ffplay播放YUV文件。
在播放YUV文件时,我们要知道这个视频的分辨率以及YUV格式。
查看多媒体文件的大小
ffprobe ./test.mp4

分辨率是1280x720,YUV格式是yuv420p
播放YUV
ffplay -pix_fmt yuv420p -s 1280x720 out.yuv

2、C语言实现从外部设备中获取到YUV数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <libavutil/avutil.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#define WIDTH 640
#define HEIGHT 480
/*
*/
static AVFormatContext* open_dev()
{
int ret = 0;
char errors[1024] = {0,};
char *devicename = "/dev/video0"; //0机子的摄像头
//ctx
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
avdevice_register_all();
//get format
AVInputFormat *iformat = av_find_input_format("video4linux2");
//option 视频需要参数
char video_size[10];
sprintf(video_size, "%dx%d", WIDTH, HEIGHT);
av_dict_set(&options,"video_size",video_size,0);
//av_dict_set(&options,"pixel_format","yuyv422",0);
if((ret = avformat_open_input(&fmt_ctx,devicename,iformat,&options)) < 0)
{
av_strerror(ret,errors,1024);
fprintf(stderr,"Failed to open video device ,[%d]%s\n",ret,errors);
return NULL;
}
return fmt_ctx;
}
void rec_video(){
int ret = 0;
int flag=-1;
char input_command[128];
AVFormatContext *fmt_ctx = NULL;
AVPacket pkt;//数据存放
flag=fcntl(0,F_GETFL); //获取当前flag
av_log_set_level(AV_LOG_DEBUG);
char *out = "./out.yuv";
FILE *outfile = fopen(out,"wb+");
//打开设备
fmt_ctx = open_dev();
flag |=O_NONBLOCK; //设置新falg
fcntl(0,F_SETFL,flag); //更新flag
av_init_packet(&pkt);//数据初始化 干净的空间;
while((ret = av_read_frame(fmt_ctx,&pkt)) == 0)
{
av_log(NULL,AV_LOG_DEBUG,
"packet size is %d(%p)\n",
pkt.size,pkt.data);
fwrite(pkt.data,1,pkt.size,outfile);
fflush(outfile);
av_packet_unref(&pkt);
if((ret=read(0,input_command,sizeof(input_command))) > 0)
{
if(strncmp(input_command, "over",2) == 0)
{
av_log(NULL,AV_LOG_DEBUG,"over\n");
break;
}
else
{
av_log(NULL,AV_LOG_DEBUG,"请重新输入\n");
}
memset(input_command, 0, sizeof(input_command));
}
}
fclose(outfile);
avformat_close_input(&fmt_ctx);
av_log(NULL, AV_LOG_DEBUG,"finish\n");
}
int main(int argc, char** argv)
{
rec_video();
return 0;
}
gcc编译:
gcc av_video.c -I/usr/ffmpeg4.1/include -L/usr/ffmpeg4.1/lib -o av_video -lavutil -lavdevice -lavformat -lavcodec
a)执行av_video文件

此时摄像头录像,获取YUV数据,程序默认使用yuv420p格式采集,从提示可以看到,最后采用yuyv422格式采集yuv数据。因为摄像头输出的yuv数据格式是yuyv422格式,怎样修改程序默认采集YUV格式? 取消av_dict_set(&options,"pixel_format","yuyv422",0);注释,指定YUV数据输入格式。
b)文件播放
yuv420p格式播放
ffplay -pix_fmt yuv420p -s 640x480 ./out.yuv

因为文件格式是YUYV422,与FFplay制定的播放格式不一致,导致显示效果有差异。
yuyv422格式播放
ffplay -pix_fmt yuyv422 -s 640x480 ./out.yuv
此时显示就正常了啦。

想了解更多,可以关注 小昭debug 公众号,交个朋友,交流下。

作者介绍
烦
V1