zhuizhu 8 mesi fa
parent
commit
2602818d22

+ 4 - 1
AvPlayer2/audio_play_thread.cpp

@@ -224,10 +224,13 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
                 // return -1;
             }
 
-            if (is->abort_request)
+            if (is->abort_request || isExit())
                 break;
         }
 
+        if (is->abort_request || isExit())
+            return -1;
+
         if (!(af = frame_queue_peek_readable(&is->sampq)))
             return -1;
         frame_queue_next(&is->sampq);

+ 1 - 18
AvPlayer2/mainwindowa.cpp

@@ -304,7 +304,7 @@ MainWindowA::MainWindowA(QWidget* parent)
 
     hide_play_control(false);
 
-    m_playerController->startToPlay("C:/Users/zhuizhu/Videos/1.mp4");
+    m_playerController->startToPlay("C:/Users/zhuizhu/Videos/2.mp4");
 }
 
 MainWindowA::~MainWindowA()
@@ -1012,23 +1012,6 @@ void MainWindowA::update_play_time()
     }
 }
 
-void MainWindowA::video_seek_inc(double incr) // incr seconds
-{
-    auto pState = m_playerController->state();
-    if (!pState)
-        return;
-
-    auto pos = get_master_clock(pState);
-
-    if (isnan(pos))
-        pos = (double) pState->seek_pos / AV_TIME_BASE;
-
-    qDebug("!seek_by_bytes pos=%lf", pos);
-
-    pos += incr;
-    m_playerController->videoSeek(pos, incr);
-}
-
 
 void MainWindowA::play_seek()
 {

+ 0 - 1
AvPlayer2/mainwindowa.h

@@ -159,7 +159,6 @@ private:
     void create_audio_effect();
     void show_audio_effect(bool bShow = true);
     void play_control_key(Qt::Key key);
-    void video_seek_inc(double incr);
     void update_menus();
     void enable_menus(bool enable = true);
     void enable_v_menus(bool enable = true);

+ 175 - 5
AvPlayer2/playercontroller.cpp

@@ -366,6 +366,7 @@ void PlayerController::playStarted(bool success)
 
 void PlayerController::playFailed(const QString& file)
 {
+    dump();
     qCWarning(playerControllerLog) << "Playback failed for file:" << toNativePath(file);
 
     // 确保状态一致性
@@ -383,39 +384,49 @@ void PlayerController::playFailed(const QString& file)
 // 线程 finished 槽函数只做日志和信号
 void PlayerController::readPacketStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Read packets thread stopped signal received.";
     //m_packetReadThread.reset();
 
-    if (m_videoState) {
-        m_videoState->delete_video_state();
+    auto state = m_videoState->get_state();
+    if (state) {
+        state->abort_request = 1;
     }
+    // if (m_videoState) {
+    //     m_videoState->delete_video_state();
+    // }
 
     emit audioStopped();
 }
 
 void PlayerController::decodeVideoStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Video decode thread stopped.";
 }
 
 void PlayerController::decodeAudioStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Audio decode thread stopped.";
 }
 
 void PlayerController::decodeSubtitleStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Subtitle decode thread stopped.";
 }
 
 void PlayerController::audioPlayStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Audio play thread stopped.";
     emit audioStopped();
 }
 
 void PlayerController::videoPlayStopped()
 {
+    dump();
     qCDebug(playerControllerLog) << "************* Video play thread stopped.";
     emit videoStopped();
 }
@@ -456,11 +467,45 @@ void PlayerController::videoSeek(double position, double increment)
         && position < state->ic->start_time / static_cast<double>(AV_TIME_BASE)) {
         position = state->ic->start_time / static_cast<double>(AV_TIME_BASE);
     }
+
+    // 边界检查:防止seek到超出视频时长的位置
+    double max_position = state->ic->duration / static_cast<double>(AV_TIME_BASE);
+    if (state->ic->start_time != AV_NOPTS_VALUE)
+        max_position += state->ic->start_time / static_cast<double>(AV_TIME_BASE);
+
+    qDebug() << "[videoSeek] 边界检查: position=" << position << ", max_position=" << max_position
+             << ", duration=" << state->ic->duration << ", start_time=" << state->ic->start_time;
+
+    // 更保守的边界检查:减去3秒作为安全边界
+    double safe_boundary = 3.0;
+    if (position > max_position - safe_boundary) {
+        qDebug() << "[videoSeek] 调整seek位置: 原始position=" << position
+                 << ", 最大position=" << max_position;
+        position = max_position - safe_boundary;
+        if (position < 0)
+            position = 0;
+        qDebug() << "[videoSeek] 调整后position=" << position;
+    }
+
     // 添加量化操作,精确到0.01秒
