ffmpeg解封装rtsp并录制视频-(1)解封装rtsp断网或摄像机重启后自动重连处理

本文主要是介绍ffmpeg解封装rtsp并录制视频-(1)解封装rtsp断网或摄像机重启后自动重连处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

头文件:

xtools.h


#pragma once
#include <thread>
#include <iostream>
#include <mutex>
//日志级别 DEBUG INFO ERROR FATAL
enum XLogLevel
{XLOG_TYPE_DEBUG,XLOG_TYPE_INFO,XLOG_TPYE_ERROR,XLOG_TYPE_FATAL
};
#define LOG_MIN_LEVEL XLOG_TYPE_DEBUG
#define XLOG(s,level) \if(level>=LOG_MIN_LEVEL) \std::cout<<level<<":"<<__FILE__<<":"<<__LINE__<<":\n"\<<s<<std::endl;
#define LOGDEBUG(s) XLOG(s,XLOG_TYPE_DEBUG)
#define LOGINFO(s) XLOG(s,XLOG_TYPE_INFO)
#define LOGERROR(s) XLOG(s,XLOG_TPYE_ERROR)
#define LOGFATAL(s) XLOG(s,XLOG_TYPE_FATAL)
void MSleep(unsigned int ms);//获取当前时间戳 毫秒
long long NowMs();class XThread
{
public://启动线程virtual void Start();//停止线程(设置退出标志,等待线程退出)virtual void Stop();protected://线程入口函数virtual void Main() = 0;//标志线程退出bool is_exit_ = false;//线程索引号int index_ = 0;
private:std::thread th_;std::mutex m_;};
class XTools
{
};

xdemux_task.h

#pragma once
#include "xtools.h"
#include "xdemux.h"
class XDemuxTask :public XThread
{
public:void Main();/// <summary>/// 打开解封装/// </summary>/// <param name="url">rtsp地址</param>/// <param name="timeout_ms">超时时间 单位毫秒</param>/// <returns></returns>bool Open(std::string url,int timeout_ms = 1000);
private:XDemux demux_;std::string url_;int timeout_ms_ = 0;//超时时间
};

xformat.h

#pragma once
/// <summary>
/// 封装和解封装基类
/// </summary>
#include <mutex>
#include"xtools.h"
struct AVFormatContext;
struct AVCodecParameters;
struct AVPacket;
struct AVCodecContext;
struct XRational
{int num; ///< Numeratorint den; ///< Denominator
};
class XFormat
{
public:/// <summary>/// 复制参数 线程安全/// </summary>/// <param name="stream_index">对应c_->streams 下标</param>/// <param name="dst">输出参数</param>/// <returns>是否成功</returns>bool CopyPara(int stream_index, AVCodecParameters* dst);bool CopyPara(int stream_index, AVCodecContext* dts);/// <summary>/// 设置上下文,并且清理上次的设置的值,如果传递NULL,相当于关闭上下文3/// 线程安全/// </summary>/// <param name="c"></param>void set_c(AVFormatContext* c);int audio_index() { return audio_index_; }int video_index() { return video_index_; }XRational video_time_base(){ return video_time_base_; }XRational audio_time_base() { return audio_time_base_; }//根据timebase换算时间bool RescaleTime(AVPacket *pkt,long long offset_pts, XRational time_base);int video_codec_id() { return video_codec_id_; }//判断是否超时bool IsTimeout(){if (NowMs() - last_time_ > time_out_ms_) //超时{last_time_ = NowMs();is_connected_ = false;return true;}return false;}//设定超时时间void set_time_out_ms(int ms);bool is_connected() { return is_connected_; }protected:int time_out_ms_ = 0;           //超时时间 毫秒long long last_time_ = 0;bool is_connected_ = false;     //是否连接成功AVFormatContext* c_ = nullptr;  //封装解封装上下文std::mutex mux_;                //c_ 资源互斥int video_index_ = 0;//video和audio在stream中索引int audio_index_ = 1;XRational video_time_base_ = {1,25};XRational audio_time_base_ = {1,9000};int video_codec_id_ = 0;       //编码器ID
};

xdemux.h


