zhuizhu 8 mesi fa
parent
commit
acfca9b0e9
1 ha cambiato i file con 124 aggiunte e 20 eliminazioni
  1. 124 20
      AvPlayer2/read_thread.cpp

+ 124 - 20
AvPlayer2/read_thread.cpp

@@ -1,6 +1,7 @@
 #include "read_thread.h"
 #include "AVPlayer2/playercontroller.h"
 #include <QLoggingCategory>
+#include <QString>
 Q_LOGGING_CATEGORY(playerControllerReadThread, "player.controller.ReadThread")
 
 extern int infinite_buffer;
@@ -34,6 +35,13 @@ int ReadThread::loop_read()
     int pkt_in_play_range = 0;
     int64_t stream_start_time = 0;
     int64_t pkt_ts = 0;
+    
+    // 重连计数器和配置
+    static int reconnect_count = 0;
+    static const int max_fast_reconnect_attempts = 3;  // 快速重连次数
+    static const int max_normal_reconnect_attempts = 10; // 正常重连次数
+    static bool infinite_reconnect_mode = false;       // 无限重连模式
+    
     if (!is)
         return ret;
 
@@ -156,33 +164,129 @@ int ReadThread::loop_read()
                 << ", pb_error:" << (is->ic->pb ? is->ic->pb->error : 0)
                 << "error:" << QString::fromUtf8(buf);
 