-    position = round(position * 100) / 100.0;
     qDebug() << "position:" << position
              << "position * AV_TIME_BASE:" << static_cast<int64_t>(position * AV_TIME_BASE)
              << "increment * AV_TIME_BASE:" << static_cast<int64_t>(increment * AV_TIME_BASE);
+
+    // 启用精确帧定位
+    int64_t target_pts = static_cast<int64_t>(position * AV_TIME_BASE);
+    // if (state->video_st) {
+    //     // 将目标时间转换为视频流的时间基准
+    //     target_pts = av_rescale_q(target_pts,
+    //                               AV_TIME_BASE_Q,
+    //                               state->video_st->time_base);
+    //     qDebug() << "[精确帧定位] 设置目标PTS:" << target_pts
+    //              << "原始位置(秒):" << position
+    //              << "视频时间基准:" << state->video_st->time_base.num << "/" << state->video_st->time_base.den;
+    //     state->exact_seek = 1;
+    //     state->target_pts = target_pts;
+    // }
+
     stream_seek(state,
                 static_cast<int64_t>(position * AV_TIME_BASE),
                 static_cast<int64_t>(increment * AV_TIME_BASE),
@@ -516,6 +561,41 @@ void PlayerController::videoSeekEx(double value, double maxValue)
         ts = frac * cur_stream->ic->duration;
         if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
             ts += cur_stream->ic->start_time;
+
+        // 边界检查:防止seek到超出视频时长的位置
+        int64_t max_ts = cur_stream->ic->duration;
+        if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
+            max_ts += cur_stream->ic->start_time;
+
+        qDebug() << "[videoSeekEx] 边界检查: ts=" << ts << ", max_ts=" << max_ts
+                 << ", duration=" << cur_stream->ic->duration
+                 << ", start_time=" << cur_stream->ic->start_time
+                 << ", 请求位置(秒)=" << ts / (double) AV_TIME_BASE
+                 << ", 最大位置(秒)=" << max_ts / (double) AV_TIME_BASE;
+
+        // 更保守的边界检查:减去3秒作为安全边界
+        int64_t safe_boundary = 3 * AV_TIME_BASE;
+        if (ts > max_ts - safe_boundary) {
+            qDebug() << "[videoSeekEx] 调整seek位置: 原始ts=" << ts << ", 最大ts=" << max_ts;
+            ts = max_ts - safe_boundary;
+            if (ts < 0)
+                ts = 0;
+            qDebug() << "[videoSeekEx] 调整后ts=" << ts
+                     << ", 调整后位置(秒)=" << ts / (double) AV_TIME_BASE;
+        }
+
+        // // 启用精确帧定位
+        // if (cur_stream->video_st) {
+        //     int64_t target_pts = av_rescale_q(ts,
+        //                                       AV_TIME_BASE_Q,
+        //                                       cur_stream->video_st->time_base);
+        //     qDebug() << "[精确帧定位Ex] 设置目标PTS:" << target_pts
+        //              << "原始位置(秒):" << ts / (double)AV_TIME_BASE
+        //              << "视频时间基准:" << cur_stream->video_st->time_base.num << "/" << cur_stream->video_st->time_base.den;
+        //     state->exact_seek = 1;
+        //     state->target_pts = target_pts;
+        // }
+
         stream_seek(cur_stream, ts, 0, 0);
     }
     return;
@@ -566,11 +646,15 @@ void PlayerController::stopAndResetThreads()
     if (m_videoState) {
         m_videoState->delete_video_state();
     }
+    // auto state = m_videoState->get_state();
+    // if (state) {
+    //     state->abort_request = 1;
+    // }
+    stopAndReset(m_packetReadThread, "PacketRead");
 
     stopAndReset(m_decodeVideoThread, "DecodeVideo");
     stopAndReset(m_decodeAudioThread, "DecodeAudio");
     stopAndReset(m_decodeSubtitleThread, "DecodeSubtitle");
-    stopAndReset(m_packetReadThread, "PacketRead");
 }
 
 bool PlayerController::areAllThreadsStopped() const
