当前位置: 首页 > news >正文

基于FFMPEG读取摄像头图像编码为h264

1.调用ffmpeg命令采集摄像头图像

$ ffmpeg -f v4l2 -framerate 30 -video_size 1280*720 -i /dev/video0 -c:v libx264 -preset veryfast -f h264 output.h264

  -f v4l2: 指定输入设备采用Video4Linux2框架。
  -framerate 30: 设置帧率为30。
  -video_size 1280720: 设置视频分辨率为1280720
  -i /dev/video0: 指定输入设备文件路径。
  -c:v libx264: 指定使用H.264编码。
  -preset veryfast: 选择快速编码预设。
  -f h264: 输出格式为H.264帧。
  Output.h264: 输出文件。

2 调用ffmpeg库实现摄像头采集并编码为h264

  • ffmpeg 采集摄像头图像,编码为H264格式步骤:

  1.注册设备avdevice_register_all();
  2.查找摄像头框架格式av_find_input_format(“video4Linux2”);
  3.设置摄像头参数options:图像尺寸(video_size)、帧率(framerate)、图像格式(input_format),av_dict_set();
  4.打开输入文件,获取输入上下文指针avformat_open_input();
  5.获取摄像头图像流信息avformat_find_stream_info;
  6.查找摄像头中的视频流av_find_best_stream;
  7.根据编码格式,获取解码器avcodec_find_decoder_by_name(“libx264”);
  8.分配编码器上下文指针avcodec_alloc_context3();
  9.设置图像编码参数:图像尺寸、帧率framebate、time_base、gop_size、pix_fmt,将编码器关联到AVDocodecCotext指针;
  10.创建输出文件fopen
  11.创建视频帧av_frame_alloc();
  12.设置frame参数:宽度、高度、图像格式;
  13.为frame中data和buf分配空间:av_frame_get_buffer();
  14.分配packet包,用于存放h264编码后的数据;
  15.从摄像头中读取采集的数据av_read_frame();
  17.判断是否为视频流,将packet中的yuv422数据转换为yuv420p格式,并保存到frame中;
  18.将frame中的流数据进行h264格式编码encodec_video();

  • 编码流程图如下:

在这里插入图片描述
示例代码:

