From 28ba3d2281f09ee7772b0444001376e34e048d36 Mon Sep 17 00:00:00 2001 From: cbwu <504-wuchengbo@htsdfp.com> Date: Mon, 14 Oct 2024 11:16:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=89=E6=B5=81=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=87=8D=E8=BF=9E=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PayloadAPP.pro | 2 +- Src/Video/cffmpeg_decode.cpp | 88 ++++++++++++++++++++++++++++++------ Src/Video/cffmpeg_decode.h | 13 +++++- Src/Video/ffmpegvideodlg.cpp | 1 + global.cpp | 2 +- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/PayloadAPP.pro b/PayloadAPP.pro index 68e6aae..b898e74 100644 --- a/PayloadAPP.pro +++ b/PayloadAPP.pro @@ -14,7 +14,7 @@ QMAKE_PROJECT_DEPTH = 0 # QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\'\" #程序版本 -VERSION = 1.0.0.1011 +VERSION = 1.0.0.1014 #程序版本 QMAKE_TARGET_COMPANY = "HTSDFP" diff --git a/Src/Video/cffmpeg_decode.cpp b/Src/Video/cffmpeg_decode.cpp index ed3dda9..9e16a43 100644 --- a/Src/Video/cffmpeg_decode.cpp +++ b/Src/Video/cffmpeg_decode.cpp @@ -1,10 +1,7 @@ #include "cffmpeg_decode.h" // Cffmpeg_decode::Cffmpeg_decode() { Cffmpeg_decode::Cffmpeg_decode(QObject *parent) : QObject(parent) { - inputFormatCtx = avformat_alloc_context(); - inputPacket = av_packet_alloc(); - yuvFrame = av_frame_alloc(); - rgbFrame = av_frame_alloc(); + initFFmpegContainer(); avformat_network_init(); m_rtsp_transport = "tcp"; } @@ -59,6 +56,13 @@ void Cffmpeg_decode::setPushStream(bool bPushStream) { mutex.unlock(); } +void Cffmpeg_decode::initFFmpegContainer() { + inputFormatCtx = avformat_alloc_context(); + inputPacket = av_packet_alloc(); + yuvFrame = av_frame_alloc(); + rgbFrame = av_frame_alloc(); +} + bool Cffmpeg_decode::open_input_file() { if (_url.isEmpty()) return 0; @@ -79,7 +83,7 @@ bool Cffmpeg_decode::open_input_file() { av_dict_set(&avdic, "buffer_size", "2048000", 0); // 设置超时断开连接时间,单位微秒//listen_timeout // av_dict_set(&avdic, "listen_timeout", "200000", 0); - av_dict_set(&avdic, "stimeout", "3000000", 0); // 设置超时3秒 + av_dict_set(&avdic, "stimeout", "5000000", 0); // 设置超时5秒 av_dict_set(&avdic, "max_delay", "300000", 0); // 设置最大时延300ms av_dict_set(&avdic, "tune", "zerolatency", 0); // 实时编码 av_dict_set(&avdic, "preset", "faster", 0); // ultrafast @@ -87,6 +91,9 @@ bool Cffmpeg_decode::open_input_file() { // 设置最大重试时间为1s,解决avformat_open_input打开空流时间过长问题 av_dict_set(&avdic, "max_interleave_delta", "1000000", 0); + // av_dict_set(&avdic, "reconnect", "1", 0); // 开启自动重连 + // av_dict_set(&avdic, "reconnect_streamed", "1", 0); // 对于流媒体自动重连 + // av_dict_set(&avdic, "reconnect_delay_max", "5", 0); // 最大重连延时 5 秒 // av_dict_set(&avdic, "rtsp_flags", "prefer_tcp", 0); // 保持TCP连接 // av_dict_set(&avdic, "timeout", "500000", 0); // 超时时间设置为500ms // av_dict_set(&avdic, "probesize", "50000000", 0); // 增加探针大小,1MB @@ -183,7 +190,8 @@ bool Cffmpeg_decode::open_input_file() { // 线程里持续执行 void Cffmpeg_decode::run() { - if (!open_input_file()) { + m_startPullFlag = open_input_file(); + if (!m_startPullFlag) { qDebug() << "Please open video file first."; emit sendConnectFail(1); IsstopPlay = true; @@ -199,15 +207,40 @@ void Cffmpeg_decode::run() { firstDts = AV_NOPTS_VALUE; // 初始化第一帧的DTS startTime = av_gettime(); + int ret1; // 读取数据包 - while (av_read_frame(inputFormatCtx, inputPacket) >= 0) { - // ret = av_read_frame(inputFormatCtx, inputPacket); - // if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - // qDebug() << "ret:" << QString::number(ret); - // av_packet_unref(inputPacket); - // frm_cnt++; - // continue; - // } + while (m_startPullFlag) { + ret1 = av_read_frame(inputFormatCtx, inputPacket); + if (ret1 == AVERROR(EAGAIN)) { // 暂时没有数据流 + qDebug() << "No Stream Data"; + av_usleep(100000); // 等待 100 毫秒 + continue; + } else if (ret1 == AVERROR_EOF) { // 流文件结束 + qDebug() << "Stream End"; + break; + } else if (ret1 < 0) { // 发生错误 + qDebug() << "Stream Error"; + bool ss = reconnect(); + if (ss) { + qDebug() << "reconnect sucess"; + // 初始化 previous_pts_time 为无效值(拉流使用) + previous_pts_time = -1.0; // 表示没有上一帧的时间戳 + first_frame_pts_time = 0.0; // 第一帧的 PTS 时间 + first_frame_system_time = 0.0; // 第一帧解码时的系统时间 + + // 推流使用 + firstDts = AV_NOPTS_VALUE; // 初始化第一帧的DTS + startTime = av_gettime(); + + frm_cnt = 0; + continue; + } else { + qDebug() << "reconnect failed"; + break; + } + // break; + // continue; + } if (IsstopPlay) { qDebug() << "video play stop"; @@ -291,7 +324,7 @@ void Cffmpeg_decode::run() { // qDebug() << "pull stream sleep time:" // << QString::number(wait_time / 1000.0); if (wait_time > 0) { - av_usleep(wait_time); // 延时以同步 PTS + // av_usleep(wait_time); // 延时以同步 PTS } // 更新 previous_pts_time 为当前帧的 PTS previous_pts_time = pts_time; @@ -342,6 +375,11 @@ void Cffmpeg_decode::stop() { avformat_close_input(&inputFormatCtx); } +void Cffmpeg_decode::stopPull() { + m_startPullFlag = false; + m_reconnectFlag = false; +} + /** * @brief 设置拉流视频保存路径 * @param fileDirPath: 拉流视频保存路径 @@ -432,6 +470,26 @@ void Cffmpeg_decode::saveDone() { m_saveVideoFlag = false; } +bool Cffmpeg_decode::reconnect() { + m_reconnectFlag = true; + stop(); + for (int i = 0; i < MAXRECONNECT; ++i) { + initFFmpegContainer(); + m_startPullFlag = open_input_file(); + if (!m_startPullFlag) { + stop(); + } else { + IsstopPlay = false; + bOpenPushStreamFlag = false; + return true; + } + if (!m_reconnectFlag) + break; + qDebug() << "reconnect" << QString::number(i + 1); + } + return false; +} + void Cffmpeg_decode::setFlowType(QString param) { m_rtsp_transport = param; m_bSetRtspTransport = true; diff --git a/Src/Video/cffmpeg_decode.h b/Src/Video/cffmpeg_decode.h index c3c3609..6391a2b 100644 --- a/Src/Video/cffmpeg_decode.h +++ b/Src/Video/cffmpeg_decode.h @@ -23,6 +23,7 @@ public: bool open_input_file(); void stop(); + void stopPull(); /** * @brief 设置拉流保存文件夹路径 * @param fileDirPath 文件夹路径 @@ -87,7 +88,7 @@ private: double previous_pts_time; // 表示没有上一帧的时间戳 double first_frame_pts_time; // 第一帧的 PTS 时间 double first_frame_system_time; // 第一帧解码时的系统时间 - /******** 保存裸流使用 ******************/ + /****************** 保存裸流使用 ******************/ AVFormatContext *m_formatContextSave = nullptr; // 封装上下文 QString m_strCodecName; // 编解码器名称 AVStream *m_videoStreamOut = nullptr; // 输出视频流 @@ -97,7 +98,11 @@ private: bool m_bSetRtspTransport = false; private: + int MAXRECONNECT = 12; bool m_saveVideoFlag = false; + bool m_startPullFlag = false; + bool m_reconnectFlag = false; + void initFFmpegContainer(); /** * @brief 打开视频保存文件 * @return @@ -107,6 +112,12 @@ private: * @brief 视频保存成功,释放资源 */ void saveDone(); + + /** + * @brief 重连 + * @return 重连成功或失败 + */ + bool reconnect(); }; #endif // CFFMPEG_DECODE_H diff --git a/Src/Video/ffmpegvideodlg.cpp b/Src/Video/ffmpegvideodlg.cpp index c6dce82..f12c2ad 100644 --- a/Src/Video/ffmpegvideodlg.cpp +++ b/Src/Video/ffmpegvideodlg.cpp @@ -65,6 +65,7 @@ void ffmpegvideoDlg::play(QString url) { */ void ffmpegvideoDlg::stop() { if (m_PlayStatus) { + ffmpeg->stopPull(); ffmpeg->stop(); m_PlayStatus = false; ffmpeg->IsstopPlay = true; // 线程结束标志 diff --git a/global.cpp b/global.cpp index 5e04fa5..322282a 100644 --- a/global.cpp +++ b/global.cpp @@ -2,7 +2,7 @@ global::global() {} -QString g_SoftwareVersion = "版本号:V1.0.0.1011"; +QString g_SoftwareVersion = "版本号:V1.0.0.1014"; QColor g_themeColor(51, 51, 51); QString g_PushBtnStyle = /**正常情况下样式**/