@@ -827,7 +911,7 @@ bool PlayerController::createAudioPlayThread()
     m_audioPlayThread->setOnFinished([this]() { audioPlayStopped(); });
     m_audioPlayThread->setOnUpdatePlayTime([this]() {
         // TODO: 实现 PlayerController::onUpdatePlayTime() 处理播放时间更新
-        //emit updatePlayTime();
+        emit updatePlayTime();
     });
     m_audioPlayThread->setOnDataVisualReady([this](const AudioData& data) {
         // 异步 ?
@@ -885,3 +969,89 @@ void PlayerController::onFrameReady(AVFrame* frame)
     // 这里可以做帧处理、缓存、同步等操作
     emit frameReady(frame); // 直接转发给 UI 层
 }
+
+void PlayerController::dump() const
+{
+    qCInfo(playerControllerLog) << "=== PlayerController Thread Status Dump ===";
+    qCInfo(playerControllerLog) << "Current State:" << static_cast<int>(m_state.load());
+    qCInfo(playerControllerLog) << "Current File:" << m_currentFile;
+
+    // 检查数据包读取线程
+    if (m_packetReadThread) {
+        qCInfo(playerControllerLog)
+            << "ReadThread: exists, isRunning:" << m_packetReadThread->isRunning()
+            << ", isFinished:" << m_packetReadThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "ReadThread: null";
+    }
+
+    // 检查视频解码线程
+    if (m_decodeVideoThread) {
+        qCInfo(playerControllerLog)
+            << "VideoDecodeThread: exists, isRunning:" << m_decodeVideoThread->isRunning()
+            << ", isFinished:" << m_decodeVideoThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "VideoDecodeThread: null";
+    }
+
+    // 检查音频解码线程
+    if (m_decodeAudioThread) {
+        qCInfo(playerControllerLog)
+            << "AudioDecodeThread: exists, isRunning:" << m_decodeAudioThread->isRunning()
+            << ", isFinished:" << m_decodeAudioThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "AudioDecodeThread: null";
+    }
+
+    // 检查字幕解码线程
+    if (m_decodeSubtitleThread) {
+        qCInfo(playerControllerLog)
+            << "SubtitleDecodeThread: exists, isRunning:" << m_decodeSubtitleThread->isRunning()
+            << ", isFinished:" << m_decodeSubtitleThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "SubtitleDecodeThread: null";
+    }
+
+    // 检查音频播放线程
+    if (m_audioPlayThread) {
+        qCInfo(playerControllerLog)
+            << "AudioPlayThread: exists, isRunning:" << m_audioPlayThread->isRunning()
+            << ", isFinished:" << m_audioPlayThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "AudioPlayThread: null";
+    }
+
+    // 检查视频播放线程
+    if (m_videoPlayThread) {
+        qCInfo(playerControllerLog)
+            << "VideoPlayThread: exists, isRunning:" << m_videoPlayThread->isRunning()
+            << ", isFinished:" << m_videoPlayThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "VideoPlayThread: null";
+    }
+
+    // 检查播放前准备线程
+    if (m_beforePlayThread) {
+        qCInfo(playerControllerLog)
+            << "StartPlayThread: exists, isRunning:" << m_beforePlayThread->isRunning()
+            << ", isFinished:" << m_beforePlayThread->isExit();
+    } else {
+        qCInfo(playerControllerLog) << "StartPlayThread: null";
+    }
+
+    // 检查初始化线程
+    if (m_initThread.joinable()) {
+        qCInfo(playerControllerLog) << "InitThread: joinable (running)";
+    } else {
+        qCInfo(playerControllerLog) << "InitThread: not joinable (stopped or not started)";
+    }
+
+    // 检查事件线程
+    if (m_eventThread.joinable()) {
+        qCInfo(playerControllerLog) << "EventThread: joinable (running)";
+    } else {
+        qCInfo(playerControllerLog) << "EventThread: not joinable (stopped or not started)";
+    }
+
+    qCInfo(playerControllerLog) << "=== End of Thread Status Dump ===";
+}

+ 5 - 0
AvPlayer2/playercontroller.h

@@ -161,6 +161,11 @@ public:
      */
     void setDeviceVolume(float volume);
 
+    /**
+     * @brief 打印所有线程状态信息
+     */
+    void dump() const;
+
 public slots:
     //--------------------------------------------------------------------------
     // 线程生命周期管理槽函数

+ 8 - 3
AvPlayer2/read_thread.cpp

@@ -136,15 +136,20 @@ int ReadThread::loop_read()
                 << "[ReadThread] queues full, waiting... audioq:" << is->audioq.size
                 << ", videoq:" << is->videoq.size << ", subtitleq:" << is->subtitleq.size;
             std::unique_lock<std::mutex> lock(m_mutex);
-            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this] { return isExit(); });
+            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this, is] { return isExit() || is->abort_request; });
             continue;
         }
 
         ret = av_read_frame(is->ic, pkt);
         if (ret < 0) {
+            char buf[256] = {0};
+            av_strerror(ret, buf, 256);
+
             qCWarning(playerControllerReadThread)
                 << "[ReadThread] av_read_frame failed, ret:" << ret << ", eof:" << is->eof
-                << ", pb_error:" << (is->ic->pb ? is->ic->pb->error : 0);
+                << ", 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)
@@ -167,7 +172,7 @@ int ReadThread::loop_read()
             }
 
             std::unique_lock<std::mutex> lock(m_mutex);
