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

news/2024/9/23 7:30:04/

ffmpeg_0">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: 输出文件。

ffmpegh264_13">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);
}

http://www.ppmy.cn/news/1517456.html

相关文章

const、inline、nullptr的使用

目录 1.const引用 1.1权限的放大 1.2权限的缩小 2.inline 3.nullptr 1.const引用 可以引用一个const对象&#xff0c;但是必须用const引用。const引用也可以引用普通对象&#xff0c;因为对象的访 问权限在引用过程中可以缩小&#xff0c;但是不能放大。 不需要注意的是…

数据世界的新篇章:精通INSERT INTO数据插入艺术

标题&#xff1a;数据世界的新篇章&#xff1a;精通INSERT INTO数据插入艺术 在数据库管理的宏伟画卷中&#xff0c;INSERT INTO语句扮演着至关重要的角色。它是将新数据记录插入到表中的基石。本文将带领你深入理解INSERT INTO语句的精髓&#xff0c;并通过丰富的代码示例&am…

Node.js原生开发脚手架工具(下)

前言 在现代软件开发中&#xff0c;脚手架工具成为提高开发效率和一致性的关键利器。使用Node.js原生开发自己的脚手架工具不仅能帮助自动化常见任务&#xff0c;还能根据具体需求进行高度定制。Node.js的异步非阻塞特性和丰富的模块系统使其成为构建这种工具的理想选择。本篇文…

JavaEE-TCP协议

上篇文章介绍了TCP可靠传输主要依靠的确认应答和超时重传机制&#xff0c;超时重传是确认应答的重要补充&#xff0c;还介绍了TCP的连接管理机制。本篇文章补充上一篇文章的TCP十个常用核心机制的其他七个。 目录 滑动窗口 窗口大小 流量控制 拥塞控制 延时应答 捎带应答…

Launcher3 长按Hotseat图标,显示删除角标(红底白杠杠用于删除图标或者显示应用未读消息数量)

基于Android 13,Launcher3实现需求&#xff1a; 1. 长按Hotseat的图标显示红色删除角标 2. 点击角标&#xff0c;删除图标并保存到Database 3.点击其他地方&#xff0c;取消编辑hotseat图标模式 实现效果&#xff1a; 实现原理&#xff1a; 图标是由BubbleTextView来是实现…

【XR】优化SLAM SDK的稳定性

优化SLAM SDK的稳定性是确保增强现实 (AR) 和虚拟现实 (VR) 应用在各种环境和设备上都能稳定运行的关键。以下是一些主要的优化方法&#xff1a; 1. 传感器融合优化 方法: 将多个传感器的数据&#xff08;如摄像头、加速度计、陀螺仪、磁力计&#xff09;进行融合&#xff0c…

Docker基础概述、Docker安装、Docker镜像加速、Docker镜像指令

1.为什么学docker 开发环境与测试环境不同&#xff0c;导致错误 因此docker提供解决方法———系统平滑移植&#xff0c;容器虚拟化技术 将代码与软件与配置文件 打包成一个镜像 2.docker的历练 创建一个开发环境内成为镜像文件再用docker使用镜像 3.什么是docker Docke…

提升学术论文质量的智能助手:ChatGPT

提升学术论文质量的智能助手&#xff1a;ChatGPT 前言ChatGPT的核心功能ChatGPT的优势具体应用案例局限性与最佳实践结语 前言 在这个知识爆炸的时代&#xff0c;学术研究已成为推动社会进步和科技发展的重要力量。每一篇论文的撰写&#xff0c;都是对人类知识边界的一次探索和…