ffmpeg读取rtsp并保存到mp4文件

2023-11-12

本文章只讲述mp4文件的录像 至于音频录入 会在下个文章中介绍

总体思路为:初始化----连接相机获取码流--读取码流中的视频--创建输出mp4上下文---写mp4头----循环读取码流--写入mp4----写文件尾--关闭文件

第一步:初始化网络环境

	//环境注册
	av_register_all();

	avcodec_register_all();

	avformat_network_init();


第二步:连接相机 读取rtsp码流

一个avformatcontext包含多个avstream--也就是说一个连接可能有多个码流通道--例如:0通道为视频 1通道为音频等等

他们的包含关系为:AVFormatContext----AVStream[N]                  AVStream----AVCodecContext----AVCodec

如果需要编解码--则需要为每个码流指定一个编解码器--

(1)如果是读取到的码流 则每个码流里面就自动包含了编码器和编码器上下文

(2)如果是需要构造码流 则需要创建编解码器上下文和编解码器

                                                                                         

BOOL CRecordRtspAndMicrophone::ConnectRtsp()
{


	//分配网络文件格式
	m_pRtspFmt = avformat_alloc_context();
	if (!m_pRtspFmt) return FALSE;



	//连接rtsp
	if (avformat_open_input(&m_pRtspFmt, m_strIPCAddr, NULL, NULL) != 0)
	{
		return FALSE;
	}

	//读入一串流 用于分析
	if (avformat_find_stream_info(m_pRtspFmt, NULL) < 0)
	{
		return FALSE;
	}

	//分析读入的流 读取其音频和视频下标
	for (int i = 0; i<m_pRtspFmt->nb_streams; i++)
	{
		if (m_pRtspFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			m_pInVst = m_pRtspFmt->streams[i];
			m_nInViStreamIdx = i;
		}
		else if (m_pRtspFmt->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			m_pInAst =m_pRtspFmt->streams[i];
			m_nInAuStreamIdx = i;
		}
	}
	return CreateMp4File();

}

第三步:创建一个用于输出的mp4

ffmpeg对文件操作上下文是AVFormatContext-->AVOutputFormat

ffmpeg操作文件的对象是AVFormatContext-->AVIOContext