-            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this] { return isExit(); });
+            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this, is] { return isExit() || is->abort_request; });
             continue;
         } else {
             is->eof = 0;

+ 16 - 18
AvPlayer2/video_state.cpp

@@ -363,20 +363,19 @@ void VideoStateData::threads_exit_wait(VideoState* is)
     if (!is)
         return;
 
-    auto try_join_and_delete = [](ThreadBase*& t) {
+    auto try_stop_and_join = [](ThreadBase* t) {
         if (t) {
             t->stop(); // 通知线程退出
             t->join(); // 等待线程结束
-            delete t;  // 释放内存
-            t = nullptr;
+            // 注意:不要delete,线程对象由PlayerController的unique_ptr管理
         }
     };
 
-    try_join_and_delete(is->threads.video_play_tid);
-    try_join_and_delete(is->threads.audio_play_tid);
-    try_join_and_delete(is->threads.video_decode_tid);
-    try_join_and_delete(is->threads.audio_decode_tid);
-    try_join_and_delete(is->threads.subtitle_decode_tid);
+    try_stop_and_join(is->threads.video_play_tid);
+    try_stop_and_join(is->threads.audio_play_tid);
+    try_stop_and_join(is->threads.video_decode_tid);
+    try_stop_and_join(is->threads.audio_decode_tid);
+    try_stop_and_join(is->threads.subtitle_decode_tid);
 }
 
 void VideoStateData::stream_close(VideoState* is)
@@ -387,17 +386,16 @@ void VideoStateData::stream_close(VideoState* is)
 
     // SDL_WaitThread(is->read_tid, NULL);
     {
-        if (is->read_thread_exit != 0)
-            return;
-
-        if (is->threads.read_tid) {
-            av_log(nullptr, AV_LOG_INFO, "read thread wait before!\n");
-            is->threads.read_tid->join();
-            av_log(nullptr, AV_LOG_INFO, "read thread wait after!\n");
-            is->threads.read_tid = nullptr;
+        if (is->read_thread_exit == 0) {
+            if (is->threads.read_tid) {
+                is->threads.read_tid->stop();
+                av_log(nullptr, AV_LOG_INFO, "read thread wait before!\n");
+                is->threads.read_tid->join();
+                av_log(nullptr, AV_LOG_INFO, "read thread wait after!\n");
+                is->threads.read_tid = nullptr;
+            }
         }
     }
-
     /* close each stream */
     if (is->audio_stream >= 0)
         stream_component_close(is, is->audio_stream);
@@ -406,7 +404,7 @@ void VideoStateData::stream_close(VideoState* is)
     if (is->subtitle_stream >= 0)
         stream_component_close(is, is->subtitle_stream);
 
-    //threads_exit_wait(is); // 这里插入线程关闭 ?
+    //threads_exit_wait(is); // 等待所有线程退出
 
     avformat_close_input(&is->ic);
 

+ 3 - 2
main.cpp

@@ -52,12 +52,13 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q
 namespace avrecorder::video { void InitWinRTCapture(); }
 int main(int argc, char* argv[])
 {
-    QLoggingCategory::setFilterRules(QStringLiteral("player.controller.*.debug=false\n"));
+    // QLoggingCategory::setFilterRules(QStringLiteral("player.controller.*.debug=false\n"));
     // QLoggingCategory::setFilterRules("*.debug=false\n"
     //                                  "*.info=false\n"
     //                                  "*.warning=false\n"
     //                                  "*.critical=false\n"
-    //                                  "player.controller.*.debug=true\n");
+    //                                  "player.controller.*.debug=true\n"
+    //                                  "player.controller.*.info=true\n");
 
     // 打开日志文件(覆盖模式)
     g_logFile.setFileName("log.txt");