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

2024-08-28 19:28

本文主要是介绍基于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);
}

这篇关于基于FFMPEG读取摄像头图像编码为h264的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1115670

相关文章

java对接海康摄像头的完整步骤记录

《java对接海康摄像头的完整步骤记录》在Java中调用海康威视摄像头通常需要使用海康威视提供的SDK,下面这篇文章主要给大家介绍了关于java对接海康摄像头的完整步骤,文中通过代码介绍的非常详细,需... 目录一、开发环境准备二、实现Java调用设备接口(一)加载动态链接库(二)结构体、接口重定义1.类型

SpringBoot读取ZooKeeper(ZK)属性的方法实现

《SpringBoot读取ZooKeeper(ZK)属性的方法实现》本文主要介绍了SpringBoot读取ZooKeeper(ZK)属性的方法实现,强调使用@ConfigurationProperti... 目录1. 在配置文件中定义 ZK 属性application.propertiesapplicati

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

C/C++和OpenCV实现调用摄像头

《C/C++和OpenCV实现调用摄像头》本文主要介绍了C/C++和OpenCV实现调用摄像头,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录准备工作1. 打开摄像头2. 读取视频帧3. 显示视频帧4. 释放资源5. 获取和设置摄像头属性

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

基于Python实现读取嵌套压缩包下文件的方法

《基于Python实现读取嵌套压缩包下文件的方法》工作中遇到的问题,需要用Python实现嵌套压缩包下文件读取,本文给大家介绍了详细的解决方法,并有相关的代码示例供大家参考,需要的朋友可以参考下... 目录思路完整代码代码优化思路打开外层zip压缩包并遍历文件:使用with zipfile.ZipFil