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

news2024/12/23 15:06:58

头文件:

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; ///< Numerator
    int 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协议,默认udp
    av_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函数进行重新解封装。

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

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

相关文章

【Android】基于webView打造富文本编辑器(H5)

目录 前言一、实现效果二、具体实现1. 导入网页资源2. 页面设计3. 功能调用4. 完整代码 总结 前言 HTML5是构建Web内容的一种语言描述方式。HTML5是Web中核心语言HTML的规范&#xff0c;用户使用任何手段进行网页浏览时看到的内容原本都是HTML格式的&#xff0c;在浏览器中通过…

C语言实现动态栈

#include<stdio.h> #include<stdlib.h> #include<stdbool.h>// 每一个节点的数据类型 typedef struct Node {int data;struct Node * pNext; }NODE, * PNODE; // NODE等价 struct Node PNODE等价于 struct Node *// 栈 typedef struct Stack {PNODE pTop;P…

如何判断一个js对象是否存在循环引用

一、背景 在前端JSON.stringfy是我们常用的一个方法&#xff0c;可以将一个对象序列化。 例如将如下对象序列化 const person { name: kalory, age:18}JSON.stringfy(person) // 结果 {"name":"kalory","age":18}将一个数组序列化const arr …

Gson的常见用法

一引入依赖 <!-- json解析的工具包 --> <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version> </dependency> <!-- 主要为了代码简洁和日志打印 --> <…

天翼云认证专家解决方案架构师(理论)

1.某大型互联网公司为了提升应用程序和基础设施的稳定性&#xff0c;计划引入自动化监控工具。以下哪些工具可以满足公司的需求? A.Grafana B.Nagios C.Prometheus D.Jenkins 2.天翼智能边缘云ECX是位于网络边缘位置的云&#xff0c;兼具云和CDN的特性&#xff0c;将计算、存…

Windows11安装并使用Gstreamer-1.0

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、下载二、使用步骤1.安装2.命令行测试 总结 前言 Gstreamer这个工具的重要性就不用多说了吧&#xff0c;在一些视频播放和解码领域大放异彩。以前更多的是在…

[AIGC] python遍历以及字符串的切片

以下是一篇关于Python遍历方法和字符串切片的文章&#xff0c;以及一个在LeetCode中的问题进行解释。文章最后会给出解题思路和代码实现。 Python遍历方法与字符串切片入门教程 在Python语言中&#xff0c;我们包含了许多内置的函数和方法令其适合于各种数据处理任务。在这个…

使用 Oracle SQL Developer 导入数据

使用 Oracle SQL Developer 导入数据 1. 导入过程 1. 导入过程 选择要导入数据的表&#xff0c; 然后单击右键&#xff0c;选择"导入数据"&#xff0c; 浏览本地文件&#xff0c;选择正确的工作表&#xff0c; 按默认&#xff0c; 按默认&#xff0c; 根据情况修改&…

正能量情感语录热门素材文案去哪里找?文案素材网站分享

正能量情感语录热门素材文案去哪里找&#xff1f;文案素材网站分享 想为你的作品注入正能量和情感温度&#xff1f;不知如何获取热门情感语录素材&#xff1f;别担心&#xff0c;今天我将为大家推荐一些海外知名的素材网站&#xff0c;让你轻松找到受欢迎的文案素材&#xff…

fetch_lfw_people()报错urllib.error.HTTPError: HTTP Error 403: Forbidden的解决方案

零、实验报告地址 计算机视觉实验二:基于支持向量机和随机森林的分类(Part one: 编程实现基于支持向量机的人脸识别分类 )-CSDN博客 一、代码报错 fetch_lfw_people()报错urllib.error.HTTPError: HTTP Error 403: Forbidden 二、报错原因 通常是由于访问权限不足导致的…

【Java】已解决:java.lang.OutOfMemoryError: Java heap space

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决Java&#xff1a;java.lang.OutOfMemoryError: Java heap space 一、问题分析背景 在Java开发过程中&#xff0c;有时我们会遇到java.lang.OutOfMemoryError: Java heap spa…

IDEA模版快速生成Java方法体

新建模版组myLive 在模版组下新建模版finit 在模版text内输入以下脚本 LOGGER.info("$className$.$methodName$>$parmas1$", $parmas2$); try {} catch (Exception e) {LOGGER.error("$className$.$methodName$>error:", e); }LOGGER.info("$c…

Javaweb10 登录认证

登录认证 创建LoginController&#xff0c;直接用员工的Service【接口查询】和Mapper【员工表查询语句】 登录 登录校验 http是无状态的协议。每次请求都是独立的。 会话技术 浏览器与服务器的一次链接就是一次会话。一次会话中可以包含多次请求和响应。session与浏览器个…

SQLAlchemy:filter()和filter_by()的微妙差异

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 在Python编程中&#xff0c;SQLAlchemy是一个强大的ORM&#xff08;对象关系映射&#xff09;工具&#xff0c;它允许使用Python代码来操作数据库。然而&#xff0c;对于新手来说&#xff0c;SQLAlchemy中的一些函数…

【数据库编程-SQLite3(二)】API-增删改查基础函数-(含源码)

学习分享 1、sqlite3_exec函数1.1、使用sqlite3_exec进行【查】操作1.1.1、callback函数 1.2、使用sqlite3_exec进行【增、删、改】操作 2、sqlite3_get_table函数2.1、使用sqlite3_get_table函数进行【查】操作 1、sqlite3_exec函数 1.1、使用sqlite3_exec进行【查】操作 由于…

论文阅读:RAM++ | Open-Set Image Tagging with Multi-Grained Text Supervision

发表时间&#xff1a;2023年11月16 论文地址&#xff1a;https://arxiv.org/pdf/2310.15200 项目地址&#xff1a;https://github.com/xinyu1205/recognize-anything Recognize Anything Plus Model&#xff08;RAM&#xff09;&#xff0c;这是一种有效利用多粒度文本监督的开…

【iOS】UI学习——cell的复用及自定义cell

目录 前言cell的复用手动&#xff08;非注册&#xff09;自动&#xff08;注册&#xff09; 自定义cell总结 前言 Cell复用和自定义Cell是在开发iOS应用时常见的一种优化技巧和定制需求。   Cell复用是UITableView或UICollectionView的一个重要优化机制。当用户滚动这些视图时…

【宠粉赠书】科研绘图神器:MATLAB科技绘图与数据分析

小智送书第二期~ 为了回馈粉丝们的厚爱&#xff0c;今天小智给大家送上一套科研绘图的必备书籍——MATLAB科技绘图与数据分析。下面我会详细给大家介绍这套图书&#xff0c;文末留有领取方式。 图书介绍 《MATLAB科技绘图与数据分析》是一本综合性强、内容丰富的书籍&#x…

【Android面试八股文】Java中有几种引用关系,它们的区别是什么?

在Java中,引用关系主要分为以下几种: 强引用(Strong Reference)软引用(Soft Reference)弱引用(Weak Reference)虚引用(Phantom Reference) 这些引用类型的区别在于它们对垃圾回收的影响程度。下面是对每种引用类型的详细解释及代码示例: 1. 强引用(Strong Referen…