zhuizhu hai 8 meses
pai
achega
d6fcc42b91

+ 14 - 22
AvPlayer2/audio_decode_thread.cpp

@@ -8,13 +8,18 @@
 
 #include "audio_decode_thread.h"
 
-AudioDecodeThread::AudioDecodeThread(QObject* parent, VideoState* pState)
-    : QThread(parent)
-    , m_pState(pState)
+AudioDecodeThread::AudioDecodeThread(VideoState* pState)
+    : m_pState(pState)
 {}
 
 AudioDecodeThread::~AudioDecodeThread() {}
 
+void AudioDecodeThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 void AudioDecodeThread::run()
 {
     assert(m_pState);
@@ -34,8 +39,10 @@ void AudioDecodeThread::run()
         return;
 
     do {
-        /*if (is->abort_request)
-            break;*/
+        if (is->abort_request)
+            break;
+        if (m_exit)
+            break;
 
         if ((got_frame = decoder_decode_frame(&is->auddec, frame, nullptr)) < 0)
             goto the_end;
@@ -44,8 +51,6 @@ void AudioDecodeThread::run()
             tb = AVRational{1, frame->sample_rate};
 
 #if USE_AVFILTER_AUDIO
-            // dec_channel_layout = get_valid_channel_layout(frame->channel_layout,
-            // frame->ch_layout.nb_channels);
             dec_channel_layout = frame->ch_layout; // frame->channel_layout; //
 
             reconfigure = cmp_audio_fmts(is->audio_filter_src.fmt,
@@ -59,10 +64,6 @@ void AudioDecodeThread::run()
 
             if (reconfigure || is->req_afilter_reconfigure) {
                 char buf1[1024], buf2[1024];
-                // av_get_channel_layout_string(buf1, sizeof(buf1), -1,
-                // is->audio_filter_src.channel_layout);
-                // av_get_channel_layout_string(buf2, sizeof(buf2), -1,
-                // dec_channel_layout);
                 av_channel_layout_describe(&is->audio_filter_src.ch_layout, buf1, sizeof(buf1));
                 av_channel_layout_describe(&dec_channel_layout, buf2, sizeof(buf2));
 
@@ -106,7 +107,7 @@ void AudioDecodeThread::run()
                     goto the_end;
 
                 af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
-                af->pos = frame->pkt_pos; //AV_CODEC_FLAG_COPY_OPAQUE; //
+                af->pos = frame->pkt_pos;
                 af->serial = is->auddec.pkt_serial;
                 af->duration = av_q2d(AVRational{frame->nb_samples, frame->sample_rate});
 
@@ -120,15 +121,6 @@ void AudioDecodeThread::run()
             if (ret == AVERROR_EOF)
                 is->auddec.finished = is->auddec.pkt_serial;
 #endif
-
-#if PRINT_PACKETQUEUE_AUDIO_INFO
-            // int64 lld, double lf
-            qDebug("queue audio sample, pts:%lf, duration:%lf, pos:%lld, serial:%d",
-                   af->pts,
-                   af->duration,
-                   af->pos,
-                   af->serial);
-#endif
         }
     } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);
 
@@ -143,6 +135,6 @@ the_end:
 #endif
 
     av_frame_free(&frame);
-    qDebug("-------- audio decode thread exit.");
+    // 可加日志输出
     return;
 }

+ 6 - 5
AvPlayer2/audio_decode_thread.h

@@ -1,16 +1,17 @@
 #pragma once
 
-#include <QThread>
+#include "ThreadBase.h"
 #include "packets_sync.h"
+#include <atomic>
 
