FFmpeg采集摄像头数据
文章目录
前言
博主对音视频蛮感兴趣的,学习了一段时间的FFmpeg。前几天,在逛博客的时候发现在Linux下使用FFmpeg采集摄像头的数据,由于这采集摄像头的数据没写过代码,于是复现了下人家的代码,是一个比较简单的demo程序,仅供参考。
一、查看Linux系统下的摄像头设备
前排提示:在看此博客的之前,需要在自己的Ubuntu环境中安装FFmpeg,若没安装FFmpeg,可以在网上搜教程(网上的教程还是蛮详细的)。
在Linux系统中,使用的是v4l2框架来驱动摄像头设备的。
使用FFmpeg的ffprobe命令来查看连接到Ubuntu系统中的摄像头设备,如下图。
从图中可以看到,所使用摄像头的参数,从图中可以看出摄像头输出的原始参数是yuyv422的形式,一般我们使用的是yuv420p的数据,因此在保存摄像头数据的时候,先对yuyu422的格式进行变化,将其变换为yuv420p的格式,然后再进行编码保存为h264的文件。
二、代码
1.在main函数中,所需要用到的参数的声明
int ret = 0;
// 注册所有的设备
avdevice_register_all();
// 输入设备的相关参数
AVFormatContext *inFmtCtx = avformat_alloc_context();
AVCodec *inCodec = NULL;
AVCodecContext *inCodecCtx = NULL;
int inVideoSteamIndex = -1;
struct SwsContext *img_ctx = NULL;
AVFrame *yuvFrame = NULL;
AVFrame *srcFrame = NULL;
AVPacket *inPkt = av_packet_alloc();
// 输出文件的相关参数
AVFormatContext *outFmtCtx = avformat_alloc_context();
AVOutputFormat *outFmt = NULL;
AVStream *outStream = NULL;
AVCodecContext *outCodecCtx=NULL;
AVCodec *outCodec = NULL;
AVPacket *outPkt = av_packet_alloc();
2.解码摄像头原始参数设置
// 解码部分
// 打开v4l2的相机输入
AVInputFormat *inFmt = av_find_input_format("v4l2");
if(avformat_open_input(&inFmtCtx,"/dev/video0",inFmt,NULL) < 0){
fprintf(stderr,"Cannot open camera.\n");
return -1;
}
// 查找流
if(avformat_find_stream_info(inFmtCtx,NULL) < 0){
fprintf(stderr,"Cannot find any stream in file.\n");
return -1;
}
// 寻找视频流
for(size_t i = 0;i < inFmtCtx->nb_streams;i++){
if(inFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
inVideoSteamIndex = i;
break;
}
}
// 没找到视频流
if(inVideoSteamIndex == -1){
fprintf(stderr,"Cannot find video stream in file.\n");
return -1;
}
// 创建解码器的参数集
AVCodecParameters* inVideoCodecPara = inFmtCtx->streams[inVideoSteamIndex]->codecpar;
// 查找解码器
if(!(inCodec = avcodec_find_decoder(inVideoCodecPara->codec_id))){
fprintf(stderr,"Cannot find valid video decoder.\n");
return -1;
}
if(!(inCodecCtx = avcodec_alloc_context3(inCodec))){
fprintf(stderr,"Cannot alloc valid decode codec context.\n");
return -1;
}
if(avcodec_parameters_to_context(inCodecCtx,inVideoCodecPara) < 0){
fprintf(stderr,"Cannot initialize parameters.\n");
return -1;
}
// 打开编解码器
if(avcodec_open2(inCodecCtx,inCodec,NULL) < 0){
fprintf(stderr,"Cannot open codec.\n");
return -1;
}
img_ctx = sws_getContext(inCodecCtx->width,inCodecCtx->height,inCodecCtx->pix_fmt,
inCodecCtx->width,inCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
// 获取图像的大小
int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,inCodecCtx->width,inCodecCtx->height,1);
// 创建out_buffer缓冲区
uint8_t *out_buffer = (unsigned char *)av_malloc(num_bytes*sizeof(unsigned char));
yuvFrame = av_frame_alloc();
srcFrame = av_frame_alloc();
// 将yuvframe和out_buffer进行关联
int ret = av_image_fill_arrays(yuvFrame->data,yuvFrame->linesize,out_buffer,AV_PIX_FMT_YUV420P,inCodecCtx->width,inCodecCtx->height,1);
if(ret < 0){
fprintf(stderr,"Fill arrays failed.\n");
return -1;
}
3.输出H264文件部分
// 输出文件,编码器部分
const char* out_file = "output.h264";
if(avformat_alloc_output_context2(&outFmtCtx,NULL,NULL,out_file) < 0){
fprintf(stderr,"Cannot alloc output file context.\n");
return -1;
}
outFmt = outFmtCtx->oformat;
// 打开输出文件
if(avio_open(&outFmtCtx->pb,out_file,AVIO_FLAG_READ_WRITE) < 0){
fprintf(stderr,"output file open failed.\n");
return -1;
}
// 创建保存的H264流,并设置参数
outStream = avformat_new_stream(outFmtCtx,outCodec