BOOL CRecordRtspAndMicrophone::CreateMp4File()
{
	SYSTEMTIME st;
	m_nAudioPts = 0;
	m_nVideoPts = 0;

	GetLocalTime(&st);
	
	//关闭之前的文件
	if (m_pMp4Fmt)
	{
		av_write_trailer(m_pMp4Fmt);
		avio_close(m_pMp4Fmt->pb);
		avcodec_close(m_pOutAst->codec);
		avcodec_close(m_pOutVst->codec);
		m_pOutAst = NULL;
		m_pOutVst = NULL;
		avformat_free_context(m_pMp4Fmt);
		RTSPLOG("Close mp4 file %s success!", m_strOutFilePath);
	}


	sprintf(m_strOutFilePath, "%s%04d%02d%02d%02d%02d%02d%03d.mp4", m_strFilePath,st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
	
	//创建文件夹
	CreateMultistageFolder();
	
	//构造输出文件格式
	m_pMp4Fmt = avformat_alloc_context();
	if (!m_pMp4Fmt)
	{
		RTSPLOG("Open mp4 file %s failed because of avformat_alloc_context failed!", m_strOutFilePath);
		return FALSE;
	}

	//输出上下文
	m_pOutFmt = av_guess_format(NULL, m_strOutFilePath, NULL);
	if (NULL == m_pOutFmt)
	{
		RTSPLOG("Open mp4 file %s failed because of av_guess_format failed!", m_strOutFilePath);
		return FALSE;
	}
	m_pMp4Fmt->oformat = m_pOutFmt;

	m_pMp4Fmt->audio_codec_id = CODEC_ID_AAC;
	m_pMp4Fmt->video_codec_id = CODEC_ID_H264;

	

	if (avio_open2(&m_pMp4Fmt->pb, m_strOutFilePath, AVIO_FLAG_WRITE, NULL, NULL) < 0)
	{
		RTSPLOG("Open mp4 file %s failed!", m_strOutFilePath);
		return FALSE;
	}
	RTSPLOG("Open mp4 file %s success!", m_strOutFilePath);

	if (NULL != m_pFun)
	{
		m_pFun(m_strOutFilePath, m_pUser);
	}
	return TRUE;
}

//创建多级目录
BOOL CRecordRtspAndMicrophone::CreateMultistageFolder()
{
	int i = 0;
	while (m_strOutFilePath[i] != '0')
	{
		//如果是文件夹目录
		if ('\\' == m_strOutFilePath[i] || '/' == m_strOutFilePath[i])
		{
			char szTempPath[URL_LEN] = { 0 };
			memcpy(szTempPath, m_strOutFilePath, i + 1);
			if (!PathIsDirectoryA(szTempPath))
			{
				RTSPLOG("File Folder %s not exisit! will create!", szTempPath);
				if (!CreateDirectoryA(szTempPath, NULL))
				{
					RTSPLOG("Close mp4 file %s failed!", szTempPath);
					return FALSE;
				}

			}
		}
	
		i++;
	}
	return TRUE;
}


四、循环读取码流

思路为:创建一个AVPacket对象  循环读取数据到对象中 读取完成之后释放对象内存

DWORD WINAPI CRecordRtspAndMicrophone::ReadStreamThread(LPVOID param)
{
	
	AVPacket pkt;
	CRecordRtspAndMicrophone *pDlg = (CRecordRtspAndMicrophone*)param;

	if (NULL == pDlg) return -1;

	if (!pDlg->ConnectRtsp())
	{
		RTSPLOG("ConnectRtsp failed!");
		return 0;
	}

	pDlg->m_bPause = FALSE;
	av_init_packet(&pkt);
	av_read_play(pDlg->m_pRtspFmt);//play RTSP

	while (pDlg->m_bContinue )
	{
		if ( !pDlg->m_bRecord)
		{
			if (!pDlg->m_bPause)
			{
				av_read_pause(pDlg->m_pRtspFmt);
				pDlg->m_bPause = TRUE;
			}
			Sleep(1);
			continue;
		}
		pDlg->m_bPause = FALSE;

		if (av_read_frame(pDlg->m_pRtspFmt, &pkt) < 0 )
		{
			Sleep(1);
			continue;
		}

		pDlg->m_nVideoFrameCnt++;

		if (pDlg->NeedCreateNewFile())
		{
			pDlg->CreateMp4File();
			pDlg->m_nVideoFrameCnt = 0;
		}
		if (( pDlg->m_nAudioPts < pDlg->m_nVideoPts) && pDlg->m_pOutAst )
		{
			if (FALSE == pDlg->WriteAudioFrame())
			{
				Sleep(0);
			}
		}

		if (pDlg->m_nInViStreamIdx == pkt.stream_index)
		{
			if (NULL == pDlg->m_pOutVst)
			{
				if (!pDlg->AddVideoOutput())
				{
					RTSPLOG("AddVideoOutput failed!");
					break;
				}

				if (!pDlg->AddAudioOutput())
				{
					RTSPLOG("AddVideoOutput failed!");
					break;
				}

				if (!pDlg->WriteMP4Header())
				{
					RTSPLOG("AddVideoOutput failed!");
					break;
				}
				continue;
			}
			pDlg->WriteVideoFrame(&pkt);
			/*pDlg->VideoPacketPush(&pkt);
			if (pDlg->m_bDisplay)
			{
				pDlg->VideoPacketPush( &pkt);
			}
			else
			{
				av_free_packet(&pkt);
			}*/
			if (!pDlg->m_player.InputStream(&pkt))
			{
				av_free_packet(&pkt);
			}

		}

		//if (pDlg->m_nInViStreamIdx == pkt.stream_index)
		//{
		//	//TODO:目前工程不需要接收来自于网络的音频
		//}
		
	}
	pDlg->WriteMP4Trailer();

	if (!pDlg->DisconnectRtsp())
	{
		RTSPLOG("ConnectRtsp failed!");
	}

	return TRUE;
}
//将读取到的数据包写入到mp4文件中

这里有个很重要的问题 Pts和dts的问题 如果这两个值设置不争取 那么图像有可能会显示不出来
BOOL CRecordRtspAndMicrophone::WriteVideoFrame( AVPacket *pPkt )
{
	pPkt->stream_index = m_nOutViStreamIdx;//输出文件中包含码流通道为0--视频 1音频
	pPkt->dts = m_nVideoFrameCnt;		//dts为视频包数目				
	pPkt->pts = 2 * av_rescale_q(m_nVideoFrameCnt, m_pOutVst->codec->time_base, m_pOutVst->time_base);//pts计算出来
	if (av_write_frame(m_pMp4Fmt, pPkt))
	{
		RTSPLOG("Error while writing video frame,fmt=0x%x,audiostream=0x%x,videostream=0x%x,pktsize=%d", m_pMp4Fmt,m_pOutAst,m_pOutVst, pPkt->size);
	}
	else
	{
		//RTSPLOG("[video] VIO frame len=%d", pPkt->size);
	}
	
	m_nVideoPts = pPkt->pts;
	return TRUE;
}


五、文件操作

1、写文件头

BOOL CRecordRtspAndMicrophone::WriteMP4Header()
{	
	av_dump_format(m_pMp4Fmt, 0, "mydump0.txt", 1);
	
	if (NULL == m_pMp4Fmt) return FALSE;
	if (0 != avformat_write_header(m_pMp4Fmt, NULL))
	{
		RTSPLOG("Write file header error!");
		return FALSE;
	}

	return TRUE;
}


2、写文件尾

BOOL CRecordRtspAndMicrophone::WriteMP4Trailer()
{
	av_write_trailer( m_pMp4Fmt );
	return TRUE;
}

3、关闭文件--关闭相机连接

BOOL CRecordRtspAndMicrophone::DisconnectRtsp()
{

	if (m_pMp4Fmt)					//关闭文件
	{
		avio_close(m_pMp4Fmt->pb);
		avformat_free_context(m_pMp4Fmt);	//释放MP4文件上下文
		m_pMp4Fmt = NULL;
	}
	
	if (NULL != m_pVideoDecCtx)			//释放视频解码器
	{
		avcodec_close(m_pVideoDecCtx);
		m_pVideoDecCtx = NULL;
		m_pVideoDec = NULL;
	}

	if (m_pRtspFmt)					//关闭相机连接
	{
		avformat_close_input(&m_pRtspFmt);
		//avformat_free_context(m_pRtspFmt);
		m_pRtspFmt = NULL;
	}
	
	m_pInVst = NULL;
	m_pInAst = NULL;
	m_pOutVst = NULL;
	m_pOutAst = NULL;
	m_bFileInitOK = FALSE;
	m_bContinue = FALSE;
	
	return TRUE;
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ffmpeg读取rtsp并保存到mp4文件 的相关文章

  • iOS-如何使用 AVAsset 或 AVURLAsset 获取 .mp4 文件的持续时间

    我知道视频类型问题的持续时间之前已得到解答 但我在获取视频持续时间方面遇到了真正的麻烦 mp4文件通过使用AVAsset and by AVURLAsset 我正在使用以下代码 NSString itemPathString NSSearc
  • Node.js - 将数据缓冲到 Ffmpeg

    我使用 Node js 和 Ffmpeg 来创建动画 因为我试图避免第三方 avi mp4 解析器 所以我决定将动画输出为原始 rgb24 数据文件 然后使用一些程序将其转换为 mp4 文件 我发现 Ffmpeg 是免费且开源的 它完全可以
  • 将 ffmpeg 编译为独立二进制文件

    我正在尝试编译ffmpeg作为独立的二进制文件 因为我想在 AWS lambda 中使用它 我可以让事情在我正在编译的服务器上正常工作 但是如果我复制二进制文件并从另一台服务器运行它 我会得到 ffmpeg error while load
  • 如何在google colab中安装ffmpeg包

    我正在尝试使用 Google Colab 来做一些事情 我特别想用ffmpeg打包到从图像创建视频 https stackoverflow com a 44951066 6890234 但 ffmpeg 似乎工作得不太好 这个 在最后一个块
  • 如何在 RTMP 流中嵌入 pic_timing SEI 挂钟时间码?

    我需要将我的桌面流式传输到 AWS MediaLive 服务 并且根据要求 我必须在流中包含挂钟时间码 AWS 支持人员善意地通知我 对于 h 264 编码流 我需要提供时间码作为 pic timing SEI 消息 我在 Windows
  • FFMPEG:将 YUV 数据转储到 AVFrame 结构中

    我正在尝试转储YUV420数据进入AVFrameFFMPEG 的结构 从下面的链接 http ffmpeg org doxygen trunk structAVFrame html http ffmpeg org doxygen trunk
  • id3 图像编辑后播放 mp3 时遇到问题

    由于硬件限制 我们生产的软件试图确保导入到其库中的任何音频文件 准备复制到硬件上 都是可接受的比特率 最近 我们开始使用 FFmpeg 将许多不同的音频类型转换为 mp3 以便在我们的硬件上导入和使用它们 虽然转换工作正常并且 mp3 文件
  • 无法从 webm 视频中提取 webp 中的帧

    我无法从 8K webp 视频中提取帧 我正在使用 ffmpeg 来提取帧 这是我正在使用的命令 ffmpeg i content to extract webm frame 2d webp 我得到的输出文件中没有任何数据 但是 当我导出为
  • FFMPEG 帧到 DirectX 表面

    给定一个指向 FFMPEG 的 AVFrame 的指针avcodec decode video 函数如何将图像复制到 DirectX 表面 假设我有一个指向适当大小的 DX X8R8G8B8 表面的指针 Thanks John 您可以使用
  • ffmpeg h264 问题:在 XP 上“找不到预设文件...”

    我有 XP 因为我不熟悉编译 所以我下载了 ffmpeg 的 win32 静态版本 svn r26251 我想调整 mp4 最初为 1280x720 视频的大小 以获得较小的文件大小 但质量大致相同 我的命令是 ffmpeg i ma mp
  • 以 Gif 形式在 Android 上以编程方式共享 WhatsApp 视频

    我如何将 mp4 视频文件转换为 WhatsApp gif 文件 在应用程序 UI 中简单显示为 gif 但内部是特定的 mp4 格式 以在 android 共享意图中使用 并被 Whatsapp 聊天应用程序识别为此类媒体 我搜索了很多
  • ffmpeg 将 m4s 转换为 mp4

    我正在研究 DASH 试图为最终用户优化 QoE 我有一个视频 并使用 ffmpeg 将其编码为不同的比特率 一切都很好 并且可以使用 dash 播放该视频 我想要的是将用户收到的片段合并为一个 m4 并将该 m4 转换为 mp4 我在 f
  • ffprobe show_frames 用于多个视频

    有什么方法可以在一个文件中同时查看多个视频的帧吗 我知道如何在一个视频中做到这一点 ffprobe show frameshttp myvirtualdirectory myvideo mp4 http myvirtualdirectory
  • 用PHP+FFMPEG生成随机缩略图

    我正在尝试使用 FFMPEG 和 FFMPEG PHP 扩展从电影中的随机点生成缩略图 我的脚本工作正常 但是需要 20 分钟才能生成 5 10 个缩略图 该脚本通过生成随机数来工作 这些随机数稍后用作帧号 生成的所有数字均在电影帧数之内
  • HTML5 视频(webm 和 MP4)显示空白屏幕并且无法播放

    我已经用谷歌搜索了一个小时 但我要么很糟糕 要么这不是一个常见问题 这是我的视频标签
  • FFMPEG:尝试从图像和音频创建 facebook 流时出现转换失败错误?

    目标是从图像和音频文件创建 Facebook 流 这是我的命令 ffmpeg re y loop 1 f image2 i maxresdefault jpg i audio loop mp3 ar 44100 b a 128k vcode
  • 如何使用 FFmpeg 连接 MTS 视频并应用过滤器而不重新编码?

    我有一个包含许多 MTS 视频文件的 txt 文件 我想使用将它们全部合并在一起FFmpeg并获取一个大的 MTS 文件 但我想申请fade in and fade out到最后的视频 我可以在不重新编码的情况下做到这一点吗 因为重新编码需
  • 如何获取视频时长(以秒为单位)? [复制]

    这个问题在这里已经有答案了 如何获取以秒为单位的视频时长 我尝试过的 ffmpeg i file flv 2 gt 1 grep Duration Duration 00 39 43 08 start 0 040000 bitrate 38
  • ffmpeg 无法复制正确的持续时间

    我想使用以下命令修剪视频 ffmpeg exe i in mp4 ss 76 t 10 c copy an out mp4 确切地说 76 秒后复制 10 秒 并且不复制音频 然而out mp4长度为 5 秒 这是 ffmpeg 控制台输出
  • 从图像创建视频时,每秒减慢 ffmpeg 的图像速度

    我有一系列演示的屏幕截图 我想将其放入视频中 我正在使用 ffmpeg 为此目的 命令是ffmpeg f image2 i screenshot 5d png vcodec mpeg4 demo avi 不过 视频长度比我想要的要短 而且移

随机推荐

  • linux学成之路(基础篇(二十三)MySQL服务(中)

    目录 MySQL服务之SQL语句 一 SQL语句类型 一 DDL语句 二 DML语句 三 DCL语句 四 DQL 语句 二 数据库操作 一 查看 二 创建 三 进入 四 删除数据库 五 更改数据库名称 六 更改字符集 三 数据表管理 一 数
  • Hadoop HDFS shell 命令行常用操作

    前言 Hadoop HDFS shell的命令和Linux的shell命令有类似的地方 基本上会使用Linux命令的话 把HDFS的理论理解一下就可以了 一 创建文件夹 hadoop fs mkdir p
  • 【915程序设计】21西交大软件专硕915程序设计真题讲解

    28 马鞍点 include
  • 【华为OD机试c++】Excel单元格数值统计【 2023 Q1

    华为OD机试 题目列表 2023Q1 点这里 2023华为OD机试 刷题指南 点这里 题目描述 Excel 工作表中对选定区域的数值进行统计的功能非常实用 仿照Excel的这个功能 请对给定表格中选中区域中的单元格进行求和统计 并输出统计结
  • Halide学习笔记---Halide语言设计的初衷

    Halide语言设计初衷 我们正处于一个数据密集的时代 4D广场相机 图形渲染 3D打印 图像传感器 高质量医学图像等 每天生产大量的图像数据 面对这样一个图像时代 急需要针对图像处理算法设计的高性能图像处理编程语言 在这样的需求下 Hal
  • 第五届蓝桥杯——java c组 1/a 的分数称为单位分数

    形如 1 a 的分数称为单位分数 可以把1分解为若干个互不相同的单位分数之和 例如 1 1 2 1 3 1 9 1 18 1 1 2 1 3 1 10 1 15 1 1 3 1 5 1 7 1 9 1 11 1 15 1 35 1 45 1
  • 一文读懂 React16.0-16.6 新特性(实践 思考)

    首先 把我知道从16 0开始到现在为止新特性全部列举出来 v16 0 render 支持返回数组和字符串 createPortal 支持自定义 DOM 属性 Error Boundary Fiber SSR优化 减少文件体积v16 1 re
  • 对区块链钱包的简单认识

    钱包是存储和使用数字货币的工具 在区块链领域有举足轻重的地位 在对钱包分类之前 需要先理解几个概念 钱包地址 它类似于银行卡号 一个人可以拥有多张银行卡 所以他也可以拥有多个钱包地址 一个钱包地址只能对应一个私钥 在一个钱包中 可以拥有多个
  • VMware导入别人的虚拟机

    根据老师发的文件 将分卷压缩文件下载到一个文件夹中 选择解压第一个文件 即可得到所有资源 可以看出得到如下文件 这里需要导入Vmware中 我查询了几种导入的方法 1 导入OVF 首先选择导入ovf文件 直接点击打开虚拟机 选择导入Ubun
  • Go 每日一库之 cobra

    简介 cobra是一个命令行程序库 可以用来编写命令行程序 同时 它也提供了一个脚手架 用于生成基于 cobra 的应用程序框架 非常多知名的开源项目使用了 cobra 库构建命令行 如Kubernetes Hugo etcd等等等等 本文
  • $('#dg').datagrid('getSelections');", 得到的rows.length在多选情况下总为1, 不选择时为0.

    dg datagrid getSelections 得到的rows length在多选情况下总为1 不选择时为0 方案 title ID field dataId width 80 hidden true 的field一定要与idField
  • 订单系统设计 —— 订单管理

    文章目录 一 方案背景 1 1 考虑因素 1 2 数据特点 二 方案演进 三 MySQL架构 3 1 单库单表 3 2 读写分离 3 3 垂直拆分 3 4 数据归档 冷热分离 3 5 分库分表 方案1 路由表 方案2 哈希 四 MySQL
  • windows linux环境搭建

    1 windows中linux环境wsl 2 powershell scoop https zhuanlan zhihu com p 463284082 powershell通过scoop可以安装各种软件 安装git就是 scoop ins
  • 提升UE5写实效果的项目设置

    随着虚幻引擎5 Unreal Engine 5 简称UE5 的发布 游戏开发者和数字艺术家们迎来了一个全新的机会 可以在其强大的渲染引擎下创建更加逼真和令人惊叹的游戏和虚拟场景 然而 要实现出色的写实效果 需要合理设置项目并运用一些技巧和策
  • 使用IDEA2023创建Servlet模板,使其右键显示Servlet选项

    使用IDEA2023创建Servlet模板 使其右键显示Servlet选项 之前在IDEA2022及以前可以通过一些方法创建Servlet模板 但我不知道为什么2023版本没有作用 下面提供另一种方式实现Servlet的创建 直接参考IDE
  • 系统化程序分析

    左志强 南京大学计算机系副研究员 研究领域包括程序分析 编译技术 系统软件等 本文以技术文章的方式回顾左老师在 SIG 程序分析 技术沙龙上的分享 回顾视频也已经上传 B 站 欢迎小伙伴们点开观看 SIG 程序分析技术沙龙回顾 面向千万行代
  • 单电源运放和双电源运放及其供电方式选择与转换的注意事项

    文章目录 前言 一 运放之双电源供电和单电源供电 1 如何区分单电源运放和双电源运放 2 单电源供电运放特性 3 运放的两种供电模式转换 4 单端偏置的缺陷 二 仿真验证 1 两阶高通滤波放大电路 两端偏置 2 两阶高通滤波放大电路 单端偏
  • 20-10-026-安装-KyLin-2.6.0-单机版安装(MAC官网下载)-spark引擎

    文章目录 1 视界 1 官网 2 安装要求 2 1 软件要求 2 2 硬件要求 2 3 Hadoop 环境 3 本次环境 4 HBASE 1 2 0安装 5 kylin 安装 6 检查zk jar 7 启动Hbase 8 添加依赖 9 添加
  • VS附加到进程调试

    操作 要附加到进程中调试外部可执行文件 您需要使用Visual Studio的 调试附加 功能 以下是附加到进程中调试外部可执行文件的步骤 打开您要调试的源代码文件或可执行文件 打开Visual Studio 选择 调试 菜单 然后选择 附
  • ffmpeg读取rtsp并保存到mp4文件

    本文章只讲述mp4文件的录像 至于音频录入 会在下个文章中介绍 总体思路为 初始化 连接相机获取码流 读取码流中的视频 创建输出mp4上下文 写mp4头 循环读取码流 写入mp4 写文件尾 关闭文件 第一步 初始化网络环境 环境注册 av