#pragma once
#include "xformat.h"
class XDemux :public XFormat
{
public:/// <summary>/// 打开解封装/// </summary>/// <param name="url">解封装地址 支持rtsp</param>/// <returns>失败返回nullptr</returns>static AVFormatContext* Open(const char* url);/// <summary>/// 读取一帧数据/// </summary>/// <param name="pkt">输出数据</param>/// <returns>是否成功</returns>bool Read(AVPacket* pkt);bool Seek(long long pts,int stream_index);};

源文件:

xtools.cpp

#include "xtools.h"
#include <sstream>
using namespace std;
void MSleep(unsigned int ms)
{auto beg = clock();for (int i = 0; i < ms; i++){this_thread::sleep_for(1ms);if ((clock() - beg) / (CLOCKS_PER_SEC / 1000) >= ms)break;}
}
long long NowMs()
{return clock() / (CLOCKS_PER_SEC / 1000);
}
//启动线程
void XThread::Start()
{unique_lock<mutex> lock(m_);static int i = 0;i++;index_ = i;is_exit_ = false;//启动线程th_ = thread(&XThread::Main, this);stringstream ss;ss << "XThread::Start()" << index_;LOGINFO(ss.str());
}//停止线程(设置退出标志,等待线程退出)
void XThread::Stop()
{stringstream ss;ss << "XThread::Stop() begin" << index_;LOGINFO(ss.str());is_exit_ = true;if (th_.joinable()) //判断子线程是否可以等待th_.join();     //等待子线程退出ss.str("");ss << "XThread::Stop() end" << index_;LOGINFO(ss.str());
}

xdemux_task.cpp


#include "xdemux_task.h"
extern "C"
{
#include <libavformat/avformat.h>
}
using namespace std;
bool XDemuxTask::Open(std::string url, int timeout_ms)
{LOGDEBUG("XDemuxTask::Open begin!");demux_.set_c(nullptr);//断开之前的连接this->url_ = url;this->timeout_ms_ = timeout_ms;auto c = demux_.Open(url.c_str());if (!c)return false;demux_.set_c(c);demux_.set_time_out_ms(timeout_ms);LOGDEBUG("XDemuxTask::Open end!");return true;
}
void XDemuxTask::Main()
{AVPacket pkt;while (!is_exit_){if (!demux_.Read(&pkt)){//读取失败cout << "-" << flush;if (!demux_.is_connected())//如果断开连接,需要重新连接{Open(url_, timeout_ms_);}this_thread::sleep_for(1ms);continue;}cout << "." << flush;this_thread::sleep_for(1ms);}
}

xformat.cpp

#include "xformat.h"
#include <iostream>
#include <thread>
#include "xtools.h"
using namespace std;
extern "C" { //指定函数是c语言函数,函数名不包含重载标注
//引用ffmpeg头文件
#include <libavformat/avformat.h>
}
//预处理指令导入库
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
using namespace std;
static int TimeoutCallback(void* para)
{auto xf = (XFormat*)para;if (xf->IsTimeout())return 1;//超时退出Read//cout << "TimeoutCallback" << endl;return 0; //正常阻塞
}
void XFormat::set_c(AVFormatContext* c)
{unique_lock<mutex> lock(mux_);if (c_) //清理原值{if (c_->oformat) //输出上下文{if (c_->pb)avio_closep(&c_->pb);avformat_free_context(c_);}else if (c_->iformat)  //输入上下文{avformat_close_input(&c_);}else{avformat_free_context(c_);}}c_ = c;if (!c_){is_connected_ = false;return;}is_connected_ = true;//计时 用于超时判断last_time_ = NowMs();//设定超时处理回调if (time_out_ms_ > 0){AVIOInterruptCB cb = { TimeoutCallback ,this };c_->interrupt_callback = cb;}//用于区分是否有音频或者视频流audio_index_ = -1; video_index_ = -1;//区分音视频stream 索引for (int i = 0; i < c->nb_streams; i++){//音频if (c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){audio_index_ = i;audio_time_base_.den = c->streams[i]->time_base.den;audio_time_base_.num = c->streams[i]->time_base.num;}else if (c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){video_index_ = i;video_time_base_.den = c->streams[i]->time_base.den;video_time_base_.num = c->streams[i]->time_base.num;video_codec_id_ = c->streams[i]->codecpar->codec_id;}}
}bool XFormat::CopyPara(int stream_index, AVCodecContext* dts)
{unique_lock<mutex> lock(mux_);if (!c_){return false;}if (stream_index<0 || stream_index>c_->nb_streams)return false;auto re = avcodec_parameters_to_context(dts, c_->streams[stream_index]->codecpar);if (re < 0){return false;}return true;
}
/// <summary>
/// 复制参数 线程安全
/// </summary>
/// <param name="stream_index">对应c_->streams 下标</param>
/// <param name="dst">输出参数</param>
/// <returns>是否成功</returns>
bool XFormat::CopyPara(int stream_index, AVCodecParameters* dst)
{unique_lock<mutex> lock(mux_);if (!c_){return false;}if (stream_index<0 || stream_index>c_->nb_streams)return false;auto re = avcodec_parameters_copy(dst, c_->streams[stream_index]->codecpar);if (re < 0){return false;}return true;
}bool XFormat::RescaleTime(AVPacket* pkt, long long offset_pts, XRational time_base)
{unique_lock<mutex> lock(mux_);if (!c_)return false;        auto out_stream = c_->streams[pkt->stream_index];AVRational in_time_base;in_time_base.num = time_base.num;in_time_base.den = time_base.den;pkt->pts = av_rescale_q_rnd(pkt->pts- offset_pts, in_time_base,out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt->dts = av_rescale_q_rnd(pkt->dts- offset_pts, in_time_base,out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));pkt->duration = av_rescale_q(pkt->duration, in_time_base, out_stream->time_base);pkt->pos = -1;return true;
}//设定超时时间
void XFormat::set_time_out_ms(int ms)
{unique_lock<mutex> lock(mux_);this->time_out_ms_ = ms;//设置回调函数,处理超时退出if (c_){//AVIOInterruptCB结构体:这是FFmpeg中用于处理I/O操作中断的结构体,包含一个回调函数指针和一个用户数据指针。AVIOInterruptCB cb = { TimeoutCallback ,this };//看下面的注释结构体 c_->interrupt_callback = cb;}/* typedef struct AVIOInterruptCB {int (*callback)(void*);void* opaque;} AVIOInterruptCB;*/}

xdemux.cpp

#include "xdemux.h"
#include <iostream>
#include <thread>
#include"xtools.h"
#include"xcodec.h"
using namespace std;
extern "C" { //指定函数是c语言函数,函数名不包含重载标注
//引用ffmpeg头文件
#include <libavformat/avformat.h>
}
void PrintErr(int err);
#define BERR(err) if(err!= 0){PrintErr(err);return 0;}
AVFormatContext* XDemux::Open(const char* url)
{AVFormatContext* c = nullptr;AVDictionary* opts = nullptr;//av_dict_set(&opts, "rtsp_transport", "tcp", 0);//传输媒体流为tcp协议,默认udpav_dict_set(&opts, "stimeout", "1000000", 0);//连接超时1秒//打开封装上下文auto re = avformat_open_input(&c, url, nullptr, &opts);if (opts)av_dict_free(&opts);BERR(re);//获取媒体信息re = avformat_find_stream_info(c, nullptr);BERR(re);//打印输入封装信息av_dump_format(c, 0, url, 0);return c;
}bool XDemux::Read(AVPacket* pkt)
{unique_lock<mutex> lock(mux_);if (!c_)return false;auto re = av_read_frame(c_, pkt);BERR(re);//计时 用于超时判断last_time_ = NowMs();return true;
}bool XDemux::Seek(long long pts, int stream_index)
{unique_lock<mutex> lock(mux_);if (!c_)return false;auto re = av_seek_frame(c_, stream_index, pts,AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);BERR(re);return true;
}

main.cpp

​#include <iostream>
#include <thread>
#include "xtools.h"
#include "xdemux_task.h"
using namespace std;
//class TestThread :public XThread
//{
//public:
//    void Main()
//    {
//        LOGDEBUG("TestThread Main begin");
//        while (!is_exit_)
//        {
//            this_thread::sleep_for(1ms);
//        }
//        LOGDEBUG("TestThread Main end");
//    }
//};
#define CAM1 \
"rtsp://admin:admin@192.168.2.108/cam/realmonitor?channel=1&subtype=0"
#define MP4 "v1080.mp4"
int main(int argc, char* argv[])
{XDemuxTask det;for (;;)//保证第一次连接就失败的情况{if (det.Open(CAM1))//进行解封装得到pkt{break;}MSleep(100);continue;}
//启动主线程Main函数进行解码det.Start();//TestThread tt;//tt.Start();//this_thread::sleep_for(3s);//tt.Stop();getchar();return 0;
}[点击并拖拽以移动]
​

主函数执行分析:

det对象用于对rtsp进行解封装,通过调用open函数,该函数调用Xdemux_task的成员demux对象对其进行解封装得到解封装上下文,再用set_c初始化设置解封装上下文,再用demux对象调用set_time_out_ms函数,该函数在xformat里,demux类继承xformat所以可以调用,作用是设定超时时间,如果断开连接则会调用一个回调函数TimeOutCallBack函数对时间进行判断是否超时,如果超时则退出,解封装后det再调用线程启动函数启动主线程Main函数,用read_frame函数把解封装后的rtsp进行读取存储到pkt中准备进行pkt->frame的解码,读取过程中如果摄像头断开会自动调用Open函数进行重新解封装。

这篇关于ffmpeg解封装rtsp并录制视频-(1)解封装rtsp断网或摄像机重启后自动重连处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

MyBatis-Plus 自动赋值实体字段最佳实践指南

《MyBatis-Plus自动赋值实体字段最佳实践指南》MyBatis-Plus通过@TableField注解与填充策略,实现时间戳、用户信息、逻辑删除等字段的自动填充,减少手动赋值,提升开发效率与... 目录1. MyBATis-Plus 自动赋值概述1.1 适用场景1.2 自动填充的原理1.3 填充策略

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

Java堆转储文件之1.6G大文件处理完整指南

《Java堆转储文件之1.6G大文件处理完整指南》堆转储文件是优化、分析内存消耗的重要工具,:本文主要介绍Java堆转储文件之1.6G大文件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言文件为什么这么大?如何处理这个文件?分析文件内容(推荐)删除文件(如果不需要)查看错误来源如何避

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核