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

news2024/9/24 21:25:42

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 v
	yuv422 每两个y公用一组UV分量,yuyv(yuv422)一个像素大小:y+1/2(u)+1/2(v)=2byte
	yuv420p  存储最简单,先存所以的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 v
    int line_start = 0;
    int is_u = 1;
    int u_index = 0;
    int v_index = 0;
    // copy u, v per line. skip a line once
    for (i = 0; i < video_height; i += 2) 
	{
        line_start = i * (video_width<<1);//每一行的起始位置,相当于:video_width*2
        for (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.coloradmin.cn/o/2084231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shell 脚本入门指南

Shell 脚本入门指南 引言1.1 什么是 Shell 脚本&#xff1f;1.2 为什么学习 Shell 脚本&#xff1f; 准备工作2.1 选择和安装 Shell&#xff08;Bash&#xff09;2.2 设置脚本编辑环境&#xff08;文本编辑器&#xff09; Hello World&#xff01;基本语法4.1 变量4.2 条件判断…

【OpenCV】离散傅里叶变换

离散傅里叶变换 傅里叶变换代码实现扩展图片创建储存实部和虚部值的矩阵进行离散傅里叶变换将复数转换成振幅对数转换裁剪和重排归一化 离散傅里叶变换在图像处理中的应用参考 傅里叶变换 在图片处理中&#xff0c;傅里叶变化会将对图片的时域分析转变为频域分析。 傅里叶的基…

模型 麦肯锡七步成诗法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。问题到解决方案的七步跨越。 1 麦肯锡七步成诗法的应用 1.1零售业客户体验转型实践 随着消费者对购物体验的要求日益提高&#xff0c;一家零售企业面临客户流失和销售增长放缓的问题。企业管理层决定…

分类预测|基于哈里斯鹰优化混合核极限学习机的数据分类预测Matlab程序HHO-HKELM多特征输入多类别输出含基础程序

分类预测|基于哈里斯鹰优化混合核极限学习机的数据分类预测Matlab程序HHO-HKELM多特征输入多类别输出含基础程序 文章目录 前言分类预测|基于哈里斯鹰优化混合核极限学习机的数据分类预测Matlab程序HHO-HKELM多特征输入多类别输出含基础程序 一、HHO-HKELM模型HHO-HKELM 分类预…

告别PDF格式困扰,2024年PDF转换器推荐

PDF现在已经逐渐成为了文件传输的主流格式了&#xff0c;它有保存文件页面版式的优点&#xff0c;但是这个格式编辑对大部分人来说还是不那么方便&#xff0c;日常我们还是习惯将它们转换成我们常见的 文本格式来操作。今天我分享一下可以实现PDF格式转换的pdf转换器有哪些吧。…

CSS3实现购物车动画效果

概述 小程序商城或者 web 端网站时,我们可以只通过 CSS 的 animation 和transform,而不需要借助额外的第三方库轻松实现简单的动画效果,丰富页面的表达效果 效果 如下图所示,点击按钮就会有个商品进入左下角的购物车内 购物车动画示例地址 代码示例 元素 开始只需要写…

Uniapp 调用aar、jar包

废话 坑是真的多&#xff0c;官方文档简陋到可以忽略不计。 大概流程 1. 新建一个Android模块&#xff0c;需要用这个模块打包成aar 2. 用这个模块引用uniapp-v8-release.aar以及你需要用到的aar、jar&#xff0c;用不到则忽略这步 坑一&#xff1a;不要直接放到这个模块的…

window11彻底关闭Microsoft Defender

Microsoft Defender Antivirus 是 Microsoft Windows 11 操作系统的默认防病毒解决方案。默认情况下它处于打开的状态。大多数第三方的杀毒软件都可以识别&#xff0c;并代替它。 但是大多数情况下&#xff0c;我们总是有各种理由需要关闭它&#xff0c;例如 Windows Defender …

轻量级冠军:NVIDIA 发布具有领先准确率的小语言模型

Mistral-NeMo-Minitron 8B 是最近发布的 Mistral NeMo 12B 模型的微型版本&#xff0c;具有高精度和高计算效率&#xff0c;可在 GPU 加速数据中心、云和工作站上运行模型。 生成式 AI 开发者通常需要在模型尺寸和准确性之间做出权衡。然而&#xff0c;NVIDIA 发布的一款新语言…

内存管理篇-14kmalloc机制实现分析

引入这个kmalloc的目的&#xff0c;是因为前面的slab接口太过于复杂&#xff0c;因此需要一个全新的封装kmalloc接口&#xff0c;内存申请编程接口实现。kmalloc底层起始也是基于slab缓存实现的。 1.kmalloc 调用流程 参数解析: 解析 gfp_mask 参数&#xff0c;确定分配时是否…

数据结构与算法学习day18-层序遍历

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。需要借用一个辅助数据结构即队列来实现&#xff0c;队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。…

硬盘崩溃数据无踪?Windows数据恢复TOP4揭秘,2024年助你找回宝贵资料

现在我们的生活、工作、学习都离不开电脑&#xff0c;电脑里的硬盘就像个装满宝贝的箱子&#xff0c;里面全是我们宝贵的照片、文档、视频和美好回忆。但要是硬盘突然坏了&#xff0c;东西一下子全没了&#xff0c;那感觉真是太糟糕了。别担心&#xff0c;今天我们就给你介绍几…

2024年中国运筹学会运筹竞赛(数据驱动赛道)报名通知

竞赛组织 主办单位&#xff1a;中国运筹学会&#xff08;国家一级学会&#xff09; 承办单位&#xff1a;中国科学技术大学 支持单位&#xff1a;杉数科技、海康威视、中国科学技术大学管理学院、《运筹学学报》杂志 竞赛内容 本次竞赛&#xff08;本科生组&#xff09;由竞…

不平衡数据集的单类分类算法

不平衡数据集的单类分类算法 异常值或异常是与其他数据不符的罕见例子。 识别数据中的离群值称为离群值或异常检测&#xff0c;机器学习中专注于此问题的子领域称为单类分类。这些是无监督学习算法&#xff0c;旨在对“正常”示例进行建模&#xff0c;以便将新示例分类为正常…

记录一次给iOS 工程添加.gitignore文件

新建了一个iOS工程&#xff0c;修改过代码之后&#xff0c;提交发现有一些自己不想要提交的内容 如下图&#xff0c;里面有.DS_Store文件&#xff0c;还有xcsuserstate文件&#xff0c; 这个时候需要添加忽略文件 首先在工程文件夹中执行 touch .gitignore 创建忽略文件&#…

Unity2D游戏开发-Pak木鱼

在接下来文章里我会以Unity为主一起制作游戏 在unity 里如何制作一个简单的敲木鱼游戏&#xff1f; 创建一个2D场景&#xff08;本人使用Unity2023&#xff09; (每个一段时间要申请一个个人许可证) 点击下方蓝色按钮创建 将以下素材拖动到Assets文件夹中 这张图随意命名我…

Swift concurrency 4 — Task和.task的理解与使用

Task Swift中的Task是一种异步操作&#xff0c;它提供了一种替代DispatchQueue.main.async{}等传统方法的方法。通过使用Task&#xff0c;我们可以简化代码并更好地控制异步操作。此外&#xff0c;Task还提供了其他选项&#xff0c;可以进一步增强任务执行。 先看一个Task的基…

net core中byte数组如何高效转换为16进制字符串

在 .NET Core 中&#xff0c;如何把 byte[] 转换为 16 进制字符串&#xff1f;你能想到哪些方法&#xff1f;什么方式性能最好&#xff1f;今天和大家分享几种转换方式。 往往在处理字符串性能问题时&#xff0c;首先应该想到的是怎么想办法减少内存分配&#xff0c;怎么优化字…

22.优化器

优化器 当使用损失函数时&#xff0c;可以调用损失函数的 backward&#xff0c;得到反向传播&#xff0c;反向传播可以求出每个需要调节的参数对应的梯度&#xff0c;有了梯度就可以利用优化器&#xff0c;优化器根据梯度对参数进行调整&#xff0c;以达到整体误差降低的目的。…

Cryptomator:开源云存储加密

采用最新技术标准&#xff0c;提供最佳保护 如果有人查看您云中的文件夹&#xff0c;他们无法对您的数据得出任何结论。 Cryptomator 提供开源的客户端云文件加密。 它适用于 Windows、Linux、macOS 和 iOS。 Cryptomator 可与 Dropbox、Google Drive、OneDrive、MEGA、pClo…