-            if ((ret == AVERROR_EOF || avio_feof(is->ic->pb)) && !is->eof) {
-                qCDebug(playerControllerReadThread) << "[ReadThread] EOF reached, send null packets.";
-                if (is->video_stream >= 0)
-                    packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
-                if (is->audio_stream >= 0)
-                    packet_queue_put_nullpacket(&is->audioq, pkt, is->audio_stream);
-                if (is->subtitle_stream >= 0)
-                    packet_queue_put_nullpacket(&is->subtitleq, pkt, is->subtitle_stream);
-                is->eof = 1;
-
-                // if (is->loop) {
-                //     stream_seek(is, 0, 0, 0);
-                // } else {
-                //     is->eof = 1;
-                //     break; // added for auto exit read thread
-                // }
-            }
-            if (is->ic->pb && is->ic->pb->error) {
-                qCWarning(playerControllerReadThread) << "[ReadThread] IO error detected.";
-                break;
+            // 统一处理EOF和I/O错误 - 无感重连策略
+            bool shouldAttemptReconnect = false;
+            bool isEOFError = (ret == AVERROR_EOF || avio_feof(is->ic->pb)) && !is->eof;
+            bool isIOError = is->ic->pb && is->ic->pb->error;
+            
+            // 增强错误检测:检查更多可能的网络错误
+            bool isNetworkError = (ret == AVERROR(EAGAIN) || ret == AVERROR(ETIMEDOUT) || 
+                                 ret == AVERROR(ECONNRESET) || ret == AVERROR(EPIPE) ||
+                                 ret == AVERROR(ECONNABORTED) || ret == -5); // -5 是常见的I/O错误
+            
+            if (isEOFError || isIOError || isNetworkError) {
+                if (isEOFError) {
+                    qCDebug(playerControllerReadThread) << "[ReadThread] EOF reached for stream:" << is->filename;
+                }
+                if (isIOError) {
+                    qCWarning(playerControllerReadThread) << "[ReadThread] IO error detected, error code:" << is->ic->pb->error;
+                }
+                if (isNetworkError) {
+                    qCWarning(playerControllerReadThread) << "[ReadThread] Network error detected, return code:" << ret;
+                }
+                
+                // 检查是否为实时流(RTMP、RTSP等)
+                bool isRealTimeStream = false;
+                if (is->filename) {
+                    QString filename = QString::fromUtf8(is->filename);
+                    isRealTimeStream = filename.startsWith("rtmp://") || 
+                                     filename.startsWith("rtsp://") || 
+                                     filename.startsWith("rtp://") ||
+                                     filename.startsWith("udp://") ||
+                                     filename.startsWith("tcp://") ||
+                                     filename.startsWith("http://") ||
+                                     filename.startsWith("https://");
+                }
+                
+                if (isRealTimeStream) {
+                    reconnect_count++;
+                    QString errorType = isEOFError ? "EOF" : (isIOError ? "I/O error" : "Network error");
+                    
+                    // 分层重连策略
+                    bool shouldContinueReconnect = false;
+                    int wait_time_ms = 0;
+                    
+                    if (reconnect_count <= max_fast_reconnect_attempts) {
+                        // 快速重连阶段:短间隔,快速恢复
+                        wait_time_ms = 500 * reconnect_count; // 0.5s, 1s, 1.5s
+                        shouldContinueReconnect = true;
+                        qCWarning(playerControllerReadThread) << "[ReadThread] Fast reconnection" << errorType 
+                                                             << "attempt" << reconnect_count << "/" << max_fast_reconnect_attempts;
+                    } else if (reconnect_count <= max_normal_reconnect_attempts) {
+                        // 正常重连阶段:较长间隔
+                        wait_time_ms = 2000 + (reconnect_count - max_fast_reconnect_attempts) * 1000; // 2s, 3s, 4s...
+                        shouldContinueReconnect = true;
+                        qCWarning(playerControllerReadThread) << "[ReadThread] Normal reconnection" << errorType 
+                                                             << "attempt" << reconnect_count << "/" << max_normal_reconnect_attempts;
+                    } else {
+                        // 无限重连模式:长间隔,但永不放弃
+                        infinite_reconnect_mode = true;
+                        wait_time_ms = 10000; // 固定10秒间隔
+                        shouldContinueReconnect = true;
+                        qCWarning(playerControllerReadThread) << "[ReadThread] Infinite reconnection mode" << errorType 
+                                                             << "attempt" << reconnect_count << "(never give up)";
+                    }
+                    
+                    if (shouldContinueReconnect) {
+                         // 清空当前的错误状态
+                         if (is->ic && is->ic->pb) {
+                             is->ic->pb->error = 0;
+                             is->ic->pb->eof_reached = 0;
+                         }
+                         
+                         // 无感重连:不设置EOF状态,保持播放器运行
+                         // 这样即使网络中断,播放器界面也不会退出
+                         qCDebug(playerControllerReadThread) << "[ReadThread] Seamless reconnection: waiting" << wait_time_ms << "ms...";
+                         
+                         std::unique_lock<std::mutex> lock(m_mutex);
+                         m_cv.wait_for(lock, std::chrono::milliseconds(wait_time_ms), [this, is] { return isExit() || is->abort_request; });
+                         
+                         if (!isExit() && !is->abort_request) {
+                             qCDebug(playerControllerReadThread) << "[ReadThread] Attempting seamless reconnection...";
+                             continue; // 继续循环,永不放弃,真正的无感重连
+                         }
+                     }
+                } else {
+                    // 非实时流:传统处理方式
+                    qCDebug(playerControllerReadThread) << "[ReadThread] File stream error, send null packets.";
+                    
+                    // 发送null packets并设置EOF
+                    if (isEOFError) {
+                        if (is->video_stream >= 0)
+                            packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
+                        if (is->audio_stream >= 0)
+                            packet_queue_put_nullpacket(&is->audioq, pkt, is->audio_stream);
+                        if (is->subtitle_stream >= 0)
+                            packet_queue_put_nullpacket(&is->subtitleq, pkt, is->subtitle_stream);
+                        is->eof = 1;
+                    }
+                    
+                    // 对于I/O错误,直接退出循环
+                    if (isIOError) {
+                        break;
+                    }
+                }
             }
+            // 错误处理已经统一到上面的逻辑中
 
             std::unique_lock<std::mutex> lock(m_mutex);
             m_cv.wait_for(lock, std::chrono::milliseconds(10), [this, is] { return isExit() || is->abort_request; });
             continue;
         } else {
             is->eof = 0;
+            
+            // 成功读取数据时重置重连计数器和状态
+            if (reconnect_count > 0) {
+                if (infinite_reconnect_mode) {
+                    qCInfo(playerControllerReadThread) << "[ReadThread] Stream recovered from infinite reconnection mode after" 
+                                                       << reconnect_count << "attempts";
+                    infinite_reconnect_mode = false;
+                } else {
+                    qCDebug(playerControllerReadThread) << "[ReadThread] Stream recovered, resetting reconnection counter";
+                }
+                reconnect_count = 0;
+            }
+            
             qCDebug(playerControllerReadThread)
                 << "[ReadThread] av_read_frame success, stream_index:" << pkt->stream_index
                 << ", pts:" << pkt->pts;