-class AudioDecodeThread : public QThread
+class AudioDecodeThread : public ThreadBase
 {
-    Q_OBJECT
-
 public:
-    explicit AudioDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    explicit AudioDecodeThread(VideoState* pState = nullptr);
     ~AudioDecodeThread();
 
+    void stop() override;
+
 protected:
     void run() override;
 

+ 103 - 88
AvPlayer2/audio_play_thread.cpp

@@ -7,6 +7,7 @@
 // ***********************************************************/
 
 #include "audio_play_thread.h"
+#include <utility>
 
 #if !NDEBUG
 #define DEBUG_PLAYFILTER 0
@@ -20,15 +21,17 @@
 #include <fstream>
 #endif
 
-AudioPlayThread::AudioPlayThread(QObject* parent, VideoState* pState) : QThread(parent), m_pState(pState)
+AudioPlayThread::AudioPlayThread(QObject* parent, VideoState* pState)
+    : QThread(parent)
+    , m_pOutput(nullptr)
+    , m_pState(pState)
 {
-    print_device();
+    // print_device();
     qRegisterMetaType<AudioData>("AudioData");
 }
 
 AudioPlayThread::~AudioPlayThread()
 {
-    // stop_thread();
     stop_device();
     final_resample_param();
 }
@@ -37,38 +40,41 @@ void AudioPlayThread::print_device() const
 {
     QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
     auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
-    for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
+    for (const QAudioDeviceInfo& deviceInfo : std::as_const(deviceInfos))
         qDebug() << "Input device name: " << deviceInfo.deviceName();
 
     deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
-    for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
+    for (const QAudioDeviceInfo& deviceInfo : std::as_const(deviceInfos))
         qDebug() << "Output device name: " << deviceInfo.deviceName();
 
-    auto edians = deviceInfo.supportedByteOrders();
+    const auto edians = deviceInfo.supportedByteOrders();
     for (const QAudioFormat::Endian& endian : edians)
         qDebug() << "Endian: " << endian;
-    auto sampleTypes = deviceInfo.supportedSampleTypes();
+    const auto sampleTypes = deviceInfo.supportedSampleTypes();
     for (const QAudioFormat::SampleType& sampleType : sampleTypes)
         qDebug() << "sampleType: " << sampleType;
 
-    auto codecs = deviceInfo.supportedCodecs();
+    const auto codecs = deviceInfo.supportedCodecs();
     for (const QString& codec : codecs)
         qDebug() << "codec: " << codec;
 
-    auto sampleRates = deviceInfo.supportedSampleRates();
+    const auto sampleRates = deviceInfo.supportedSampleRates();
     for (const int& sampleRate : sampleRates)
         qDebug() << "sampleRate: " << sampleRate;
 
-    auto ChannelCounts = deviceInfo.supportedChannelCounts();
+    const auto ChannelCounts = deviceInfo.supportedChannelCounts();
     for (const int& channelCount : ChannelCounts)
         qDebug() << "channelCount: " << channelCount;
 
-    auto sampleSizes = deviceInfo.supportedSampleSizes();
+    const auto sampleSizes = deviceInfo.supportedSampleSizes();
     for (const int& sampleSize : sampleSizes)
         qDebug() << "sampleSize: " << sampleSize;
 }
 
-bool AudioPlayThread::init_device(int sample_rate, int channel, AVSampleFormat sample_fmt, float default_vol)
+bool AudioPlayThread::init_device(int sample_rate,
+                                  int channel,
+                                  AVSampleFormat sample_fmt,
+                                  float default_vol)
 {
     QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
 
@@ -82,13 +88,15 @@ bool AudioPlayThread::init_device(int sample_rate, int channel, AVSampleFormat s
     format.setSampleType(QAudioFormat::SignedInt);
 
     // qDebug("sample size=%d\n", 8 * av_get_bytes_per_sample(sample_fmt));
-    if (!deviceInfo.isFormatSupported(format))
-    {
+    if (!deviceInfo.isFormatSupported(format)) {
         qWarning() << "Raw audio format not supported!";
         return false;
     }
 
-    m_pOutput = std::make_unique<QAudioOutput>(deviceInfo, format);
+    // m_pOutput = std::make_unique<QAudioOutput>(deviceInfo, format);
+    m_pOutput = new QAudioOutput(deviceInfo, format, this); // this 为 parent
+    // 析构时 Qt 自动 delete
+
     set_device_volume(default_vol);
 
     m_audioDevice = m_pOutput->start();
@@ -111,8 +119,7 @@ void AudioPlayThread::set_device_volume(float volume)
 
 void AudioPlayThread::stop_device()
 {
-    if (m_pOutput)
-    {
+    if (m_pOutput) {
         m_pOutput->stop();
         m_pOutput->reset();
     }
@@ -132,14 +139,12 @@ void AudioPlayThread::play_buf(const uint8_t* buf, int datasize)
     if (!m_audioDevice)
         return;
 
-    uint8_t* data = (uint8_t*)buf;
-    while (datasize > 0)
-    {
-        qint64 len = m_audioDevice->write((const char*)data, datasize);
+    uint8_t* data = (uint8_t*) buf;
+    while (datasize > 0) {
+        qint64 len = m_audioDevice->write((const char*) data, datasize);
         if (len < 0)
             break;
-        if (len > 0)
-        {
+        if (len > 0) {
             data = data + len;
             datasize -= len;
         }
@@ -153,16 +158,14 @@ void AudioPlayThread::run()
     VideoState* is = m_pState;
     int audio_size;
 
-    for (;;)
-    {
+    for (;;) {
         if (m_bExitThread)
             break;
 
         if (is->abort_request)
             break;
 
-        if (is->paused)
-        {
+        if (is->paused) {
             msleep(10);
             continue;
         }
@@ -171,16 +174,19 @@ void AudioPlayThread::run()
         if (audio_size < 0)
             break;
 
-        if (!isnan(is->audio_clock))
-        {
+        if (!isnan(is->audio_clock)) {
             AVCodecContext* pAudioCodex = is->auddec.avctx;
-            if (pAudioCodex)
-            {
-                int bytes_per_sec = av_samples_get_buffer_size(nullptr, pAudioCodex->ch_layout.nb_channels,
-                                                               pAudioCodex->sample_rate, AV_SAMPLE_FMT_S16, 1);
+            if (pAudioCodex) {
+                int bytes_per_sec = av_samples_get_buffer_size(nullptr,
+                                                               pAudioCodex->ch_layout.nb_channels,
+                                                               pAudioCodex->sample_rate,
+                                                               AV_SAMPLE_FMT_S16,
+                                                               1);
                 int64_t audio_callback_time = av_gettime_relative();
-                set_clock_at(&is->audclk, is->audio_clock - (double)(audio_size) / bytes_per_sec,
-                             is->audio_clock_serial, audio_callback_time / 1000000.0);
+                set_clock_at(&is->audclk,
+                             is->audio_clock - (double) (audio_size) / bytes_per_sec,
+                             is->audio_clock_serial,
+                             audio_callback_time / 1000000.0);
                 sync_clock_to_slave(&is->extclk, &is->audclk);
             }
         }
@@ -194,17 +200,12 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
     int data_size;
     Frame* af;
 
-    do
-    {
-        while (frame_queue_nb_remaining(&is->sampq) == 0)
-        {
-            if (is->eof)
-            {
+    do {
+        while (frame_queue_nb_remaining(&is->sampq) == 0) {
+            if (is->eof) {
                 // break;
                 return -1;
-            }
-            else
-            {
+            } else {
                 av_usleep(1000);
                 // return -1;
             }
@@ -223,23 +224,29 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
           AVSampleFormat(af->frame->format), 1);*/
 
 #if USE_AVFILTER_AUDIO
-    data_size = av_samples_get_buffer_size(nullptr, af->frame->ch_layout.nb_channels,
-                                           af->frame->nb_samples, AV_SAMPLE_FMT_S16, 1);
-    uint8_t* const buffer_audio = (uint8_t*)av_malloc(data_size * sizeof(uint8_t));
+    data_size = av_samples_get_buffer_size(nullptr,
+                                           af->frame->ch_layout.nb_channels,
+                                           af->frame->nb_samples,
+                                           AV_SAMPLE_FMT_S16,
+                                           1);
+    uint8_t* const buffer_audio = (uint8_t*) av_malloc(data_size * sizeof(uint8_t));
 
     memcpy(buffer_audio, af->frame->data[0], data_size);
 #else
     struct SwrContext* swrCtx = m_audioResample.swrCtx;
-    data_size = av_samples_get_buffer_size(
-        nullptr, af->frame->channels, af->frame->nb_samples, AV_SAMPLE_FMT_S16,
-        0); // AVSampleFormat(af->frame->format)
-    uint8_t* buffer_audio = (uint8_t*)av_malloc(data_size * sizeof(uint8_t));
-
-    int ret =
-        swr_convert(swrCtx, &buffer_audio, af->frame->nb_samples,
-                    (const uint8_t**)(af->frame->data), af->frame->nb_samples);
-    if (ret < 0)
-    {
+    data_size = av_samples_get_buffer_size(nullptr,
+                                           af->frame->channels,
+                                           af->frame->nb_samples,
+                                           AV_SAMPLE_FMT_S16,
+                                           0); // AVSampleFormat(af->frame->format)
+    uint8_t* buffer_audio = (uint8_t*) av_malloc(data_size * sizeof(uint8_t));
+
+    int ret = swr_convert(swrCtx,
+                          &buffer_audio,
+                          af->frame->nb_samples,
+                          (const uint8_t**) (af->frame->data),
+                          af->frame->nb_samples);
+    if (ret < 0) {
         return 0;
     }
 #endif
@@ -250,17 +257,14 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
 #if WRITE_AUDIO_FILE
     std::ofstream myfile;
     myfile.open("audio.pcm", std::ios::out | std::ios::app | std::ios::binary);
-    if (myfile.is_open())
-    {
-        myfile.write((char*)buffer_audio, data_size);
+    if (myfile.is_open()) {
+        myfile.write((char*) buffer_audio, data_size);
     }
 #endif
 
-    if (m_bSendToVisual)
-    {
+    if (m_bSendToVisual) {
         AudioData data;
-        if (data_size > BUFFER_LEN)
-        {
+        if (data_size > BUFFER_LEN) {
             qDebug() << "audio frame is too long,data_size:" << data_size
                      << ", buffer_len:" << BUFFER_LEN << "\n";
         }
@@ -273,19 +277,19 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
 
     play_buf(buffer_audio, data_size);
 
-    av_free((void*)buffer_audio);
+    av_free((void*) buffer_audio);
 
     /* update the audio clock with the pts */
-    if (!isnan(af->pts))
-    {
+    if (!isnan(af->pts)) {
         // is->audio_clock = af->pts + (double)af->frame->nb_samples /
         // af->frame->sample_rate;
-        double frame = (double)af->frame->nb_samples / af->frame->sample_rate;
+        double frame = (double) af->frame->nb_samples / af->frame->sample_rate;
         // frame = frame * is->audio_speed;
         is->audio_clock = af->pts + frame;
 
 #if USE_AVFILTER_AUDIO
-        is->audio_clock = is->audio_clock_old + (is->audio_clock - is->audio_clock_old) * is->audio_speed;
+        is->audio_clock = is->audio_clock_old
+                          + (is->audio_clock - is->audio_clock_old) * is->audio_speed;
         // is->audio_clock = is->audio_clock * is->audio_speed;
 #endif
 
@@ -294,16 +298,18 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
         pks_num++;
 
         qDebug("[%d]audio: clock=%0.3f pts=%0.3f, (nb:%d, sr:%d)frame:%0.3f\n",
-               pks_num, is->audio_clock, af->pts, af->frame->nb_samples,
-               af->frame->sample_rate, frame);
+               pks_num,
+               is->audio_clock,
+               af->pts,
+               af->frame->nb_samples,
+               af->frame->sample_rate,
+               frame);
 
         // qDebug("audio: clock=%0.3f pts=%0.3f, (nb:%d, sr:%d)frame:%0.3f\n",
         // is->audio_clock, af->pts, af->frame->nb_samples, af->frame->sample_rate,
         // frame);
 #endif
-    }
-    else
-    {
+    } else {
         is->audio_clock = NAN;
     }
     is->audio_clock_serial = af->serial;
@@ -321,15 +327,15 @@ int AudioPlayThread::audio_decode_frame(VideoState* is)
     return data_size;
 }
 
-bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio, AVSampleFormat sample_fmt, VideoState* is)
+bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio,
+                                          AVSampleFormat sample_fmt,
+                                          VideoState* is)
 {
-    if (pAudio)
-    {
+    if (pAudio) {
         int ret = -1;
         struct SwrContext* swrCtx = nullptr;
 #if USE_AVFILTER_AUDIO
-        if (is)
-        {
+        if (is) {
             AVFilterContext* sink = is->out_audio_filter;
             // int sample_rate = av_buffersink_get_sample_rate(sink);
             // int nb_channels = av_buffersink_get_channels(sink);
@@ -339,9 +345,14 @@ bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio, AVSampleFormat
             // int64_t channel_layout = av_buffersink_get_channel_layout(sink);
             int format = av_buffersink_get_format(sink);
 
-            ret = swr_alloc_set_opts2(&swrCtx, &pAudio->ch_layout, sample_fmt,
-                                      pAudio->sample_rate, &channel_layout,
-                                      (AVSampleFormat)format, pAudio->sample_rate, 0,
+            ret = swr_alloc_set_opts2(&swrCtx,
+                                      &pAudio->ch_layout,
+                                      sample_fmt,
+                                      pAudio->sample_rate,
+                                      &channel_layout,
+                                      (AVSampleFormat) format,
+                                      pAudio->sample_rate,
+                                      0,
                                       nullptr);
 
             /*m_audioResample.channel_layout = channel_layout;
@@ -349,14 +360,18 @@ bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio, AVSampleFormat
       m_audioResample.sample_rate = pAudio->sample_rate;*/
         }
 #else
-        ret = swr_alloc_set_opts2(&swrCtx, &pAudio->ch_layout, sample_fmt,
-                                  pAudio->sample_rate, &pAudio->ch_layout,
-                                  pAudio->sample_fmt, pAudio->sample_rate, 0,
+        ret = swr_alloc_set_opts2(&swrCtx,
+                                  &pAudio->ch_layout,
+                                  sample_fmt,
+                                  pAudio->sample_rate,
+                                  &pAudio->ch_layout,
+                                  pAudio->sample_fmt,
+                                  pAudio->sample_rate,
+                                  0,
                                   nullptr);
 #endif
 
-        if (!(ret < 0))
-        {
+        if (!(ret < 0)) {
             swr_init(swrCtx);
             m_audioResample.swrCtx = swrCtx;
             return true;
@@ -373,5 +388,5 @@ void AudioPlayThread::final_resample_param()
 void AudioPlayThread::stop_thread()
 {
     m_bExitThread = true;
-    wait();
+    // 如有条件变量/阻塞等待,这里唤醒
 }

+ 3 - 2
AvPlayer2/audio_play_thread.h

@@ -11,6 +11,7 @@
 #include <QWaitCondition>
 
 #include <memory>
+#include <atomic>
 
 #include "packets_sync.h"
 
@@ -75,10 +76,10 @@ private:
     } Audio_Resample;
 
 private:
-    std::unique_ptr<QAudioOutput> m_pOutput;
+    QAudioOutput* m_pOutput;
     QIODevice* m_audioDevice{nullptr};
     VideoState* m_pState{nullptr};
     Audio_Resample m_audioResample;
-    bool m_bExitThread{false};
+    std::atomic<bool> m_bExitThread{false}; // 统一为原子变量
     bool m_bSendToVisual{false};
 };

+ 165 - 97
AvPlayer2/playercontroller.cpp

@@ -28,119 +28,169 @@ PlayerController::PlayerController(QWidget* parent)
     : QObject(parent)
 {
     ffmpeg_init();
+    connect(this,
+            &PlayerController::asyncInitFinished,
+            this,
+            &PlayerController::onAsyncInitFinished);
+    m_state = PlayerState::Idle;
 }
 
 PlayerController::~PlayerController()
 {
-    // 等待初始化线程安全退出
     if (m_initThread.joinable()) {
-        {
-            std::lock_guard<std::mutex> lock(m_initMutex);
-            m_initInProgress = false;
-        }
-        m_initCv.notify_all();
         m_initThread.join();
     }
     stopPlay();
 }
 
-// 播放控制接口
 void PlayerController::startToPlay(const QString& file)
 {
-    if (isPlaying()) {
-        if (m_currentFile == file)
-            return;
-        waitStopPlay(file);
+    std::lock_guard<std::mutex> lock(m_stopMutex);
+    // 自愈:如果状态为Playing但所有线程都已退出,强制Idle
+    if (m_state == PlayerState::Playing) {
+        if (!m_packetReadThread && !m_decodeVideoThread && !m_decodeAudioThread &&
+            !m_audioPlayThread && !m_videoPlayThread && !m_decodeSubtitleThread) {
+            m_videoState.reset();
+            m_currentFile.clear();
+            m_state = PlayerState::Idle;
+            qDebug() << "[PlayerController] All threads stopped, force reset to Idle.";
+        }
+    }
+    if (m_state == PlayerState::Initializing) {
+        qDebug() << "Player is initializing. Ignoring request.";
         return;
     }
-    m_currentFile = file;
-    if (m_initInProgress) return; // 正在初始化,直接返回
-    m_initInProgress = true;
-    if (m_initThread.joinable()) m_initThread.join();
-    m_initThread = std::thread(&PlayerController::asyncInit, this, file);
+    if (m_state == PlayerState::Playing) {
+        if (m_currentFile == file) {
+            qDebug() << "Already playing the same file. Ignoring request.";
+            return;
+        } else {
+            qDebug() << "Player is busy with another file, stopping and switching to:" << file;
+            stopPlay();
+            // 这里直接 fallthrough 到 Idle 状态
+        }
+    }
+    if (m_state == PlayerState::Idle) {
+        qDebug() << "Player is idle. Starting playback for:" << file;
+        m_state = PlayerState::Initializing;
+        m_currentFile = file;
+        if (m_initThread.joinable()) {
+            m_initThread.join();
+        }
+        m_initThread = std::thread(&PlayerController::asyncInit, this, file);
+    }
 }
 
 void PlayerController::asyncInit(const QString& file)
 {
     bool success = false;
-    // startPlay 的主体迁移到这里
     QElapsedTimer timer;
     timer.start();
+    qDebug("[Init] asyncInit started");
     if (file.isEmpty()) {
         qWarning("Filename is invalid. Please select a valid media file.");
         success = false;
     } else {
-        qInfo("Starting playback: %s", qUtf8Printable(toNativePath(file)));
-        if (!createReadThread()) {
-            qWarning("Packet read thread creation failed");
-            success = false;
-        } else if (!createVideoState(file)) {
-            qWarning("Video state creation failed");
-            readPacketStopped();
-            success = false;
-        } else if (!m_videoState) {
-            qWarning("Video state initialization error");
-            success = false;
-        } else {
-            m_packetReadThread->set_video_state(m_videoState->get_state());
-            const bool hasVideo = playingHasVideo();
-            const bool hasAudio = playingHasAudio();
-            const bool hasSubtitle = playingHasSubtitle();
-            if (hasVideo) {
-                if (!createDecodeVideoThread() || !createVideoPlayThread()) {
-                    qWarning("Video processing setup failed");
-                    success = false;
-                }
-            }
-            if (hasAudio) {
-                if (!createDecodeAudioThread() || !createAudioPlayThread()) {
-                    qWarning("Audio processing setup failed");
-                    success = false;
-                }
-            }
-            if (hasSubtitle && !createDecodeSubtitleThread()) {
-                qWarning("Subtitle processing setup failed");
-                success = false;
-            }
-            // 开始播放
-            if (hasAudio) {
-                // 音频设备初始化在独立线程中完成
-                startPlayThread();
-            } else {
-                playStarted();
-            }
-            success = true;
-        }
-    }
-    {
-        std::lock_guard<std::mutex> lock(m_initMutex);
-        m_initSuccess = success;
-        m_initInProgress = false;
+        qInfo("Check file: %s", qUtf8Printable(toNativePath(file)));
+        success = true;
     }
-    m_initCv.notify_all();
-    onInitFinished(success);
-    qDebug("Playback initialized in %lld ms", timer.elapsed());
+    m_initSuccess = success;
+    emit asyncInitFinished(file, success);
+    qDebug("[Init] asyncInit finished in %lld ms", timer.elapsed());
 }
 
-void PlayerController::onInitFinished(bool success)
+void PlayerController::onAsyncInitFinished(const QString& file, bool success)
 {
-    // 这里可以发 Qt 信号或回调
+    std::lock_guard<std::mutex> lock(m_stopMutex);
     if (!success) {
         playFailed(m_currentFile);
+        m_state = PlayerState::Idle;
+        return;
+    }
+    qDebug("[Init] createReadThread...");
+    if (!createReadThread()) {
+        qWarning("Packet read thread creation failed");
+        playFailed(m_currentFile);
+        m_state = PlayerState::Idle;
         return;
     }
+    qDebug("[Init] createVideoState...");
+    if (!createVideoState(m_currentFile)) {
+        qWarning("Video state creation failed");
+        readPacketStopped();
+        playFailed(m_currentFile);
+        m_state = PlayerState::Idle;
+        return;
+    }
+    assert(m_videoState);
+    if (!m_videoState) {
+        qWarning("Video state initialization error");
+        playFailed(m_currentFile);
+        m_state = PlayerState::Idle;
+        return;
+    }
+    m_packetReadThread->set_video_state(m_videoState->get_state());
+    const bool hasVideo = playingHasVideo();
+    const bool hasAudio = playingHasAudio();
+    const bool hasSubtitle = playingHasSubtitle();
+    if (hasVideo) {
+        if (!createDecodeVideoThread() || !createVideoPlayThread()) {
+            qWarning("Video processing setup failed");
+            playFailed(m_currentFile);
+            stopPlay();
+            m_state = PlayerState::Idle;
+            return;
+        }
+    }
+    if (hasAudio) {
+        if (!createDecodeAudioThread() || !createAudioPlayThread()) {
+            qWarning("Audio processing setup failed");
+            playFailed(m_currentFile);
+            stopPlay();
+            m_state = PlayerState::Idle;
+            return;
+        }
+    }
+    if (hasSubtitle && !createDecodeSubtitleThread()) {
+        qWarning("Subtitle processing setup failed");
+        playFailed(m_currentFile);
+        stopPlay();
+        m_state = PlayerState::Idle;
+        return;
+    }
+    if (hasAudio) {
+        startPlayThread();
+    } else {
+        playStarted();
+    }
     emit startToPlaySignal();
+    m_state = PlayerState::Playing;
 }
 
 void PlayerController::stopPlay()
 {
-    deleteVideoState();
-    m_packetReadThread.reset();
-    m_decodeVideoThread.reset();
-    m_decodeAudioThread.reset();
-    m_decodeSubtitleThread.reset();
-    m_videoPlayThread.reset();
-    m_audioPlayThread.reset();
+    std::lock_guard<std::mutex> lock(m_stopMutex);
+    if (m_state == PlayerState::Idle)
+        return;
+    m_state = PlayerState::Stopping;
+    auto stopAndReset = [](auto& threadPtr) {
+        if (threadPtr) {
+            threadPtr->stop_thread();
+            threadPtr->wait(3000); // 最多等3秒
+            threadPtr.reset();
+        }
+    };
+    stopAndReset(m_stopPlayWaitingThread);
+    stopAndReset(m_beforePlayThread);
+    stopAndReset(m_packetReadThread);
+    stopAndReset(m_decodeVideoThread);
+    stopAndReset(m_decodeAudioThread);
+    stopAndReset(m_decodeSubtitleThread);
+    stopAndReset(m_videoPlayThread);
+    stopAndReset(m_audioPlayThread);
+    m_videoState.reset();
+    m_currentFile.clear();
+    m_state = PlayerState::Idle;
 }
 
 void PlayerController::pausePlay()
@@ -207,8 +257,7 @@ QString PlayerController::playingFile() const
 
 bool PlayerController::isPlaying() const
 {
-    return m_videoState || m_packetReadThread || m_decodeVideoThread || m_decodeAudioThread
-           || m_audioPlayThread || m_videoPlayThread || m_decodeSubtitleThread;
+    return m_state == PlayerState::Playing;
 }
 
 bool PlayerController::playingHasVideo()
@@ -259,48 +308,45 @@ void PlayerController::playFailed(const QString& file)
     emit showMessage(QString("Playback failed: %1").arg(toNativePath(file)), "Warning", "");
 }
 
-// 线程生命周期管理槽函数
+// 线程 finished 槽函数只做日志和信号
 void PlayerController::readPacketStopped()
 {
-    if (m_packetReadThread) {
-        m_packetReadThread.reset();
-        qDebug("************* Read packets thread stopped.");
-    }
-
-    stopPlay();
+    qDebug("************* Read packets thread stopped signal received.");
+    m_packetReadThread.reset();
+    checkAndResetState();
+    emit audioStopped();
 }
-
 void PlayerController::decodeVideoStopped()
 {
-    m_decodeVideoThread.reset();
     qDebug("************* Video decode thread stopped.");
+    m_decodeVideoThread.reset();
+    checkAndResetState();
 }
-
 void PlayerController::decodeAudioStopped()
 {
-    m_decodeAudioThread.reset();
     qDebug("************* Audio decode thread stopped.");
+    m_decodeAudioThread.reset();
+    checkAndResetState();
 }
-
 void PlayerController::decodeSubtitleStopped()
 {
-    m_decodeSubtitleThread.reset();
     qDebug("************* Subtitle decode thread stopped.");
+    m_decodeSubtitleThread.reset();
+    checkAndResetState();
 }
-
 void PlayerController::audioPlayStopped()
 {
-    m_audioPlayThread.reset();
     qDebug("************* Audio play thread stopped.");
-    emit audioStopped(); // 音频结束
+    emit audioStopped();
+    m_audioPlayThread.reset();
+    checkAndResetState();
 }
-
 void PlayerController::videoPlayStopped()
 {
-    m_videoPlayThread.reset();
     qDebug("************* Video play thread stopped.");
-
-    emit videoStopped(); // 视频结束
+    emit videoStopped();
+    m_videoPlayThread.reset();
+    checkAndResetState();
 }
 
 // 线程管理槽函数
@@ -492,6 +538,28 @@ void PlayerController::videoSeekInc(double increment)
     videoSeek(position, increment);
 }
 
+// 新增自愈机制辅助函数
+void PlayerController::checkAndResetState()
+{
+    std::lock_guard<std::mutex> lock(m_stopMutex);
+    if (!m_packetReadThread && !m_decodeVideoThread && !m_decodeAudioThread &&
+        !m_audioPlayThread && !m_videoPlayThread && !m_decodeSubtitleThread) {
+        m_videoState.reset();
+        m_currentFile.clear();
+        m_state = PlayerState::Idle;
+        qDebug() << "[PlayerController] All threads stopped, state reset to Idle.";
+        emit playbackFinished(); // 新增:通知UI
+    } else {
+        qDebug() << "[PlayerController] checkAndResetState: "
+                 << "m_packetReadThread:" << (bool)m_packetReadThread
+                 << "m_decodeVideoThread:" << (bool)m_decodeVideoThread
+                 << "m_decodeAudioThread:" << (bool)m_decodeAudioThread
+                 << "m_audioPlayThread:" << (bool)m_audioPlayThread
+                 << "m_videoPlayThread:" << (bool)m_videoPlayThread
+                 << "m_decodeSubtitleThread:" << (bool)m_decodeSubtitleThread;
+    }
+}
+
 // 线程创建方法
 bool PlayerController::createVideoState(const QString& file)
 {

+ 21 - 12
AvPlayer2/playercontroller.h

@@ -1,16 +1,10 @@
 #pragma once
 
-#include <QActionGroup>
 #include <QElapsedTimer>
-#include <QMimeData>
-#include <QMouseEvent>
 #include <QMutex>
 #include <QObject>
-#include <QSettings>
-#include <QSizePolicy>
 #include <QThread>
-#include <QTimer>
-#include <QWidget>
+
 #include <memory>
 #include <thread>
 #include <atomic>
@@ -38,6 +32,13 @@ class PlayerController : public QObject
     Q_OBJECT
 
 public:
+    enum class PlayerState {
+        Idle,
+        Initializing,
+        Playing,
+        Stopping
+    };
+
     explicit PlayerController(QWidget* parent = nullptr);
     ~PlayerController();
 
@@ -62,7 +63,7 @@ public:
     float deviceVolume() const;
     void setDeviceVolume(float volume);
 
-    // // 线程访问接口
+    // 线程访问接口
     AudioPlayThread* audioPlayThread() const { return m_audioPlayThread.get(); }
     VideoPlayThread* videoPlayThread() const { return m_videoPlayThread.get(); }
     VideoStateData* videoStateData() const { return m_videoState.get(); }
@@ -97,6 +98,7 @@ signals:
 
     void audioStopped(); // 音频结束
     void videoStopped(); // 视频结束
+    void playbackFinished(); // 新增:播放自然结束
 
     // 多媒体数据处理
     void frameReady(AVFrame*);
@@ -113,6 +115,11 @@ signals:
     void requestFullscreen(bool fullscreen);
     void requestHideStatusBar(bool hide);
 
+    void asyncInitFinished(const QString& file, bool success);
+
+private slots:
+    void onAsyncInitFinished(const QString& file, bool success);
+
 private:
     // 核心播放逻辑
     bool startPlay();
@@ -142,12 +149,13 @@ private:
 
     // 异步初始化相关
     void asyncInit(const QString& file);
-    void onInitFinished(bool success);
     std::thread m_initThread;
     std::atomic<bool> m_initInProgress{false};
-    std::mutex m_initMutex;
-    std::condition_variable m_initCv;
-    bool m_initSuccess{false};
+    std::atomic<bool> m_initSuccess{false};
+    std::mutex m_stopMutex;
+
+    // 状态机
+    std::atomic<PlayerState> m_state{PlayerState::Idle};
 
 private:
     // 多媒体线程
@@ -164,4 +172,5 @@ private:
     std::unique_ptr<StopWaitingThread> m_stopPlayWaitingThread;
 
     QString m_currentFile; // 更清晰的命名
+    void checkAndResetState();
 };

+ 18 - 26
AvPlayer2/read_thread.cpp

@@ -12,13 +12,15 @@ extern int infinite_buffer;
 extern int64_t start_time;
 static int64_t duration = AV_NOPTS_VALUE;
 
-ReadThread::ReadThread(QObject* parent, VideoState* pState)
-    : QThread(parent), m_pPlayData(pState)
+ReadThread::ReadThread(VideoState* pState)
+    : m_pPlayData(pState)
 {
 }
 
 ReadThread::~ReadThread()
 {
+    stop();
+    join();
 }
 
 void ReadThread::set_video_state(VideoState* pState)
@@ -27,6 +29,12 @@ void ReadThread::set_video_state(VideoState* pState)
     m_pPlayData = pState;
 }
 
+void ReadThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 int ReadThread::loop_read()
 {
     int ret = -1;
@@ -35,9 +43,6 @@ int ReadThread::loop_read()
     int pkt_in_play_range = 0;
     int64_t stream_start_time = 0;
     int64_t pkt_ts = 0;
-    // AVFormatContext* pFormatCtx = is->ic;
-    assert(is);
-    // assert(pFormatCtx);
     if (!is)
         return ret;
 
@@ -53,6 +58,9 @@ int ReadThread::loop_read()
 
     for (;;)
     {
+        if (m_exit)
+            break;
+
         if (is->abort_request)
             break;
 
@@ -72,9 +80,6 @@ int ReadThread::loop_read()
                 is->seek_rel > 0 ? seek_target - is->seek_rel + 2 : INT64_MIN;
             int64_t seek_max =
                 is->seek_rel < 0 ? seek_target - is->seek_rel - 2 : INT64_MAX;
-            // FIXME the +-2 is due to rounding being not done in the correct
-            // direction in generation
-            //      of the seek_pos/seek_rel variables
 
             ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max,
                                      is->seek_flags);
@@ -131,11 +136,8 @@ int ReadThread::loop_read()
               stream_has_enough_packets(is->subtitle_st, is->subtitle_stream,
                                         &is->subtitleq))))
         {
-            /* wait 10 ms */
-            m_waitMutex.lock();
-            // SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
-            is->continue_read_thread->wait(&m_waitMutex, 10);
-            m_waitMutex.unlock();
+            std::unique_lock<std::mutex> lock(m_mutex);
+            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this]{ return m_exit; });
             continue;
         }
 
@@ -166,9 +168,8 @@ int ReadThread::loop_read()
                 break;
             }
 
-            m_waitMutex.lock();
-            is->continue_read_thread->wait(&m_waitMutex, 10);
-            m_waitMutex.unlock();
+            std::unique_lock<std::mutex> lock(m_mutex);
+            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this]{ return m_exit; });
             continue;
         }
         else
@@ -205,8 +206,6 @@ int ReadThread::loop_read()
         {
             av_packet_unref(pkt);
         }
-
-        // print_state_info(is);
     }
 
     is->read_thread_exit = -1;
@@ -217,12 +216,5 @@ int ReadThread::loop_read()
 void ReadThread::run()
 {
     int ret = loop_read();
-    if (ret < 0)
-    {
-        qDebug("-------- Read packets thread exit, with error=%d\n", ret);
-    }
-    else
-    {
-        qDebug("-------- Read packets thread exit.");
-    }
+    // 可加日志输出
 }

+ 6 - 10
AvPlayer2/read_thread.h

@@ -1,28 +1,24 @@
 #pragma once
 
-#include <QThread>
+#include "ThreadBase.h"
 #include "packets_sync.h"
+#include <atomic>
 
-class ReadThread : public QThread
+class ReadThread : public ThreadBase
 {
-    Q_OBJECT
-
 public:
-    explicit ReadThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    explicit ReadThread(VideoState* pState = nullptr);
     ~ReadThread();
 
-public:
     void set_video_state(VideoState* pState = nullptr); // call before start
-                                                        // thread
+    void stop() override;
+
 protected:
     int stream_component_open(int stream_index);
     void stream_component_close(VideoState* is, int stream_index);
     int loop_read();
-
-protected:
     void run() override;
 
 private:
     VideoState* m_pPlayData;
-    QMutex m_waitMutex;
 };

+ 11 - 25
AvPlayer2/start_play_thread.cpp

@@ -8,30 +8,28 @@
 // ***********************************************************/
 
 #include "start_play_thread.h"
-
 #include "AVPlayer2/video_state.h"
-
 #include "playercontroller.h"
 
-StartPlayThread::StartPlayThread(PlayerController* playerController, QObject* parent)
-    : QThread(parent)
-    , m_playerController(playerController)
+StartPlayThread::StartPlayThread(PlayerController* playerController)
+    : m_playerController(playerController)
 {}
 
 StartPlayThread::~StartPlayThread() {}
 
+void StartPlayThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 void StartPlayThread::run()
 {
     assert(m_playerController);
+    if (m_exit) return;
     bool ret = false;
 
-#if !NDEBUG
-    QElapsedTimer timer;
-    timer.start();
-#endif
-
     float vol = 1.0;
-    //pParent->volume_settings(false);
     AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16; // play out format
 
     VideoStateData* pVideoStateData = m_playerController->videoStateData();
@@ -39,29 +37,17 @@ void StartPlayThread::run()
         AVCodecContext* pAudio = pVideoStateData->get_contex(AVMEDIA_TYPE_AUDIO);
         VideoState* pState = pVideoStateData->get_state();
         if (pAudio) {
-            AudioPlayThread* pThread = m_playerController->audioPlayThread();
+            auto* pThread = m_playerController->audioPlayThread();
             if (pThread) {
                 ret = pThread->init_device(pAudio->sample_rate,
                                            pAudio->ch_layout.nb_channels,
                                            sample_fmt,
                                            vol); // pAudio->sample_fmt
-                if (!ret) {
-                    qWarning("audio play init_device failed.");
-                }
-
                 if (ret) {
                     ret = pThread->init_resample_param(pAudio, sample_fmt, pState);
-                    if (!ret) {
-                        qWarning("audio play init resample param failed.");
-                    }
                 }
             }
         }
     }
-
-    emit audio_device_init(ret);
-#if !NDEBUG
-    qDebug("Start play operation took %d milliseconds", timer.elapsed());
-#endif
-    qDebug("-------- start play thread(audio device initial) exit,ret=%d.", ret);
+    // 可加回调或日志
 }

+ 6 - 7
AvPlayer2/start_play_thread.h

@@ -1,18 +1,17 @@
 #pragma once
 
-#include <QThread>
+#include "ThreadBase.h"
+#include <atomic>
 
 class PlayerController;
 
-class StartPlayThread : public QThread
+class StartPlayThread : public ThreadBase
 {
-    Q_OBJECT
-
 public:
-    explicit StartPlayThread(PlayerController *playerController, QObject *parent = Q_NULLPTR);
+    explicit StartPlayThread(PlayerController *playerController);
     ~StartPlayThread();
-signals:
-    void audio_device_init(bool ret);
+
+    void stop() override;
 
 protected:
     void run() override;

+ 18 - 12
AvPlayer2/stopplay_waiting_thread.cpp

@@ -8,23 +8,29 @@
 #include "stopplay_waiting_thread.h"
 #include "playercontroller.h"
 
-StopWaitingThread::StopWaitingThread(QObject* parent, const QString& file)
-    : QThread(parent)
-    , m_file(file)
+StopWaitingThread::StopWaitingThread(PlayerController* parent, const std::string& file)
+    : m_file(file), m_parent(parent)
 {}
 
 StopWaitingThread::~StopWaitingThread() {}
 
-void StopWaitingThread::run()
+void StopWaitingThread::stop()
 {
-    PlayerController* pMainWnd = (PlayerController*) parent();
-    emit stopPlay();
+    m_exit = true;
+    m_cv.notify_all();
+}
 
-    while (pMainWnd && pMainWnd->isPlaying()) {
-        msleep(2);
+void StopWaitingThread::run()
+{
+    // 这里建议用回调或轮询方式通知主线程
+    if (!m_parent) return;
+    m_parent->stopPlay();
+    while (m_parent->isPlaying()) {
+        if (m_exit) break;
+        std::this_thread::sleep_for(std::chrono::milliseconds(2));
     }
-
-    emit startPlay(m_file);
-    qDebug("-------- stopplay waiting thread exit.");
-    return;
+    if (!m_exit) {
+        m_parent->startToPlay(m_file.c_str());
+    }
+    // 可加日志
 }

+ 11 - 10
AvPlayer2/stopplay_waiting_thread.h

@@ -1,22 +1,23 @@
 #pragma once
 
-#include <QDebug>
-#include <QThread>
+#include "ThreadBase.h"
+#include <atomic>
+#include <string>
 
-class StopWaitingThread : public QThread
-{
-    Q_OBJECT
+class PlayerController;
 
+class StopWaitingThread : public ThreadBase
+{
 public:
-    explicit StopWaitingThread(QObject* parent = Q_NULLPTR, const QString& file = "");
+    explicit StopWaitingThread(PlayerController* parent, const std::string& file = "");
     ~StopWaitingThread();
-signals:
-    void stopPlay();
-    void startPlay(const QString& file);
+
+    void stop() override;
 
 protected:
     void run() override;
 
 private:
-    QString m_file;
+    std::string m_file;
+    PlayerController* m_parent;
 };

+ 13 - 6
AvPlayer2/subtitle_decode_thread.cpp

@@ -8,8 +8,8 @@
 
 #include "subtitle_decode_thread.h"
 
-SubtitleDecodeThread::SubtitleDecodeThread(QObject* parent, VideoState* pState)
-    : QThread(parent), m_pState(pState)
+SubtitleDecodeThread::SubtitleDecodeThread(VideoState* pState)
+    : m_pState(pState)
 {
 }
 
@@ -17,6 +17,12 @@ SubtitleDecodeThread::~SubtitleDecodeThread()
 {
 }
 
+void SubtitleDecodeThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 void SubtitleDecodeThread::run()
 {
     assert(m_pState);
@@ -27,11 +33,12 @@ void SubtitleDecodeThread::run()
 
     for (;;)
     {
+        if (m_exit)
+            break;
         if (!(sp = frame_queue_peek_writable(&is->subpq)))
             return;
 
-        if ((got_subtitle = decoder_decode_frame(&is->subdec, nullptr, &sp->sub)) <
-            0)
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, nullptr, &sp->sub)) < 0)
             break;
 
         pts = 0;
@@ -51,11 +58,11 @@ void SubtitleDecodeThread::run()
         }
         else if (got_subtitle)
         {
-            qWarning("Not handled subtitle type:%d", sp->sub.format);
+            // qWarning("Not handled subtitle type:%d", sp->sub.format);
             avsubtitle_free(&sp->sub);
         }
     }
 
-    qDebug("-------- subtitle decode thread exit.");
+    // 可加日志输出
     return;
 }

+ 6 - 5
AvPlayer2/subtitle_decode_thread.h

@@ -1,16 +1,17 @@
 #pragma once
 
-#include <QThread>
+#include "ThreadBase.h"
 #include "packets_sync.h"
+#include <atomic>
 
-class SubtitleDecodeThread : public QThread
+class SubtitleDecodeThread : public ThreadBase
 {
-    Q_OBJECT
-
 public:
-    explicit SubtitleDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    explicit SubtitleDecodeThread(VideoState* pState = nullptr);
     ~SubtitleDecodeThread();
 
+    void stop() override;
+
 protected:
     void run() override;
 

+ 12 - 9
AvPlayer2/video_decode_thread.cpp

@@ -9,13 +9,18 @@
 
 #include "video_decode_thread.h"
 
-VideoDecodeThread::VideoDecodeThread(QObject* parent, VideoState* pState)
-    : QThread(parent)
-    , m_pState(pState)
-{}
+VideoDecodeThread::VideoDecodeThread(VideoState* pState)
+    : m_pState(pState)
+    {}
 
 VideoDecodeThread::~VideoDecodeThread() {}
 
+void VideoDecodeThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 void VideoDecodeThread::run()
 {
     assert(m_pState);
@@ -30,7 +35,6 @@ void VideoDecodeThread::run()
     AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, nullptr);
 
 #if USE_AVFILTER_VIDEO
-    // AVFilterGraph* graph = nullptr;
     AVFilterContext *filt_out = nullptr, *filt_in = nullptr;
     int last_w = 0;
     int last_h = 0;
@@ -43,8 +47,8 @@ void VideoDecodeThread::run()
         return;
 
     for (;;) {
-        /*if (is->abort_request)
-            break;*/
+        if (m_exit)
+            break;
 
         ret = get_video_frame(is, frame);
         if (ret < 0)
@@ -130,7 +134,6 @@ void VideoDecodeThread::run()
                            "Error transferring the data to system memory\n");
                     goto the_end;
                 }
-                // sw_frame->hw_frames_ctx = frame->hw_frames_ctx;
                 sw_frame->pts = frame->pts;
                 sw_frame->pkt_dts = frame->pkt_dts;
                 tmp_frame = sw_frame;
@@ -164,6 +167,6 @@ the_end:
 #endif
     av_frame_free(&frame);
     av_frame_free(&sw_frame);
-    qDebug("-------- video decode thread exit.");
+    // 可加日志输出
     return;
 }

+ 6 - 5
AvPlayer2/video_decode_thread.h

@@ -1,16 +1,17 @@
 #pragma once
 
-#include <QThread>
+#include "ThreadBase.h"
 #include "packets_sync.h"
+#include <atomic>
 
-class VideoDecodeThread : public QThread
+class VideoDecodeThread : public ThreadBase
 {
-    Q_OBJECT
-
 public:
-    explicit VideoDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    explicit VideoDecodeThread(VideoState* pState = nullptr);
     ~VideoDecodeThread();
 
+    void stop() override;
+
 protected:
     void run() override;
 

+ 12 - 13
AvPlayer2/video_play_thread.cpp

@@ -17,19 +17,23 @@ const QRegularExpression VideoPlayThread::m_assNewLineReplacer = QRegularExpress
 
 Q_DECLARE_METATYPE(AVFrame*)
 
-VideoPlayThread::VideoPlayThread(QObject* parent, VideoState* pState)
-    : QThread(parent)
-    , m_pState(pState)
+VideoPlayThread::VideoPlayThread(VideoState* pState)
+    : m_pState(pState)
 {
     qRegisterMetaType<AVFrame*>();
 }
 
 VideoPlayThread::~VideoPlayThread()
 {
-    stop_thread();
     final_resample_param();
 }
 
+void VideoPlayThread::stop()
+{
+    m_exit = true;
+    m_cv.notify_all();
+}
+
 void VideoPlayThread::run()
 {
     assert(m_pState);
@@ -37,14 +41,15 @@ void VideoPlayThread::run()
     double remaining_time = 0.0;
 
     for (;;) {
-        if (m_bExitThread)
+        if (m_exit)
             break;
 
         if (is->abort_request)
             break;
 
         if (is->paused) {
-            msleep(10);
+            std::unique_lock<std::mutex> lock(m_mutex);
+            m_cv.wait_for(lock, std::chrono::milliseconds(10), [this]{ return m_exit; });
             continue;
         }
 
@@ -56,7 +61,7 @@ void VideoPlayThread::run()
             video_refresh(is, &remaining_time);
     }
 
-    qDebug("-------- Video play thread exit.");
+    // 可加日志输出
 }
 
 void VideoPlayThread::video_refresh(VideoState* is, double* remaining_time)
@@ -537,12 +542,6 @@ void VideoPlayThread::final_resample_param()
     av_free(pResample->pFrameRGB);
 }
 
-void VideoPlayThread::stop_thread()
-{
-    m_bExitThread = true;
-    wait();
-}
-
 void VideoPlayThread::parse_subtitle_ass(const QString& text)
 {
     QString str = text;

+ 5 - 16
AvPlayer2/video_play_thread.h

@@ -1,10 +1,9 @@
 #pragma once
 
+#include "ThreadBase.h"
 #include <QDebug>
 #include <QImage>
 #include <QRegularExpression>
-#include <QThread>
-
 #include "packets_sync.h"
 
 #define PRINT_VIDEO_BUFFER_INFO 0
@@ -16,23 +15,14 @@ typedef struct Video_Resample
     struct SwsContext* sws_ctx{nullptr};
 } Video_Resample;
 
-class VideoPlayThread : public QThread
+class VideoPlayThread : public ThreadBase
 {
-    Q_OBJECT
 public:
-    explicit VideoPlayThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    explicit VideoPlayThread(VideoState* pState = nullptr);
     ~VideoPlayThread();
 
-public:
     bool init_resample_param(AVCodecContext* pVideo, bool bHardware = false);
-
-public slots:
-    void stop_thread();
-
-signals:
-    void frame_ready(const QImage&);
-    void frameReady(AVFrame*);
-    void subtitle_ready(const QString&);
+    void stop() override;
 
 protected:
     void run() override;
@@ -41,7 +31,6 @@ private:
     void video_refresh(VideoState* is, double* remaining_time);
     void video_image_display(VideoState* is);
     void video_display(VideoState* is);
-    // void video_audio_display(VideoState* s);
     void final_resample_param();
     inline int compute_mod(int a, int b) { return a < 0 ? (a % b + b) : (a % b); }
     void parse_subtitle_ass(const QString& text);
@@ -49,7 +38,7 @@ private:
 private:
     VideoState* m_pState{nullptr};
     Video_Resample m_Resample;
-    bool m_bExitThread{false};
+    std::atomic<bool> m_bExitThread{false};
 
     const static QRegularExpression m_assFilter;
     const static QRegularExpression m_assNewLineReplacer;

+ 217 - 221
AvPlayer2/video_state.cpp

@@ -13,9 +13,9 @@ int64_t start_time = AV_NOPTS_VALUE;
 static enum AVPixelFormat hw_pix_fmt;
 
 VideoStateData::VideoStateData(bool use_hardware, bool loop_play)
-    : m_bUseHardware(use_hardware), m_bLoopPlay(loop_play)
-{
-}
+    : m_bUseHardware(use_hardware)
+    , m_bLoopPlay(loop_play)
+{}
 
 VideoStateData::~VideoStateData()
 {
@@ -25,8 +25,7 @@ VideoStateData::~VideoStateData()
 
 void VideoStateData::delete_video_state()
 {
-    if (m_pState)
-    {
+    if (m_pState) {
         stream_close(m_pState);
         m_pState = nullptr;
     }
@@ -45,15 +44,13 @@ bool VideoStateData::is_hardware_decode() const
 int VideoStateData::create_video_state(const char* filename)
 {
     int ret = -1;
-    if (!filename || !filename[0])
-    {
+    if (!filename || !filename[0]) {
         qDebug("filename is invalid, please select a valid media file.");
         return ret;
     }
 
     m_pState = stream_open(filename);
-    if (!m_pState)
-    {
+    if (!m_pState) {
         qDebug("stream_open failed!");
         return ret;
     }
@@ -63,8 +60,7 @@ int VideoStateData::create_video_state(const char* filename)
 
 void VideoStateData::print_state() const
 {
-    if (const auto is = m_pState)
-    {
+    if (const auto is = m_pState) {
         qDebug("[VideoState]PacketQueue(v:%p,a:%p,s:%p)", &is->videoq, &is->audioq, &is->subtitleq);
         qDebug("[VideoState]FrameQueue(v:%p,a:%p,s:%p)", &is->pictq, &is->sampq, &is->subpq);
         qDebug("[VideoState]Decoder(v:%p,a:%p,s:%p)", &is->viddec, &is->auddec, &is->subdec);
@@ -87,8 +83,7 @@ int VideoStateData::open_media(VideoState* is)
     is->eof = 0;
 
     ic = avformat_alloc_context();
-    if (!ic)
-    {
+    if (!ic) {
         av_log(nullptr, AV_LOG_FATAL, "Could not allocate context.\n");
         ret = AVERROR(ENOMEM);
         goto fail;
@@ -98,8 +93,7 @@ int VideoStateData::open_media(VideoState* is)
     ic->interrupt_callback.opaque = is;
 
     err = avformat_open_input(&ic, is->filename, is->iformat, nullptr);
-    if (err < 0)
-    {
+    if (err < 0) {
         av_log(nullptr, AV_LOG_FATAL, "failed to open %s: %d", is->filename, err);
         ret = -1;
         goto fail;
@@ -115,8 +109,7 @@ int VideoStateData::open_media(VideoState* is)
     //int orig_nb_streams = ic->nb_streams;
 
     err = avformat_find_stream_info(ic, nullptr);
-    if (err < 0)
-    {
+    if (err < 0) {
         av_log(nullptr, AV_LOG_WARNING, "%s: could not find codec parameters\n", is->filename);
         ret = -1;
         goto fail;
@@ -134,8 +127,7 @@ int VideoStateData::open_media(VideoState* is)
     is->max_frame_duration = 2.0;
 
     /* if seeking requested, we execute it */
-    if (start_time != AV_NOPTS_VALUE)
-    {
+    if (start_time != AV_NOPTS_VALUE) {
         int64_t timestamp;
 
         timestamp = start_time;
@@ -143,9 +135,12 @@ int VideoStateData::open_media(VideoState* is)
         if (ic->start_time != AV_NOPTS_VALUE)
             timestamp += ic->start_time;
         ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
-        if (ret < 0)
-        {
-            av_log(nullptr, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n", is->filename, (double)timestamp / AV_TIME_BASE);
+        if (ret < 0) {
+            av_log(nullptr,
+                   AV_LOG_WARNING,
+                   "%s: could not seek to position %0.3f\n",
+                   is->filename,
+                   (double) timestamp / AV_TIME_BASE);
         }
     }
 
@@ -153,8 +148,7 @@ int VideoStateData::open_media(VideoState* is)
 
     av_dump_format(ic, 0, is->filename, 0);
 
-    for (i = 0; i < ic->nb_streams; i++)
-    {
+    for (i = 0; i < ic->nb_streams; i++) {
         AVStream* st = ic->streams[i];
         enum AVMediaType type = st->codecpar->codec_type;
         st->discard = AVDISCARD_ALL;
@@ -162,41 +156,52 @@ int VideoStateData::open_media(VideoState* is)
             if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0)
                 st_index[type] = i;
     }
-    for (i = 0; i < AVMEDIA_TYPE_NB; i++)
-    {
-        if (wanted_stream_spec[i] && st_index[i] == -1)
-        {
-            av_log(nullptr, AV_LOG_ERROR, "Stream specifier %s does not match any %s stream\n",
-                   wanted_stream_spec[i], av_get_media_type_string(AVMediaType(i)));
+    for (i = 0; i < AVMEDIA_TYPE_NB; i++) {
+        if (wanted_stream_spec[i] && st_index[i] == -1) {
+            av_log(nullptr,
+                   AV_LOG_ERROR,
+                   "Stream specifier %s does not match any %s stream\n",
+                   wanted_stream_spec[i],
+                   av_get_media_type_string(AVMediaType(i)));
             st_index[i] = INT_MAX;
         }
     }
 
-    st_index[AVMEDIA_TYPE_VIDEO] = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, st_index[AVMEDIA_TYPE_VIDEO], -1, nullptr, 0);
-    st_index[AVMEDIA_TYPE_AUDIO] = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, st_index[AVMEDIA_TYPE_AUDIO],
-                                                       st_index[AVMEDIA_TYPE_VIDEO], nullptr, 0);
-    st_index[AVMEDIA_TYPE_SUBTITLE] = av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, st_index[AVMEDIA_TYPE_SUBTITLE],
-                                                          (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? st_index[AVMEDIA_TYPE_AUDIO] : st_index[AVMEDIA_TYPE_VIDEO]), nullptr, 0);
+    st_index[AVMEDIA_TYPE_VIDEO]
+        = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, st_index[AVMEDIA_TYPE_VIDEO], -1, nullptr, 0);
+    st_index[AVMEDIA_TYPE_AUDIO] = av_find_best_stream(ic,
+                                                       AVMEDIA_TYPE_AUDIO,
+                                                       st_index[AVMEDIA_TYPE_AUDIO],
+                                                       st_index[AVMEDIA_TYPE_VIDEO],
+                                                       nullptr,
+                                                       0);
+    st_index[AVMEDIA_TYPE_SUBTITLE] = av_find_best_stream(ic,
+                                                          AVMEDIA_TYPE_SUBTITLE,
+                                                          st_index[AVMEDIA_TYPE_SUBTITLE],
+                                                          (st_index[AVMEDIA_TYPE_AUDIO] >= 0
+                                                               ? st_index[AVMEDIA_TYPE_AUDIO]
+                                                               : st_index[AVMEDIA_TYPE_VIDEO]),
+                                                          nullptr,
+                                                          0);
 
     /* open the streams */
-    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0)
-    {
+    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
         stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
     }
 
-    if (st_index[AVMEDIA_TYPE_AUDIO] >= 0)
-    {
+    if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
         stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
     }
 
-    if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0)
-    {
+    if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
         stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
     }
 
-    if (is->video_stream < 0 && is->audio_stream < 0)
-    {
-        av_log(nullptr, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n", is->filename);
+    if (is->video_stream < 0 && is->audio_stream < 0) {
+        av_log(nullptr,
+               AV_LOG_FATAL,
+               "Failed to open file '%s' or configure filtergraph\n",
+               is->filename);
         ret = -1;
         goto fail;
     }
@@ -219,7 +224,7 @@ VideoState* VideoStateData::stream_open(const char* filename, const AVInputForma
     int startup_volume = 100;
     int av_sync_type = AV_SYNC_AUDIO_MASTER;
 
-    is = (VideoState*)av_mallocz(sizeof(VideoState));
+    is = (VideoState*) av_mallocz(sizeof(VideoState));
     if (!is)
         return nullptr;
     is->last_video_stream = is->video_stream = -1;
@@ -240,13 +245,11 @@ VideoState* VideoStateData::stream_open(const char* filename, const AVInputForma
     if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
         goto fail;
 
-    if (packet_queue_init(&is->videoq) < 0 ||
-        packet_queue_init(&is->audioq) < 0 ||
-        packet_queue_init(&is->subtitleq) < 0)
+    if (packet_queue_init(&is->videoq) < 0 || packet_queue_init(&is->audioq) < 0
+        || packet_queue_init(&is->subtitleq) < 0)
         goto fail;
 
-    if (!(is->continue_read_thread = new QWaitCondition()))
-    {
+    if (!(is->continue_read_thread = new QWaitCondition())) {
         av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() failed!\n");
         goto fail;
     }
@@ -302,8 +305,7 @@ void VideoStateData::read_thread_exit_wait(VideoState* is)
     if (is->read_thread_exit != 0)
         return;
 
-    if (is->threads.read_tid)
-    {
+    if (is->threads.read_tid) {
         av_log(nullptr, AV_LOG_INFO, "read thread wait before!\n");
         is->threads.read_tid->wait();
         av_log(nullptr, AV_LOG_INFO, "read thread wait after!\n");
@@ -316,32 +318,27 @@ void VideoStateData::threads_exit_wait(VideoState* is)
     if (!is)
         return;
 
-    if (is->threads.video_play_tid)
-    {
+    if (is->threads.video_play_tid) {
         is->threads.video_play_tid->wait();
         is->threads.video_play_tid = nullptr;
     }
 
-    if (is->threads.audio_play_tid)
-    {
+    if (is->threads.audio_play_tid) {
         is->threads.audio_play_tid->wait();
         is->threads.audio_play_tid = nullptr;
     }
 
-    if (is->threads.video_decode_tid)
-    {
+    if (is->threads.video_decode_tid) {
         is->threads.video_decode_tid->wait();
         is->threads.video_decode_tid = nullptr;
     }
 
-    if (is->threads.audio_decode_tid)
-    {
+    if (is->threads.audio_decode_tid) {
         is->threads.audio_decode_tid->wait();
         is->threads.audio_decode_tid = nullptr;
     }
 
-    if (is->threads.subtitle_decode_tid)
-    {
+    if (is->threads.subtitle_decode_tid) {
         is->threads.subtitle_decode_tid->wait();
         is->threads.subtitle_decode_tid = nullptr;
     }
@@ -384,8 +381,7 @@ void VideoStateData::stream_close(VideoState* is)
     frame_queue_destory(&is->sampq);
     frame_queue_destory(&is->subpq);
 
-    if (is->continue_read_thread)
-    {
+    if (is->continue_read_thread) {
         delete is->continue_read_thread;
         is->continue_read_thread = nullptr;
     }
@@ -406,13 +402,12 @@ void VideoStateData::stream_close(VideoState* is)
 
 static enum AVPixelFormat get_hw_format(AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts)
 {
-    for (const enum AVPixelFormat* p = pix_fmts; *p != -1; p++)
-    {
+    for (const enum AVPixelFormat* p = pix_fmts; *p != -1; p++) {
         if (*p == hw_pix_fmt)
             return *p;
     }
 
-    fprintf(stderr, "Failed to get HW surface format, codec_id=%d\n", (int)ctx->codec_id);
+    fprintf(stderr, "Failed to get HW surface format, codec_id=%d\n", (int) ctx->codec_id);
     return AV_PIX_FMT_NONE;
 }
 
@@ -435,8 +430,7 @@ int VideoStateData::hw_decoder_init(AVCodecContext* ctx, const enum AVHWDeviceTy
 {
     int err = 0;
 
-    if ((err = av_hwdevice_ctx_create(&m_hw_device_ctx, type, nullptr, nullptr, 0)) < 0)
-    {
+    if ((err = av_hwdevice_ctx_create(&m_hw_device_ctx, type, nullptr, nullptr, 0)) < 0) {
         fprintf(stderr, "Failed to create specified HW device.\n");
         return err;
     }
@@ -479,7 +473,7 @@ int VideoStateData::stream_component_open(VideoState* is, int stream_index)
     int ret = 0;
     int stream_lowres = 0;
 
-    if (stream_index < 0 || ((unsigned int)stream_index) >= ic->nb_streams)
+    if (stream_index < 0 || ((unsigned int) stream_index) >= ic->nb_streams)
         return -1;
 
     avctx = avcodec_alloc_context3(nullptr);
@@ -493,45 +487,46 @@ int VideoStateData::stream_component_open(VideoState* is, int stream_index)
 
     codec = avcodec_find_decoder(avctx->codec_id);
 
-    switch (avctx->codec_type)
-    {
-        case AVMEDIA_TYPE_AUDIO:
-            is->last_audio_stream = stream_index;
-            break;
-        case AVMEDIA_TYPE_SUBTITLE:
-            is->last_subtitle_stream = stream_index;
-            break;
-        case AVMEDIA_TYPE_VIDEO:
-            is->last_video_stream = stream_index;
-
-            if (m_bUseHardware)
-            {
-                m_bHardwareSuccess = false;
-                const char* hardware_device = "dxva2"; // device = <vaapi|vdpau|dxva2|d3d11va>
-                ret = open_hardware(avctx, codec, hardware_device);
-                if (!ret)
-                {
-                    qWarning("hardware-accelerated opened failed, device:%s", hardware_device);
-                    goto fail;
-                }
-
-                qInfo("hardware-accelerated opened, device:%s", hardware_device);
-                m_bHardwareSuccess = true;
+    switch (avctx->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
+        is->last_audio_stream = stream_index;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        is->last_subtitle_stream = stream_index;
+        break;
+    case AVMEDIA_TYPE_VIDEO:
+        is->last_video_stream = stream_index;
+
+        if (m_bUseHardware) {
+            m_bHardwareSuccess = false;
+            const char* hardware_device = "dxva2"; // device = <vaapi|vdpau|dxva2|d3d11va>
+            ret = open_hardware(avctx, codec, hardware_device);
+            if (!ret) {
+                qWarning("hardware-accelerated opened failed, device:%s", hardware_device);
+                goto fail;
             }
-            break;
+
+            qInfo("hardware-accelerated opened, device:%s", hardware_device);
+            m_bHardwareSuccess = true;
+        }
+        break;
     }
 
-    if (!codec)
-    {
-        av_log(nullptr, AV_LOG_WARNING, "No decoder could be found for codec %s\n", avcodec_get_name(avctx->codec_id));
+    if (!codec) {
+        av_log(nullptr,
+               AV_LOG_WARNING,
+               "No decoder could be found for codec %s\n",
+               avcodec_get_name(avctx->codec_id));
         ret = AVERROR(EINVAL);
         goto fail;
     }
 
     avctx->codec_id = codec->id;
-    if (stream_lowres > codec->max_lowres)
-    {
-        av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n", codec->max_lowres);
+    if (stream_lowres > codec->max_lowres) {
+        av_log(avctx,
+               AV_LOG_WARNING,
+               "The maximum value for lowres supported by the decoder is %d\n",
+               codec->max_lowres);
         stream_lowres = codec->max_lowres;
     }
     avctx->lowres = stream_lowres;
@@ -543,83 +538,85 @@ int VideoStateData::stream_component_open(VideoState* is, int stream_index)
     if (stream_lowres)
         av_dict_set_int(&opts, "lowres", stream_lowres, 0);*/
 
-    if ((ret = avcodec_open2(avctx, codec, &opts)) < 0)
-    {
+    if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
         goto fail;
     }
 
     is->eof = 0;
     ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
-    switch (avctx->codec_type)
-    {
-        case AVMEDIA_TYPE_AUDIO:
+    switch (avctx->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
 #if USE_AVFILTER_AUDIO
-        {
-            AVFilterContext* sink;
-            // const char* afilters =
-            // "aresample=8000,aformat=sample_fmts=s16:channel_layouts=mono"; //
-            // "atempo=2"; const char* afilters = nullptr; const char* afilters =
-            // "atempo=2.0";
-            is->audio_filter_src.freq = avctx->sample_rate;
-            is->audio_filter_src.ch_layout.nb_channels = avctx->ch_layout.nb_channels; // avctx->channels;
-            is->audio_filter_src.ch_layout = avctx->ch_layout;                         //  avctx->channel_layout
-            is->audio_filter_src.fmt = avctx->sample_fmt;
-            if ((ret = configure_audio_filters(is, is->afilters, 0)) < 0)
-                goto fail;
-
-            sink = is->out_audio_filter;
-            sample_rate = av_buffersink_get_sample_rate(sink);
-            nb_channels = av_buffersink_get_channels(sink);
-            // channel_layout = av_buffersink_get_channel_layout(sink);
-            format = av_buffersink_get_format(sink);
-            AVChannelLayout chn_layout;
-            av_buffersink_get_ch_layout(sink, &chn_layout);
-            qDebug("afilter sink: sample rate:%d, chn:%d, fmt:%d, chn_layout:%d", sample_rate, nb_channels, format, chn_layout.u);
-        }
+    {
+        AVFilterContext* sink;
+        // const char* afilters =
+        // "aresample=8000,aformat=sample_fmts=s16:channel_layouts=mono"; //
+        // "atempo=2"; const char* afilters = nullptr; const char* afilters =
+        // "atempo=2.0";
+        is->audio_filter_src.freq = avctx->sample_rate;
+        is->audio_filter_src.ch_layout.nb_channels = avctx->ch_layout.nb_channels; // avctx->channels;
+        is->audio_filter_src.ch_layout = avctx->ch_layout; //  avctx->channel_layout
+        is->audio_filter_src.fmt = avctx->sample_fmt;
+        if ((ret = configure_audio_filters(is, is->afilters, 0)) < 0)
+            goto fail;
+
+        sink = is->out_audio_filter;
+        sample_rate = av_buffersink_get_sample_rate(sink);
+        nb_channels = av_buffersink_get_channels(sink);
+        // channel_layout = av_buffersink_get_channel_layout(sink);
+        format = av_buffersink_get_format(sink);
+        AVChannelLayout chn_layout;
+        av_buffersink_get_ch_layout(sink, &chn_layout);
+        qDebug("afilter sink: sample rate:%d, chn:%d, fmt:%d, chn_layout:%d",
+               sample_rate,
+               nb_channels,
+               format,
+               chn_layout.u);
+    }
 #else
-            sample_rate = avctx->sample_rate;
-            ret = av_channel_layout_copy(&ch_layout, &avctx->ch_layout);
-            if (ret < 0)
-                goto fail;
+        sample_rate = avctx->sample_rate;
+        ret = av_channel_layout_copy(&ch_layout, &avctx->ch_layout);
+        if (ret < 0)
+            goto fail;
 
 #endif
-            /* prepare audio output */
-            /*if ((ret = audio_open(is, chn_layout, nb_channels, sample_rate,
+        /* prepare audio output */
+        /*if ((ret = audio_open(is, chn_layout, nb_channels, sample_rate,
     &is->audio_tgt)) < 0) goto fail;
 
     is->audio_src = is->audio_tgt;*/
 
-            is->audio_stream = stream_index;
-            is->audio_st = ic->streams[stream_index];
+        is->audio_stream = stream_index;
+        is->audio_st = ic->streams[stream_index];
 
-            if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)))
-            {
-                is->auddec.start_pts = is->audio_st->start_time;
-                is->auddec.start_pts_tb = is->audio_st->time_base;
-            }
+        if ((is->ic->iformat->flags
+             & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK))) {
+            is->auddec.start_pts = is->audio_st->start_time;
+            is->auddec.start_pts_tb = is->audio_st->time_base;
+        }
 
-            m_bHasAudio = true;
-            m_avctxAudio = avctx;
-            break;
+        m_bHasAudio = true;
+        m_avctxAudio = avctx;
+        break;
 
-        case AVMEDIA_TYPE_VIDEO:
-            is->video_stream = stream_index;
-            is->video_st = ic->streams[stream_index];
+    case AVMEDIA_TYPE_VIDEO:
+        is->video_stream = stream_index;
+        is->video_st = ic->streams[stream_index];
 
-            m_bHasVideo = true;
-            m_avctxVideo = avctx;
-            break;
+        m_bHasVideo = true;
+        m_avctxVideo = avctx;
+        break;
 
-        case AVMEDIA_TYPE_SUBTITLE:
-            is->subtitle_stream = stream_index;
-            is->subtitle_st = ic->streams[stream_index];
+    case AVMEDIA_TYPE_SUBTITLE:
+        is->subtitle_stream = stream_index;
+        is->subtitle_st = ic->streams[stream_index];
 
-            m_bHasSubtitle = true;
-            m_avctxSubtitle = avctx;
-            break;
+        m_bHasSubtitle = true;
+        m_avctxSubtitle = avctx;
+        break;
 
-        default:
-            break;
+    default:
+        break;
     }
 
     goto out;
@@ -637,63 +634,61 @@ void VideoStateData::stream_component_close(VideoState* is, int stream_index)
     AVFormatContext* ic = is->ic;
     AVCodecParameters* codecpar;
 
-    if (stream_index < 0 || ((unsigned int)stream_index) >= ic->nb_streams)
+    if (stream_index < 0 || ((unsigned int) stream_index) >= ic->nb_streams)
         return;
 
     codecpar = ic->streams[stream_index]->codecpar;
 
-    switch (codecpar->codec_type)
-    {
-        case AVMEDIA_TYPE_AUDIO:
-            decoder_abort(&is->auddec, &is->sampq);
-            // SDL_CloseAudioDevice(audio_dev);
-            decoder_destroy(&is->auddec);
+    switch (codecpar->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
+        decoder_abort(&is->auddec, &is->sampq);
+        // SDL_CloseAudioDevice(audio_dev);
+        decoder_destroy(&is->auddec);
 
-            // swr_free(&is->swr_ctx);
-            // av_freep(&is->audio_buf1);
-            // is->audio_buf1_size = 0;
-            // is->audio_buf = nullptr;
+        // swr_free(&is->swr_ctx);
+        // av_freep(&is->audio_buf1);
+        // is->audio_buf1_size = 0;
+        // is->audio_buf = nullptr;
 
-            /*if (is->rdft) {
+        /*if (is->rdft) {
             av_rdft_end(is->rdft);
             av_freep(&is->rdft_data);
             is->rdft = nullptr;
             is->rdft_bits = 0;
             }*/
-            break;
+        break;
 
-        case AVMEDIA_TYPE_VIDEO:
-            decoder_abort(&is->viddec, &is->pictq);
-            decoder_destroy(&is->viddec);
-            break;
+    case AVMEDIA_TYPE_VIDEO:
+        decoder_abort(&is->viddec, &is->pictq);
+        decoder_destroy(&is->viddec);
+        break;
 
-        case AVMEDIA_TYPE_SUBTITLE:
-            decoder_abort(&is->subdec, &is->subpq);
-            decoder_destroy(&is->subdec);
-            break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        decoder_abort(&is->subdec, &is->subpq);
+        decoder_destroy(&is->subdec);
+        break;
 
-        default:
-            qDebug("Not handled yet.......code type:%d", codecpar->codec_type);
-            break;
+    default:
+        qDebug("Not handled yet.......code type:%d", codecpar->codec_type);
+        break;
     }
 
     ic->streams[stream_index]->discard = AVDISCARD_ALL;
-    switch (codecpar->codec_type)
-    {
-        case AVMEDIA_TYPE_AUDIO:
-            is->audio_st = nullptr;
-            is->audio_stream = -1;
-            break;
-        case AVMEDIA_TYPE_VIDEO:
-            is->video_st = nullptr;
-            is->video_stream = -1;
-            break;
-        case AVMEDIA_TYPE_SUBTITLE:
-            is->subtitle_st = nullptr;
-            is->subtitle_stream = -1;
-            break;
-        default:
-            break;
+    switch (codecpar->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
+        is->audio_st = nullptr;
+        is->audio_stream = -1;
+        break;
+    case AVMEDIA_TYPE_VIDEO:
+        is->video_st = nullptr;
+        is->video_stream = -1;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        is->subtitle_st = nullptr;
+        is->subtitle_stream = -1;
+        break;
+    default:
+        break;
     }
 }
 
@@ -715,19 +710,18 @@ bool VideoStateData::has_subtitle() const
 AVCodecContext* VideoStateData::get_contex(AVMediaType type) const
 {
     AVCodecContext* pCtx = nullptr;
-    switch (type)
-    {
-        case AVMEDIA_TYPE_AUDIO:
-            pCtx = m_avctxAudio;
-            break;
-        case AVMEDIA_TYPE_VIDEO:
-            pCtx = m_avctxVideo;
-            break;
-        case AVMEDIA_TYPE_SUBTITLE:
-            pCtx = m_avctxSubtitle;
-            break;
-        default:
-            break;
+    switch (type) {
+    case AVMEDIA_TYPE_AUDIO:
+        pCtx = m_avctxAudio;
+        break;
+    case AVMEDIA_TYPE_VIDEO:
+        pCtx = m_avctxVideo;
+        break;
+    case AVMEDIA_TYPE_SUBTITLE:
+        pCtx = m_avctxSubtitle;
+        break;
+    default:
+        break;
     }
     return pCtx;
 }
@@ -736,8 +730,7 @@ enum AVHWDeviceType VideoStateData::get_hwdevice(const char* device) const
 {
     // device = <vaapi|vdpau|dxva2|d3d11va>
     enum AVHWDeviceType type = av_hwdevice_find_type_by_name(device);
-    if (type == AV_HWDEVICE_TYPE_NONE)
-    {
+    if (type == AV_HWDEVICE_TYPE_NONE) {
         av_log(nullptr, AV_LOG_WARNING, "Device type %s is not supported.\n", device);
 
         av_log(nullptr, AV_LOG_INFO, "Available device types:");
@@ -749,21 +742,24 @@ enum AVHWDeviceType VideoStateData::get_hwdevice(const char* device) const
     return type;
 }
 
-enum AVPixelFormat VideoStateData::get_hwdevice_decoder(const AVCodec* decoder, enum AVHWDeviceType type) const
+enum AVPixelFormat VideoStateData::get_hwdevice_decoder(const AVCodec* decoder,
+                                                        enum AVHWDeviceType type) const
 {
     if (!decoder || AV_HWDEVICE_TYPE_NONE == type)
         return AV_PIX_FMT_NONE;
 
-    for (int i = 0;; i++)
-    {
+    for (int i = 0;; i++) {
         const AVCodecHWConfig* config = avcodec_get_hw_config(decoder, i);
-        if (!config)
-        {
-            av_log(nullptr, AV_LOG_WARNING, "Decoder %s does not support device type %s.\n", decoder->name, av_hwdevice_get_type_name(type));
+        if (!config) {
+            av_log(nullptr,
+                   AV_LOG_WARNING,
+                   "Decoder %s does not support device type %s.\n",
+                   decoder->name,
+                   av_hwdevice_get_type_name(type));
             return AV_PIX_FMT_NONE;
         }
-        if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type)
-        {
+        if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX
+            && config->device_type == type) {
             return config->pix_fmt;
         }
     }

+ 4 - 8
MainPanel.cpp

@@ -79,15 +79,11 @@ MainPanel::MainPanel(QWidget *parent)
                 const QString id = webSocketClient->roomId();
 
                 if (PlayWidget *playWidget = qobject_cast<PlayWidget *>(playerWidget)) {
-                    if (!playWidget->isPlaying()) {
+                    if (!playWidget->isPlaying() && !m_isStartingPlay) {
+                        m_isStartingPlay = true;
                         playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + id);
-                        // QFuture<void> playFuture = QtConcurrent::run([playWidget, id] {
-                        //     playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + id);
-                        // });
-                        // QtPromise::QPromise<void> playPromise = QtPromise::resolve(playFuture);
-                        // playPromise.then([] {
-                        //     // 可选:播放成功后UI反馈
-                        // });
+                        m_isStartingPlay = false; // 如果 startToPlay 是同步的
+                        // 如果 startToPlay 是异步的,建议在播放真正开始/结束的回调里重置 m_isStartingPlay
                     }
                 }
             }

+ 6 - 0
MainPanel.h

@@ -2,6 +2,9 @@
 #include <QStringList>
 #include <QWidget>
 #include "qobjectdefs.h"
+#include <QMutex>
+#include <QWaitCondition>
+
 class QSplitter;
 class QListWidget;
 class UserProfileWidget;
@@ -36,4 +39,7 @@ private:
     ChatWindow *chatView = nullptr;
     QListWidget *m_roomListWidget = nullptr;
     WebSocketClient *webSocketClient = nullptr;
+    bool m_isStartingPlay = false;
+    QMutex m_playMutex;
+    QWaitCondition m_playCond;
 };

+ 2 - 2
views/loginwindow.cpp

@@ -436,8 +436,8 @@ void LoginWidgetPrivate::applyCurrentTheme()
 
 void LoginWidgetPrivate::loadSettings()
 {
-    usernameEdit->setText(settings.value("username", "admin").toString());
-    passwordEdit->setText((settings.value("password", "6MLAoh$#xR#0").toByteArray()));
+    usernameEdit->setText(settings.value("username", "test1").toString());
+    passwordEdit->setText((settings.value("password", "123456").toByteArray()));
     rememberCheck->setChecked(settings.value("rememberPassword", false).toBool());
 }