#include <stdio.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavutil/imgutils.h>
#include <unistd.h>
#include <signal.h>
#define VIDEO_DEV "/dev/video0"
static int video_width;
static int video_height;
int camera_flag=0;
void YUYV422_toYuv420p(AVFrame *frame,AVPacket *pkt){
/*yuv422 存储格式为 y      u y v y u y v y u y v y u y vyuv422 每两个y公用一组UV分量,yuyv(yuv422)一个像素大小:y+1/2(u)+1/2(v)=2byteyuv420p  存储最简单,先存所以的y,再存u,最后v,yuv420p 每4个Y共用一组UV分量所以先把422所有的y存在一起,再提奇数行的u  ,偶数行舍弃。提完u后,再提v,v也是偶数行不提取。
*/int i = 0;int yuv422_length=video_width*video_height*2;//yuv422图像大小int y_index = 0;// 取出Y分量数据for (i = 0; i < yuv422_length; i += 2) {frame->data[0][y_index] = pkt->data[i];y_index++;}// copy u and vint line_start = 0;int is_u = 1;int u_index = 0;int v_index = 0;// copy u, v per line. skip a line oncefor (i = 0; i < video_height; i += 2) {line_start = i * (video_width<<1);//每一行的起始位置,相当于:video_width*2for (int j = line_start + 1; j < line_start + (video_width<<1); j += 4){frame->data[1][u_index++]=pkt->data[j];frame->data[2][v_index++]=pkt->data[j+2];}}	
}
//编码视频格式
int encodec_video(FILE *fp,AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt){int ret=0;//将数据帧传入编码器进行编码,该函数仅编码数据,并不会写入ret=avcodec_send_frame(ctx,frame);if(ret){av_log(ctx,AV_LOG_ERROR,"编码视频帧失败ret=%s\n",av_err2str(ret));return -1;}//从编码器中读取编码好的数据帧while((ret=avcodec_receive_packet(ctx,pkt))>=0){if(ret==AVERROR(EAGAIN) || ret==AVERROR_EOF)//数据帧不可用或者没有新的数据帧{av_packet_unref(pkt);//减少引用次数break;}else if(ret==AVERROR(EINVAL)){//没有正确打开编码器av_packet_unref(pkt);//减少引用次数return -1;}//将编码好的数据写入到文件fwrite(pkt->data,pkt->size,1,fp);av_packet_unref(pkt);//减少引用次数}return 0;
}//采集摄像头数据,将摄像头数据进行h264编码
//摄像头初始化
void *video_CollectImage(void *arg)
{//1.注册设备avdevice_register_all();const AVInputFormat *ifmt=NULL;//输入格式AVFormatContext *pfmtctx=NULL;//输入上下文AVDictionary *options=NULL;//其它参数const AVCodec *ocodec=NULL;AVCodecContext *icodecCtx=NULL;//解码器上下文指针AVPacket *opkt=NULL;AVFrame *iframe=NULL;FILE *fp=NULL;int ret=0;int idx=-1;//视频流下标//2.查找输入格式ifmt=av_find_input_format("video4linux2");if(ifmt==NULL){av_log(NULL,AV_LOG_ERROR,"video4linux2格式信息获取失败\n");return (void *)-1;}av_dict_set(&options,"video_size","1280*720",0);//设置图像大小av_dict_set(&options,"framerate","30",0);//帧率av_dict_set(&options,"input_format","yuv420p",0);//图像格式ret=avformat_open_input(&pfmtctx,VIDEO_DEV,ifmt,&options);if(ret<0){av_log(NULL,AV_LOG_ERROR,"打开输入文件,设置输入上下文指针失败,ret=%s\n",av_err2str(ret));return 0;}//通过读取数据包,获取流信息avformat_find_stream_info(pfmtctx,NULL);av_dump_format(pfmtctx, 0, VIDEO_DEV, 0);//3.寻找视频流idx=av_find_best_stream(pfmtctx,AVMEDIA_TYPE_VIDEO, -1,-1,NULL, 0);if(idx<0){av_log(&pfmtctx,AV_LOG_ERROR,"获取视频流失败ret=%s\n",av_err2str(idx));goto _fil;}av_log(pfmtctx,AV_LOG_INFO,"idx=%d\n",idx);video_width=pfmtctx->streams[idx]->codecpar->width;video_height=pfmtctx->streams[idx]->codecpar->height;//1.根据名字获取注册的编码器ocodec=avcodec_find_encoder_by_name("libx264");if(!ocodec){av_log(NULL, AV_LOG_ERROR, "libx264 获取编码器失败\n");goto _fil;}av_log(pfmtctx,AV_LOG_INFO,"libx264格式:%d\n",ocodec->id);//5.分配AVCodecContext上下文指针icodecCtx=avcodec_alloc_context3(ocodec);if(icodecCtx==NULL){av_log(pfmtctx,AV_LOG_ERROR,"分配上下文指针失败\n");goto _fil;}//设置图像尺寸icodecCtx->width=video_width;icodecCtx->height=video_height;icodecCtx->bit_rate=1500000;//码率icodecCtx->time_base=(AVRational){1,25};//时间基准icodecCtx->framerate=(AVRational){25,1};//帧率icodecCtx->gop_size=10;//一组图像的是数量icodecCtx->max_b_frames=2;//B帧数量icodecCtx->pix_fmt=AV_PIX_FMT_YUV420P;//图像格式if(ocodec->id==AV_CODEC_ID_H264)//编码流格式{/*设置私有属性信息int av_opt_set(void *obj, const char *name, const char *val, int search_flags);obj: 需要设置选项的对象。name: 要设置的选项名称。val: 设置的选项值。search_flags: 搜索标志,通常为0。*/av_opt_set(icodecCtx->priv_data,"preset","slow", 0);}//关联编码器上下文ret=avcodec_open2(icodecCtx,ocodec, NULL);if(ret<0){av_log(icodecCtx,AV_LOG_ERROR,"关联编码器上下文件指针失败ret=%s\n",av_err2str(ret));goto _fil;}fp=fopen("camera.h264","w+b");if(fp==NULL){av_log(icodecCtx,AV_LOG_ERROR,"文件创建失败\n");goto _fil;}//创建视频帧iframe=av_frame_alloc();if(iframe==NULL){av_log(icodecCtx,AV_LOG_ERROR,"创建视频帧frame失败\n");goto _fil;}iframe->width=video_width;iframe->height=video_height;iframe->format=icodecCtx->pix_fmt;ret=av_frame_get_buffer(iframe, 0);if(ret<0){av_log(icodecCtx,AV_LOG_ERROR,"分别frame buffer缓冲区失败,ret=%s\n",av_err2str(ret));goto _fil;}//7.创建数据包AVPacket ipkt;opkt=av_packet_alloc();if(!opkt){av_log(icodecCtx,AV_LOG_ERROR,"分配packet失败\n");goto _fil;}int i=0;av_log(NULL,AV_LOG_INFO,"开始读取数据包\n");camera_flag=1;//读取数据包while(av_read_frame(pfmtctx, &ipkt)>=0 && camera_flag==1){if(ipkt.stream_index == idx)//判断是否为视频帧{av_log(pfmtctx,AV_LOG_INFO,"pts=%ld\n",ipkt.pts);YUYV422_toYuv420p(iframe,&ipkt);//格式转换iframe->pts=av_rescale_q_rnd(ipkt.dts,pfmtctx->streams[idx]->time_base ,icodecCtx->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);av_log(pfmtctx,AV_LOG_INFO,"pts:%ld\n",iframe->pts);encodec_video(fp,icodecCtx,iframe,opkt);if(ret<0){goto _fil;}}av_packet_unref(&ipkt);//减少引用次数}encodec_video(fp,icodecCtx,iframe,opkt);fclose(fp);av_log(pfmtctx,AV_LOG_INFO,"数据采集完成\n");
_fil:if(pfmtctx){avformat_close_input(&pfmtctx);//释放上下文指针pfmtctx=NULL;}av_log(NULL,AV_LOG_INFO,"上下文指针释放成功\n");avcodec_free_context(&icodecCtx);av_frame_free(&iframe);av_packet_free(&opkt);}
void sig_work(int sig)
{if(sig==SIGINT){camera_flag=0;}
}
int main(int argc,char **argv)
{signal(SIGINT,sig_work);av_log_set_level(AV_LOG_DEBUG);video_CollectImage(NULL);
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Selenium自动化测试 常见API的使用
  • vue中的监听器(watch,watchEffect)和计算属性(computed)
  • 95.SAP MII功能详解(08)Workbench-Transaction介绍
  • MySQL:简述事务的SQL操作
  • 48.x86游戏实战-封包抓取进图call
  • 数据结构之邻接表
  • PTA - C语言国庆题集1
  • 【Python机器学习】NLP分词——利用分词器构建词汇表(一)
  • 题解:UVA1590 IP网络 IP Networks
  • iframe中postmessage重复返回多次相同信息
  • STM32G474的HAL_ADC_Start_DMA()传递指针问题
  • JS中this的指向问题、JS的执行机制、offset、client、scroll
  • 面试题:遍历三颗相连的满二叉树
  • OpenCV(第二关--读取图片和摄像头)实例+代码
  • 探索贪心算法:解决优化问题的高效策略
  • 10个确保微服务与容器安全的最佳实践
  • Android组件 - 收藏集 - 掘金
  • Django 博客开发教程 16 - 统计文章阅读量
  • GraphQL学习过程应该是这样的
  • Java 23种设计模式 之单例模式 7种实现方式
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • JS+CSS实现数字滚动
  • React as a UI Runtime(五、列表)
  • v-if和v-for连用出现的问题
  • 解析 Webpack中import、require、按需加载的执行过程
  • 配置 PM2 实现代码自动发布
  • 前端工程化(Gulp、Webpack)-webpack
  • 前端设计模式
  • 用Visual Studio开发以太坊智能合约
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 追踪解析 FutureTask 源码
  • zabbix3.2监控linux磁盘IO
  • 积累各种好的链接
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​人工智能书单(数学基础篇)
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #laravel 通过手动安装依赖PHPExcel#
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (3)llvm ir转换过程
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)php新闻发布平台 毕业设计 141646
  • (七)Knockout 创建自定义绑定
  • (七)理解angular中的module和injector,即依赖注入
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (三十)Flask之wtforms库【剖析源码上篇】
  • (四) 虚拟摄像头vivi体验
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)可以带来幸福的一本书
  • .JPG图片,各种压缩率下的文件尺寸
  • .Net Memory Profiler的使用举例
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET+WPF 桌面快速启动工具 GeekDesk
  • .Net组件程序设计之线程、并发管理(一)