Selaa lähdekoodia

解耦

添加: action处理特性

添加: 特效

移除: mainwindowa.ui 方便代码控制

添加: opengl渲染

完善: 视频播放控件
zhuizhu 9 kuukautta sitten
vanhempi
säilyke
4f4c02162e
100 muutettua tiedostoa jossa 16221 lisäystä ja 395 poistoa
  1. 183 118
      AvPlayer/AudioPlayer.cpp
  2. 35 15
      AvPlayer/AudioPlayer.h
  3. 9 9
      AvPlayer/AvPlayer.pri
  4. 26 0
      AvPlayer/RingBuffer.h
  5. 143 182
      AvPlayer/ffmpegvideopuller.cpp
  6. 56 17
      AvPlayer/ffmpegvideopuller.h
  7. 74 30
      AvPlayer/playerdemowindow.cpp
  8. 13 7
      AvPlayer/playerdemowindow.h
  9. 65 0
      AvPlayer2/AvPlayer2.pri
  10. BIN
      AvPlayer2/VideoPlayer_en_001.qm
  11. 3 0
      AvPlayer2/VideoPlayer_en_001.ts
  12. 25 0
      AvPlayer2/Visual Leak Detector/AUTHORS.txt
  13. 565 0
      AvPlayer2/Visual Leak Detector/CHANGES.txt
  14. 458 0
      AvPlayer2/Visual Leak Detector/COPYING.txt
  15. 7 0
      AvPlayer2/Visual Leak Detector/bin/Win32/Microsoft.DTfW.DHL.manifest
  16. BIN
      AvPlayer2/Visual Leak Detector/bin/Win32/dbghelp.dll
  17. BIN
      AvPlayer2/Visual Leak Detector/bin/Win32/vld_x86.dll
  18. 7 0
      AvPlayer2/Visual Leak Detector/bin/Win64/Microsoft.DTfW.DHL.manifest
  19. BIN
      AvPlayer2/Visual Leak Detector/bin/Win64/dbghelp.dll
  20. BIN
      AvPlayer2/Visual Leak Detector/bin/Win64/vld_x64.dll
  21. 350 0
      AvPlayer2/Visual Leak Detector/include/vld.h
  22. 49 0
      AvPlayer2/Visual Leak Detector/include/vld_def.h
  23. BIN
      AvPlayer2/Visual Leak Detector/lib/Win32/vld.lib
  24. BIN
      AvPlayer2/Visual Leak Detector/lib/Win64/vld.lib
  25. BIN
      AvPlayer2/Visual Leak Detector/unins000.dat
  26. BIN
      AvPlayer2/Visual Leak Detector/unins000.exe
  27. 171 0
      AvPlayer2/Visual Leak Detector/vld.ini
  28. 114 0
      AvPlayer2/app_settings.cpp
  29. 54 0
      AvPlayer2/app_settings.h
  30. 148 0
      AvPlayer2/audio_decode_thread.cpp
  31. 19 0
      AvPlayer2/audio_decode_thread.h
  32. 75 0
      AvPlayer2/audio_effect_gl.cpp
  33. 38 0
      AvPlayer2/audio_effect_gl.h
  34. 338 0
      AvPlayer2/audio_effect_helper.cpp
  35. 58 0
      AvPlayer2/audio_effect_helper.h
  36. 377 0
      AvPlayer2/audio_play_thread.cpp
  37. 84 0
      AvPlayer2/audio_play_thread.h
  38. 66 0
      AvPlayer2/clickable_slider.cpp
  39. 18 0
      AvPlayer2/clickable_slider.h
  40. 19 0
      AvPlayer2/common.cpp
  41. 7 0
      AvPlayer2/common.h
  42. 538 0
      AvPlayer2/ffmpeg_init.cpp
  43. 26 0
      AvPlayer2/ffmpeg_init.h
  44. 118 0
      AvPlayer2/log.cpp
  45. 29 0
      AvPlayer2/log.h
  46. 1673 0
      AvPlayer2/mainwindowa.cpp
  47. 259 0
      AvPlayer2/mainwindowa.h
  48. 39 0
      AvPlayer2/network_url_dlg.cpp
  49. 27 0
      AvPlayer2/network_url_dlg.h
  50. 204 0
      AvPlayer2/network_url_dlg.ui
  51. 1304 0
      AvPlayer2/packets_sync.cpp
  52. 437 0
      AvPlayer2/packets_sync.h
  53. 395 0
      AvPlayer2/play_control_window.cpp
  54. 89 0
      AvPlayer2/play_control_window.h
  55. 658 0
      AvPlayer2/playercontroller.cpp
  56. 149 0
      AvPlayer2/playercontroller.h
  57. 406 0
      AvPlayer2/playlist_window.cpp
  58. 92 0
      AvPlayer2/playlist_window.h
  59. 51 0
      AvPlayer2/playlist_window.ui
  60. 259 0
      AvPlayer2/qimage_operation.cpp
  61. 37 0
      AvPlayer2/qimage_operation.h
  62. 8 0
      AvPlayer2/qmake_qmake_qm_files.qrc
  63. 228 0
      AvPlayer2/read_thread.cpp
  64. 28 0
      AvPlayer2/read_thread.h
  65. BIN
      AvPlayer2/res/QSS-master.zip
  66. 576 0
      AvPlayer2/res/QSS/AMOLED.qss
  67. 559 0
      AvPlayer2/res/QSS/Aqua.qss
  68. 181 0
      AvPlayer2/res/QSS/ConsoleStyle.qss
  69. 196 0
      AvPlayer2/res/QSS/ElegantDark.qss
  70. 21 0
      AvPlayer2/res/QSS/LICENSE
  71. 434 0
      AvPlayer2/res/QSS/MacOS.qss
  72. 531 0
      AvPlayer2/res/QSS/ManjaroMix.qss
  73. 390 0
      AvPlayer2/res/QSS/MaterialDark.qss
  74. 47 0
      AvPlayer2/res/QSS/NeonButtons.qss
  75. BIN
      AvPlayer2/res/QSS/QSS_IMG/go-down-symbolic.symbolic.png
  76. BIN
      AvPlayer2/res/QSS/QSS_IMG/go-next-symbolic.symbolic.png
  77. BIN
      AvPlayer2/res/QSS/QSS_IMG/go-previous-symbolic.symbolic.png
  78. BIN
      AvPlayer2/res/QSS/QSS_IMG/go-up-symbolic.symbolic.png
  79. BIN
      AvPlayer2/res/QSS/QSS_IMG/object-select-symbolic.symbolic.png
  80. 45 0
      AvPlayer2/res/QSS/README.md
  81. 496 0
      AvPlayer2/res/QSS/Ubuntu.qss
  82. BIN
      AvPlayer2/res/bkground.png
  83. BIN
      AvPlayer2/res/player.png
  84. 64 0
      AvPlayer2/start_play_thread.cpp
  85. 17 0
      AvPlayer2/start_play_thread.h
  86. 30 0
      AvPlayer2/stopplay_waiting_thread.cpp
  87. 22 0
      AvPlayer2/stopplay_waiting_thread.h
  88. 61 0
      AvPlayer2/subtitle_decode_thread.cpp
  89. 19 0
      AvPlayer2/subtitle_decode_thread.h
  90. 23 0
      AvPlayer2/version.h
  91. 169 0
      AvPlayer2/video_decode_thread.cpp
  92. 19 0
      AvPlayer2/video_decode_thread.h
  93. 555 0
      AvPlayer2/video_play_thread.cpp
  94. 56 0
      AvPlayer2/video_play_thread.h
  95. 771 0
      AvPlayer2/video_state.cpp
  96. 59 0
      AvPlayer2/video_state.h
  97. 120 7
      AvRecorder/ui/opengl_video_widget.cpp
  98. 40 9
      AvRecorder/ui/opengl_video_widget.h
  99. 1 1
      LearningSmartClient.pro
  100. 11 0
      bin/VideoPlayer.ini

+ 183 - 118
AvPlayer/AudioPlayer.cpp

@@ -1,6 +1,8 @@
 #include "AudioPlayer.h"
 #include <cstring>
 #include <cmath>
+#include <QDebug>
+
 extern "C" {
 #include <libavutil/avutil.h>
 #include <libavutil/samplefmt.h>
@@ -10,143 +12,77 @@ extern "C" {
 #include <libswresample/swresample.h>
 }
 #include "sonic/sonic.h"
-#include <QAudioDeviceInfo>
-#include <QAudioFormat>
-#include <QDebug>
 
 AudioPlayer::AudioPlayer() {}
 AudioPlayer::~AudioPlayer() { reset(); }
 
-void AudioPlayer::reset() {
-    if (m_sonicCtx) {
-        sonicDestroyStream(m_sonicCtx);
-        m_sonicCtx = nullptr;
-    }
-    if (m_swrCtx) {
-        swr_free(&m_swrCtx);
-        m_swrCtx = nullptr;
-    }
-    if (m_swrBuffer) {
-        av_free(m_swrBuffer);
-        m_swrBuffer = nullptr;
-        m_swrBufferSize = 0;
-    }
-    freeBuffers();
-    if (m_audioOutput) {
-        m_audioOutput->stop();
-        delete m_audioOutput;
-        m_audioOutput = nullptr;
-    }
-    m_audioDevice = nullptr;
-    m_sampleRate = 0;
-    m_channels = 0;
-    m_format = AV_SAMPLE_FMT_NONE;
-    m_lastSpeed = 1.0f;
-}
-
-void AudioPlayer::freeBuffers() {
-    if (m_abuf) {
-        av_freep(&m_abuf);
-        m_abuf = nullptr;
-    }
-    if (m_abufOut) {
-        av_freep(&m_abufOut);
-        m_abufOut = nullptr;
-        m_abufOutSize = 0;
-    }
-    m_maxBufSize = -1;
-}
-
-void AudioPlayer::init(const AVFrame* frame, float speed) {
+bool AudioPlayer::open(const AVFrame* frame, float speed) {
+    qDebug() << "[AUDIO-PLAYER] open: sample_rate=" << frame->sample_rate << "channels=" << frame->ch_layout.nb_channels << "format=" << frame->format << "speed=" << speed;
+    QMutexLocker locker(&m_mutex);
+    qDebug() << "[AUDIO-PLAYER] got mutex";
     reset();
-    initAudioOutput(frame, speed);
+    qDebug() << "[AUDIO-PLAYER] after reset";
+    return initAudioOutput(frame, speed);
 }
 
-void AudioPlayer::initAudioOutput(const AVFrame* frame, float speed) {
+bool AudioPlayer::initAudioOutput(const AVFrame* frame, float speed) {
+    qDebug() << "[AUDIO-PLAYER] initAudioOutput begin";
     m_sampleRate = frame->sample_rate;
     m_channels = frame->ch_layout.nb_channels;
     m_format = (AVSampleFormat)frame->format;
-    m_lastSpeed = speed;
-    if (m_sonicCtx) {
-        sonicDestroyStream(m_sonicCtx);
-        m_sonicCtx = nullptr;
-    }
-    m_sonicCtx = sonicCreateStream(m_sampleRate, m_channels);
+    m_speed = speed;
+    m_volume = 1.0f;
+    releaseSonic();
+    ensureSonic(m_sampleRate, m_channels);
     setSpeed(speed);
     QAudioFormat fmt;
     fmt.setSampleRate(m_sampleRate);
     fmt.setChannelCount(m_channels);
-    fmt.setSampleSize(16); // 目标格式 S16
+    fmt.setSampleSize(16); // S16
     fmt.setCodec("audio/pcm");
     fmt.setByteOrder(QAudioFormat::LittleEndian);
     fmt.setSampleType(QAudioFormat::SignedInt);
-    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
-    if (!info.isFormatSupported(fmt)) {
-        qWarning() << "音频格式不支持,尝试最近格式";
-        fmt = info.nearestFormat(fmt);
+    qDebug() << "[AUDIO-PLAYER] before QAudioOutput, deviceInfo isNull=" << m_deviceInfo.isNull();
+    if (!m_deviceInfo.isNull()) {
+        if (!m_deviceInfo.isFormatSupported(fmt)) {
+            qWarning() << "音频格式不支持,尝试最近格式";
+            fmt = m_deviceInfo.nearestFormat(fmt);
+        }
+        qDebug() << "[AUDIO-PLAYER] before QAudioOutput (custom device)";
+        m_audioOutput = new QAudioOutput(m_deviceInfo, fmt, nullptr);
+    } else {
+        QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
+        if (!info.isFormatSupported(fmt)) {
+            qWarning() << "音频格式不支持,尝试最近格式";
+            fmt = info.nearestFormat(fmt);
+        }
+        qDebug() << "[AUDIO-PLAYER] before QAudioOutput (default device)";
+        m_audioOutput = new QAudioOutput(fmt, nullptr);
     }
-    m_audioOutput = new QAudioOutput(fmt, nullptr);
+    qDebug() << "[AUDIO-PLAYER] after QAudioOutput, m_audioOutput=" << m_audioOutput;
+    // m_audioOutput->setBufferSize(16384);
+    m_audioOutput->setVolume(m_volume);
+    qDebug() << "[AUDIO-PLAYER] before start";
     m_audioDevice = m_audioOutput->start();
+    qDebug() << "[AUDIO-PLAYER] after start, m_audioDevice=" << m_audioDevice;
+    return m_audioDevice != nullptr;
 }
 
-void AudioPlayer::setSpeed(float speed) {
-    if (m_sonicCtx) {
-        sonicSetSpeed(m_sonicCtx, speed);
-        sonicSetPitch(m_sonicCtx, 1.0f);
-        sonicSetRate(m_sonicCtx, 1.0f);
+bool AudioPlayer::play(const AVFrame* frame, float speed) {
+    QMutexLocker locker(&m_mutex);
+    if (!m_audioDevice) return false;
+    if (m_sampleRate != frame->sample_rate || m_channels != frame->ch_layout.nb_channels || m_format != (AVSampleFormat)frame->format) {
+        open(frame, speed);
     }
-    m_lastSpeed = speed;
-}
-
-void AudioPlayer::setOutputDevice(QIODevice* device) {
-    m_audioDevice = device;
-}
-
-void AudioPlayer::setAudioOutput(QAudioOutput* output) {
-    m_audioOutput = output;
-}
-
-QIODevice* AudioPlayer::getOutputDevice() const {
-    return m_audioDevice;
-}
-
-QAudioOutput* AudioPlayer::getAudioOutput() const {
-    return m_audioOutput;
-}
-
-bool AudioPlayer::needReinit(const AVFrame* frame, float speed) const {
-    if (!m_audioOutput) return true;
-    if (m_sampleRate != frame->sample_rate) return true;
-    if (m_channels != frame->ch_layout.nb_channels) return true;
-    if (m_format != (AVSampleFormat)frame->format) return true;
-    if (std::abs(m_lastSpeed - speed) > 1e-3) return true;
-    return false;
-}
-
-void AudioPlayer::play(AVFrame* frame, float speed) {
-    if (!m_audioDevice) return;
-    m_sampleRate = frame->sample_rate;
-    m_channels = frame->ch_layout.nb_channels;
-    m_format = (AVSampleFormat)frame->format;
+    m_speed = speed;
+    setSpeed(speed);
     // 1. 重采样到 S16
     const AVSampleFormat targetFmt = AV_SAMPLE_FMT_S16;
     uint8_t* inputData = nullptr;
     int inputSamples = frame->nb_samples;
     int inputBytes = av_samples_get_buffer_size(nullptr, m_channels, inputSamples, targetFmt, 1);
     if (m_format != targetFmt) {
-        if (!m_swrCtx) {
-            m_swrCtx = swr_alloc();
-            swr_alloc_set_opts2(&m_swrCtx,
-                                &frame->ch_layout, // out_ch_layout
-                                targetFmt,
-                                m_sampleRate,
-                                &frame->ch_layout, // in_ch_layout
-                                m_format,
-                                m_sampleRate,
-                                0,
-                                nullptr);
-            swr_init(m_swrCtx);
-        }
+        ensureResampler(frame);
         if (!m_swrBuffer || m_swrBufferSize < inputBytes) {
             if (m_swrBuffer) av_free(m_swrBuffer);
             m_swrBuffer = (uint8_t*)av_malloc(inputBytes);
@@ -165,9 +101,7 @@ void AudioPlayer::play(AVFrame* frame, float speed) {
         inputBytes = av_samples_get_buffer_size(nullptr, m_channels, inputSamples, targetFmt, 1);
     }
     // 2. 送 sonic 变速
-    if (!m_sonicCtx) {
-        m_sonicCtx = sonicCreateStream(m_sampleRate, m_channels);
-    }
+    ensureSonic(m_sampleRate, m_channels);
     setSpeed(speed);
     int out_ret = sonicWriteShortToStream(m_sonicCtx, (int16_t*)inputData, inputBytes / (m_channels * 2));
     int num_samples = sonicSamplesAvailable(m_sonicCtx);
@@ -178,11 +112,142 @@ void AudioPlayer::play(AVFrame* frame, float speed) {
         sonic_samples = sonicReadShortFromStream(m_sonicCtx, (int16_t*)m_abufOut, num_samples);
         out_size = sonic_samples * 2 * m_channels;
     }
-    // 3. 输出到Qt
-    int bytesWrite = 0;
-    while (bytesWrite < out_size) {
-        int written = m_audioDevice->write((const char*)m_abufOut + bytesWrite, out_size - bytesWrite);
-        if (written <= 0) break;
-        bytesWrite += written;
+    // 3. 写入音频设备前判断可用空间
+    if (m_audioOutput && m_audioOutput->bytesFree() < out_size) {
+        qWarning() << "[AUDIO] QAudioOutput 缓冲区不够,丢帧 out_size=" << out_size << "bytesFree=" << m_audioOutput->bytesFree();
+        return false;
+    }
+    if (out_size > 0)
+        m_audioDevice->write((const char*)m_abufOut, out_size);
+    return true;
+}
+
+void AudioPlayer::setSpeed(float speed) {
+    // QMutexLocker locker(&m_mutex);
+    m_speed = speed;
+    if (m_sonicCtx) {
+        sonicSetSpeed(m_sonicCtx, speed);
+        sonicSetPitch(m_sonicCtx, 1.0f);
+        sonicSetRate(m_sonicCtx, 1.0f);
     }
 }
+
+void AudioPlayer::setVolume(float volume) {
+    QMutexLocker locker(&m_mutex);
+    m_volume = volume;
+    if (m_audioOutput) {
+        m_audioOutput->setVolume(volume);
+    }
+}
+
+bool AudioPlayer::setOutputDevice(const QAudioDeviceInfo& deviceInfo) {
+    QMutexLocker locker(&m_mutex);
+    m_deviceInfo = deviceInfo;
+    if (m_audioOutput) {
+        m_audioOutput->stop();
+        delete m_audioOutput;
+        m_audioOutput = nullptr;
+    }
+    return true;
+}
+
+void AudioPlayer::stop() {
+    QMutexLocker locker(&m_mutex);
+    if (m_audioOutput) {
+        m_audioOutput->stop();
+    }
+}
+
+void AudioPlayer::reset()
+{
+    releaseSonic();
+    releaseResampler();
+    releaseAudioOutput();
+    if (m_abufOut) {
+        av_freep(&m_abufOut);
+        m_abufOut = nullptr;
+        m_abufOutSize = 0;
+    }
+    m_sampleRate = 0;
+    m_channels = 0;
+    m_format = AV_SAMPLE_FMT_NONE;
+    m_speed = 1.0f;
+    m_volume = 1.0f;
+    m_deviceInfo = QAudioDeviceInfo();
+}
+
+void AudioPlayer::freeBuffers() {
+    if (m_abufOut) {
+        av_freep(&m_abufOut);
+        m_abufOut = nullptr;
+        m_abufOutSize = 0;
+    }
+}
+
+bool AudioPlayer::ensureResampler(const AVFrame* frame) {
+    if (!m_swrCtx) {
+        m_swrCtx = swr_alloc();
+        swr_alloc_set_opts2(&m_swrCtx,
+                            &frame->ch_layout, // out_ch_layout
+                            AV_SAMPLE_FMT_S16,
+                            frame->sample_rate,
+                            &frame->ch_layout, // in_ch_layout
+                            (AVSampleFormat)frame->format,
+                            frame->sample_rate,
+                            0,
+                            nullptr);
+        swr_init(m_swrCtx);
+    }
+    return m_swrCtx != nullptr;
+}
+
+void AudioPlayer::releaseResampler() {
+    if (m_swrCtx) {
+        swr_free(&m_swrCtx);
+        m_swrCtx = nullptr;
+    }
+    if (m_swrBuffer) {
+        av_free(m_swrBuffer);
+        m_swrBuffer = nullptr;
+        m_swrBufferSize = 0;
+    }
+}
+
+bool AudioPlayer::ensureSonic(int sampleRate, int channels) {
+    if (!m_sonicCtx) {
+        m_sonicCtx = sonicCreateStream(sampleRate, channels);
+    }
+    return m_sonicCtx != nullptr;
+}
+
+void AudioPlayer::releaseSonic() {
+    if (m_sonicCtx) {
+        sonicDestroyStream(m_sonicCtx);
+        m_sonicCtx = nullptr;
+    }
+}
+
+void AudioPlayer::releaseAudioOutput() {
+    if (m_audioOutput) {
+        m_audioOutput->stop();
+        delete m_audioOutput;
+        m_audioOutput = nullptr;
+    }
+    m_audioDevice = nullptr;
+}
+
+QIODevice* AudioPlayer::getOutputDevice() const {
+    return m_audioDevice;
+}
+
+QAudioOutput* AudioPlayer::getAudioOutput() const {
+    return m_audioOutput;
+}
+
+float AudioPlayer::getSpeed() const {
+    return m_speed;
+}
+
+float AudioPlayer::getVolume() const {
+    return m_volume;
+}

+ 35 - 15
AvPlayer/AudioPlayer.h

@@ -10,38 +10,58 @@ extern "C" {
 struct sonicStreamStruct;
 
 #include <QAudioOutput>
+#include <QAudioDeviceInfo>
 #include <QIODevice>
+#include <QMutex>
 
 class AudioPlayer {
 public:
     AudioPlayer();
     ~AudioPlayer();
-    void init(const AVFrame* frame, float speed);
+
+    // 初始化音频输出,准备播放
+    bool open(const AVFrame* frame, float speed = 1.0f);
+    // 播放一帧音频数据
+    bool play(const AVFrame* frame, float speed = 1.0f);
+    // 设置播放速度
     void setSpeed(float speed);
-    void play(AVFrame* frame, float speed = 1.0f);
+    // 设置音量(0.0~1.0)
+    void setVolume(float volume);
+    // 切换音频输出设备
+    bool setOutputDevice(const QAudioDeviceInfo& deviceInfo);
+    // 停止播放
+    void stop();
+    // 释放所有资源
     void reset();
-    void setOutputDevice(QIODevice* device);
-    void setAudioOutput(QAudioOutput* output);
+
+    // 获取当前输出设备
     QIODevice* getOutputDevice() const;
     QAudioOutput* getAudioOutput() const;
-    bool needReinit(const AVFrame* frame, float speed) const;
+    float getSpeed() const;
+    float getVolume() const;
+
 private:
+    void freeBuffers();
+    bool initAudioOutput(const AVFrame* frame, float speed);
+    bool ensureResampler(const AVFrame* frame);
+    bool ensureSonic(int sampleRate, int channels);
+    void releaseResampler();
+    void releaseSonic();
+    void releaseAudioOutput();
+
     int m_sampleRate = 0;
     int m_channels = 0;
     AVSampleFormat m_format = AV_SAMPLE_FMT_NONE;
-    sonicStreamStruct* m_sonicCtx = nullptr;
-    int m_maxBufSize = -1;
-    uint8_t* m_abuf = nullptr;
-    uint8_t* m_abufOut = nullptr;
-    unsigned int m_abufOutSize = 0;
-    void freeBuffers();
-    // swresample相关
     SwrContext* m_swrCtx = nullptr;
     uint8_t* m_swrBuffer = nullptr;
     int m_swrBufferSize = 0;
-    // Qt音频相关
+    sonicStreamStruct* m_sonicCtx = nullptr;
     QAudioOutput* m_audioOutput = nullptr;
     QIODevice* m_audioDevice = nullptr;
-    float m_lastSpeed = 1.0f;
-    void initAudioOutput(const AVFrame* frame, float speed);
+    QAudioDeviceInfo m_deviceInfo;
+    float m_speed = 1.0f;
+    float m_volume = 1.0f;
+    uint8_t* m_abufOut = nullptr;
+    unsigned int m_abufOutSize = 0;
+    QMutex m_mutex;
 };

+ 9 - 9
AvPlayer/AvPlayer.pri

@@ -1,14 +1,14 @@
 HEADERS += \
-    $$PWD/AudioPlayer.h \
-    $$PWD/RingBuffer.h \
-    $$PWD/VideoPlayer.h \
-    $$PWD/ffmpegvideopuller.h \
-    $$PWD/playerdemowindow.h \
+    # $$PWD/AudioPlayer.h \
+    # $$PWD/RingBuffer.h \
+    # $$PWD/VideoPlayer.h \
+    # $$PWD/ffmpegvideopuller.h \
+    # $$PWD/playerdemowindow.h \
     $$PWD/sonic/sonic.h
 
 SOURCES += \
-    $$PWD/AudioPlayer.cpp \
-    $$PWD/VideoPlayer.cpp \
-    $$PWD/ffmpegvideopuller.cpp \
-    $$PWD/playerdemowindow.cpp \
+    # $$PWD/AudioPlayer.cpp \
+    # $$PWD/VideoPlayer.cpp \
+    # $$PWD/ffmpegvideopuller.cpp \
+    # $$PWD/playerdemowindow.cpp \
     $$PWD/sonic/sonic.cpp

+ 26 - 0
AvPlayer/RingBuffer.h

@@ -90,6 +90,32 @@ public:
         m_writeIndex = 0;
     }
 
+    // 按PTS弹出最近帧
+    T popNearest(double pts) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        if (m_size == 0) return nullptr;
+        size_t best = m_readIndex;
+        double minDiff = std::abs(m_pts[best] - pts);
+        for (size_t i = 1; i < m_size; ++i) {
+            size_t idx = (m_readIndex + i) % m_capacity;
+            double diff = std::abs(m_pts[idx] - pts);
+            if (diff < minDiff) {
+                minDiff = diff;
+                best = idx;
+            }
+        }
+        T result = m_data[best];
+        m_data[best] = nullptr;
+        // 移动readIndex和size
+        if (best == m_readIndex) {
+            m_readIndex = (m_readIndex + 1) % m_capacity;
+            --m_size;
+        } else {
+            // 只清空,不移动readIndex,保证弹出的是最近帧
+        }
+        return result;
+    }
+
 private:
     std::vector<T> m_data;
     std::vector<double> m_pts;

+ 143 - 182
AvPlayer/ffmpegvideopuller.cpp

@@ -1,4 +1,7 @@
 #include "ffmpegvideopuller.h"
+#include "AvPlayer/AudioPlayer.h"
+#include "AvPlayer/VideoPlayer.h"
+#include "qdatetime.h"
 
 #include <QDebug>
 #include <chrono>
@@ -69,6 +72,11 @@ bool FFmpegVideoPuller::open(const QString& url, int videoBufferSec, int audioBu
     m_audioBuffer = new RingBuffer<AVFrame*>(audioBufferSize);
     m_videoBuffer->setDeleter([](AVFrame*& f) { av_frame_free(&f); });
     m_audioBuffer->setDeleter([](AVFrame*& f) { av_frame_free(&f); });
+    // 新增:创建SPSC无锁包队列
+    if (m_videoPacketQueue) { m_videoPacketQueue->clear(); delete m_videoPacketQueue; }
+    if (m_audioPacketQueue) { m_audioPacketQueue->clear(); delete m_audioPacketQueue; }
+    m_videoPacketQueue = new SpscRingBuffer(2000);
+    m_audioPacketQueue = new SpscRingBuffer(2000);
 
     m_videoPlayIndex = 0;
     m_audioPlayIndex = 0;
@@ -106,27 +114,37 @@ void FFmpegVideoPuller::start()
     m_videoPts = 0.0;
     m_audioPts = 0.0;
 
-    // 解码线程
-    m_decodeThread = QThread::create([this]() { this->decodeLoop(); });
-    m_decodeThread->start();
-
-    // 视频播放线程
-    m_videoPlayThread = QThread::create([this]() { this->videoPlayLoop(); });
-    m_videoPlayThread->start();
-
-    // 音频播放线程
-    m_audioPlayThread = QThread::create([this]() { this->audioPlayLoop(); });
-    m_audioPlayThread->start();
+    // 启动包读取线程
+    m_packetReadThread = QThread::create([this]() { this->packetReadLoop(); });
+    m_packetReadThread->start();
+    // 启动视频解码线程
+    m_videoDecodeThread = QThread::create([this]() { this->videoDecodeLoop(); });
+    m_videoDecodeThread->start();
+    // 启动音频解码线程
+    m_audioDecodeThread = QThread::create([this]() { this->audioDecodeLoop(); });
+    m_audioDecodeThread->start();
 }
 
 void FFmpegVideoPuller::stop()
 {
     m_running = false;
-    if (m_decodeThread) {
-        m_decodeThread->quit();
-        m_decodeThread->wait();
-        delete m_decodeThread;
-        m_decodeThread = nullptr;
+    if (m_packetReadThread) {
+        m_packetReadThread->quit();
+        m_packetReadThread->wait();
+        delete m_packetReadThread;
+        m_packetReadThread = nullptr;
+    }
+    if (m_videoDecodeThread) {
+        m_videoDecodeThread->quit();
+        m_videoDecodeThread->wait();
+        delete m_videoDecodeThread;
+        m_videoDecodeThread = nullptr;
+    }
+    if (m_audioDecodeThread) {
+        m_audioDecodeThread->quit();
+        m_audioDecodeThread->wait();
+        delete m_audioDecodeThread;
+        m_audioDecodeThread = nullptr;
     }
     if (m_videoPlayThread) {
         m_videoPlayThread->quit();
@@ -140,6 +158,9 @@ void FFmpegVideoPuller::stop()
         delete m_audioPlayThread;
         m_audioPlayThread = nullptr;
     }
+    // 清空包队列
+    if (m_videoPacketQueue) { m_videoPacketQueue->clear(); delete m_videoPacketQueue; m_videoPacketQueue = nullptr; }
+    if (m_audioPacketQueue) { m_audioPacketQueue->clear(); delete m_audioPacketQueue; m_audioPacketQueue = nullptr; }
     // 释放缓冲区
     if (m_videoBuffer) {
         m_videoBuffer->clear();
@@ -160,189 +181,70 @@ void FFmpegVideoPuller::stop()
         avformat_close_input(&m_fmtCtx);
 }
 
-void FFmpegVideoPuller::decodeLoop()
+void FFmpegVideoPuller::packetReadLoop()
 {
-    AVPacket* pkt = av_packet_alloc();
-    AVFrame* frame = av_frame_alloc();
-
     while (m_running) {
+        AVPacket* pkt = av_packet_alloc();
         if (av_read_frame(m_fmtCtx, pkt) < 0) {
+            av_packet_free(&pkt);
             std::this_thread::sleep_for(std::chrono::milliseconds(10));
             continue;
         }
         if (pkt->stream_index == m_videoStreamIdx) {
-            avcodec_send_packet(m_videoCodecCtx, pkt);
-            while (avcodec_receive_frame(m_videoCodecCtx, frame) == 0) {
-                double pts = frame->best_effort_timestamp
-                             * av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base);
-                m_videoBuffer->push(av_frame_clone(frame), pts);
+            while (!m_videoPacketQueue->push(pkt)) {
+                std::this_thread::sleep_for(std::chrono::milliseconds(1));
+                if (!m_running) { av_packet_free(&pkt); return; }
             }
         } else if (pkt->stream_index == m_audioStreamIdx) {
-            avcodec_send_packet(m_audioCodecCtx, pkt);
-            while (avcodec_receive_frame(m_audioCodecCtx, frame) == 0) {
-                double pts = frame->best_effort_timestamp
-                             * av_q2d(m_fmtCtx->streams[m_audioStreamIdx]->time_base);
-                //qDebug() << "解码到音频帧, pts=" << pts << " nb_samples=" << frame->nb_samples;
-                m_audioBuffer->push(av_frame_clone(frame), pts);
+            while (!m_audioPacketQueue->push(pkt)) {
+                std::this_thread::sleep_for(std::chrono::milliseconds(1));
+                if (!m_running) { av_packet_free(&pkt); return; }
             }
+        } else {
+            av_packet_free(&pkt);
         }
-        av_packet_unref(pkt);
     }
-    av_frame_free(&frame);
-    av_packet_free(&pkt);
 }
 
-void FFmpegVideoPuller::videoPlayLoop()
+void FFmpegVideoPuller::videoDecodeLoop()
 {
-    // 如果没有视频流,直接返回
-    if (m_videoStreamIdx == -1) {
-        return;
-    }
-
+    AVFrame* frame = av_frame_alloc();
     while (m_running) {
-        AVFrame* vFrame = getCurrentVideoFrame();
-        if (!vFrame) {
-            std::this_thread::sleep_for(std::chrono::milliseconds(5));
-            continue;
-        }
-        
-        double vPts = vFrame->best_effort_timestamp
-                      * av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base);
-        
-        // 更新视频PTS
-        m_videoPts = vPts;
-        
-        // 渲染视频帧
-        if (m_videoRenderCallback) {
-            m_videoRenderCallback(vFrame);
-        }
-
-        // 控制倍速
-        AVRational fr = m_fmtCtx->streams[m_videoStreamIdx]->avg_frame_rate;
-        double frame_delay = 1.0 / (fr.num ? av_q2d(fr) : 25.0);
-        float speed = getSpeed();
-        int delay = int(frame_delay * 1000 / speed);
-        
-        // 如果只有视频没有音频,直接按照视频帧率播放
-        if (m_audioStreamIdx == -1) {
-            if (delay > 0) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-            }
-        } else {
-            // 有音频时,需要考虑音视频同步
-            double aPts = m_audioPts.load();
-            
-            // 如果视频比音频快太多,等待音频追上
-            if (vPts > aPts + 0.1) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(10));
-                continue; // 不前进到下一帧,等待音频追上
-            }
-            
-            // 正常播放速度控制
-            if (delay > 0) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-            }
-        }
-        
-        // 前进到下一帧
-        nextVideoFrame();
-        
-        // 更新当前pts(用于进度显示)
-        {
-            std::lock_guard<std::mutex> lock(m_ptsMutex);
-            m_currentPts = vPts;
+        AVPacket* pkt = m_videoPacketQueue->pop();
+        if (!pkt) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; }
+        avcodec_send_packet(m_videoCodecCtx, pkt);
+        while (avcodec_receive_frame(m_videoCodecCtx, frame) == 0) {
+            double pts = frame->best_effort_timestamp
+                         * av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base);
+            m_videoBuffer->push(av_frame_clone(frame), pts);
         }
+        av_packet_free(&pkt);
     }
+    av_frame_free(&frame);
 }
 
-void FFmpegVideoPuller::audioPlayLoop()
+void FFmpegVideoPuller::audioDecodeLoop()
 {
-    // 如果没有音频流,直接返回
-    if (m_audioStreamIdx == -1) {
-        return;
-    }
-    
+    if (m_audioStreamIdx == -1) return;
+    AVFrame* frame = av_frame_alloc();
+    static qint64 lastDecodeTs = 0;
     while (m_running) {
-        // 获取当前音频帧
-        AVFrame* aFrame = getCurrentAudioFrame();
-        if (!aFrame) {
-            std::this_thread::sleep_for(std::chrono::milliseconds(5));
-            continue;
-        }
-        
-        // 计算音频PTS
-        double aPts = aFrame->best_effort_timestamp
-                      * av_q2d(m_fmtCtx->streams[m_audioStreamIdx]->time_base);
-        
-        // 更新音频PTS
-        m_audioPts = aPts;
-        
-        // 如果只有音频没有视频,直接播放音频
-        if (m_videoStreamIdx == -1) {
-            // 播放音频帧
-            if (m_audioPlayCallback) {
-                m_audioPlayCallback(aFrame);
-            }
-            
-            // 更新当前pts(用于进度显示)
-            {
-                std::lock_guard<std::mutex> lock(m_ptsMutex);
-                m_currentPts = aPts;
-            }
-            
-            // 前进到下一帧
-            nextAudioFrame();
-            
-            // 控制音频播放速度
-            float speed = getSpeed();
-            int samples = aFrame->nb_samples;
-            int sample_rate = aFrame->sample_rate;
-            double frame_duration = samples / (double)sample_rate;
-            int delay = int(frame_duration * 1000 / speed);
-            if (delay > 0) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-            }
-            
-            continue; // 继续下一帧处理
-        }
-        
-        // 有视频时,需要考虑音视频同步
-        double vPts = m_videoPts.load();
-        
-        // 如果音频PTS超前于视频PTS太多,等待视频追上
-        if (aPts > vPts + 0.1) {
-            std::this_thread::sleep_for(std::chrono::milliseconds(5));
-            continue; // 不前进到下一帧,等待视频追上
-        }
-        
-        // 如果音频比视频慢太多,可能需要跳过一些音频帧来追赶视频
-        if (aPts < vPts - 0.2) {
-            // 跳过当前音频帧,直接前进
-            nextAudioFrame();
-            continue;
-        }
-        
-        // 播放音频帧
-        if (m_audioPlayCallback) {
-            m_audioPlayCallback(aFrame);
-        }
-        
-        // 前进到下一帧
-        nextAudioFrame();
-        
-        // 控制音频播放速度,与视频保持一致
-        float speed = getSpeed();
-        if (speed != 1.0f) {
-            // 根据倍速调整音频播放间隔
-            int samples = aFrame->nb_samples;
-            int sample_rate = aFrame->sample_rate;
-            double frame_duration = samples / (double)sample_rate;
-            int delay = int(frame_duration * 1000 / speed);
-            if (delay > 0) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-            }
+        AVPacket* pkt = m_audioPacketQueue->pop();
+        if (!pkt) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; }
+        avcodec_send_packet(m_audioCodecCtx, pkt);
+        while (avcodec_receive_frame(m_audioCodecCtx, frame) == 0) {
+            double pts = frame->best_effort_timestamp
+                         * av_q2d(m_fmtCtx->streams[m_audioStreamIdx]->time_base);
+            m_audioBuffer->push(av_frame_clone(frame), pts);
+
+            qint64 nowDecode = QDateTime::currentMSecsSinceEpoch();
+            qDebug() << "[AUDIO-DECODE] 解码间隔(ms):"
+                     << (lastDecodeTs ? nowDecode - lastDecodeTs : 0);
+            lastDecodeTs = nowDecode;
         }
+        av_packet_free(&pkt);
     }
+    av_frame_free(&frame);
 }
 
 // 进度相关
@@ -363,7 +265,7 @@ void FFmpegVideoPuller::seekToPts(double pts)
 {
     // 停止线程
     m_running = false;
-    if (m_decodeThread) { m_decodeThread->quit(); m_decodeThread->wait(); delete m_decodeThread; m_decodeThread = nullptr; }
+    if (m_packetReadThread) { m_packetReadThread->quit(); m_packetReadThread->wait(); delete m_packetReadThread; m_packetReadThread = nullptr; }
     if (m_videoPlayThread) { m_videoPlayThread->quit(); m_videoPlayThread->wait(); delete m_videoPlayThread; m_videoPlayThread = nullptr; }
     if (m_audioPlayThread) { m_audioPlayThread->quit(); m_audioPlayThread->wait(); delete m_audioPlayThread; m_audioPlayThread = nullptr; }
     
@@ -388,12 +290,8 @@ void FFmpegVideoPuller::seekToPts(double pts)
     
     // 重新启动线程
     m_running = true;
-    m_decodeThread = QThread::create([this]() { this->decodeLoop(); });
-    m_decodeThread->start();
-    m_videoPlayThread = QThread::create([this]() { this->videoPlayLoop(); });
-    m_videoPlayThread->start();
-    m_audioPlayThread = QThread::create([this]() { this->audioPlayLoop(); });
-    m_audioPlayThread->start();
+    m_packetReadThread = QThread::create([this]() { this->packetReadLoop(); });
+    m_packetReadThread->start();
 }
 
 // 取帧接口
@@ -429,3 +327,66 @@ size_t FFmpegVideoPuller::audioBufferSize() const
 {
     return m_audioBuffer ? m_audioBuffer->size() : 0;
 }
+
+void FFmpegVideoPuller::startSyncPlay() {
+    if (m_syncPlayRunning) return;
+    m_syncPlayRunning = true;
+    m_syncPlayThread = QThread::create([this]() { this->syncPlayLoop(); });
+    m_syncPlayThread->start();
+}
+
+void FFmpegVideoPuller::stopSyncPlay() {
+    m_syncPlayRunning = false;
+    if (m_syncPlayThread) {
+        m_syncPlayThread->quit();
+        m_syncPlayThread->wait();
+        delete m_syncPlayThread;
+        m_syncPlayThread = nullptr;
+    }
+}
+
+void FFmpegVideoPuller::syncPlayLoop() {
+    // 以音频为主时钟
+    using namespace std::chrono;
+    double basePts = 0.0;
+    bool baseSet = false;
+    auto startTime = high_resolution_clock::now();
+    while (m_syncPlayRunning) {
+        auto now = high_resolution_clock::now();
+        double elapsed = duration_cast<milliseconds>(now - startTime).count();
+        if (!baseSet) {
+            // 获取首帧音频PTS作为基准
+            AVFrame* af = m_audioBuffer->get(0);
+            if (af) {
+                basePts = af->best_effort_timestamp * av_q2d(m_fmtCtx->streams[m_audioStreamIdx]->time_base);
+                baseSet = true;
+            } else {
+                std::this_thread::sleep_for(milliseconds(1));
+                continue;
+            }
+        }
+        double curPts = basePts + elapsed / 1000.0 * getSpeed();
+        // 音频
+        // AVFrame* audioFrame = m_audioBuffer->popNearest(curPts);
+        // if (audioFrame && m_audioPlayer) {
+        //     m_audioPlayer->play(audioFrame, getSpeed());
+        // }
+        // // 视频
+        // AVFrame* videoFrame = m_videoBuffer->popNearest(curPts);
+        // if (videoFrame && m_videoPlayer) {
+        //     m_videoPlayer->render(videoFrame);
+        // }
+        // 控制主循环节奏
+        std::this_thread::sleep_for(milliseconds(1));
+    }
+}
+
+AVFrame* FFmpegVideoPuller::popNearestAudioFrame(double pts) {
+    if (!m_audioBuffer) return nullptr;
+    return m_audioBuffer->popNearest(pts);
+}
+
+AVFrame* FFmpegVideoPuller::popNearestVideoFrame(double pts) {
+    if (!m_videoBuffer) return nullptr;
+    return m_videoBuffer->popNearest(pts);
+}

+ 56 - 17
AvPlayer/ffmpegvideopuller.h

@@ -14,9 +14,45 @@ extern "C" {
 
 #include <atomic>
 #include <functional>
+#include <queue>
+#include <mutex>
+#include <condition_variable>
 
 #include "RingBuffer.h"
 
+// SPSC无锁环形缓冲区
+class SpscRingBuffer {
+public:
+    SpscRingBuffer(size_t size) : size_(size + 1), buffer_(new AVPacket*[size_]), head_(0), tail_(0) {}
+    ~SpscRingBuffer() { delete[] buffer_; }
+    bool push(AVPacket* pkt) {
+        size_t next = (head_ + 1) % size_;
+        if (next == tail_.load(std::memory_order_acquire)) return false; // full
+        buffer_[head_] = pkt;
+        head_.store(next, std::memory_order_release);
+        return true;
+    }
+    AVPacket* pop() {
+        size_t tail = tail_.load(std::memory_order_acquire);
+        if (tail == head_.load(std::memory_order_acquire)) return nullptr; // empty
+        AVPacket* pkt = buffer_[tail];
+        tail_.store((tail + 1) % size_, std::memory_order_release);
+        return pkt;
+    }
+    bool empty() const { return tail_.load(std::memory_order_acquire) == head_.load(std::memory_order_acquire); }
+    void clear() {
+        while (!empty()) {
+            AVPacket* pkt = pop();
+            if (pkt) av_packet_free(&pkt);
+        }
+    }
+private:
+    size_t size_;
+    AVPacket** buffer_;
+    std::atomic<size_t> head_;
+    std::atomic<size_t> tail_;
+};
+
 class FFmpegVideoPuller : public QObject
 {
     Q_OBJECT
@@ -26,7 +62,7 @@ public:
 
     bool open(const QString& url,
               int videoBufferSec = 300,
-              int audioBufferSec = 300); // 默认缓存5分钟
+              int audioBufferSec = 300);
     void setSpeed(float speed);          // 设置倍速
     float getSpeed() const;
     void start();
@@ -41,25 +77,33 @@ public:
     // 取帧接口
     AVFrame* getCurrentVideoFrame();
     AVFrame* getCurrentAudioFrame();
-
-    // 播放指针前进
     void nextVideoFrame();
     void nextAudioFrame();
-
-    // 缓冲区大小
     size_t videoBufferSize() const;
     size_t audioBufferSize() const;
 
-    void setVideoRenderCallback(std::function<void(AVFrame*)> cb) { m_videoRenderCallback = cb; }
-    void setAudioPlayCallback(std::function<void(AVFrame*)> cb) { m_audioPlayCallback = cb; }
-
     double getTotalDuration() const { return m_totalDuration; }
 
+    // 主时钟驱动同步播放
+    void startSyncPlay();
+    void stopSyncPlay();
+
+    AVFrame* popNearestAudioFrame(double pts);
+    AVFrame* popNearestVideoFrame(double pts);
+
 private:
-    void decodeLoop();
-    void videoPlayLoop();
-    void audioPlayLoop();
-    
+    void packetReadLoop();
+    void videoDecodeLoop();
+    void audioDecodeLoop();
+    QThread* m_packetReadThread = nullptr;
+    QThread* m_videoDecodeThread = nullptr;
+    QThread* m_audioDecodeThread = nullptr;
+    SpscRingBuffer* m_videoPacketQueue = nullptr;
+    SpscRingBuffer* m_audioPacketQueue = nullptr;
+    QThread* m_syncPlayThread = nullptr;
+    void syncPlayLoop();
+    std::atomic<bool> m_syncPlayRunning{false};
+
     // 音视频同步
     std::atomic<double> m_audioPts{0.0};
     std::atomic<double> m_videoPts{0.0};
@@ -82,7 +126,6 @@ private:
     RingBuffer<AVFrame*>* m_audioBuffer = nullptr;
 
     // 线程
-    QThread* m_decodeThread = nullptr;
     QThread* m_videoPlayThread = nullptr;
     QThread* m_audioPlayThread = nullptr;
 
@@ -90,12 +133,8 @@ private:
     std::atomic<size_t> m_videoPlayIndex{0};
     std::atomic<size_t> m_audioPlayIndex{0};
     std::atomic<double> m_currentPts{0.0};
-
     mutable std::mutex m_ptsMutex;
 
-    std::function<void(AVFrame*)> m_videoRenderCallback;
-    std::function<void(AVFrame*)> m_audioPlayCallback;
-
     double m_totalDuration = -1.0;
 };
 

+ 74 - 30
AvPlayer/playerdemowindow.cpp

@@ -25,7 +25,6 @@ PlayerDemoWindow::PlayerDemoWindow(QWidget* parent)
     : QWidget(parent)
     , m_audioPlayer(new AudioPlayer())
     , m_videoPlayer(new VideoPlayer())
-    , speedFlag(false)
 {
     QVBoxLayout* layout = new QVBoxLayout(this);
     m_videoWidget = new OpenGLVideoWidget(this);
@@ -98,45 +97,57 @@ PlayerDemoWindow::~PlayerDemoWindow()
 
 void PlayerDemoWindow::startPlay(const QString& url)
 {
-    if (m_puller) {
-        m_puller->stop();
-        delete m_puller;
-        m_puller = nullptr;
-    }
+    stopPlay();
     m_puller = new FFmpegVideoPuller();
+    m_audioPlayer = new AudioPlayer();
+    m_videoPlayer = new VideoPlayer();
     if (!m_puller->open(url, 300, 300)) {
         QMessageBox::critical(this, "错误", "无法打开流");
         return;
     }
     m_puller->setSpeed(m_speedCombo->currentData().toFloat());
-
-    // 设置回调
-    m_puller->setVideoRenderCallback([this](AVFrame* frame) {
-        static bool firstFrame = true;
-        if (firstFrame) {
-            m_videoPlayer->init(frame);
-            m_videoWidget->Open(frame->width, frame->height);
-            firstFrame = false;
-        }
-        m_videoPlayer->render(frame, (void*)videoRenderFunc, m_videoWidget);
-        updateProgress();
-    });
-    m_puller->setAudioPlayCallback([this](AVFrame* frame) {
-        float speed = m_puller->getSpeed();
-        if (m_audioPlayer->needReinit(frame, speed)) {
-            m_audioPlayer->init(frame, speed);
-        }
-        m_audioPlayer->play(frame, speed);
-    });
-
-    m_puller->start();
-
-    // 进度条初始化
+    // 启动主同步播放线程
+    m_syncPlayRunning = true;
+    m_syncPlayThread = QThread::create([this]() { this->syncPlayLoop(); });
+    m_syncPlayThread->start();
     m_firstPts = m_puller->getFirstPts();
     m_lastPts = m_puller->getLastPts();
     m_duration = m_lastPts - m_firstPts;
     m_progressSlider->setValue(0);
     updateProgress();
+    m_playing = true;
+}
+
+void PlayerDemoWindow::stopPlay()
+{
+    m_syncPlayRunning = false;
+    if (m_syncPlayThread) {
+        m_syncPlayThread->quit();
+        m_syncPlayThread->wait();
+        delete m_syncPlayThread;
+        m_syncPlayThread = nullptr;
+    }
+    if (m_puller) {
+        m_puller->stop();
+        delete m_puller;
+        m_puller = nullptr;
+    }
+    if (m_audioPlayer) {
+        delete m_audioPlayer;
+        m_audioPlayer = nullptr;
+    }
+    if (m_videoPlayer) {
+        delete m_videoPlayer;
+        m_videoPlayer = nullptr;
+    }
+    m_playing = false;
+}
+
+void PlayerDemoWindow::setSpeed(float speed)
+{
+    if (m_puller) {
+        m_puller->setSpeed(speed);
+    }
 }
 
 void PlayerDemoWindow::onPlayClicked()
@@ -188,7 +199,6 @@ void PlayerDemoWindow::onSpeedChanged(int index)
         return;
     float speed = m_speedCombo->itemData(index).toFloat();
     m_puller->setSpeed(speed);
-    speedFlag.store(true, std::memory_order_release);
 }
 
 void PlayerDemoWindow::updateProgress()
@@ -236,3 +246,37 @@ QString PlayerDemoWindow::formatTime(double seconds) const
         .arg(m, 2, 10, QChar('0'))
         .arg(s, 2, 10, QChar('0'));
 }
+
+void PlayerDemoWindow::syncPlayLoop()
+{
+    using namespace std::chrono;
+    double basePts = 0.0;
+    bool baseSet = false;
+    auto startTime = high_resolution_clock::now();
+    while (m_syncPlayRunning) {
+        auto now = high_resolution_clock::now();
+        double elapsed = duration_cast<milliseconds>(now - startTime).count();
+        if (!baseSet) {
+            AVFrame* af = m_puller->getCurrentAudioFrame();
+            if (af) {
+                basePts = af->best_effort_timestamp * av_q2d(m_puller->m_fmtCtx->streams[m_puller->m_audioStreamIdx]->time_base);
+                baseSet = true;
+            } else {
+                std::this_thread::sleep_for(milliseconds(1));
+                continue;
+            }
+        }
+        double curPts = basePts + elapsed / 1000.0 * m_puller->getSpeed();
+        // 音频
+        AVFrame* audioFrame = m_puller->popNearestAudioFrame(curPts);
+        if (audioFrame && m_audioPlayer) {
+            m_audioPlayer->play(audioFrame, m_puller->getSpeed());
+        }
+        // 视频
+        AVFrame* videoFrame = m_puller->popNearestVideoFrame(curPts);
+        if (videoFrame && m_videoPlayer) {
+            m_videoPlayer->render(videoFrame);
+        }
+        std::this_thread::sleep_for(milliseconds(1));
+    }
+}

+ 13 - 7
AvPlayer/playerdemowindow.h

@@ -1,19 +1,21 @@
 #ifndef PLAYERDEMOWINDOW_H
 #define PLAYERDEMOWINDOW_H
 
+#include "AvPlayer/FFmpegVideoPuller.h"
 #pragma once
 
 #include <QAudioOutput>
+#include <QCheckBox>
 #include <QComboBox>
 #include <QIODevice>
 #include <QLabel>
 #include <QPushButton>
 #include <QSlider>
 #include <QWidget>
-#include <QCheckBox>
-#include "AvRecorder/ui/opengl_video_widget.h"
-#include "Avplayer/FFmpegVideoPuller.h"
+#include <QThread>
+
 #include "AudioPlayer.h"
+#include "AvRecorder/ui/opengl_video_widget.h"
 #include "VideoPlayer.h"
 
 extern "C" {
@@ -30,6 +32,8 @@ public:
     ~PlayerDemoWindow();
 
     void startPlay(const QString& url);
+    void stopPlay();
+    void setSpeed(float speed);
 
 private slots:
     void onProgressSliderMoved(int value);
@@ -39,24 +43,26 @@ private slots:
 
 private:
     FFmpegVideoPuller* m_puller = nullptr;
-    OpenGLVideoWidget* m_videoWidget = nullptr;
     AudioPlayer* m_audioPlayer = nullptr;
     VideoPlayer* m_videoPlayer = nullptr;
+    QThread* m_syncPlayThread = nullptr;
+    std::atomic<bool> m_syncPlayRunning{false};
+    void syncPlayLoop();
+    OpenGLVideoWidget* m_videoWidget = nullptr;
     // UI
     QSlider* m_progressSlider = nullptr;
     QLabel* m_timeLabel = nullptr;
     QComboBox* m_speedCombo = nullptr;
     QPushButton* m_playBtn = nullptr;
+    QCheckBox* m_keepAspectCheck = nullptr;
     // 进度相关
     bool m_sliderPressed = false;
     double m_firstPts = 0.0;
     double m_lastPts = 0.0;
     double m_duration = 0.0;
-    std::atomic<bool> speedFlag;
-    bool m_playing;
+    bool m_playing = false;
     void updateProgress();
     QString formatTime(double seconds) const;
-    QCheckBox* m_keepAspectCheck = nullptr;
 
 private:
     SwrContext* m_swrCtx = nullptr;

+ 65 - 0
AvPlayer2/AvPlayer2.pri

@@ -0,0 +1,65 @@
+
+
+HEADERS += \
+    $$PWD/app_settings.h \
+    $$PWD/audio_decode_thread.h \
+    $$PWD/audio_effect_gl.h \
+    $$PWD/audio_effect_helper.h \
+    $$PWD/audio_play_thread.h \
+    $$PWD/clickable_slider.h \
+    $$PWD/common.h \
+    $$PWD/ffmpeg_init.h \
+    $$PWD/log.h \
+    $$PWD/mainwindowa.h \
+    $$PWD/network_url_dlg.h \
+    $$PWD/packets_sync.h \
+    $$PWD/play_control_window.h \
+    $$PWD/playercontroller.h \
+    $$PWD/playlist_window.h \
+    $$PWD/qimage_operation.h \
+    $$PWD/read_thread.h \
+    $$PWD/start_play_thread.h \
+    $$PWD/stopplay_waiting_thread.h \
+    $$PWD/subtitle_decode_thread.h \
+    $$PWD/version.h \
+    $$PWD/video_decode_thread.h \
+    $$PWD/video_play_thread.h \
+    $$PWD/video_state.h
+
+SOURCES += \
+    $$PWD/app_settings.cpp \
+    $$PWD/audio_decode_thread.cpp \
+    $$PWD/audio_effect_gl.cpp \
+    $$PWD/audio_effect_helper.cpp \
+    $$PWD/audio_play_thread.cpp \
+    $$PWD/clickable_slider.cpp \
+    $$PWD/common.cpp \
+    $$PWD/ffmpeg_init.cpp \
+    $$PWD/log.cpp \
+    $$PWD/mainwindowa.cpp \
+    $$PWD/network_url_dlg.cpp \
+    $$PWD/packets_sync.cpp \
+    $$PWD/play_control_window.cpp \
+    $$PWD/playercontroller.cpp \
+    $$PWD/playlist_window.cpp \
+    $$PWD/qimage_operation.cpp \
+    $$PWD/read_thread.cpp \
+    $$PWD/start_play_thread.cpp \
+    $$PWD/stopplay_waiting_thread.cpp \
+    $$PWD/subtitle_decode_thread.cpp \
+    $$PWD/video_decode_thread.cpp \
+    $$PWD/video_play_thread.cpp \
+    $$PWD/video_state.cpp
+
+RESOURCES += \
+    $$PWD/qmake_qmake_qm_files.qrc
+
+FORMS += \
+    $$PWD/network_url_dlg.ui \
+    $$PWD/playlist_window.ui
+
+DISTFILES += \
+    $$PWD/VideoPlayer_en_001.qm
+
+TRANSLATIONS += \
+    $$PWD/VideoPlayer_en_001.ts

BIN
AvPlayer2/VideoPlayer_en_001.qm


+ 3 - 0
AvPlayer2/VideoPlayer_en_001.ts

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="en_001"></TS>

+ 25 - 0
AvPlayer2/Visual Leak Detector/AUTHORS.txt

@@ -0,0 +1,25 @@
+------------------------------------------------
+KindDragon              http://www.codeplex.com/site/users/view/KindDragon
+chrisJohnson            http://www.codeplex.com/site/users/view/chrisJohnson
+
+Contributors: (listed alphabetically)
+------------------------------------------------
+akaStiX
+benpmorgan
+Eric Bissonnette
+geordi
+ioannis-e
+jerker_back
+jlddodger
+Kristian Paradis
+Laurent Lessieux (llessieux)
+mnissl
+snakefoot
+VictorKharkov
+xpol
+
+Many thanks to everyone who supported development without having
+git push access.
+
+Based on the original version (C) 2005-2009 by Dan Moulding (e-mail dmoulding@gmail.com)
+http://sites.google.com/site/dmoulding/vld

+ 565 - 0
AvPlayer2/Visual Leak Detector/CHANGES.txt

@@ -0,0 +1,565 @@
+Visual Leak Detector (VLD) Version 2.5.1
+
+
+  Change Log / Release Notes
+
+2.5.1 (30 January 2017)
+----------------------------
+  Enhancements:
+  + PDB added to installer.
+
+  Bugs Fixed:
+  + Fix ntdll loader patch for Windows 10 (1607) Anniversary Update causing crashes (thanks to ioannis-e).
+  + Vld dll loading order fixed with MFC.
+  + Supressible msgboxes in setup with cmdline /silent /suppressmsgboxes.
+
+2.5 (09 January 2016)
+----------------------------
+  Enhancements:
+  + VS2015 support added.
+  + Windows 10 support added.
+  + Support MFC 12 MBCS (thanks to mnissl).
+  + VLD config through env. vars (thanks to akaStiX).
+  + Support detection DllMain allocations (thanks to ioannis-e).
+  + Add option to skip reporting crt startup allocations as memory leaks (thanks to ioannis-e).
+  + Improve the vld.ini searching from additional locations (thanks to ioannis-e).
+  + Changed implementation of FastCallStack::getStackTrace for 32-bit code.
+
+  Bugs Fixed:
+  + Fix #9519, #9859, #10544, use LoaderLock to serialize any calls which load additional libraries or require access to the Loader Lock (thanks to ioannis-e).
+  + Fix #6359, #10553, fix crashes and failure to register COM dlls where Visual Leak Detector is unloaded before IMalloc interface is released (thanks to ioannis-e).
+  + Fix #10548, vld.ini search path (thanks to ioannis-e).
+  + Fix #10587, Visual Leak Detector reporting strange leaks in CRT module of VC++ (thanks to ioannis-e).
+  + Fix #10588, false positives from CRT in VS2013 with /MTd (thanks to ioannis-e).
+  + Deadlock fixed with StackWalkMethod=safe.
+
+2.4RC2 (07 April 2014)
+----------------------------
+  Enhancements:
+  + VLD will display last internal frame.
+  + Installer also add path for static library.
+
+  Bugs Fixed:
+  + Internal memory leak fixed.
+
+2.4RC (23 March 2014)
+----------------------------
+  Enhancements:
+  + VS2013 support added.
+  + Improved usage in C code.
+  + Setup rewrited to InnoSetup, autopatching common props implemented for VS2008-2013.
+  + Called allocation function added to printed callstack.
+  ! Memory leaks count fixed for case static/dynamic linked MFC.
+  ! Release static linked CRT detection improved (VLD_OPT_RELEASE_CRT_RUNTIME define removed).
+
+  Bugs Fixed:
+  + Memory leaks with static linked CRT fixed for VS2012/2013.
+  + Deadlock fixed and missed memory leaks when dll loading (first appear in version 2.2.2).
+
+2.3 (29 April 2013)
+----------------------------
+  Enhancements:
+  + Windows 8 support added.
+
+  Bugs Fixed:
+  + Memory leaks with duplicate thread id fixed (thanks to jlddodger).
+
+2.2.3 (15 Febrary 2012)
+----------------------------
+  Enhancements:
+  + New option VLD_OPT_RELEASE_CRT_RUNTIME added. Useful only with define VLD_FORCE_ENABLE.
+
+  Bugs Fixed:
+  + Memory leaks with static linking fixed finally.
+
+2.2.2 (18 December 2011)
+----------------------------
+  Bugs Fixed:
+  + Memory leaks with static linking fixed.
+  + Visual Studio C++ 2008/2010 Express Edition compilation fixed.
+  + Hang fixed with GetOpenFileName().
+
+2.2.1 (22 November 2011)
+----------------------------
+  Enhancements:
+  + strdup and _wcsdup functions support added.
+  + Preliminary support for VS 11 added.
+
+  Bugs Fixed:
+  + Low performance after upgrading from VLD v2.1.
+  + Runtime error R6002 fixed because of wrong memory dump format.
+  + version.h fixed in installer.
+  + Some PVS studio warning fixed.
+
+2.2 (21 Jule 2011)
+----------------------------
+  Enhancements:
+  + New functions added: VLDSetReportHook.
+
+  Bugs Fixed:
+  + Resolved call stack printing fixed.
+
+2.2b (8 Jule 2011)
+----------------------------
+  Enhancements:
+  + New functions added: VLDGetLeaksCount, VLDMarkAllLeaksAsReported (see vld.h).
+  + Introduced define called VLD_FORCE_ENABLE that allows one to active VLD even if not running in DEBUG.
+  + Adding Heap Validation.
+  + _aligned... functions and _recalloc support added.
+  + Memory leaks additional statistic added.
+
+  Bugs Fixed:
+  + Issue fixed with loading wrong version of dbghelp.dll on Windows XP and bellow.
+  + VLDReportLeaks with aggregate duplicate links fixed.
+  + CoTaskMemAlloc memory leak detection fixed.
+  + Rare crash at exit on some platforms fixed.
+  + Asserts in release build disabled.
+  + LoaderLock fixed.
+  + LoadLibrary crash fixed with some applications like regsrv32.
+  + Callstack hash fixed with ASLR.
+  + VLDGlobalEnable fixed with new threads.
+  + Option VLD_OPT_MODULE_LIST_INCLUDE fixed.
+
+2.1 (26 March 2011)
+----------------------------
+  Enhancements:
+  + New functions added: VLDGlobalDisable, VLDGlobalEnable, VLDGetOptions,
+    VLDGetReportFilename, VLDSetOptions, VLDSetModulesList,
+    VLDGetModulesList, VLDResolveCallstacks (see vld.h).
+  + Option VLD_OPT_SKIP_HEAPFREE_LEAKS added.
+  + Hash for each leak added
+  + Supported loading symbols from Visual Studio symbol cache directory
+
+  Bugs Fixed:
+  + Improved LdrLoadDll, GetProcAddress hooking on Windows 7.
+  + "HMODULE not founded" bug fixed.
+  + Bugs fixed when VLD off.
+  + Problem fixed with GetModuleHandleW for SxS dll's (mfc*.dll, msvcr*.dll).
+  + Unicode-to-multibyte conversion fixed.
+
+2.1 (26 March 2011)
+----------------------------
+  Enhancements:
+  + New functions added: VLDGlobalDisable, VLDGlobalEnable, VLDGetOptions,
+    VLDGetReportFilename, VLDSetOptions, VLDSetModulesList,
+    VLDGetModulesList, VLDResolveCallstacks (see vld.h).
+  + Option VLD_OPT_SKIP_HEAPFREE_LEAKS added.
+  + Hash for each leak added
+  + Supported loading symbols from Visual Studio symbol cache directory
+
+  Bugs Fixed:
+  + Improved LdrLoadDll, GetProcAddress hooking on Windows 7.
+  + "HMODULE not founded" bug fixed.
+  + Bugs fixed when VLD off.
+  + Problem fixed with GetModuleHandleW for SxS dll's (mfc*.dll, msvcr*.dll).
+  + Unicode-to-multibyte conversion fixed.
+
+2.0b (24 August 2010)
+----------------------------
+  Enhancements:
+  + Added new commands: VLDReportLeaks, VLDRefreshModules, VLDEnableModule,
+    VLDDisableModule, VLDSetReportOptions (see vld.h).
+
+  Bugs Fixed:
+  + Problems with MSVC 2008 SP1 fixed. Thanks to Laurent Lessieux for contributing this patch.
+
+2.0a (13 May 2010)
+----------------------------
+  Enhancements:
+  + Renamed vld dll files.
+
+  Bugs Fixed:
+  + Problem with MSVC 2010 Unicode library fixed.
+
+2.0 (25 April 2010)
+----------------------------
+  Enhancements:
+  + Added support to work with 64-bit applications
+  + Added support to work with Visual Studio 2010
+
+1.9h beta (24 February 2009)
+----------------------------
+  Enhancements:
+  + Added support to work with Visual Studio 2008.
+
+  Known Bugs/Restrictions:
+  + Same bugs/restrictions as version 1.9f.
+
+
+1.9g beta (16 April 2008)
+----------------------------
+  Bugs Fixed:
+  + Another deadlock condition may occur when loading DLLs into the process
+    being debugged. Special thanks to Eric Bissonnette and Kristian Paradis for
+    contributing this patch.
+
+  Known Bugs/Restrictions:
+  + Same bugs/restrictions as version 1.9f.
+
+
+1.9f beta (18 November 2006)
+----------------------------
+  Bugs Fixed:
+  + Deadlocks or access violations may occur when loading DLLs into
+    multithreaded processes.
+
+  + In multithreaded programs, if the main thread terminates before other
+    threads in the process, then Visual Leak Detector may cause an access
+    violation while generating the memory leak report.
+
+  Known Bugs/Restrictions:
+  + Memory allocations made through calls to functions loaded from a DLL using
+    delayed loading may not be detected.
+
+  + Support for programs that use MFC 7.0 or MFC 7.1 is not complete yet. Some
+    memory leaks from such MFC-based programs may not be detected.
+
+  + Visual Leak Detector may report leaks internal to Visual Leak Detector
+    if the main thread of the process terminates while other threads are still
+    running.
+
+  + If more than one copy of the same C Runtime DLL is loaded in the process at
+    the same time, then some leaks may go undetected (note that loading more
+    than one copy of the C Runtime DLL into a process at the same time is
+    probably a bad idea to begin with).
+
+
+1.9e beta (16 November 2006)
+----------------------------
+  New Features/Enhancements:
+  + Added a master on/off switch configuration option to vld.ini that can be
+    used to completely disable Visual Leak Detector.
+
+  Bugs Fixed:
+  + Numerous deadlock situations. The multithread synchronization scheme has
+    been completely re-written which should make deadlocks in VLD much less
+    likely to happen.
+
+  + An access violation will occur in VLD if GetProcAddress is called to obtain
+    an export's address by ordinal, for certain libraries.
+
+  + Problems may potentially occur when the program being debugged exits due to
+    the Debug Help Library having been detached from the process too early.
+    Symptoms might include access violation exceptions or other erratic behavior
+    just as the program exits and while VLD is generating the leak report.
+
+  + The copy of vld.ini installed in VLD's installation directory overrides any
+    other copies of vld.ini that are created, even copies placed in the
+    working directory of the program being debugged.
+
+  Known Bugs/Restrictions:
+  + Memory allocations made through calls to functions loaded from a DLL using
+    delayed loading may not be detected.
+
+  + Support for programs that use MFC 7.0 or MFC 7.1 is not complete yet. Some
+    memory leaks from such MFC-based programs may not be detected.
+
+  + If more than one copy of the same C Runtime DLL is loaded in the process at
+    the same time, then some leaks may go undetected (note that loading more
+    than one copy of the C Runtime DLL into a process at the same time is
+    probably a bad idea to begin with).
+
+
+1.9d beta (12 November 2006)
+----------------------------
+  Bugs Fixed:
+  + Failed assertion "freed == TRUE" pops up when running a program with VLD
+    without the debugger attached.
+
+  + Some, but not all, multithreaded programs that dynamically load and unload
+    many DLLs have been known to experience problems, such as deadlocks or
+    exceptions, when used with VLD.
+
+  + Failed assertion "exportmodule != NULL" pops up when running some programs
+    with VLD.
+
+  + VLD fails to show file names or function names in the memory leak report for
+    some programs that are linked with the dynamic CRT library.
+
+  + Access violation exceptions are thrown, but caught by the operating system,
+    when running some programs with VLD.
+
+
+1.9c beta (6 November 2006)
+---------------------------
+  New Features/Enhancements:
+  + New NSIS installer makes setting up and using VLD much easier.
+
+  + No need to manually copy dbghelp.dll to the right location, VLD will always
+    find the right version.
+
+  + MFC 8.0 is now fully supported.
+
+  + The memory leak report is now written to the output window much faster.
+    Support has been added, through a new configuration option, to slow down
+    the report output for older versions of Visual Studio that have trouble
+    when it is written too quickly.
+
+  Bugs Fixed:
+  + All known compatibilities with Visual Studio 2005 have been eliminated.
+
+  + Leaks from calloc may go undetected.
+
+  + Leaks from vector new operator may go undetected.
+
+  + VLDDisable and VLDEnable do not work as expected; some memory leaks that
+    should be ignored by VLD due to a previous call to VLDDisable are still
+    reported.
+
+  + Unloading and reloading a previously loaded module may cause leaks that
+    occur in the module after it was reloaded to go undetected.
+
+  + If vld.h is included in a release build, then the compiler will generate
+    errors if the VLDEnable or VLDDisable APIs have been used.
+
+
+1.9b beta (26 October 2006)
+---------------------------
+  Bugs Fixed:
+  + Source compiles under Visual Studio 2005 and the binaries are compatible
+    with applications that link with the Visual Studio 2005 C Runtime Library
+    (msvcr80d.dll).
+
+  Known Restrictions in this Release:
+  + Memory allocations made through calls to functions loaded from a DLL using
+    delayed loading may not be detected.
+
+  + Support for programs that use MFC 7.0, MFC 7.1, or MFC 8.0 is not complete
+    yet. Some memory leaks from such MFC-based programs may not be detected. A
+    workaround for this restriction is to forcefully include the MFC DLLs in
+    memory leak detection, by setting the "ForceIncludeModules" configuration
+    option to: "mfc70d.dll mfc71d.dll mfc80d.dll" and explicitly adding vld.lib
+    as an input file on the linker command line (can be added through project
+    settings by adding it to the list of library modules in the linker options).
+    This restriction does not apply to programs that use MFC 4.2, which is fully
+    supported.
+
+
+1.9a beta (9 March 2006)
+------------------------
+  New Features/Enhancements:
+  + All new leak detection engine detects most, if not all, in-process memory
+    leaks, not just leaks from "new" or "malloc", including COM-based leaks.
+
+  + Packaged as an easier-to-use DLL. There's no longer any need to carefully
+    decide which modules should be linked with the VLD library. Instead, you
+    just include the vld.h header file in at least one source file from each
+    module (DLL or EXE) to be included in memory leak detection.
+
+  + Configuration is done from an INI file instead of using preprocessor macros.
+    This allows VLD's configuration to be changed without needing to recompile
+    the program.
+
+  + Many new configuration options have been added. One of the most often
+    requested option that has been added is the option to save the leak report
+    to a file instead of, or in addition to, the debugger.
+
+  Bugs Fixed:
+  + The improved design of the new leak detection engine has resolved all of the
+    previously known restrictions in version 1.0.
+
+  Known Restrictions in this Release:
+  + Memory allocations made through calls to functions loaded from a DLL using
+    delayed loading may not be detected.
+
+  + Support for programs that use MFC 7.0, MFC 7.1, or MFC 8.0 is not complete
+    yet. Some memory leaks from such MFC-based programs may not be detected. A
+    workaround for this restriction is to forcefully include the MFC DLLs in
+    memory leak detection, by setting the "ForceIncludeModules" configuration
+    option to: "mfc70d.dll mfc71d.dll mfc80d.dll" and explicitly adding vld.lib
+    as an input file on the linker command line (can be added through project
+    settings by adding it to the list of library modules in the linker options).
+    This restriction does not apply to programs that use MFC 4.2, which is fully
+    supported.
+
+
+1.0 (5 August 2005)
+-------------------
+  New Features/Enhancements:
+  + Memory leak detection can now be selectively disabled and enabled at
+    runtime, using provided APIs. This provides a straightforward way of
+    allowing VLD to selectively "ignore" certain allocations. It can also be
+    used to disable VLD altogether at runtime, improving application performance
+    without needing to recompile.
+
+  + If there are multiple identical memory leaks (i.e. leaks that originate from
+    the same call stack and that leak the same size memory block) then VLD can
+    optionally aggregate all of the repeated leaks, showing only the first such
+    leaked block in detail in the memory leak report. A tally of the total
+    number of leaks that match that particular size and call stack accompanies
+    the information for that leak.
+
+  + When VLD is initialized at program startup, the library type which was
+    linked-in is displayed. This can help verify that the expected VLD library
+    (either single-threaded static, multithreaded static, or multithreaded DLL)
+    is being linked with your program.
+
+  + The Visual Leak Detector name is displayed on most messages output to the
+    debugger to easily differentiate VLD's output from the output produced by
+    the built-in memory leak detector.
+
+  + If any of the compile-time configuration options have been changed from
+    their default values, then the current state of the option is displayed in
+    the debugger when VLD is initialized.
+
+  + VLD's memory leak self-checking capability (checking for leaks in VLD
+    itself) can be verified using a new preprocessor macro that allows VLD to
+    perform a self-test at runtime.
+
+  Bugs Fixed:
+  + If the MFC libraries are statically linked to the program being debugged,
+    then MFC will erroneously report memory leaks in the Visual Leak Detector
+    code and may cause an access violation while attempting to report the false
+    memory leaks. These bogus leaks are always reported as "client block at
+    <address>, subtype bf42" and are claimed to be "invalid objects".
+
+  + VLD will leak a fixed-sized block of memory when the program exits if VLD
+    failed to initialize because the Debug Help library (dbghelp.dll) could not
+    be loaded.
+
+  + In multithreaded programs, if the program's main thread terminates before
+    other threads in the same process, then VLD may cause an access violation
+    while freeing resources used internally by VLD.
+
+
+0.9i beta (30 April 2005)
+-------------------------
+  New Features/Enhancements:
+  + Added support in the source code for x64 architecture. The pre-built
+    libraries will continue to support 32-bit only. If you need 64-bit support
+    you'll need to build 64-bit versions of the libraries from source. Note that
+    x64 is the only 64-bit architecture supported at this time. Itanium (aka
+    IA-64) is NOT currently supported.
+
+  Bugs Fixed:
+  + VLD does not report memory leaks that are the result of a failure to free
+    memory allocated via a call to realloc().
+  + In multithreaded programs, if the program's main thread terminates before
+    other threads in the same process, then VLD may cause an access violation
+    while checking for memory leaks.
+  + If VLD cannot find the source file and line number information for a program
+    address, the last known file and line number will be repeated in the call
+    stack section of the memory leak report. The correct behavior should be for
+    VLD to print "File and line number not available" for that call stack entry.
+
+
+0.9h beta (22 April 2005)
+-------------------------
+  Bugs Fixed:
+  + Access Violations occur at random places within the VLD code when using
+    VLD version 0.9g.
+  + When using VLD version 0.9g, VLD may fail to report some memory leaks.
+
+
+0.9g beta (22 April 2005)
+-------------------------
+  New Features/Enhancements:
+  + Replaced the temporary internal search algorithm with a permanent search
+    algorithm that is much faster. Programs that dynamically allocate a large
+    number of memory blocks (tens of thousands or more) will see the most
+    significant performance boost from this version of VLD versus the previous
+    version. Overall, this is the fastest version of VLD released to date.
+
+
+0.9f beta (13 April 2005)
+-------------------------
+  New Features/Enhancements:
+  + Changed the internal search algorithm to a temporary simpler, but
+    more stable algorithm. A permanent algorithm which should be much
+    more efficient will be in a forthcoming release.
+
+  Bugs Fixed:
+  + Access Violation at line 319 in vldutil.cpp may occur when running a
+    program linked with the VLD library.
+
+
+0.9e beta (12 April 2005)
+-------------------------
+  New Features/Enhancements:
+  + VLD no longer uses any STL containers or STL strings. This solves all of the
+    compatibility problems with Visual Studio .NET when using the pre-built
+    VLD libraries.
+
+  + The configuration preprocessor macros now work with C programs without the
+    need to call VLDConfigure from within the program being debugged.
+    Because VLDConfigure is now obsolete, it has been removed.
+
+  + One new source file (vldutil.cpp) and one new header (vldutil.h) have been
+    added. They contain utility functions and utility classes that replace
+    functionality previously performed by STL containers and strings.
+
+  + The VisualLeakDetector global class object is now constructed at C runtime
+    initialization (i.e. it resides in the "compiler" initialization area).
+    Because VLD no longer uses any STL components, there is no longer the risk
+    that VLD will conflict with any STL libraries that also are constructed at
+    C runtime initialization. The end result is that VLD starts running earlier
+    and is destroyed later, which leads to more accurate leak detection.
+
+  Bugs Fixed:
+  + Linking to the VLD 0.9d libraries from the VLD distribution under Visual
+    Studio .NET results in a number of linker "unresolved external symbol"
+    errors. Unresolved symbols include "__declspec(dllimport) void __cdecl
+    std::_Xran(void)" and "__declspec(dllimport) private: void __thiscall
+    std::basic_string,class std::allocator >::_Eos(unsigned int)", among others.
+
+  + Call stacks do not appear in the memory leak report when linking against
+    release VLD libraries built from source with Visual Studio .NET.
+
+  + If the preprocessor macro VLD_MAX_DATA_DUMP is defined as 0 (zero), then VLD
+    will get stuck in an infinite loop, repeatedly printing the same information
+    while attempting to display the memory leak report in the debugger's output
+    window.
+
+
+0.9d beta (30 March 2005)
+-------------------------
+  New Features/Enhancements:
+  + This version of VLD brings with it some major changes to the way VLD
+    interfaces with programs that use it. Instead of requiring that VLD be built
+    from source and then linked with the application, VLD is now packaged as a
+    pre-built static library. For those who just want to use VLD and are not
+    interested in modifying the source, this eliminates the complexities of
+    building VLD from source. A single header file, vld.h, has been added. To
+    link with the static library, this header needs to be included in one of the
+    program's source files. Please see the README.txt file for details on how
+    these changes affect how to use Visual Leak Detector.
+
+  + The Microsoft Debug Help Library (dbghelp.dll) version 6.3 is now included
+    with the VLD distribution.
+
+
+0.9c beta (17 March 2005)
+-------------------------
+  Bugs Fixed:
+  + Compile error, "error C2039: 'size' : is not a member of '_CrtMemBlockHeader'"
+    occurs at line 644 of vld.cpp when building VLD with the VLD_MAX_DATA_DUMP
+    preprocessor macro defined.
+
+
+0.9b beta (15 March 2005)
+-------------------------
+  Bugs Fixed:
+  + VLD fails to detect memory leaks in class constructors if the objects
+    constructed are global objects.
+
+  + If a debug executable is built with certain compiler optimizations turned on,
+    specifically frame pointer omission optimization or automatic inlining, then
+    theoretically VLD may produce incomplete or inaccurate stack traces or might
+    fail to produce stack traces altogether.
+
+
+0.9a beta (12 March 2005)
+-------------------------
+  Initial Public Release
+
+
+
+
+
+
+
+
+
+
+
+

+ 458 - 0
AvPlayer2/Visual Leak Detector/COPYING.txt

@@ -0,0 +1,458 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS

+ 7 - 0
AvPlayer2/Visual Leak Detector/bin/Win32/Microsoft.DTfW.DHL.manifest

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- $Id -->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+    <noInheritable />
+    <assemblyIdentity type="win32" name="Microsoft.DTfW.DHL" version="6.11.1.404" processorArchitecture="x86" />
+    <file name="dbghelp.dll" />
+</assembly>

BIN
AvPlayer2/Visual Leak Detector/bin/Win32/dbghelp.dll


BIN
AvPlayer2/Visual Leak Detector/bin/Win32/vld_x86.dll


+ 7 - 0
AvPlayer2/Visual Leak Detector/bin/Win64/Microsoft.DTfW.DHL.manifest

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- $Id -->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+    <noInheritable />
+    <assemblyIdentity type="win32" name="Microsoft.DTfW.DHL" version="6.11.1.404" processorArchitecture="amd64" />
+    <file name="dbghelp.dll" />
+</assembly>

BIN
AvPlayer2/Visual Leak Detector/bin/Win64/dbghelp.dll


BIN
AvPlayer2/Visual Leak Detector/bin/Win64/vld_x64.dll


+ 350 - 0
AvPlayer2/Visual Leak Detector/include/vld.h

@@ -0,0 +1,350 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Visual Leak Detector - Import Library Header
+//  Copyright (c) 2005-2014 VLD Team
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+//  See COPYING.txt for the full terms of the GNU Lesser General Public License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include "vld_def.h"
+
+typedef int            VLD_BOOL;
+typedef unsigned int   VLD_UINT;
+typedef size_t         VLD_SIZET;
+typedef void*          VLD_HMODULE;
+
+#if defined _DEBUG || defined VLD_FORCE_ENABLE
+
+#ifdef __AFXWIN_H__
+#error[VLD COMPILE ERROR] '#include <vld.h>' should appear before '#include <afxwin.h>' in file stdafx.h
+#endif
+
+#pragma comment(lib, "vld.lib")
+
+// Force a symbolic reference to the global VisualLeakDetector class object from
+// the DLL. This ensures that the DLL is loaded and linked with the program,
+// even if no code otherwise imports any of the DLL's exports.
+#pragma comment(linker, "/include:__imp_?g_vld@@3VVisualLeakDetector@@A")
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Visual Leak Detector APIs
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// VLDDisable - Disables Visual Leak Detector's memory leak detection at
+//   runtime. If memory leak detection is already disabled, then calling this
+//   function has no effect.
+//
+//  Note: In multithreaded programs, this function operates on a per-thread
+//    basis. In other words, if you call this function from one thread, then
+//    memory leak detection is only disabled for that thread. If memory leak
+//    detection is enabled for other threads, then it will remain enabled for
+//    those other threads. It was designed to work this way to insulate you,
+//    the programmer, from having to ensure thread synchronization when calling
+//    VLDEnable() and VLDDisable(). Without this, calling these two functions
+//    unsynchronized could result in unpredictable and unintended behavior.
+//    But this also means that if you want to disable memory leak detection
+//    process-wide, then you need to call this function from every thread in
+//    the process.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDDisable ();
+
+// VLDEnable - Enables Visual Leak Detector's memory leak detection at runtime.
+//   If memory leak detection is already enabled, which it is by default, then
+//   calling this function has no effect.
+//
+//  Note: In multithreaded programs, this function operates on a per-thread
+//    basis. In other words, if you call this function from one thread, then
+//    memory leak detection is only enabled for that thread. If memory leak
+//    detection is disabled for other threads, then it will remain disabled for
+//    those other threads. It was designed to work this way to insulate you,
+//    the programmer, from having to ensure thread synchronization when calling
+//    VLDEnable() and VLDDisable(). Without this, calling these two functions
+//    unsynchronized could result in unpredictable and unintended behavior.
+//    But this also means that if you want to enable memory leak detection
+//    process-wide, then you need to call this function from every thread in
+//    the process.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDEnable ();
+
+// VLDRestore - Restore Visual Leak Detector's previous state.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDRestore ();
+
+// VLDGlobalDisable - Disables Visual Leak Detector's memory leak detection at
+//   runtime in all threads. If memory leak detection is already disabled,
+//   then calling this function has no effect.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDGlobalDisable ();
+
+// VLDGlobalEnable - Enables Visual Leak Detector's memory leak detection
+//   at runtime in all threads. If memory leak detection is already enabled,
+//   which it is by default, then calling this function has no effect.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDGlobalEnable ();
+
+// VLDReportLeaks - Report leaks up to the execution point.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) VLD_UINT VLDReportLeaks ();
+
+// VLDReportThreadLeaks - Report thread leaks up to the execution point.
+//
+// threadId: thread Id.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) VLD_UINT VLDReportThreadLeaks (VLD_UINT threadId);
+
+// VLDGetLeaksCount - Return memory leaks count to the execution point.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) VLD_UINT VLDGetLeaksCount ();
+
+// VLDGetThreadLeaksCount - Return thread memory leaks count to the execution point.
+//
+// threadId: thread Id.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) VLD_UINT VLDGetThreadLeaksCount (VLD_UINT threadId);
+
+// VLDMarkAllLeaksAsReported - Mark all leaks as reported.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDMarkAllLeaksAsReported ();
+
+// VLDMarkThreadLeaksAsReported - Mark thread leaks as reported.
+//
+// threadId: thread Id.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDMarkThreadLeaksAsReported (VLD_UINT threadId);
+
+
+// VLDRefreshModules - Look for recently loaded DLLs and patch them if necessary.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDRefreshModules();
+
+
+// VLDEnableModule - Enable Memory leak checking on the specified module.
+//
+// module: module handle.
+//
+//  Return Value:
+//
+//    None.
+//
+
+__declspec(dllimport) void VLDEnableModule(VLD_HMODULE module);
+
+
+// VLDDisableModule - Disable Memory leak checking on the specified module.
+//
+// module: module handle.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDDisableModule(VLD_HMODULE module);
+
+// VLDGetOptions - Return all current options.
+//
+//  Return Value:
+//
+//    Mask of current options.
+//
+__declspec(dllimport) VLD_UINT VLDGetOptions();
+
+// VLDGetReportFilename - Return current report filename.
+//
+// filename: current report filename (max characters - MAX_PATH).
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDGetReportFilename(wchar_t *filename);
+
+// VLDSetOptions - Update the report options via function call rather than INI file.
+//
+// option_mask: Only the following flags are checked
+// VLD_OPT_AGGREGATE_DUPLICATES
+// VLD_OPT_MODULE_LIST_INCLUDE
+// VLD_OPT_SAFE_STACK_WALK
+// VLD_OPT_SLOW_DEBUGGER_DUMP
+// VLD_OPT_TRACE_INTERNAL_FRAMES
+// VLD_OPT_START_DISABLED
+// VLD_OPT_SKIP_HEAPFREE_LEAKS
+// VLD_OPT_VALIDATE_HEAPFREE
+//
+// maxDataDump: maximum number of user-data bytes to dump for each leaked block.
+//
+// maxTraceFrames: maximum number of frames per stack trace for each leaked block.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDSetOptions(VLD_UINT option_mask, VLD_SIZET maxDataDump, VLD_UINT maxTraceFrames);
+
+// VLDSetModulesList - Set list of modules included/excluded in leak detection
+// depending on parameter "includeModules".
+//
+// modules: list of modules to be forcefully included/excluded in leak detection.
+//
+// includeModules: include or exclude that modules.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDSetModulesList(const wchar_t *modules, VLD_BOOL includeModules);
+
+// VLDGetModulesList - Return current list of included/excluded modules
+// depending on flag VLD_OPT_TRACE_INTERNAL_FRAMES.
+//
+// modules: destination string for list of included/excluded modules (maximum length 512 characters).
+//
+// size: maximum string size.
+//
+//  Return Value:
+//
+//    VLD_BOOL: TRUE if include modules, otherwise FALSE.
+//
+__declspec(dllimport) VLD_BOOL VLDGetModulesList(wchar_t *modules, VLD_UINT size);
+
+// VLDSetReportOptions - Update the report options via function call rather than INI file.
+//
+// Only the following flags are checked
+// VLD_OPT_REPORT_TO_DEBUGGER
+// VLD_OPT_REPORT_TO_FILE
+// VLD_OPT_REPORT_TO_STDOUT
+// VLD_OPT_UNICODE_REPORT
+//
+// filename is optional and can be NULL.
+//
+//  Return Value:
+//
+//    None.
+//
+__declspec(dllimport) void VLDSetReportOptions(VLD_UINT option_mask, const wchar_t *filename);
+
+// VLDSetReportHook - Installs or uninstalls a client-defined reporting function by hooking it
+//  into the C run-time debug reporting process (debug version only).
+//
+// mode: The action to take: VLD_RPTHOOK_INSTALL or VLD_RPTHOOK_REMOVE.
+//
+// pfnNewHook: Report hook to install or remove.
+//
+//  Return Value:
+//
+//    int: 0 if success.
+//
+__declspec(dllimport) int VLDSetReportHook(int mode,  VLD_REPORT_HOOK pfnNewHook);
+
+// VLDResolveCallstacks - Performs symbol resolution for all saved extent CallStack's that have
+// been tracked by Visual Leak Detector. This function is necessary for applications that
+// dynamically load and unload modules, and through which memory leaks might be included.
+// If this is NOT called, stack traces may have stack frames with no symbol information. This
+// happens because the symbol API's cannot look up symbols for a binary / module that has been unloaded
+// from the process.
+//
+//  Return Value:
+//
+//    int: 0 if successfully resolved all callstacks.
+//
+__declspec(dllexport) int VLDResolveCallstacks();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#else // !_DEBUG
+
+#define VLDEnable()
+#define VLDDisable()
+#define VLDRestore()
+#define VLDGlobalDisable()
+#define VLDGlobalEnable()
+#define VLDReportLeaks() (0)
+#define VLDReportThreadLeaks() (0)
+#define VLDGetLeaksCount() (0)
+#define VLDGetThreadLeaksCount() (0)
+#define VLDMarkAllLeaksAsReported()
+#define VLDMarkThreadLeaksAsReported(a)
+#define VLDRefreshModules()
+#define VLDEnableModule(a)
+#define VLDDisableModule(b)
+#define VLDGetOptions() (0)
+#define VLDGetReportFilename(a)
+#define VLDSetOptions(a, b, c)
+#define VLDSetReportHook(a, b)
+#define VLDSetModulesList(a)
+#define VLDGetModulesList(a, b) (FALSE)
+#define VLDSetReportOptions(a, b)
+#define VLDResolveCallstacks() (0)
+
+#endif // _DEBUG

+ 49 - 0
AvPlayer2/Visual Leak Detector/include/vld_def.h

@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Visual Leak Detector - Import Library Header
+//  Copyright (c) 2005-2014 VLD Team
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+//  See COPYING.txt for the full terms of the GNU Lesser General Public License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#ifndef _WCHAR_T_DEFINED
+#   include <wchar.h>
+#endif
+
+#define VLD_OPT_AGGREGATE_DUPLICATES    0x0001 //   If set, aggregate duplicate leaks in the leak report.
+#define VLD_OPT_MODULE_LIST_INCLUDE     0x0002 //   If set, modules in the module list are included, all others are excluded.
+#define VLD_OPT_REPORT_TO_DEBUGGER      0x0004 //   If set, the memory leak report is sent to the debugger.
+#define VLD_OPT_REPORT_TO_FILE          0x0008 //   If set, the memory leak report is sent to a file.
+#define VLD_OPT_SAFE_STACK_WALK         0x0010 //   If set, the stack is walked using the "safe" method (StackWalk64).
+#define VLD_OPT_SELF_TEST               0x0020 //   If set, perform a self-test to verify memory leak self-checking.
+#define VLD_OPT_SLOW_DEBUGGER_DUMP      0x0040 //   If set, inserts a slight delay between sending output to the debugger.
+#define VLD_OPT_START_DISABLED          0x0080 //   If set, memory leak detection will initially disabled.
+#define VLD_OPT_TRACE_INTERNAL_FRAMES   0x0100 //   If set, include useless frames (e.g. internal to VLD) in call stacks.
+#define VLD_OPT_UNICODE_REPORT          0x0200 //   If set, the leak report will be encoded UTF-16 instead of ASCII.
+#define VLD_OPT_VLDOFF                  0x0400 //   If set, VLD will be completely deactivated. It will not attach to any modules.
+#define VLD_OPT_REPORT_TO_STDOUT        0x0800 //   If set, the memory leak report is sent to stdout.
+#define VLD_OPT_SKIP_HEAPFREE_LEAKS     0x1000 //   If set, VLD skip HeapFree memory leaks.
+#define VLD_OPT_VALIDATE_HEAPFREE       0x2000 //   If set, VLD verifies and reports heap consistency for HeapFree calls.
+#define VLD_OPT_SKIP_CRTSTARTUP_LEAKS   0x4000 //   If set, VLD skip crt srtartup memory leaks.
+
+#define VLD_RPTHOOK_INSTALL  0
+#define VLD_RPTHOOK_REMOVE   1
+
+typedef int (__cdecl * VLD_REPORT_HOOK)(int reportType, wchar_t *message, int *returnValue);

BIN
AvPlayer2/Visual Leak Detector/lib/Win32/vld.lib


BIN
AvPlayer2/Visual Leak Detector/lib/Win64/vld.lib


BIN
AvPlayer2/Visual Leak Detector/unins000.dat


BIN
AvPlayer2/Visual Leak Detector/unins000.exe


+ 171 - 0
AvPlayer2/Visual Leak Detector/vld.ini

@@ -0,0 +1,171 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;  Visual Leak Detector - Initialization/Configuration File
+;;  Copyright (c) 2005-2017 VLD Team
+;;
+;;  This library is free software; you can redistribute it and/or
+;;  modify it under the terms of the GNU Lesser General Public
+;;  License as published by the Free Software Foundation; either
+;;  version 2.1 of the License, or (at your option) any later version.
+;;
+;;  This library is distributed in the hope that it will be useful,
+;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;  Lesser General Public License for more details.
+;;
+;;  You should have received a copy of the GNU Lesser General Public
+;;  License along with this library; if not, write to the Free Software
+;;  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+;;
+;;  See COPYING.txt for the full terms of the GNU Lesser General Public License.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Any options left blank or not present will revert to their default values.
+[Options]
+
+; The main on/off switch. If off, Visual Leak Detector will be completely
+; disabled. It will do nothing but print a message to the debugger indicating
+; that it has been turned off.
+;
+;  Valid Values: on, off
+;  Default: on
+;
+VLD = on
+
+; If yes, duplicate leaks (those that are identical) are not shown individually.
+; Only the first such leak is shown, along with a number indicating the total
+; number of duplicate leaks.
+;
+;   Valid Values: yes, no
+;   Default: no
+;
+AggregateDuplicates = no
+
+; Lists any additional modules to be included in memory leak detection. This can
+; be useful for checking for memory leaks in debug builds of 3rd party modules
+; which can not be easily rebuilt with '#include "vld.h"'. This option should be
+; used only if absolutely necessary and only if you really know what you are
+; doing.
+;
+;   CAUTION: Avoid listing any modules that link with the release CRT libraries.
+;     Only modules that link with the debug CRT libraries should be listed here.
+;     Doing otherwise might result in false memory leak reports or even crashes.
+;
+;   Valid Values: Any list containing module names (i.e. names of EXEs or DLLs)
+;   Default: None.
+;
+ForceIncludeModules = 
+
+; Maximum number of data bytes to display for each leaked block. If zero, then
+; the data dump is completely suppressed and only call stacks are shown.
+; Limiting this to a low number can be useful if any of the leaked blocks are
+; very large and cause unnecessary clutter in the memory leak report.
+;
+;   Value Values: 0 - 4294967295
+;   Default: 256
+;
+MaxDataDump = 
+
+; Maximum number of call stack frames to trace back during leak detection.
+; Limiting this to a low number can reduce the CPU utilization overhead imposed
+; by memory leak detection, especially when using the slower "safe" stack
+; walking method (see StackWalkMethod below).
+;
+;   Valid Values: 1 - 4294967295
+;   Default: 64
+;
+MaxTraceFrames = 
+
+; Sets the type of encoding to use for the generated memory leak report. This
+; option is really only useful in conjuction with sending the report to a file.
+; Sending a Unicode encoded report to the debugger is not useful because the
+; debugger cannot display Unicode characters. Using Unicode encoding might be
+; useful if the data contained in leaked blocks is likely to consist of Unicode
+; text.
+;
+;   Valid Values: ascii, unicode
+;   Default: ascii
+;
+ReportEncoding = ascii
+
+; Sets the report file destination, if reporting to file is enabled. A relative
+; path may be specified and is considered relative to the process' working
+; directory.
+;
+;   Valid Values: Any valid path and filename.
+;   Default: .\memory_leak_report.txt
+;
+ReportFile = 
+
+; Sets the report destination to either a file, the debugger, or both. If
+; reporting to file is enabled, the report is sent to the file specified by the
+; ReportFile option.
+;
+;   Valid Values: debugger, file, both
+;   Default: debugger
+;
+ReportTo = debugger
+
+; Turns on or off a self-test mode which is used to verify that VLD is able to
+; detect memory leaks in itself. Intended to be used for debugging VLD itself,
+; not for debugging other programs.
+;
+;   Valid Values: on, off
+;   Default: off
+;
+SelfTest = off
+
+; Selects the method to be used for walking the stack to obtain stack traces for
+; allocated memory blocks. The "fast" method may not always be able to
+; successfully trace completely through all call stacks. In such cases, the
+; "safe" method may prove to more reliably obtain the full stack trace. The
+; disadvantage is that the "safe" method is significantly slower than the "fast"
+; method and will probably result in very noticeable performance degradation of
+; the program being debugged.
+;
+;   Valid Values: fast, safe
+;   Default: fast
+; 
+StackWalkMethod = fast
+
+; Determines whether memory leak detection should be initially enabled for all
+; threads, or whether it should be initially disabled for all threads. If set
+; to "yes", then any threads requiring memory leak detection to be enabled will
+; need to call VLDEnable at some point to enable leak detection for those
+; threads.
+;
+;   Valid Values: yes, no
+;   Default: no
+;
+StartDisabled = no
+
+; Determines whether or not all frames, including frames internal to the heap,
+; are traced. There will always be a number of frames internal to Visual Leak
+; Detector and C/C++ or Win32 heap APIs that aren't generally useful for
+; determining the cause of a leak. Normally these frames are skipped during the
+; stack trace, which somewhat reduces the time spent tracing and amount of data
+; collected and stored in memory. Including all frames in the stack trace, all
+; the way down into VLD's own code can, however, be useful for debugging VLD
+; itself.
+;
+;   Valid Values: yes, no
+;   Default: no
+;
+TraceInternalFrames = no
+
+; Determines whether or not report memory leaks when missing HeapFree calls.
+;
+;   Valid Values: yes, no
+;   Default: no
+;
+SkipHeapFreeLeaks = no
+
+; Determines whether or not report memory leaks generated from crt startup code.
+; These are not actual memory leaks as they are freed by crt after the VLD object
+; has been destroyed.
+;
+;   Valid Values: yes, no
+;   Default: yes
+;
+SkipCrtStartupLeaks = yes

+ 114 - 0
AvPlayer2/app_settings.cpp

@@ -0,0 +1,114 @@
+// ***********************************************************/
+// app_settings.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// app settings load and save
+// ***********************************************************/
+
+#include "app_settings.h"
+
+const AppSettings::Section AppSettings::m_sections[] = {
+    {SECTION_ID_GENERAL, "General"},
+    {SECTION_ID_INFO, "Info"},
+    {SECTION_ID_RECENTFILES, "RecentFiles"},
+    {SECTION_ID_SAVEDPLAYLISTFILES, "SavedPlaylistFiles"},
+};
+
+AppSettings::AppSettings(const QString& file)
+{
+    m_pSettings = std::make_unique<QSettings>(file, QSettings::IniFormat);
+
+    print_settings();
+}
+
+void AppSettings::print_settings() const
+{
+    if (m_pSettings)
+    {
+        qDebug() << "videoplayer configure file:" << toNativePath(m_pSettings->fileName());
+        qDebug() << "organizationName:" << m_pSettings->organizationName();
+        qDebug() << "applicationName:" << m_pSettings->applicationName();
+
+        for (const auto& group : m_pSettings->childGroups())
+        {
+            m_pSettings->beginGroup(group);
+            qDebug() << "group:" << group;
+            for (const auto& key : m_pSettings->childKeys())
+            {
+                qDebug() << QString("key:%1, value:%2").arg(key).arg(m_pSettings->value(key).toString());
+            }
+            m_pSettings->endGroup();
+        }
+    }
+}
+
+void AppSettings::set_value(SectionID id, const QString& key, const QVariant& value)
+{
+    if (id > SECTION_ID_NONE && id < SECTION_ID_MAX)
+        set_value(QString(m_sections[id].str), key, value);
+}
+
+QVariant AppSettings::get_value(SectionID id, const QString& key) const
+{
+    if (id > SECTION_ID_NONE && id < SECTION_ID_MAX)
+    {
+        return get_value(QString(m_sections[id].str), key);
+    }
+    return QVariant(QVariant::Invalid);
+}
+
+inline QString AppSettings::group_key(const QString& group, const QString& key)
+{
+    return group + "/" + key;
+}
+
+void AppSettings::set_value(const QString& group, const QString& key, const QVariant& value)
+{
+    m_pSettings->setValue(group_key(group, key), value);
+}
+
+QVariant AppSettings::get_value(const QString& group, const QString& key) const
+{
+    return m_pSettings->value(group_key(group, key));
+}
+
+void AppSettings::set_general(const QString& key, const QVariant& value)
+{
+    set_value(SECTION_ID_GENERAL, key, value);
+}
+
+QVariant AppSettings::get_general(const QString& key) const
+{
+    return get_value(SECTION_ID_GENERAL, key);
+}
+
+void AppSettings::set_info(const QString& key, const QVariant& value)
+{
+    set_value(SECTION_ID_INFO, key, value);
+}
+
+QVariant AppSettings::get_info(const QString& key) const
+{
+    return get_value(SECTION_ID_INFO, key);
+}
+
+void AppSettings::set_recentfiles(const QVariant& value, const QString& key)
+{
+    set_value(SECTION_ID_RECENTFILES, key, value);
+}
+
+QVariant AppSettings::get_recentfiles(const QString& key) const
+{
+    return get_value(SECTION_ID_RECENTFILES, key);
+}
+
+QVariant AppSettings::get_savedplaylists(const QString& key) const
+{
+    return get_value(SECTION_ID_SAVEDPLAYLISTFILES, key);
+}
+
+void AppSettings::set_savedplaylists(const QVariant& value, const QString& key)
+{
+    set_value(SECTION_ID_SAVEDPLAYLISTFILES, key, value);
+}

+ 54 - 0
AvPlayer2/app_settings.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include <QSettings>
+#include <QDebug>
+#include <QDir>
+#include <QVAriant>
+#include <memory>
+#include "common.h"
+
+class AppSettings
+{
+public:
+    explicit AppSettings(const QString& file = "VideoPlayer.ini");
+    ~AppSettings(){};
+
+public:
+    QVariant get_general(const QString& key) const;
+    void set_general(const QString& key, const QVariant& value);
+    QVariant get_info(const QString& key) const;
+    void set_info(const QString& key, const QVariant& value);
+    QVariant get_recentfiles(const QString& key = "files") const;
+    void set_recentfiles(const QVariant& value = QVariant::Invalid, const QString& key = "files");
+    QVariant get_savedplaylists(const QString& key = "files") const;
+    void set_savedplaylists(const QVariant& value = QVariant::Invalid, const QString& key = "files");
+
+private:
+    enum SectionID
+    {
+        SECTION_ID_NONE = -1,
+        SECTION_ID_GENERAL,
+        SECTION_ID_INFO,
+        SECTION_ID_RECENTFILES,
+        SECTION_ID_SAVEDPLAYLISTFILES,
+        SECTION_ID_MAX
+    };
+
+    typedef struct Section
+    {
+        SectionID id;
+        const char* str;
+    } Section;
+
+private:
+    void print_settings() const;
+    void set_value(SectionID id, const QString& key, const QVariant& value);
+    void set_value(const QString& group, const QString& key, const QVariant& value);
+    QVariant get_value(const QString& group, const QString& key) const;
+    QVariant get_value(SectionID id, const QString& key) const;
+    inline static QString group_key(const QString& group, const QString& key);
+
+private:
+    std::unique_ptr<QSettings> m_pSettings;
+    static const Section m_sections[];
+};

+ 148 - 0
AvPlayer2/audio_decode_thread.cpp

@@ -0,0 +1,148 @@
+// ***********************************************************/
+// audio_decode_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// audio decode thread
+// ***********************************************************/
+
+#include "audio_decode_thread.h"
+
+AudioDecodeThread::AudioDecodeThread(QObject* parent, VideoState* pState)
+    : QThread(parent)
+    , m_pState(pState)
+{}
+
+AudioDecodeThread::~AudioDecodeThread() {}
+
+void AudioDecodeThread::run()
+{
+    assert(m_pState);
+    VideoState* is = m_pState;
+    AVFrame* frame = av_frame_alloc();
+    Frame* af;
+#if USE_AVFILTER_AUDIO
+    int last_serial = -1;
+    AVChannelLayout dec_channel_layout; // int64_t
+    int reconfigure;
+#endif
+    int got_frame = 0;
+    AVRational tb;
+    int ret = 0;
+
+    if (!frame)
+        return;
+
+    do {
+        /*if (is->abort_request)
+            break;*/
+
+        if ((got_frame = decoder_decode_frame(&is->auddec, frame, nullptr)) < 0)
+            goto the_end;
+
+        if (got_frame) {
+            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,
+                                         is->audio_filter_src.ch_layout.nb_channels,
+                                         AVSampleFormat(frame->format),
+                                         frame->ch_layout.nb_channels)
+                          || is->audio_filter_src.ch_layout.nb_channels
+                                 != dec_channel_layout.nb_channels
+                          || is->audio_filter_src.freq != frame->sample_rate
+                          || is->auddec.pkt_serial != last_serial;
+
+            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));
+
+                av_log(nullptr,
+                       AV_LOG_DEBUG,
+                       "Audio frame changed from rate:%d ch:%d fmt:%s layout:%s "
+                       "serial:%d to rate:%d ch:%d fmt:%s layout:%s serial:%d\n",
+                       is->audio_filter_src.freq,
+                       is->audio_filter_src.ch_layout.nb_channels,
+                       av_get_sample_fmt_name(is->audio_filter_src.fmt),
+                       buf1,
+                       last_serial,
+                       frame->sample_rate,
+                       frame->ch_layout.nb_channels,
+                       av_get_sample_fmt_name(AVSampleFormat(frame->format)),
+                       buf2,
+                       is->auddec.pkt_serial);
+
+                is->audio_filter_src.fmt = (AVSampleFormat) frame->format;
+                ret = av_channel_layout_copy(&is->audio_filter_src.ch_layout, &frame->ch_layout);
+                if (ret < 0)
+                    goto the_end;
+                is->audio_filter_src.freq = frame->sample_rate;
+                last_serial = is->auddec.pkt_serial;
+
+                ret = configure_audio_filters(is, is->afilters, 1);
+                if (ret < 0)
+                    goto the_end;
+                is->req_afilter_reconfigure = 0;
+            }
+
+            ret = av_buffersrc_add_frame(is->in_audio_filter, frame);
+            if (ret < 0)
+                goto the_end;
+
+            while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) {
+                tb = av_buffersink_get_time_base(is->out_audio_filter);
+#endif
+
+                if (!(af = frame_queue_peek_writable(&is->sampq)))
+                    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->serial = is->auddec.pkt_serial;
+                af->duration = av_q2d(AVRational{frame->nb_samples, frame->sample_rate});
+
+                av_frame_move_ref(af->frame, frame);
+                frame_queue_push(&is->sampq);
+
+#if USE_AVFILTER_AUDIO
+                if (is->audioq.serial != is->auddec.pkt_serial)
+                    break;
+            }
+            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);
+
+the_end:
+
+#if USE_AVFILTER_AUDIO
+    avfilter_graph_free(&is->agraph);
+    if (is->afilters) {
+        av_free(is->afilters);
+        is->afilters = nullptr;
+    }
+#endif
+
+    av_frame_free(&frame);
+    qDebug("-------- audio decode thread exit.");
+    return;
+}

+ 19 - 0
AvPlayer2/audio_decode_thread.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <QThread>
+#include "packets_sync.h"
+
+class AudioDecodeThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    explicit AudioDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    ~AudioDecodeThread();
+
+protected:
+    void run() override;
+
+private:
+    VideoState* m_pState;
+};

+ 75 - 0
AvPlayer2/audio_effect_gl.cpp

@@ -0,0 +1,75 @@
+// ***********************************************************/
+// audio_effect_gl.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// audio visualization OpenGL window
+// ***********************************************************/
+
+#include <QApplication>
+#include <QDebug>
+#include <QPainter>
+#include "audio_effect_gl.h"
+
+AudioEffectGL::AudioEffectGL(QWidget* parent) : QOpenGLWidget(parent)
+{
+    auto flags = windowFlags();
+    flags |= Qt::Window;
+    flags |= Qt::WindowStaysOnTopHint;
+    flags &= (~Qt::WindowContextHelpButtonHint);
+    // flags &= (~Qt::WindowMinMaxButtonsHint);
+
+    setWindowFlags(flags);
+
+    int width = 480;
+    int height = 280;
+
+    // setFixedSize(width, height);
+    setMinimumWidth(width);
+    setMinimumHeight(height);
+
+    setWindowTitle("Audio visualization");
+    setAutoFillBackground(true);
+
+    m_img = QImage(":/images/res/bkground.png");
+}
+
+void AudioEffectGL::closeEvent(QCloseEvent* event)
+{
+    hide();
+    emit hiden();
+    event->accept();
+}
+
+void AudioEffectGL::paintEvent(QPaintEvent* event)
+{
+    QPainter painter;
+    painter.begin(this);
+    painter.setRenderHint(QPainter::Antialiasing);
+#if 0
+	painter.drawImage(rect(), m_img);
+#else
+    m_helper.paint(&painter, event, m_data);
+#endif
+    painter.end();
+}
+
+void AudioEffectGL::keyPressEvent(QKeyEvent* event)
+{
+    if (event->key() == Qt::Key_Escape)
+    {
+        hide();
+        event->accept();
+    }
+    else
+    {
+        QOpenGLWidget::keyPressEvent(event);
+    }
+}
+
+void AudioEffectGL::paint_data(const AudioData& data)
+{
+    m_data = data;
+    // qDebug() << "p=" << &data << "datalen:" << data.len;
+    repaint();
+}

+ 38 - 0
AvPlayer2/audio_effect_gl.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include <QImage>
+#include <QKeyEvent>
+// #include <QtOpenGLWidgets>
+#include <QOpenGLWidget>
+#include "audio_effect_helper.h"
+#include "audio_play_thread.h"
+
+class AudioEffectGL : public QOpenGLWidget
+{
+    Q_OBJECT
+
+public:
+    explicit AudioEffectGL(QWidget* parent = nullptr);
+    virtual ~AudioEffectGL(){};
+
+public:
+    void paint_data(const AudioData& data);
+    void paint_clear()
+    {
+        m_data.len = 0;
+        repaint();
+    };
+    void set_draw_fmt(const BarHelper::VisualFormat& fmt) { m_helper.set_draw_fmt(fmt); }
+signals:
+    void hiden(bool bSend = false);
+
+protected:
+    void paintEvent(QPaintEvent* event) override;
+    void keyPressEvent(QKeyEvent* event) override;
+    void closeEvent(QCloseEvent* event) override;
+
+private:
+    QImage m_img;
+    BarHelper m_helper;
+    AudioData m_data;
+};

+ 338 - 0
AvPlayer2/audio_effect_helper.cpp

@@ -0,0 +1,338 @@
+// ***********************************************************/
+// audio_effect_helper.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// audio visualization draw functions
+// ***********************************************************/
+
+#include <QPaintEvent>
+#include <QPainter>
+#include <QWidget>
+#include <QtMath>
+#include <algorithm>
+#include "audio_effect_helper.h"
+
+#define MAX_AUDIO_VAULE 0xffff // unsigned 16bit pcm audio max value
+
+BarHelper::BarHelper()
+{
+    m_datafmt.sample_fmt = 16;
+    m_datafmt.channel = 1;
+
+    QLinearGradient gradient(QPointF(50, 200), QPointF(50, 0));
+    gradient.setColorAt(0.0, Qt::white);
+    gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
+
+    m_background = QBrush(QColor(64, 32, 64));
+    m_brush = QBrush(gradient);
+    m_pen = QPen(Qt::green, 2); // QPen(Qt::black);
+
+    m_textPen = QPen(Qt::white);
+    m_textFont.setPixelSize(50);
+}
+
+void BarHelper::draw_data_bar(QPainter* painter, std::vector<int>& data, int n, int w, int h, int h_inter)
+{
+    assert(data.size() == n);
+
+    auto w_step = (w - (n - 1) * h_inter) * 1.0 / n;
+    for (int i = 0; i < n; ++i)
+    {
+        auto top = data[i]; // rand() % height;
+        QRectF rt(i * (w_step + h_inter), h - top, w_step, top);
+        painter->drawRect(rt);
+    }
+}
+
+void BarHelper::draw_data_line(QPainter* painter, std::vector<int>& data, int n, int w, int h, int h_inter)
+{
+    painter->setPen(m_pen);
+
+    auto w_step = (w - (n - 1) * h_inter) * 1.0 / n;
+    for (size_t i = 0; i < n - 1; ++i)
+    {
+        qreal x1 = i * (w_step + h_inter);
+        qreal y1 = h - data[i];
+        qreal x2 = x1 + (w_step + h_inter);
+        qreal y2 = h - data[i + 1];
+        // painter->drawRect(rt);
+        QLine line = QLine(QPoint(x1, y1), QPoint(x2, y2));
+        painter->drawLine(line);
+    }
+}
+
+void BarHelper::draw_data_arc(QPainter* painter, std::vector<int>& data, int n, int w, int h)
+{
+    assert(data.size() == n);
+    const qreal s_radius = 50.0;
+
+    QPointF center(w * 1.0 / 2, h * 1.0 / 2);
+    painter->translate(center);
+    painter->setBrush(QBrush(Qt::red));
+
+    const int spanAngle = 360 / n * 16;
+    for (int i = 0; i < n; ++i)
+    {
+        qreal radius = data[i] + s_radius;
+        QRect bounds(-radius, -radius, 2 * radius, 2 * radius);
+        int startAngle = 360 * i / n * 16;
+        painter->drawArc(bounds, startAngle, spanAngle);
+    }
+}
+
+void BarHelper::draw_data_polygon(QPainter* painter, std::vector<int>& data, int n, int w, int h, int r_offset)
+{
+    assert(data.size() == n);
+
+    QPointF center(w * 1.0 / 2, h * 1.0 / 2);
+    painter->translate(center);
+
+    const int pt_size = 4;
+    const int inter_a = 0;
+    const qreal s_radius = r_offset;
+    QPointF points[pt_size] = {};
+
+    for (int i = 0; i < n; ++i)
+    {
+        qreal radius = data[i] + s_radius;
+
+        float startAngle = 360 * i / n;
+        float stopAngle = 360 * (i + 1) / n - inter_a;
+
+        float start_d = qDegreesToRadians(startAngle);
+        float stop_d = qDegreesToRadians(stopAngle);
+
+        points[0] = QPointF(qCos(start_d) * s_radius, qSin(start_d) * s_radius);
+        points[1] = QPointF(qCos(start_d) * radius, qSin(start_d) * radius);
+        points[2] = QPointF(qCos(stop_d) * radius, qSin(stop_d) * radius);
+        points[3] = QPointF(qCos(stop_d) * s_radius, qSin(stop_d) * s_radius);
+
+        painter->drawPolygon(points, 4);
+    }
+}
+
+void BarHelper::draw_data_style(QPainter* painter, const QRect& rt, const AudioData& data)
+{
+    int width = rt.width();
+    int height = rt.height();
+    int h_inter = 2;
+
+    assert(data.len <= BUFFER_LEN);
+    std::vector<int> v_data;
+    get_data(data, v_data);
+    if (v_data.size() == 0)
+        return;
+
+    int n = 128;
+
+    normal_overzero(v_data);
+    if (m_visualFmt.vType == e_VtSampleing)
+    {
+        data_sample(v_data, n);
+        normal_audio_to_size(v_data, height);
+    }
+    else
+    {
+        data_frequency(v_data, n);
+    }
+
+    if (m_visualFmt.gType == e_GtBar)
+    {
+        draw_data_bar(painter, v_data, n, width, height, h_inter);
+    }
+    else if (m_visualFmt.gType == e_GtLine)
+    {
+        draw_data_line(painter, v_data, n, width, height, h_inter);
+    }
+    else if (m_visualFmt.gType == e_GtPie)
+    {
+        int r_offset = 30;
+        normal_to_size(v_data, qMin(width / 2 - r_offset, height / 2 - r_offset));
+        draw_data_polygon(painter, v_data, n, width, height, r_offset);
+    }
+    else
+    {
+        qDebug() << "Not handled yet.\n";
+    }
+}
+
+void BarHelper::paint(QPainter* painter, QPaintEvent* event, const AudioData& data)
+{
+    auto rt = event->rect();
+    painter->fillRect(rt, m_background);
+    // painter->translate(0, -1 * rt.height() / 2);
+
+    painter->save();
+    painter->setBrush(m_brush);
+    draw_data_style(painter, rt, data);
+    painter->restore();
+}
+
+void BarHelper::get_data(const AudioData& data, std::vector<int>& v, bool left) const
+{
+    v.clear();
+
+    // unsigned 16bit
+    const int16_t* p = (int16_t*)data.buffer;
+    uint32_t len = data.len / sizeof(int16_t);
+
+    int start = left ? 0 : 1;
+
+    for (uint32_t i = start; i < len; i += 2)
+        v.push_back(*(p + i));
+
+    // qDebug() << "data size: " << v.size();
+}
+
+/* v data samples to num*/
+void BarHelper::data_sample_old(std::vector<int>& v, const uint32_t num)
+{
+    auto size = v.size();
+    if (num <= 0 || size < num)
+    {
+        v.insert(v.end(), num - size, 0);
+        return;
+    }
+
+    auto numItems = size / num;
+    if (numItems <= 1)
+    {
+        v.erase(v.begin() + num, v.end());
+        return;
+    }
+
+    for (size_t i = 0; i < num; i++)
+    {
+        auto& value = v[i];
+        value = v[i * numItems];
+
+        /*for (int j = 1; j < numItems; j++) {
+            value += v[i * numItems + j];
+        }*/
+    }
+
+    v.erase(v.begin() + num, v.end());
+}
+
+void BarHelper::data_sample(std::vector<int>& v, const uint32_t num)
+{
+    auto size = v.size();
+    if (num <= 0 || size < num)
+    {
+        v.insert(v.end(), num - size, 0);
+        return;
+    }
+
+    auto numItems = size / num;
+    if (numItems <= 1)
+    {
+        v.erase(v.begin() + num, v.end());
+        return;
+    }
+
+    while (v.size() / num >= 2)
+    {
+        binary_data(v);
+    }
+
+    v.erase(v.begin() + num, v.end());
+}
+
+void BarHelper::binary_data(std::vector<int>& v)
+{
+    auto j = 0;
+    for (size_t i = 0; i < v.size(); i += 2)
+        v[j++] = v[i];
+
+    v.erase(v.begin() + j, v.end());
+}
+
+void BarHelper::normal_overzero(std::vector<int>& v)
+{
+    int min = *std::min_element(v.begin(), v.end());
+    // int max = *std::max_element(v.begin(), v.end());
+    // qDebug() << "before size: " << v.size() << ",Max value: " << max << ",Min
+    // value: " << min;
+
+    if (min < 0)
+    {
+        min *= -1;
+        for (int& x : v)
+            x += min;
+    }
+}
+
+void BarHelper::normal_audio_to_size(std::vector<int>& v, const int size)
+{
+    if (size <= 0)
+        return;
+
+#if 0
+	static int maxValue = 0xff;
+
+	int max = *std::max_element(v.begin(), v.end());
+	if (maxValue < max)
+		maxValue = max;
+#else
+    int maxValue = MAX_AUDIO_VAULE;
+#endif
+
+    for (auto& x : v)
+    {
+        x = (x > maxValue) ? maxValue : x;
+        x = (x * size) / maxValue;
+    }
+}
+
+void BarHelper::normal_to_size(std::vector<int>& v, const int size)
+{
+    if (size <= 0)
+        return;
+
+    int maxValue = *std::max_element(v.begin(), v.end());
+    if (size >= maxValue)
+        return;
+
+    for (auto& x : v)
+    {
+        x = (x > size) ? size : x;
+        x = (x * size) / maxValue;
+    }
+}
+
+void BarHelper::normal_data(std::vector<int>& v, const int height)
+{
+    normal_overzero(v);
+    normal_audio_to_size(v, height);
+}
+
+void BarHelper::data_frequency(std::vector<int>& v, const uint32_t num)
+{
+    auto size = v.size();
+    if (num <= 0 || size < num)
+    {
+        v.insert(v.end(), num - size, 0);
+        return;
+    }
+
+    std::vector<int> res(num, 0);
+#if 0
+	static uint32_t maxValue = 0xff;
+
+	int max = *std::max_element(v.begin(), v.end());
+	if (maxValue < max)
+		maxValue = max;
+#else
+    int maxValue = MAX_AUDIO_VAULE;
+#endif
+    const uint16_t step = ceil((maxValue + 1) * 1.0 / num);
+    for (const auto& i : v)
+    {
+        uint32_t index = (i % maxValue) / step;
+        assert(index < num);
+        res[index % num]++;
+    }
+
+    v = res;
+}

+ 58 - 0
AvPlayer2/audio_effect_helper.h

@@ -0,0 +1,58 @@
+#pragma once
+#include <QBrush>
+#include <QFont>
+#include <QPen>
+#include <QWidget>
+
+#include "audio_play_thread.h"
+
+class BarHelper
+{
+public:
+    explicit BarHelper();
+    virtual ~BarHelper() {};
+
+public:
+    enum GraphicType { e_GtBar, e_GtLine, e_GtPie };
+    enum VisualType { e_VtSampleing, e_VtFrequency };
+    typedef struct VisualFormat
+    {
+        GraphicType gType;
+        VisualType vType;
+
+        VisualFormat()
+            : gType(e_GtBar)
+            , vType(e_VtSampleing)
+        {}
+    } VisualFormat;
+
+public:
+    void paint(QPainter* painter, QPaintEvent* event, const AudioData& data);
+    void draw_data_style(QPainter* painter, const QRect& rt, const AudioData& data);
+    void set_draw_fmt(const VisualFormat& fmt) { m_visualFmt = fmt; }
+
+private:
+    void get_data(const AudioData& data, std::vector<int>& v, bool left = true) const;
+    void normal_data(std::vector<int>& v, const int height);
+    void normal_overzero(std::vector<int>& v);
+    void normal_audio_to_size(std::vector<int>& v, const int size);
+    void normal_to_size(std::vector<int>& v, const int size);
+    void data_sample_old(std::vector<int>& v, const uint32_t num); //wav sampling
+    void data_sample(std::vector<int>& v, const uint32_t num);     //wav sampling
+    void binary_data(std::vector<int>& v);
+    void data_frequency(std::vector<int>& v, const uint32_t num);
+    void draw_data_bar(QPainter* painter, std::vector<int>& data, int n, int w, int h, int h_inter);
+    void draw_data_line(QPainter* painter, std::vector<int>& data, int n, int w, int h, int h_inter);
+    void draw_data_arc(QPainter* painter, std::vector<int>& data, int n, int w, int h);
+    void draw_data_polygon(
+        QPainter* painter, std::vector<int>& data, int n, int w, int h, int r_offset = 50);
+
+private:
+    QBrush m_background;
+    QBrush m_brush;
+    QFont m_textFont;
+    QPen m_pen;
+    QPen m_textPen;
+    AudioFrameFmt m_datafmt;
+    VisualFormat m_visualFmt;
+};

+ 377 - 0
AvPlayer2/audio_play_thread.cpp

@@ -0,0 +1,377 @@
+// ***********************************************************/
+// audio_play_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// audio play thread
+// ***********************************************************/
+
+#include "audio_play_thread.h"
+
+#if !NDEBUG
+#define DEBUG_PLAYFILTER 0
+#define WRITE_AUDIO_FILE 0
+#else
+#define DEBUG_PLAYFILTER 0
+#define WRITE_AUDIO_FILE 0
+#endif
+
+#if WRITE_AUDIO_FILE
+#include <fstream>
+#endif
+
+AudioPlayThread::AudioPlayThread(QObject* parent, VideoState* pState) : QThread(parent), m_pState(pState)
+{
+    print_device();
+    qRegisterMetaType<AudioData>("AudioData");
+}
+
+AudioPlayThread::~AudioPlayThread()
+{
+    // stop_thread();
+    stop_device();
+    final_resample_param();
+}
+
+void AudioPlayThread::print_device() const
+{
+    QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
+    auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
+    for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
+        qDebug() << "Input device name: " << deviceInfo.deviceName();
+
+    deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+    for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
+        qDebug() << "Output device name: " << deviceInfo.deviceName();
+
+    auto edians = deviceInfo.supportedByteOrders();
+    for (const QAudioFormat::Endian& endian : edians)
+        qDebug() << "Endian: " << endian;
+    auto sampleTypes = deviceInfo.supportedSampleTypes();
+    for (const QAudioFormat::SampleType& sampleType : sampleTypes)
+        qDebug() << "sampleType: " << sampleType;
+
+    auto codecs = deviceInfo.supportedCodecs();
+    for (const QString& codec : codecs)
+        qDebug() << "codec: " << codec;
+
+    auto sampleRates = deviceInfo.supportedSampleRates();
+    for (const int& sampleRate : sampleRates)
+        qDebug() << "sampleRate: " << sampleRate;
+
+    auto ChannelCounts = deviceInfo.supportedChannelCounts();
+    for (const int& channelCount : ChannelCounts)
+        qDebug() << "channelCount: " << channelCount;
+
+    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)
+{
+    QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
+
+    QAudioFormat format;
+
+    format.setSampleRate(sample_rate);
+    format.setChannelCount(channel);
+    format.setSampleSize(8 * av_get_bytes_per_sample(sample_fmt));
+    format.setCodec("audio/pcm");
+    format.setByteOrder(QAudioFormat::LittleEndian);
+    format.setSampleType(QAudioFormat::SignedInt);
+
+    // qDebug("sample size=%d\n", 8 * av_get_bytes_per_sample(sample_fmt));
+    if (!deviceInfo.isFormatSupported(format))
+    {
+        qWarning() << "Raw audio format not supported!";
+        return false;
+    }
+
+    m_pOutput = std::make_unique<QAudioOutput>(deviceInfo, format);
+    set_device_volume(default_vol);
+
+    m_audioDevice = m_pOutput->start();
+    return true;
+}
+
+float AudioPlayThread::get_device_volume() const
+{
+    if (m_pOutput)
+        return m_pOutput->volume();
+
+    return 0;
+}
+
+void AudioPlayThread::set_device_volume(float volume)
+{
+    if (m_pOutput)
+        m_pOutput->setVolume(volume);
+}
+
+void AudioPlayThread::stop_device()
+{
+    if (m_pOutput)
+    {
+        m_pOutput->stop();
+        m_pOutput->reset();
+    }
+}
+
+void AudioPlayThread::play_file(const QString& file)
+{
+    /*play pcm file directly*/
+    QFile audioFile;
+    audioFile.setFileName(file);
+    audioFile.open(QIODevice::ReadOnly);
+    m_pOutput->start(&audioFile);
+}
+
+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);
+        if (len < 0)
+            break;
+        if (len > 0)
+        {
+            data = data + len;
+            datasize -= len;
+        }
+        // qDebug("play buf:reslen:%d, write len:%d", len, datasize);
+    }
+}
+
+void AudioPlayThread::run()
+{
+    assert(m_pState);
+    VideoState* is = m_pState;
+    int audio_size;
+
+    for (;;)
+    {
+        if (m_bExitThread)
+            break;
+
+        if (is->abort_request)
+            break;
+
+        if (is->paused)
+        {
+            msleep(10);
+            continue;
+        }
+
+        audio_size = audio_decode_frame(is);
+        if (audio_size < 0)
+            break;
+
+        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);
+                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);
+                sync_clock_to_slave(&is->extclk, &is->audclk);
+            }
+        }
+    }
+
+    qDebug("-------- Audio play thread exit.");
+}
+
+int AudioPlayThread::audio_decode_frame(VideoState* is)
+{
+    int data_size;
+    Frame* af;
+
+    do
+    {
+        while (frame_queue_nb_remaining(&is->sampq) == 0)
+        {
+            if (is->eof)
+            {
+                // break;
+                return -1;
+            }
+            else
+            {
+                av_usleep(1000);
+                // return -1;
+            }
+
+            if (is->abort_request)
+                break;
+        }
+
+        if (!(af = frame_queue_peek_readable(&is->sampq)))
+            return -1;
+        frame_queue_next(&is->sampq);
+    } while (af->serial != is->audioq.serial);
+
+    /*data_size = av_samples_get_buffer_size(nullptr, af->frame->channels,
+          af->frame->nb_samples,
+          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));
+
+    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)
+    {
+        return 0;
+    }
+#endif
+
+    if (is->muted && data_size > 0)
+        memset(buffer_audio, 0, data_size); // mute
+
+#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);
+    }
+#endif
+
+    if (m_bSendToVisual)
+    {
+        AudioData data;
+        if (data_size > BUFFER_LEN)
+        {
+            qDebug() << "audio frame is too long,data_size:" << data_size
+                     << ", buffer_len:" << BUFFER_LEN << "\n";
+        }
+
+        int len = std::min(data_size, BUFFER_LEN);
+        memcpy(data.buffer, buffer_audio, len);
+        data.len = len;
+        emit data_visual_ready(data);
+    }
+
+    play_buf(buffer_audio, data_size);
+
+    av_free((void*)buffer_audio);
+
+    /* update the audio clock with the 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;
+        // 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 * is->audio_speed;
+#endif
+
+#if DEBUG_PLAYFILTER
+        static int pks_num = 0;
+        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);
+
+        // 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
+    {
+        is->audio_clock = NAN;
+    }
+    is->audio_clock_serial = af->serial;
+
+    emit update_play_time();
+
+#if (!NDEBUG && PRINT_PACKETQUEUE_AUDIO_INFO)
+    {
+        static double last_clock;
+        qDebug("audio: delay=%0.3f clock=%0.3f\n", is->audio_clock - last_clock, is->audio_clock);
+        last_clock = is->audio_clock;
+    }
+#endif
+
+    return data_size;
+}
+
+bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio, AVSampleFormat sample_fmt, VideoState* is)
+{
+    if (pAudio)
+    {
+        int ret = -1;
+        struct SwrContext* swrCtx = nullptr;
+#if USE_AVFILTER_AUDIO
+        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);
+
+            AVChannelLayout channel_layout;
+            av_buffersink_get_ch_layout(sink, &channel_layout);
+            // 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,
+                                      nullptr);
+
+            /*m_audioResample.channel_layout = channel_layout;
+      m_audioResample.sample_fmt = sample_fmt;
+      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,
+                                  nullptr);
+#endif
+
+        if (!(ret < 0))
+        {
+            swr_init(swrCtx);
+            m_audioResample.swrCtx = swrCtx;
+            return true;
+        }
+    }
+    return false;
+}
+
+void AudioPlayThread::final_resample_param()
+{
+    swr_free(&m_audioResample.swrCtx);
+}
+
+void AudioPlayThread::stop_thread()
+{
+    m_bExitThread = true;
+    wait();
+}

+ 84 - 0
AvPlayer2/audio_play_thread.h

@@ -0,0 +1,84 @@
+#pragma once
+
+#include <QAudio>
+#include <QAudioDeviceInfo>
+#include <QAudioOutput>
+#include <QDebug>
+#include <QFile>
+#include <QIODevice>
+#include <QQueue>
+#include <QThread>
+#include <QWaitCondition>
+
+#include <memory>
+
+#include "packets_sync.h"
+
+#define BUFFER_LEN 8192 // 1024
+
+typedef struct AudioData
+{
+    uint16_t len = 0;
+    char buffer[BUFFER_LEN] = {0};
+} AudioData;
+
+typedef struct AudioFrameFmt
+{
+    uint sample_rate;
+    uint sample_fmt; // AV_SAMPLE_FMT_S16
+    uint channel;
+    int byte_order;  // QAudioFormat::LittleEndian;
+    int sample_type; // QAudioFormat::SignedInt
+} AudioFrameFmt;
+
+class AudioPlayThread : public QThread
+{
+    Q_OBJECT
+public:
+    explicit AudioPlayThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    virtual ~AudioPlayThread();
+
+public:
+    void print_device() const;
+    bool init_device(int sample_rate = 8000, int channel = 1, AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16, float default_vol = 0.8);
+    void stop_device();
+    void play_file(const QString& file);
+    void play_buf(const uint8_t* buf, int datasize);
+    bool init_resample_param(AVCodecContext* pAudio, AVSampleFormat sample_fmt, VideoState* is);
+    void final_resample_param();
+    float get_device_volume() const;
+    void set_device_volume(float volume);
+    void send_visual_open(bool bSend = true) { m_bSendToVisual = bSend; };
+signals:
+    void update_play_time();
+    void data_visual_ready(const AudioData& data);
+public slots:
+    void stop_thread();
+
+protected:
+    void run() override;
+
+private:
+    int audio_decode_frame(VideoState* is);
+
+private:
+    typedef struct Audio_Resample
+    {
+        // AVFrame* pFrame;
+        // uint8_t* buffer;
+        struct SwrContext* swrCtx{nullptr};
+
+        // uint64_t channel_layout;	// out
+        // AVChannelLayout channel_layout;
+        // AVSampleFormat sample_fmt;
+        // int sample_rate;
+    } Audio_Resample;
+
+private:
+    std::unique_ptr<QAudioOutput> m_pOutput;
+    QIODevice* m_audioDevice{nullptr};
+    VideoState* m_pState{nullptr};
+    Audio_Resample m_audioResample;
+    bool m_bExitThread{false};
+    bool m_bSendToVisual{false};
+};

+ 66 - 0
AvPlayer2/clickable_slider.cpp

@@ -0,0 +1,66 @@
+// ***********************************************************/
+// clickable_slider.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Clickable slider
+// ***********************************************************/
+
+#include "clickable_slider.h"
+#include <QDebug>
+#include <QStyleOptionSlider>
+#include "qnamespace.h"
+
+ClickableSlider::ClickableSlider(QWidget* parent) : QSlider(parent)
+{
+    setOrientation(Qt::Horizontal);
+}
+
+void ClickableSlider::mousePressEvent(QMouseEvent* event)
+{
+    QStyleOptionSlider opt;
+    initStyleOption(&opt);
+    auto sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
+
+    if (event->button() == Qt::LeftButton && !sr.contains(event->pos()))
+    {
+        int newVal = 0;
+        double normalizedPosition = 0;
+        if (orientation() == Qt::Vertical)
+        {
+            auto halfHandleHeight = (0.5 * sr.height()) + 0.5;
+            int adaptedPosY = height() - event->pos().y();
+            if (adaptedPosY < halfHandleHeight)
+                adaptedPosY = halfHandleHeight;
+            if (adaptedPosY > height() - halfHandleHeight)
+                adaptedPosY = height() - halfHandleHeight;
+            auto newHeight = (height() - halfHandleHeight) - halfHandleHeight;
+            normalizedPosition = (adaptedPosY - halfHandleHeight) / newHeight;
+        }
+        else
+        {
+            auto halfHandleWidth = (0.5 * sr.width()) + 0.5;
+            int adaptedPosX = event->pos().x();
+            if (adaptedPosX < halfHandleWidth)
+                adaptedPosX = halfHandleWidth;
+            if (adaptedPosX > width() - halfHandleWidth)
+                adaptedPosX = width() - halfHandleWidth;
+            auto newWidth = (width() - halfHandleWidth) - halfHandleWidth;
+            normalizedPosition = (adaptedPosX - halfHandleWidth) / newWidth;
+        }
+
+        newVal = minimum() + ((maximum() - minimum()) * normalizedPosition);
+        if (invertedAppearance())
+            newVal = maximum() - newVal;
+
+        setValue(newVal);
+
+        event->accept();
+
+        emit onClick(this->value());
+    }
+    else
+    {
+        QSlider::mousePressEvent(event);
+    }
+}

+ 18 - 0
AvPlayer2/clickable_slider.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <QMouseEvent>
+#include <QSlider>
+
+class ClickableSlider : public QSlider
+{
+    Q_OBJECT
+
+public:
+    explicit ClickableSlider(QWidget* parent = nullptr);
+    virtual ~ClickableSlider(){};
+signals:
+    void onClick(int value);
+
+protected:
+    void mousePressEvent(QMouseEvent* event) override;
+};

+ 19 - 0
AvPlayer2/common.cpp

@@ -0,0 +1,19 @@
+// ***********************************************************/
+// common.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Common QT funtions
+// *
+
+#include "common.h"
+
+QString appendPath(const QString& path, const QString& sub_path)
+{
+    return QDir::cleanPath(path + QDir::separator() + sub_path);
+}
+
+QString toNativePath(const QString& path)
+{
+    return QDir::toNativeSeparators(path);
+}

+ 7 - 0
AvPlayer2/common.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include <QString>
+#include <QDir>
+
+QString appendPath(const QString& path, const QString& sub_path);
+QString toNativePath(const QString& path);

+ 538 - 0
AvPlayer2/ffmpeg_init.cpp

@@ -0,0 +1,538 @@
+// ***********************************************************/
+// ffmpeg_init.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// FFmpeg utils.
+// ***********************************************************/
+
+#include "ffmpeg_init.h"
+
+#if !NDEBUG
+#define OPEN_FFMPEG_LOG 1
+#else
+#define OPEN_FFMPEG_LOG 0
+#endif
+
+#define PRINT_LIB_INFO(libname, LIBNAME)                                  \
+    if (true)                                                             \
+    {                                                                     \
+        const char* indent = "  ";                                        \
+        unsigned int version = libname##_version();                       \
+        qInfo("%slib%-11s %2d.%3d.%3d / %2d.%3d.%3d", indent, #libname,   \
+              LIB##LIBNAME##_VERSION_MAJOR, LIB##LIBNAME##_VERSION_MINOR, \
+              LIB##LIBNAME##_VERSION_MICRO, AV_VERSION_MAJOR(version),    \
+              AV_VERSION_MINOR(version), AV_VERSION_MICRO(version));      \
+    }
+
+#define BUFF_MAXLEN 256
+
+static void log_callback(void* ptr, int level, const char* fmt, va_list vl)
+{
+    if (level > av_log_get_level())
+        return;
+
+    va_list vl2;
+    char line[1024];
+    static int print_prefix = 1;
+
+    va_copy(vl2, vl);
+    av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
+    va_end(vl2);
+
+#if OPEN_FFMPEG_LOG
+    qInfo("FFMPEG:%s", line);
+#endif
+}
+
+int ffmpeg_init()
+{
+    av_log_set_flags(AV_LOG_SKIP_REPEATED);
+
+    av_log_set_level(AV_LOG_INFO);
+    av_log_set_callback(log_callback);
+
+    // print_ffmpeg_info(AV_LOG_INFO);
+    check_error(-22);
+    return 0;
+}
+
+void check_error(int error)
+{
+    char errorStr[256] = {0};
+    qDebug("ENOMEM: %d", AVERROR(ENOMEM));
+    qDebug("ENOMEM: %d", AVERROR(EINVAL));
+    qDebug("ENOMEM: %d", AVERROR_OPTION_NOT_FOUND);
+    av_strerror(error, errorStr, sizeof(errorStr));
+}
+
+void print_ffmpeg_info()
+{
+    // ffmpeg-snapshot_0401
+    qInfo("%s version %s", "ffmpeg", FFMPEG_VERSION);
+
+    PRINT_LIB_INFO(avutil, AVUTIL);
+    PRINT_LIB_INFO(avcodec, AVCODEC);
+    PRINT_LIB_INFO(avformat, AVFORMAT);
+    // PRINT_LIB_INFO(avdevice, AVDEVICE);
+    // PRINT_LIB_INFO(avfilter, AVFILTER);
+    PRINT_LIB_INFO(swscale, SWSCALE);
+    PRINT_LIB_INFO(swresample, SWRESAMPLE);
+    qInfo("");
+}
+
+QString dump_format(AVFormatContext* ic, int index, const char* url, int is_output)
+{
+    QString str;
+    char tmp[BUFF_MAXLEN];
+    const char* indent = "  ";
+    uint8_t* printed = nullptr;
+
+    if (!ic)
+    {
+        qErrnoWarning("invalid parameter!");
+        goto fail;
+    }
+
+    if (!url || !url[0])
+    {
+        qErrnoWarning("url is invalid!");
+        goto fail;
+    }
+
+    printed = ic->nb_streams ? (uint8_t*)av_mallocz(ic->nb_streams) : nullptr;
+    if (ic->nb_streams && !printed)
+        goto fail;
+
+    snprintf(tmp, sizeof(tmp), "%s #%d, %s, %s '%s':",
+             is_output ? "Output" : "Input", index,
+             is_output ? ic->oformat->name : ic->iformat->name,
+             is_output ? "to" : "from", url);
+    str += tmp;
+    str += "\n";
+
+    str += dump_metadata(ic->metadata, indent);
+
+    if (!is_output)
+    {
+        snprintf(tmp, sizeof(tmp), "%sDuration: ", indent);
+        str += tmp;
+
+        if (ic->duration != AV_NOPTS_VALUE)
+        {
+            int64_t hours, mins, secs, us;
+            int64_t duration =
+                ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0);
+            secs = duration / AV_TIME_BASE;
+            us = duration % AV_TIME_BASE;
+            mins = secs / 60;
+            secs %= 60;
+            hours = mins / 60;
+            mins %= 60;
+
+            snprintf(tmp, sizeof(tmp), "%02lld:%02lld:%02lld.%02lld", hours, mins,
+                     secs, (100 * us) / AV_TIME_BASE);
+            str += tmp;
+        }
+        else
+        {
+            str += "N/A";
+        }
+
+        if (ic->start_time != AV_NOPTS_VALUE)
+        {
+            int secs, us;
+            secs = llabs(ic->start_time / AV_TIME_BASE);
+            us = llabs(ic->start_time % AV_TIME_BASE);
+            // av_log(nullptr, AV_LOG_INFO, ", start: %s%d.%06d",
+            // ic->start_time >= 0 ? "" : "-",	secs, (int)av_rescale(us, 1000000,
+            // AV_TIME_BASE));
+            snprintf(tmp, sizeof(tmp), ", start: %s%d.%06d",
+                     ic->start_time >= 0 ? "" : "-", secs,
+                     (int)av_rescale(us, 1000000, AV_TIME_BASE));
+            str += tmp;
+        }
+
+        str += ", bitrate: ";
+        if (ic->bit_rate)
+        {
+            snprintf(tmp, sizeof(tmp), "%lld kb/s", ic->bit_rate / 1000);
+            str += tmp;
+        }
+        else
+            str += "N/A";
+
+        str += "\n";
+    }
+
+    if (ic->nb_chapters)
+    {
+        snprintf(tmp, sizeof(tmp), "%sChapters:\n", indent);
+        str += tmp;
+    }
+
+    for (unsigned int i = 0; i < ic->nb_chapters; i++)
+    {
+        const AVChapter* ch = ic->chapters[i];
+        snprintf(tmp, sizeof(tmp), "    Chapter #%d:%d: start %f, end %f\n", index,
+                 i, ch->start * av_q2d(ch->time_base),
+                 ch->end * av_q2d(ch->time_base));
+        str += tmp;
+
+        str += dump_metadata(ch->metadata, "      ");
+    }
+
+    if (ic->nb_programs)
+    {
+        unsigned int j, k, total = 0;
+        for (j = 0; j < ic->nb_programs; j++)
+        {
+            const AVProgram* program = ic->programs[j];
+            const AVDictionaryEntry* name =
+                av_dict_get(program->metadata, "name", nullptr, 0);
+            snprintf(tmp, sizeof(tmp), "  Program %d %s\n", program->id,
+                     name ? name->value : "");
+            str += tmp;
+
+            str += dump_metadata(program->metadata, "    ");
+
+            for (k = 0; k < program->nb_stream_indexes; k++)
+            {
+                str +=
+                    dump_stream_format(ic, program->stream_index[k], index, is_output);
+                printed[program->stream_index[k]] = 1;
+            }
+            total += program->nb_stream_indexes;
+        }
+        if (total < ic->nb_streams)
+        {
+            str += "  No Program\n";
+        }
+    }
+
+    str += "\n";
+    for (unsigned int i = 0; i < ic->nb_streams; i++)
+    {
+        if (!printed[i])
+        {
+            str += dump_stream_format(ic, i, index, is_output);
+            str += "\n";
+        }
+    }
+
+    return str;
+
+fail:
+    return QString("");
+}
+
+QString dump_metadata(const AVDictionary* m, const char* indent)
+{
+    QString str;
+    char tmp[BUFF_MAXLEN];
+
+    if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", nullptr, 0)))
+    {
+        const AVDictionaryEntry* tag = nullptr;
+
+        snprintf(tmp, sizeof(tmp), "%sMetadata:\n", indent);
+        str += tmp;
+
+        while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)))
+        {
+            if (strcmp("language", tag->key))
+            {
+                const char* p = tag->value;
+                // av_log(ctx, AV_LOG_INFO,"%s  %-16s: ", indent, tag->key);
+                snprintf(tmp, sizeof(tmp), "%s  %-16s: ", indent, tag->key);
+                str += tmp;
+
+                while (*p)
+                {
+                    char tmp_str[256];
+                    size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
+                    av_strlcpy(tmp_str, p, FFMIN(sizeof(tmp_str), len + 1));
+                    // str += "%s", tmp);
+                    snprintf(tmp, sizeof(tmp), "%s", tmp_str);
+                    str += tmp;
+
+                    p += len;
+                    if (*p == 0xd)
+                        str += " ";
+                    if (*p == 0xa)
+                        str += "\n%s  %-16s: ";
+                    if (*p)
+                        p++;
+                }
+                str += "\n";
+            }
+        }
+    }
+    return str;
+}
+
+QString dump_stream_format(const AVFormatContext* ic, int i, int index, int is_output)
+{
+    QString str;
+    char tmp[BUFF_MAXLEN];
+
+    char buf[256];
+    int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
+    const AVStream* st = ic->streams[i];
+    // const FFStream* const sti = cffstream(st);
+    const AVDictionaryEntry* lang =
+        av_dict_get(st->metadata, "language", nullptr, 0);
+    const char* separator = (const char*)ic->dump_separator;
+    AVCodecContext* avctx;
+    // AVCodecContext* st_avctx = (AVCodecContext*)st->priv_data;
+    int ret;
+
+    avctx = avcodec_alloc_context3(nullptr);
+    if (!avctx)
+        goto fail;
+
+    ret = avcodec_parameters_to_context(avctx, st->codecpar);
+    if (ret < 0)
+    {
+        avcodec_free_context(&avctx);
+        goto fail;
+    }
+
+    // Fields which are missing from AVCodecParameters need to be taken from the
+    // AVCodecContext
+    /*
+      avctx->properties = st_avctx->properties;
+      avctx->codec = st_avctx->codec;
+      avctx->qmin = st_avctx->qmin;
+      avctx->qmax = st_avctx->qmax;
+      avctx->coded_width = st_avctx->coded_width;
+      avctx->coded_height = st_avctx->coded_height;
+
+      if (separator)
+              av_opt_set(avctx, "dump_separator", separator, 0);*/
+    avcodec_string(buf, sizeof(buf), avctx, is_output);
+    avcodec_free_context(&avctx);
+
+    snprintf(tmp, sizeof(tmp), "  Stream #%d:%d", index, i);
+    str += tmp;
+
+    if (flags & AVFMT_SHOW_IDS)
+    {
+        snprintf(tmp, sizeof(tmp), "[0x%x]", st->id);
+        str += tmp;
+    }
+
+    if (lang)
+    {
+        snprintf(tmp, sizeof(tmp), "(%s)", lang->value);
+        str += tmp;
+    }
+
+    snprintf(tmp, sizeof(tmp), ": %s", buf);
+    str += tmp;
+
+    if (st->sample_aspect_ratio.num &&
+        av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio))
+    {
+        AVRational display_aspect_ratio;
+        av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
+                  st->codecpar->width * (int64_t)st->sample_aspect_ratio.num,
+                  st->codecpar->height * (int64_t)st->sample_aspect_ratio.den,
+                  1024 * 1024);
+
+        snprintf(tmp, sizeof(tmp), ", SAR %d:%d DAR %d:%d",
+                 st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
+                 display_aspect_ratio.num, display_aspect_ratio.den);
+        str += tmp;
+    }
+
+    if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+    {
+        int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
+        int tbr = st->r_frame_rate.den && st->r_frame_rate.num;
+        int tbn = st->time_base.den && st->time_base.num;
+
+        if (fps || tbr || tbn)
+        {
+            snprintf(tmp, sizeof(tmp), "%s", separator);
+            str += tmp;
+        }
+
+        if (fps)
+            str +=
+                print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps");
+        if (tbr)
+            str += print_fps(av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr");
+        if (tbn)
+            str += print_fps(1 / av_q2d(st->time_base), "tbn");
+    }
+
+    if (st->disposition & AV_DISPOSITION_DEFAULT)
+        str += " (default)";
+    if (st->disposition & AV_DISPOSITION_DUB)
+        str += " (dub)";
+    if (st->disposition & AV_DISPOSITION_ORIGINAL)
+        str += " (original)";
+    if (st->disposition & AV_DISPOSITION_COMMENT)
+        str += " (comment)";
+    if (st->disposition & AV_DISPOSITION_LYRICS)
+        str += " (lyrics)";
+    if (st->disposition & AV_DISPOSITION_KARAOKE)
+        str += " (karaoke)";
+    if (st->disposition & AV_DISPOSITION_FORCED)
+        str += " (forced)";
+    if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
+        str += " (hearing impaired)";
+    if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
+        str += " (visual impaired)";
+    if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
+        str += " (clean effects)";
+    if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
+        str += " (attached pic)";
+    if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)
+        str += " (timed thumbnails)";
+    if (st->disposition & AV_DISPOSITION_CAPTIONS)
+        str += " (captions)";
+    if (st->disposition & AV_DISPOSITION_DESCRIPTIONS)
+        str += " (descriptions)";
+    if (st->disposition & AV_DISPOSITION_METADATA)
+        str += " (metadata)";
+    if (st->disposition & AV_DISPOSITION_DEPENDENT)
+        str += " (dependent)";
+    if (st->disposition & AV_DISPOSITION_STILL_IMAGE)
+        str += " (still image)";
+    str += "\n";
+
+    str += dump_metadata(st->metadata, "    ");
+    str += dump_sidedata(st, "    ");
+    return str;
+
+fail:
+    return QString("");
+}
+
+QString print_fps(double d, const char* postfix)
+{
+    char tmp[BUFF_MAXLEN];
+
+    uint64_t v = lrintf(d * 100);
+    if (!v)
+    {
+        snprintf(tmp, sizeof(tmp), "%1.4f %s", d, postfix);
+    }
+    else if (v % 100)
+    {
+        snprintf(tmp, sizeof(tmp), "%3.2f %s", d, postfix);
+    }
+    else if (v % (100 * 1000))
+    {
+        snprintf(tmp, sizeof(tmp), "%1.0f %s", d, postfix);
+    }
+    else
+    {
+        snprintf(tmp, sizeof(tmp), "%1.0fk %s", d / 1000, postfix);
+    }
+
+    return QString(tmp);
+}
+
+QString dump_sidedata(const AVStream* st, const char* indent)
+{
+    QString str;
+    char tmp[BUFF_MAXLEN];
+
+    if (!st->codecpar)
+        return str;
+
+    if (st->codecpar->nb_coded_side_data)
+    {
+        snprintf(tmp, sizeof(tmp), "%sSide data:\n", indent);
+        str += tmp;
+    }
+
+    for (int i = 0; i < st->codecpar->nb_coded_side_data; i++)
+    {
+        const AVPacketSideData* sd = &st->codecpar->coded_side_data[i];
+        snprintf(tmp, sizeof(tmp), "%s  ", indent);
+        str += tmp;
+
+        switch (sd->type)
+        {
+            case AV_PKT_DATA_PALETTE:
+                str += "palette";
+                break;
+            case AV_PKT_DATA_NEW_EXTRADATA:
+                str += "new extradata";
+                break;
+            case AV_PKT_DATA_PARAM_CHANGE:
+                str += "paramchange: ";
+                // dump_paramchange(ctx, sd);
+                break;
+            case AV_PKT_DATA_H263_MB_INFO:
+                str += "H.263 macroblock info";
+                break;
+            case AV_PKT_DATA_REPLAYGAIN:
+                str += "replaygain: ";
+                // dump_replaygain(ctx, sd);
+                break;
+            case AV_PKT_DATA_DISPLAYMATRIX:
+            {
+                snprintf(tmp, sizeof(tmp), "displaymatrix: rotation of %.2f degrees",
+                         av_display_rotation_get((const int32_t*)sd->data));
+                str += tmp;
+            }
+            break;
+            case AV_PKT_DATA_STEREO3D:
+                str += "stereo3d: ";
+                // dump_stereo3d(ctx, sd);
+                break;
+            case AV_PKT_DATA_AUDIO_SERVICE_TYPE:
+                str += "audio service type: ";
+                // dump_audioservicetype(ctx, sd);
+                break;
+            case AV_PKT_DATA_QUALITY_STATS:
+            {
+                snprintf(tmp, sizeof(tmp), "quality factor: %p, pict_type: %c", sd->data,
+                         av_get_picture_type_char(AVPictureType(sd->data[4])));
+                str += tmp;
+            }
+            break;
+            case AV_PKT_DATA_CPB_PROPERTIES:
+                str += "cpb: ";
+                // dump_cpb(ctx, sd);
+                break;
+            case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
+                // dump_mastering_display_metadata(ctx, sd);
+                break;
+            case AV_PKT_DATA_SPHERICAL:
+                str += "spherical: ";
+                // dump_spherical(ctx, st->codecpar, sd);
+                break;
+            case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
+                // dump_content_light_metadata(ctx, sd);
+                break;
+            case AV_PKT_DATA_ICC_PROFILE:
+                str += "ICC Profile";
+                break;
+            case AV_PKT_DATA_DOVI_CONF:
+                str += "DOVI configuration record: ";
+                // dump_dovi_conf(ctx, sd);
+                break;
+            case AV_PKT_DATA_S12M_TIMECODE:
+                str += "SMPTE ST 12-1:2014: ";
+                // dump_s12m_timecode(ctx, st, sd);
+                break;
+            default:
+            {
+                snprintf(tmp, sizeof(tmp), "unknown side data type %d, size:%llu bytes",
+                         sd->type, sd->size);
+                str += tmp;
+            }
+            break;
+        }
+
+        str += "\n";
+    }
+
+    return str;
+}

+ 26 - 0
AvPlayer2/ffmpeg_init.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <QDebug>
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/avstring.h>
+#include <libavutil/avutil.h>
+#include <libavutil/display.h>
+#include <libavutil/ffversion.h>
+#include <libavutil/log.h>
+#include <libavutil/opt.h>
+#include <libavutil/version.h>
+#include <libswresample/swresample.h>
+#include <libswscale/swscale.h>
+}
+
+int ffmpeg_init();
+void print_ffmpeg_info();
+void check_error(int error);
+QString dump_format(AVFormatContext* ic, int index, const char* url, int is_output = 0);
+QString dump_metadata(const AVDictionary* m, const char* indent = "  ");
+QString dump_stream_format(const AVFormatContext* ic, int i, int index, int is_output);
+QString print_fps(double d, const char* postfix);
+QString dump_sidedata(const AVStream* st, const char* indent);

+ 118 - 0
AvPlayer2/log.cpp

@@ -0,0 +1,118 @@
+// ***********************************************************/
+// log.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Log handling module.
+//
+// QT bug tracking:
+// https://bugreports.qt.io/secure/Dashboard.jspa
+// ***********************************************************/
+
+#include "log.h"
+#include "qtextcodec.h"
+#include <Windows.h>
+
+#define BUFF_LEN (1024)
+
+#ifdef UNICODE
+void Output(LPCWSTR szFormat, ...)
+#else
+void Output(const char* szFormat, ...)
+#endif
+{
+#ifdef UNICODE
+    WCHAR szBuff[BUFF_LEN + 1];
+
+    va_list arg;
+    va_start(arg, szFormat);
+    _vsnwprintf_s(szBuff, _countof(szBuff), _TRUNCATE, szFormat, arg);
+    //_vsnwprintf(szBuff, BUFF_LEN, szFormat, arg);
+    va_end(arg);
+#else
+    char szBuff[BUFF_LEN];
+
+    va_list arg;
+    va_start(arg, szFormat);
+    _vsnprintf(szBuff, BUFF_LEN, szFormat, arg);
+    va_end(arg);
+#endif
+
+    OutputDebugString(szBuff);
+}
+
+void logOutput(const QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+    QString txt, type_str;
+    QString time = QDateTime::currentDateTime().toString("dd/MM/yy hh:mm:ss.zzz"); //"hh:mm:ss.zzz"
+
+    QFileInfo file(context.file);
+
+    switch (type) {
+    case QtDebugMsg:
+        type_str = "Debug";
+        break;
+    case QtInfoMsg:
+        type_str = "Info";
+        break;
+    case QtWarningMsg:
+        type_str = "Warning";
+        break;
+    case QtCriticalMsg:
+        type_str = "Critical";
+        break;
+    case QtFatalMsg:
+        type_str = "Fatal";
+        abort();
+    default:
+        break;
+    }
+
+    if (msg.startsWith("QObject::startTimer:") || msg.startsWith("QObject::killTimer:"))
+        return;
+
+#if !NDEBUG // log to debug window with debug version
+    txt = QString("[%1][%2]%3 (file:%4:%5, fun:%6)\n")
+              .arg(time)
+              .arg(type_str)
+              .arg(msg)
+              .arg(file.fileName())
+              .arg(context.line)
+              .arg(context.function);
+
+    Output(txt.toStdWString().c_str());
+#else // log to file
+    if (type <= QtDebugMsg)
+        return;
+
+    txt = QString("[%1][%2]%3").arg(time).arg(type_str).arg(msg);
+
+    Logger& logger = Logger::instance();
+    logger.log(txt);
+#endif
+}
+
+Logger::Logger(const QString& file)
+    : m_logfile(nullptr)
+    , m_ts(nullptr)
+{
+    m_logfile = std::make_unique<QFile>(file);
+    if (m_logfile) {
+        if (m_logfile->open(QIODevice::WriteOnly | QIODevice::Append)) {
+            m_ts = std::make_unique<QTextStream>(m_logfile.get());
+            m_ts->setCodec(QTextCodec::codecForName("UTF8"));
+        }
+    }
+}
+
+Logger::~Logger()
+{
+    if (m_logfile)
+        m_logfile->close();
+}
+
+void Logger::log(const QString& str)
+{
+    if (m_ts)
+        (*m_ts) << str << Qt::endl;
+}

+ 29 - 0
AvPlayer2/log.h

@@ -0,0 +1,29 @@
+#pragma once
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+#include <QTime>
+#include <QtDebug>
+#include <memory>
+
+void logOutput(const QtMsgType type, const QMessageLogContext& context, const QString& msg);
+
+class Logger
+{
+public:
+    static Logger& instance()
+    {
+        static Logger instance;
+        return instance;
+    }
+
+    void log(const QString& str);
+
+private:
+    explicit Logger(const QString& file = "log.txt");
+    virtual ~Logger();
+
+private:
+    std::unique_ptr<QFile> m_logfile;
+    std::unique_ptr<QTextStream> m_ts;
+};

+ 1673 - 0
AvPlayer2/mainwindowa.cpp

@@ -0,0 +1,1673 @@
+// ***********************************************************/
+// mainwindow.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// mainwindow of player
+// ***********************************************************/
+
+#include "mainwindowa.h"
+#include "common.h"
+
+#include <QApplication>
+#include <QStatusBar>
+
+#include "ffmpeg_init.h"
+
+#include "qimage_operation.h"
+#include "qscreen.h"
+#include "start_play_thread.h"
+#include "ui_mainwindowa.h"
+
+#include "AvRecorder/ui/opengl_video_widget.h"
+
+#if NDEBUG
+#define AUTO_HIDE_PLAYCONTROL 1 // release version
+#else
+#define AUTO_HIDE_PLAYCONTROL 0
+#endif
+
+MainWindowA::MainWindowA(QWidget* parent)
+    : QMainWindow(parent)
+{
+    m_playerController = new PlayerController;
+    // 1. 菜单栏和主菜单
+    m_menuBar = new QMenuBar(this);
+    setMenuBar(m_menuBar);
+
+    // 文件菜单
+    m_menuFile = new QMenu(tr("文件"), this);
+    m_menuBar->addMenu(m_menuFile);
+    m_actionOpen = new QAction(tr("打开"), this);
+    m_actionQuit = new QAction(tr("退出"), this);
+    m_menuFile->addAction(m_actionOpen);
+    m_menuFile->addSeparator();
+    m_menuFile->addAction(m_actionQuit);
+    connect(m_actionOpen, &QAction::triggered, this, &MainWindowA::on_actionOpen_triggered);
+    connect(m_actionQuit, &QAction::triggered, this, &MainWindowA::on_actionQuit_triggered);
+
+    // 最近文件菜单
+    m_menuRecentFiles = new QMenu(tr("最近文件"), this);
+    m_menuBar->addMenu(m_menuRecentFiles);
+
+    // 播放列表菜单
+    m_menuSavedPlaylist = new QMenu(tr("播放列表"), this);
+    m_menuBar->addMenu(m_menuSavedPlaylist);
+
+    // CV菜单
+    m_menuCV = new QMenu(tr("图像处理"), this);
+    m_menuBar->addMenu(m_menuCV);
+
+    // 样式菜单
+    m_menuStyle = new QMenu(tr("样式"), this);
+    m_menuBar->addMenu(m_menuStyle);
+
+    // 音频可视化菜单
+    m_menuAudioVisualize = new QMenu(tr("音频可视化"), this);
+    m_menuBar->addMenu(m_menuAudioVisualize);
+
+    // 帮助菜单
+    m_menuHelp = new QMenu(tr("帮助"), this);
+    m_menuBar->addMenu(m_menuHelp);
+    m_actionHelp = new QAction(tr("帮助"), this);
+    m_actionAbout = new QAction(tr("关于"), this);
+    m_actionAbout_QT = new QAction(tr("关于Qt"), this);
+    m_menuHelp->addAction(m_actionHelp);
+    m_menuHelp->addAction(m_actionAbout);
+    m_menuHelp->addAction(m_actionAbout_QT);
+    connect(m_actionHelp, &QAction::triggered, this, &MainWindowA::on_actionHelp_triggered);
+    connect(m_actionAbout, &QAction::triggered, this, &MainWindowA::on_actionAbout_triggered);
+    connect(m_actionAbout_QT, &QAction::triggered, qApp, &QApplication::aboutQt);
+
+    // 2. 主控件和状态栏
+    m_centralWidget = new QWidget(this);
+    setCentralWidget(m_centralWidget);
+    m_statusBar = new QStatusBar(this);
+    setStatusBar(m_statusBar);
+
+    // 3. 其他Action初始化
+    m_actionStop = new QAction(tr("停止"), this);
+    m_actionFullscreen = new QAction(tr("全屏"), this);
+    m_actionFullscreen->setCheckable(true);
+    m_actionHide_Play_Ctronl = new QAction(tr("隐藏控制栏"), this);
+    m_actionHide_Play_Ctronl->setCheckable(true);
+    m_actionAspect_Ratio = new QAction(tr("保持比例"), this);
+    m_actionLoop_Play = new QAction(tr("循环播放"), this);
+    m_actionLoop_Play->setCheckable(true);
+    m_actionMedia_Info = new QAction(tr("媒体信息"), this);
+    m_actionKeyboard_Usage = new QAction(tr("快捷键说明"), this);
+    m_actionPlayList = new QAction(tr("播放列表窗口"), this);
+    m_actionPlayList->setCheckable(true);
+    m_actionOpenNetworkUrl = new QAction(tr("打开网络地址"), this);
+    m_actionOriginalSize = new QAction(tr("原始大小"), this);
+    m_actionHardware_decode = new QAction(tr("硬件解码"), this);
+    m_actionHardware_decode->setCheckable(true);
+
+    // 4. CV菜单Action
+    m_actionGrayscale = new QAction(tr("灰度"), this);
+    m_actionGrayscale->setCheckable(true);
+    m_actionMirro = new QAction(tr("镜像"), this);
+    m_actionMirro->setCheckable(true);
+    m_actionTransform = new QAction(tr("变换"), this);
+    m_actionTransform->setCheckable(true);
+    m_menuCV->addAction(m_actionGrayscale);
+    m_menuCV->addAction(m_actionMirro);
+    m_menuCV->addAction(m_actionTransform);
+    m_menuCV->addSeparator();
+
+    m_actionRotate = new QAction(tr("旋转"), this);
+    m_actionRotate->setCheckable(true);
+    m_actionRepeat = new QAction(tr("重复"), this);
+    m_actionRepeat->setCheckable(true);
+    m_actionEqualizeHist = new QAction(tr("直方图均衡"), this);
+    m_actionEqualizeHist->setCheckable(true);
+    m_actionThreshold = new QAction(tr("二值化"), this);
+    m_actionThreshold->setCheckable(true);
+    m_actionThreshold_Adaptive = new QAction(tr("自适应阈值"), this);
+    m_actionThreshold_Adaptive->setCheckable(true);
+    m_actionReverse = new QAction(tr("反色"), this);
+    m_actionReverse->setCheckable(true);
+    m_actionColorReduce = new QAction(tr("色彩减少"), this);
+    m_actionColorReduce->setCheckable(true);
+    m_actionGamma = new QAction(tr("伽马"), this);
+    m_actionGamma->setCheckable(true);
+    m_actionContrastBright = new QAction(tr("对比度/亮度"), this);
+    m_actionContrastBright->setCheckable(true);
+    m_actionBlur = new QAction(tr("模糊"), this);
+    m_actionBlur->setCheckable(true);
+    m_actionCanny = new QAction(tr("Canny"), this);
+    m_actionCanny->setCheckable(true);
+    m_actionSobel = new QAction(tr("Sobel"), this);
+    m_actionSobel->setCheckable(true);
+    m_actionLaplacian = new QAction(tr("Laplacian"), this);
+    m_actionLaplacian->setCheckable(true);
+    m_actionScharr = new QAction(tr("Scharr"), this);
+    m_actionScharr->setCheckable(true);
+    m_actionPrewitt = new QAction(tr("Prewitt"), this);
+    m_actionPrewitt->setCheckable(true);
+    m_actionRemoveCV = new QAction(tr("移除选择"), this);
+    m_actionRemoveCV->setCheckable(true);
+    m_actionTest_CV = new QAction(tr("测试CV"), this);
+    m_actionTest_CV->setCheckable(true);
+
+    m_menuCV->addAction(m_actionRotate);
+    m_menuCV->addAction(m_actionRepeat);
+    m_menuCV->addAction(m_actionEqualizeHist);
+    m_menuCV->addAction(m_actionThreshold);
+    m_menuCV->addAction(m_actionThreshold_Adaptive);
+    m_menuCV->addAction(m_actionReverse);
+    m_menuCV->addAction(m_actionColorReduce);
+    m_menuCV->addAction(m_actionGamma);
+    m_menuCV->addAction(m_actionContrastBright);
+    m_menuCV->addAction(m_actionBlur);
+    m_menuCV->addAction(m_actionCanny);
+    m_menuCV->addAction(m_actionSobel);
+    m_menuCV->addAction(m_actionLaplacian);
+    m_menuCV->addAction(m_actionScharr);
+    m_menuCV->addAction(m_actionPrewitt);
+    m_menuCV->addAction(m_actionRemoveCV);
+    m_menuCV->addSeparator();
+    m_menuCV->addAction(m_actionTest_CV);
+
+    // 批量 connect CV 菜单下所有 QAction
+    for (QAction* action : m_menuCV->actions()) {
+        if (action && !action->isSeparator()) {
+            connect(action, &QAction::toggled, this, &MainWindowA::onCvActionToggled);
+        }
+    }
+
+    // 5. 音频可视化菜单Action
+    m_actionLine = new QAction(tr("线形"), this);
+    m_actionLine->setCheckable(true);
+    m_actionBar = new QAction(tr("柱形"), this);
+    m_actionBar->setCheckable(true);
+    m_actionPie = new QAction(tr("饼形"), this);
+    m_actionPie->setCheckable(true);
+    m_actionSampling = new QAction(tr("采样"), this);
+    m_actionSampling->setCheckable(true);
+    m_actionFrequency = new QAction(tr("频率"), this);
+    m_actionFrequency->setCheckable(true);
+
+    m_menuAudioVisualize->addAction(m_actionLine);
+    m_menuAudioVisualize->addAction(m_actionBar);
+    m_menuAudioVisualize->addAction(m_actionPie);
+    m_menuAudioVisualize->addSeparator();
+    m_menuAudioVisualize->addAction(m_actionSampling);
+    m_menuAudioVisualize->addAction(m_actionFrequency);
+
+    // 6. 分组
+    m_CvActsGroup = new QActionGroup(this);
+    m_CvActsGroup->addAction(m_actionRotate);
+    m_CvActsGroup->addAction(m_actionRepeat);
+    m_CvActsGroup->addAction(m_actionEqualizeHist);
+    m_CvActsGroup->addAction(m_actionThreshold);
+    m_CvActsGroup->addAction(m_actionThreshold_Adaptive);
+    m_CvActsGroup->addAction(m_actionReverse);
+    m_CvActsGroup->addAction(m_actionColorReduce);
+    m_CvActsGroup->addAction(m_actionGamma);
+    m_CvActsGroup->addAction(m_actionContrastBright);
+    m_CvActsGroup->addAction(m_actionCanny);
+    m_CvActsGroup->addAction(m_actionBlur);
+    m_CvActsGroup->addAction(m_actionSobel);
+    m_CvActsGroup->addAction(m_actionLaplacian);
+    m_CvActsGroup->addAction(m_actionScharr);
+    m_CvActsGroup->addAction(m_actionPrewitt);
+    m_CvActsGroup->addAction(m_actionRemoveCV);
+
+    m_AVisualGrapicTypeActsGroup = new QActionGroup(this);
+    m_AVisualGrapicTypeActsGroup->addAction(m_actionLine);
+    m_AVisualGrapicTypeActsGroup->addAction(m_actionBar);
+    m_AVisualGrapicTypeActsGroup->addAction(m_actionPie);
+
+    m_AVisualTypeActsGroup = new QActionGroup(this);
+    m_AVisualTypeActsGroup->addAction(m_actionSampling);
+    m_AVisualTypeActsGroup->addAction(m_actionFrequency);
+
+    // 7. 信号槽
+    connect(m_actionSampling, &QAction::triggered, this, &MainWindowA::popup_audio_effect);
+    connect(m_actionFrequency, &QAction::triggered, this, &MainWindowA::popup_audio_effect);
+    connect(m_actionLine, &QAction::triggered, this, &MainWindowA::popup_audio_effect);
+    connect(m_actionBar, &QAction::triggered, this, &MainWindowA::popup_audio_effect);
+    connect(m_actionPie, &QAction::triggered, this, &MainWindowA::popup_audio_effect);
+
+    // ... 其余Action信号槽如有也可补全 ...
+
+    // 8. 继续原有初始化流程
+    // setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint
+    //                | Qt::WindowCloseButtonHint);
+
+    // 新增:主布局,视频+控制条
+    auto mainLayout = new QVBoxLayout(m_centralWidget);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+    mainLayout->setSpacing(0);
+    create_video_label();
+    create_play_control();
+    create_audio_effect();
+
+    if (m_video_widget)
+        mainLayout->addWidget(m_video_widget.get(), 1);
+    if (m_play_control_wnd)
+        mainLayout->addWidget(m_play_control_wnd.get(), 0);
+
+    mainLayout->addWidget(m_audio_effect_wnd.get(), 0);
+    m_centralWidget->setLayout(mainLayout);
+
+    setWindowTitle(tr("Video Player"));
+
+    qApp->installEventFilter(this);
+    setAcceptDrops(true);
+
+#if AUTO_HIDE_PLAYCONTROL
+    setMouseTracking(true);
+    m_timer.setInterval(3 * 1000);
+    m_timer.setSingleShot(false);
+    connect(&m_timer, &QTimer::timeout, this, &MainWindowA::check_hide_play_control);
+    m_timer.start();
+#endif
+
+    read_settings();
+    update_menus();
+
+#if !NDEBUG
+    print_screen();
+#endif
+
+    resize(600, 600);
+
+    connect(m_playerController,
+            &PlayerController::startToPlaySignal,
+            this,
+            &MainWindowA::update_menus);
+    connect(m_playerController, &PlayerController::frameReady, this, &MainWindowA::frameReady);
+
+    connect(m_playerController, &PlayerController::audioData, this, &MainWindowA::audio_data);
+
+    connect(m_playerController,
+            &PlayerController::setPlayControlWnd,
+            this,
+            &MainWindowA::set_paly_control_wnd);
+    connect(m_playerController,
+            &PlayerController::updatePlayControlVolume,
+            this,
+            &MainWindowA::update_paly_control_volume);
+    connect(m_playerController,
+            &PlayerController::updatePlayControlStatus,
+            this,
+            &MainWindowA::update_paly_control_status);
+    // 新增:连接UI相关信号
+    connect(m_playerController, &PlayerController::showMessage, this, &MainWindowA::onShowMessage);
+    connect(m_playerController,
+            &PlayerController::requestFullscreen,
+            this,
+            &MainWindowA::onRequestFullscreen);
+    connect(m_playerController,
+            &PlayerController::requestHideStatusBar,
+            this,
+            &MainWindowA::onRequestHideStatusBar);
+    connect(m_playerController,
+            &PlayerController::updatePlayTime,
+            this,
+            &MainWindowA::update_play_time);
+    connect(m_playerController, &PlayerController::playSeek, this, &MainWindowA::play_seek);
+
+    hide_play_control(false);
+}
+
+MainWindowA::~MainWindowA()
+{
+    m_playerController->stopPlay();
+    save_settings();
+}
+
+QScreen* MainWindowA::screen() const
+{
+    return QApplication::primaryScreen();
+}
+
+QRect MainWindowA::screen_rect() const
+{
+    //auto pScreen = screen();
+    //auto scale = pScreen->devicePixelRatio();
+    //return QRect(0, 0, rt.width() * scale, rt.height() * scale);
+    return screen()->geometry();
+}
+
+qreal MainWindowA::screen_scale() const
+{
+    return screen()->devicePixelRatio();
+}
+
+QSize MainWindowA::display_video_size(AVCodecContext* pVideo) const
+{
+    auto scale = screen_scale(); //screen display scale
+    if (pVideo && scale != 0)
+        return QSize(pVideo->width / scale, pVideo->height / scale);
+
+    return QSize(0, 0);
+}
+
+void MainWindowA::create_video_label()
+{
+    m_video_widget = std::make_unique<OpenGLVideoWidget>(centralWidget());
+    // 不再单独设置布局,统一在 centralWidget 的主布局中管理
+}
+
+void MainWindowA::create_audio_effect()
+{
+    m_audio_effect_wnd = std::make_unique<AudioEffectGL>(centralWidget());
+    m_audio_effect_wnd->setObjectName(QString::fromUtf8("audio_effect"));
+    m_audio_effect_wnd->hide();
+
+    // connect(m_audio_effect_wnd.get(), &AudioEffectGL::hiden, this, &MainWindowA::start_send_data);
+    connect(m_audio_effect_wnd.get(),
+            &AudioEffectGL::hiden,
+            m_playerController,
+            &PlayerController::startSendData);
+}
+
+void MainWindowA::show_audio_effect(bool bShow)
+{
+    if (!m_audio_effect_wnd)
+        return;
+
+    auto pt = frameGeometry().center() - m_audio_effect_wnd->rect().center();
+    m_audio_effect_wnd->move(pt);
+    m_audio_effect_wnd->paint_clear();
+
+    if (bShow) {
+        m_audio_effect_wnd->show();
+    } else {
+        m_audio_effect_wnd->hide();
+    }
+}
+
+void MainWindowA::create_recentfiles_menu()
+{
+    for (int i = 0; i < MaxRecentFiles; ++i) {
+        m_recentFileActs[i] = std::make_unique<QAction>(this);
+        m_recentFileActs[i]->setVisible(false);
+        connect(m_recentFileActs[i].get(), SIGNAL(triggered()), this, SLOT(open_recentFile()));
+    }
+
+    m_recentClear = std::make_unique<QAction>(this);
+    m_recentClear->setText(tr("清除"));
+    connect(m_recentClear.get(), SIGNAL(triggered()), this, SLOT(clear_recentfiles()));
+
+    auto pMenu = m_menuRecentFiles;
+    pMenu->clear();
+    for (int i = 0; i < MaxRecentFiles; ++i)
+        pMenu->addAction(m_recentFileActs[i].get());
+    pMenu->addSeparator();
+    pMenu->addAction(m_recentClear.get());
+
+    update_recentfile_actions();
+}
+
+void MainWindowA::set_current_file(const QString& fileName)
+{
+    setWindowFilePath(fileName);
+
+    auto files = m_settings.get_recentfiles().toStringList();
+    files.removeAll(fileName);
+    files.prepend(fileName);
+    while (files.size() > MaxRecentFiles)
+        files.removeLast();
+
+    m_settings.set_recentfiles(files);
+
+    update_recentfile_actions();
+}
+
+void MainWindowA::clear_recentfiles()
+{
+    auto files = m_settings.get_recentfiles().toStringList();
+    files.clear();
+    m_settings.set_recentfiles(files);
+
+    update_recentfile_actions();
+}
+
+void MainWindowA::remove_recentfiles(const QString& fileName)
+{
+    auto files = m_settings.get_recentfiles().toStringList();
+    files.removeAll(fileName);
+    m_settings.set_recentfiles(files);
+
+    update_recentfile_actions();
+}
+
+void MainWindowA::update_recentfile_actions()
+{
+    auto files = m_settings.get_recentfiles().toStringList();
+
+    int numRecentFiles = qMin(files.size(), (int) MaxRecentFiles);
+
+    m_menuRecentFiles->setEnabled(numRecentFiles > 0);
+
+    for (int i = 0; i < numRecentFiles; ++i) {
+        QString text = tr("%1 %2").arg(i + 1).arg(stripped_name(files[i]));
+        m_recentFileActs[i]->setText(
+            QApplication::translate("MainWindowA", text.toStdString().c_str(), nullptr));
+        m_recentFileActs[i]->setData(files[i]);
+        m_recentFileActs[i]->setVisible(true);
+    }
+    for (int j = numRecentFiles; j < MaxRecentFiles; ++j)
+        m_recentFileActs[j]->setVisible(false);
+}
+
+QString MainWindowA::stripped_name(const QString& fullFileName) const
+{
+    return QFileInfo(fullFileName).fileName();
+}
+
+void MainWindowA::open_recentFile()
+{
+    if (auto action = qobject_cast<QAction*>(sender()))
+        m_playerController->startToPlay(action->data().toString());
+}
+
+void MainWindowA::about_media_info()
+{
+    // if (!m_pVideoState)
+    //     return;
+
+    // auto pState = m_playerController->state();
+    // if (!pState)
+    //     return;
+
+    // if (auto ic = pState->ic) {
+    //     auto str = dump_format(ic, 0, pState->filename);
+    //     show_msg_dlg(str, "Media information", "QLabel{min-width: 760px;}");
+    // }
+}
+
+void MainWindowA::create_play_control()
+{
+    m_play_control_wnd = std::make_unique<PlayControlWnd>(m_playerController, this);
+    m_play_control_wnd->setObjectName(QString::fromUtf8("play_control"));
+    m_play_control_wnd->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+    // 不再 setGeometry
+}
+
+void MainWindowA::update_video_label()
+{
+    // 移除手动 resize,让布局自动管理
+    // auto sizeCenter = centralWidget()->size();
+    // if (auto pLabel = get_video_widget())
+    //     pLabel->resize(sizeCenter.width(), sizeCenter.height());
+}
+
+void MainWindowA::show_msg_dlg(const QString& message,
+                               const QString& windowTitle,
+                               const QString& styleSheet)
+{
+    onShowMessage(message, windowTitle, styleSheet);
+}
+
+void MainWindowA::update_play_control()
+{
+    // if (auto pPlayControl = get_play_control()) {
+    //     auto sizeCenter = centralWidget()->size();
+
+    //     pPlayControl->resize(sizeCenter.width(), pPlayControl->size().height());
+
+    //     auto frameGeoRt = frameGeometry();
+    //     auto geoRt = geometry();
+
+    //     // QPoint pt = ui->statusbar->pos();
+    //     int borderH = frameGeoRt.height() - (geoRt.y() - frameGeoRt.y()) - geoRt.height();
+    //     //int borderH = frameGeoRt.height() - geoRt.height();
+    //     // int borderw = frameGeoRt.width() - geoRt.width();
+    //     // int borderSelf = pPlayControl->frameGeometry().height() - (pPlayControl->geometry().y() - pPlayControl->frameGeometry().y()) - pPlayControl->geometry().height();
+
+    //     //auto pt = geoRt.bottomLeft() - QPoint(0, szPlayControl.height() + borderH);
+    //     auto pt = geoRt.bottomLeft() - QPoint(0, pPlayControl->size().height() - 1);
+    //     pPlayControl->move(pt);
+    // }
+}
+
+void MainWindowA::print_size() const
+{
+    auto rt = geometry();
+    qDebug("geometry rt:(x:%d, y:%d, w:%d, h:%d)", rt.x(), rt.y(), rt.width(), rt.height());
+    rt = frameGeometry();
+    qDebug("frameGeometry rt:(x:%d, y:%d, w:%d, h:%d)", rt.x(), rt.y(), rt.width(), rt.height());
+
+    auto size = this->size();
+    qDebug("window size:(%d,%d)", size.width(), size.height());
+    // 这里不再有ui->centralwidget,直接用m_centralWidget
+    size = m_centralWidget->size();
+    qDebug("centralwidget size:(%d,%d)", size.width(), size.height());
+    // 菜单栏
+    size = m_menuBar->size();
+    qDebug("menubar size:(%d,%d)", size.width(), size.height());
+    // 状态栏
+    size = m_statusBar->size();
+    qDebug("statusbar size:(%d,%d)", size.width(), size.height());
+    // 菜单栏位置
+    auto pt = m_menuBar->pos();
+    qDebug("menuBar pt (x:%d, y:%d)", pt.x(), pt.y());
+}
+
+void MainWindowA::print_screen() const
+{
+    auto screen = QApplication::primaryScreen();
+    auto rt = screen->availableGeometry();
+    qDebug("availableGeometry rt (x:%d, y:%d, width:%d, height:%d)",
+           rt.x(),
+           rt.y(),
+           rt.width(),
+           rt.height());
+
+    auto sz = screen->availableSize();
+    qDebug("availableSize sz (width:%d, height:%d)", sz.width(), sz.height());
+
+    sz = screen->size();
+    qDebug("size sz (width:%d, height:%d)", sz.width(), sz.height());
+
+    rt = screen->virtualGeometry();
+    qDebug("virtualGeometry rt (x:%d, y:%d, width:%d, height:%d)",
+           rt.x(),
+           rt.y(),
+           rt.width(),
+           rt.height());
+
+    sz = screen->virtualSize();
+    qDebug("virtualSize sz (width:%d, height:%d)", sz.width(), sz.height());
+
+    rt = screen->availableVirtualGeometry();
+    qDebug("availableVirtualGeometry rt (x:%d, y:%d, width:%d, height:%d)",
+           rt.x(),
+           rt.y(),
+           rt.width(),
+           rt.height());
+
+    sz = screen->availableVirtualSize();
+    qDebug("availableVirtualSize sz (width:%d, height:%d)", sz.width(), sz.height());
+
+    rt = screen->geometry();
+    qDebug("geometry rt (x:%d, y:%d, width:%d, height:%d)", rt.x(), rt.y(), rt.width(), rt.height());
+
+    auto depth = screen->depth();
+    qDebug() << "depth :" << depth;
+
+    auto ratio = screen->devicePixelRatio();
+    qDebug() << "devicePixelRatio :" << ratio;
+
+    auto dot_per_inch = screen->logicalDotsPerInch();
+    qDebug() << "logicalDotsPerInch :" << dot_per_inch;
+
+    auto x = screen->logicalDotsPerInchX();
+    qDebug() << "logicalDotsPerInchX :" << x;
+
+    auto y = screen->logicalDotsPerInchY();
+    qDebug() << "logicalDotsPerInchY :" << y;
+
+    auto str = screen->manufacturer();
+    qDebug() << "manufacturer :" << str;
+
+    str = screen->model();
+    qDebug() << "model :" << str;
+
+    str = screen->name();
+    qDebug() << "name :" << str;
+
+    str = screen->serialNumber();
+    qDebug() << "serialNumber :" << str;
+
+    auto o = screen->nativeOrientation();
+    qDebug() << "nativeOrientation :" << o;
+
+    o = screen->orientation();
+    qDebug() << "orientation :" << o;
+
+    o = screen->primaryOrientation();
+    qDebug() << "primaryOrientation :" << o;
+
+    auto ph_d = screen->physicalDotsPerInch();
+    qDebug() << "physicalDotsPerInch :" << ph_d;
+
+    ph_d = screen->physicalDotsPerInchX();
+    qDebug() << "physicalDotsPerInchX :" << ph_d;
+
+    ph_d = screen->physicalDotsPerInchY();
+    qDebug() << "physicalDotsPerInchY :" << ph_d;
+
+    auto sz_f = screen->physicalSize();
+    qDebug() << "physicalSize :" << sz_f;
+
+    auto fr = screen->refreshRate();
+    qDebug() << "refreshRate :" << fr;
+}
+
+void MainWindowA::resizeEvent(QResizeEvent* event)
+{
+    update_video_label();
+    update_play_control();
+
+    QMainWindow::resizeEvent(event);
+}
+
+void MainWindowA::moveEvent(QMoveEvent* event)
+{
+    update_play_control();
+    QMainWindow::moveEvent(event);
+}
+
+void MainWindowA::keyPressEvent(QKeyEvent* event)
+{
+    qDebug() << "Mainwindow key event, event:" << event->text() << "key:" << event->key()
+             << "key_str:" << QKeySequence(event->key()).toString();
+
+    switch (event->key()) {
+    case Qt::Key_Space:  // pause/continue
+    case Qt::Key_Up:     // volume up
+    case Qt::Key_Down:   // volume down
+    case Qt::Key_Left:   // play back
+    case Qt::Key_Right:  // play forward
+    case Qt::Key_M:      // mute
+    case Qt::Key_Comma:  // speed down
+    case Qt::Key_Period: // speed up
+        play_control_key((Qt::Key) event->key());
+        break;
+
+    case Qt::Key_A: // aspect ratio
+        on_actionAspect_Ratio_triggered();
+        break;
+
+    case Qt::Key_O: // keep orginal size
+        on_actionOriginalSize_triggered();
+        break;
+
+    case Qt::Key_L: // show play list wnd
+    {
+        show_playlist();
+        m_actionPlayList->setChecked(true);
+    } break;
+
+    case Qt::Key_F: // full screen
+    {
+        bool bFullscreen = label_fullscreen();
+        show_fullscreen(!bFullscreen);
+        m_actionFullscreen->setChecked(!bFullscreen);
+    } break;
+
+    case Qt::Key_Escape: {
+        show_fullscreen(false);
+        m_actionFullscreen->setChecked(false);
+    } break;
+
+    case Qt::Key_H:
+        on_actionKeyboard_Usage_triggered();
+        break;
+
+    default:
+        qDebug("Not handled key event, key:%s(%d) pressed!\n",
+               qUtf8Printable(event->text()),
+               event->key());
+        QWidget::keyPressEvent(event);
+        break;
+    }
+}
+
+bool MainWindowA::eventFilter(QObject* obj, QEvent* event)
+{
+    if (event->type() == QEvent::MouseMove) {
+        auto mouseEvent = static_cast<QMouseEvent*>(event);
+        // displayStatusMessage(QString("Mouse move
+        // (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
+
+        check_hide_menubar(mouseEvent->pos());
+
+#if AUTO_HIDE_PLAYCONTROL
+        if (!(m_actionHide_Play_Ctronl->isChecked() || label_fullscreen())) {
+            if (cursor_in_window(get_play_control())) {
+                m_timer.stop();
+                auto_hide_play_control(false);
+            } else {
+                m_timer.start();
+            }
+        }
+
+        hide_cursor(false);
+        setCursor(Qt::ArrowCursor);
+#endif
+    }
+    return QMainWindow::eventFilter(obj, event);
+}
+
+void MainWindowA::dropEvent(QDropEvent* event)
+{
+    auto mimeData = event->mimeData();
+
+    if (!mimeData->hasUrls())
+        return;
+
+    if (auto urlList = mimeData->urls(); urlList.size() > 0)
+        m_playerController->startToPlay(urlList.at(0).toLocalFile());
+}
+
+void MainWindowA::dragEnterEvent(QDragEnterEvent* event)
+{
+    if (auto mimeData = event->mimeData(); mimeData->hasUrls())
+        event->acceptProposedAction();
+    event->accept();
+}
+
+void MainWindowA::check_hide_menubar(const QPoint& pt)
+{
+    if (isFullScreen())
+        hide_menubar(pt.y() > menuBar()->geometry().height());
+}
+
+void MainWindowA::check_hide_play_control()
+{
+    if (!m_playerController->isPlaying())
+        return;
+
+    if (!isFullScreen() && cursor_in_window(get_play_control())) {
+        qDebug() << "Cursor is in PlayControl window, don't hide it.";
+        return;
+    }
+
+    auto_hide_play_control();
+    hide_cursor();
+}
+
+void MainWindowA::auto_hide_play_control(bool bHide)
+{
+    if (!get_play_control())
+        return;
+
+    if (!m_playerController->state())
+        return;
+
+    if (m_actionHide_Play_Ctronl->isChecked())
+        return;
+
+    hide_play_control(bHide);
+}
+
+void MainWindowA::on_actionOpen_triggered()
+{
+    const QStringList filters(
+        {"Videos (*.mp4 *.avi *.mkv)", "Audios (*.mp3 *.wav *.wma)", "Any files (*)"});
+
+    QFileDialog dialog(this);
+    dialog.setFileMode(QFileDialog::ExistingFile);
+    // dialog.setNameFilter(tr("Videos (*.mp4 *.avi *.mp3)"));
+    dialog.setNameFilters(filters);
+    dialog.setViewMode(QFileDialog::List);
+
+    if (dialog.exec()) {
+        m_playerController->startToPlay(dialog.selectedFiles()[0]);
+    }
+}
+
+void MainWindowA::on_actionAspect_Ratio_triggered() {}
+
+void MainWindowA::on_actionQuit_triggered()
+{
+    QMainWindow::close();
+}
+
+void MainWindowA::on_actionHelp_triggered() {}
+
+void MainWindowA::on_actionStop_triggered()
+{
+    m_playerController->stopPlay();
+}
+
+void MainWindowA::on_actionHide_Play_Ctronl_triggered()
+{
+    hide_play_control(m_actionHide_Play_Ctronl->isChecked());
+}
+
+void MainWindowA::on_actionFullscreen_triggered()
+{
+    show_fullscreen(m_actionFullscreen->isChecked());
+}
+
+void MainWindowA::on_actionLoop_Play_triggered()
+{
+    if (auto pState = m_playerController->state())
+        pState->loop = int(m_actionLoop_Play->isChecked());
+}
+
+void MainWindowA::on_actionMedia_Info_triggered()
+{
+    if (m_playerController->isPlaying())
+        about_media_info();
+}
+
+void MainWindowA::on_actionKeyboard_Usage_triggered()
+{
+    QString str;
+    QString indent = "		";
+    str += "A" + indent + "Video aspect ratio\n";
+    str += "F" + indent + "Fulllscreen/Unfullscreen\n";
+    str += "H" + indent + "Show help\n";
+    str += "L" + indent + "Show playlist\n";
+    str += "M" + indent + "Mute/Unmute\n";
+    str += "O" + indent + "Keep video original size\n";
+    str += "Space" + indent + "Pause/Play\n";
+    str += "Up" + indent + "Volume up\n";
+    str += "Down" + indent + "Volume down\n";
+    str += "Left" + indent + "Play back\n";
+    str += "Right" + indent + "Play forward\n";
+    str += "<" + indent + "Speed down\n";
+    str += ">" + indent + "Speed up\n";
+
+    show_msg_dlg(str, "Keyboard Control");
+}
+
+void MainWindowA::set_audio_effect_format(const BarHelper::VisualFormat& fmt)
+{
+    if (m_audio_effect_wnd)
+        m_audio_effect_wnd->set_draw_fmt(fmt);
+}
+
+void MainWindowA::popup_audio_effect()
+{
+    if (m_playerController->isPlaying()) {
+        BarHelper::VisualFormat fmt;
+        get_avisual_format(fmt);
+        set_audio_effect_format(fmt);
+        show_audio_effect();
+        m_playerController->startSendData();
+    }
+}
+
+void MainWindowA::center_window(QRect screen_rec)
+{
+    auto x = (screen_rec.width() - width()) / 2;
+    auto y = (screen_rec.height() - height()) / 2;
+    move(x, y);
+    show();
+}
+
+void MainWindowA::show_fullscreen(bool bFullscreen)
+{
+    onRequestFullscreen(bFullscreen);
+}
+
+bool MainWindowA::label_fullscreen()
+{
+    if (auto pLabel = get_video_widget())
+        return pLabel->isFullScreen();
+    return false;
+}
+
+void MainWindowA::on_actionOriginalSize_triggered()
+{
+    // if (!m_pVideoState)
+    //     return;
+
+    // auto pVideoCtx = m_pVideoState->get_contex(AVMEDIA_TYPE_VIDEO);
+    // auto pLabel = get_video_widget();
+    // if (!pVideoCtx || !pLabel)
+    //     return;
+
+    // auto sizeLabel = pLabel->size();
+    // auto sz = size();
+
+    // auto video_sz = display_video_size(pVideoCtx);
+    // int new_width = video_sz.width();
+    // int new_height = video_sz.height();
+
+    // if (new_width < minimumWidth()) {
+    //     new_height = minimumWidth() * new_height / new_width;
+    //     new_width = minimumWidth();
+    // }
+
+    // if (new_height < minimumHeight()) {
+    //     new_width = minimumHeight() * new_width / new_height;
+    //     new_height = minimumHeight();
+    // }
+
+    // int w_change = new_width - sizeLabel.width();
+    // int h_change = new_height - sizeLabel.height();
+
+    // sz += QSize(w_change, h_change);
+    // resize_window(sz);
+}
+
+void MainWindowA::hide_play_control(bool bHide)
+{
+    // if (auto pPlayControl = get_play_control()) {
+    //     // pPlayControl->setVisible(true);
+    //     // pPlayControl->setDisabled(false);
+    //     if (pPlayControl->isVisible() == bHide) {
+    //         pPlayControl->setVisible(!bHide);
+    //     }
+    // }
+}
+
+void MainWindowA::set_paly_control_wnd(bool set)
+{
+    auto pPlayControl = get_play_control();
+    if (!pPlayControl)
+        return;
+
+    if (set) {
+        auto pState = m_playerController->state();
+        if (!pState)
+            return;
+
+        if (auto ic = pState->ic) {
+            int64_t hours, mins, secs, us;
+            get_duration_time(ic->duration, hours, mins, secs, us);
+            pPlayControl->set_total_time(hours, mins, secs);
+        }
+    } else {
+        pPlayControl->clear_all();
+    }
+}
+
+void MainWindowA::set_volume_updown(bool bUp, float unit)
+{
+    auto volume = m_playerController->deviceVolume();
+    auto n_volume = volume;
+    if (bUp) {
+        n_volume += unit;
+    } else {
+        n_volume -= unit;
+    }
+
+    if (n_volume > 1.0 || n_volume < 0) {
+        QApplication::beep();
+    }
+
+    n_volume = n_volume > 1.0 ? 1.0 : n_volume;
+    n_volume = n_volume < 0 ? 0 : n_volume;
+
+    set_volume(int(n_volume * 100));
+    update_paly_control_volume();
+}
+
+void MainWindowA::update_paly_control_volume()
+{
+    if (auto pPlayControl = get_play_control())
+        pPlayControl->set_volume_slider(m_playerController->deviceVolume());
+}
+
+void MainWindowA::update_paly_control_muted()
+{
+    if (auto pPlayControl = get_play_control()) {
+        if (auto pState = m_playerController->state())
+            pPlayControl->volume_muted(pState->muted);
+    }
+}
+
+void MainWindowA::update_paly_control_status()
+{
+    if (auto pPlayControl = get_play_control()) {
+        if (auto pState = m_playerController->state())
+            pPlayControl->update_btn_play(!!pState->paused);
+    }
+}
+
+void MainWindowA::update_play_time()
+{
+    if (auto pPlayControl = get_play_control()) {
+        qDebug() << "update_play_time";
+        // pPlayControl->setVisible(true);
+        // pPlayControl->resize(800, 200);
+        // pPlayControl->show();
+        if (auto pState = m_playerController->state())
+            pPlayControl->update_play_time(pState->audio_clock);
+    }
+}
+
+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;
+    video_seek(pos, incr);
+}
+
+void MainWindowA::video_seek(double pos, double incr)
+{
+    auto pState = m_playerController->state();
+    if (!pState)
+        return;
+
+#if USE_AVFILTER_AUDIO
+    // pos /= pState->audio_speed;
+#endif
+
+    if (pState->ic->start_time != AV_NOPTS_VALUE
+        && pos < pState->ic->start_time / (double) AV_TIME_BASE) {
+        // qDebug("!seek_by_bytes pos=%lf, start_time=%lf, %lf", pos,
+        // pState->ic->start_time, pState->ic->start_time / (double)AV_TIME_BASE);
+        pos = pState->ic->start_time / (double) AV_TIME_BASE;
+    }
+
+    stream_seek(pState, (int64_t) (pos * AV_TIME_BASE), (int64_t) (incr * AV_TIME_BASE), 0);
+}
+
+void MainWindowA::play_seek()
+{
+    if (auto pPlayControl = get_play_control()) {
+        auto maxValue = pPlayControl->get_progress_slider_max();
+        auto total_time = pPlayControl->get_total_time();
+        auto value = pPlayControl->get_progress_slider_value();
+
+        double seek_time = 0;
+        if (maxValue > 0)
+            seek_time = value * total_time * 1.0 / maxValue;
+
+        qDebug() << "val:" << value << ",maxVal:" << maxValue << ",total time" << total_time
+                 << ",seek time:" << seek_time;
+        m_playerController->videoSeek(seek_time);
+    }
+
+    update_paly_control_status();
+}
+
+void MainWindowA::play_mute(bool mute)
+{
+    auto pState = m_playerController->state();
+    if (pState)
+        toggle_mute(pState, mute);
+}
+
+void MainWindowA::set_volume(int volume)
+{
+    auto pPlayControl = get_play_control();
+    if (!pPlayControl)
+        return;
+
+    auto max_val = pPlayControl->get_volum_slider_max();
+    auto vol = volume * 1.0 / max_val;
+    m_playerController->setDeviceVolume(vol);
+
+    volume_settings(true, vol);
+}
+
+void MainWindowA::set_play_speed()
+{
+    auto pPlayControl = get_play_control();
+    if (!pPlayControl)
+        return;
+
+    auto speed = pPlayControl->get_speed();
+
+    // qDebug("set_play_spped, speed control changed, speed:%d", speed);
+    if (m_playerController) {
+        if (auto pState = m_playerController->state()) {
+#if USE_AVFILTER_AUDIO
+            set_audio_playspeed(pState, speed);
+#endif
+        }
+    }
+}
+
+void MainWindowA::play_speed_adjust(bool up)
+{
+    if (auto pPlayControl = get_play_control())
+        pPlayControl->speed_adjust(up);
+
+    set_play_speed();
+}
+
+void MainWindowA::hide_statusbar(bool bHide)
+{
+    onRequestHideStatusBar(bHide);
+}
+
+void MainWindowA::hide_menubar(bool bHide)
+{
+    menuBar()->setVisible(!bHide);
+
+    // qDebug("is full screen:%d, menu is visible:%d", isFullScreen(), bVisible);
+    if (isFullScreen()) {
+        centralWidget()->resize(centralWidget()->size());
+    }
+
+    update_play_control();
+}
+
+void MainWindowA::on_actionAbout_triggered() {}
+
+void MainWindowA::play_failed(const QString& file)
+{
+    show_msg_dlg(QString("File play failed, file: %1").arg(toNativePath(file)));
+}
+
+void MainWindowA::play_start_seek()
+{
+    play_seek();
+    m_playerController->pausePlay();
+}
+
+void MainWindowA::play_control_key(Qt::Key key)
+{
+    if (!m_playerController)
+        return;
+
+    auto pState = m_playerController->state();
+    if (!pState)
+        return;
+
+    switch (key) {
+    case Qt::Key_Space: // pause/continue
+        m_playerController->pausePlay();
+        break;
+
+    case Qt::Key_M:
+        toggle_mute(pState, !pState->muted);
+        update_paly_control_muted();
+        break;
+
+    case Qt::Key_Up: // volume
+        set_volume_updown(true);
+        break;
+
+    case Qt::Key_Down: // volume
+        set_volume_updown(false);
+        break;
+
+    case Qt::Key_Left:
+        m_playerController->playSeekPre();
+        break;
+
+    case Qt::Key_Right:
+        m_playerController->playSeekNext();
+        break;
+
+    case Qt::Key_Comma:
+        play_speed_adjust(false);
+        break;
+
+    case Qt::Key_Period:
+        play_speed_adjust(true);
+        break;
+
+    default:
+        qDebug("key:(%d) pressed, not handled!\n", key);
+        break;
+    }
+}
+
+void MainWindowA::frameReady(AVFrame* frame)
+{
+    auto widget = get_video_widget();
+    widget->Render(frame);
+}
+
+void MainWindowA::subtitle_ready(const QString& text)
+{
+    set_subtitle(text);
+}
+
+void MainWindowA::set_subtitle(const QString& str)
+{
+    m_subtitle = str;
+    qDebug() << "subtitle received:" << m_subtitle;
+}
+
+void MainWindowA::clear_subtitle_str()
+{
+    set_subtitle("");
+}
+
+void MainWindowA::displayStatusMessage(const QString& message)
+{
+    int timeout = 0; // 5000: 5 second timeout
+    if (auto bar = statusBar())
+        bar->showMessage(message, timeout);
+}
+
+void MainWindowA::print_decodeContext(const AVCodecContext* pDecodeCtx, bool bVideo) const
+{
+    if (!pDecodeCtx)
+        return;
+
+    if (bVideo) {
+        qInfo("video codec_name: %s", pDecodeCtx->codec->name);
+        qInfo("codec_type: %d, codec_id: %d, codec_tag: %d",
+              pDecodeCtx->codec_type,
+              pDecodeCtx->codec_id,
+              pDecodeCtx->codec_tag);
+        qInfo("width: %d, height: %d, codec_tag: %d", pDecodeCtx->width, pDecodeCtx->height);
+    } else {
+        qInfo("audio codec_name: %s", pDecodeCtx->codec->name);
+        qInfo("codec_type: %d, codec_id: %d, codec_tag: %d",
+              pDecodeCtx->codec_type,
+              pDecodeCtx->codec_id,
+              pDecodeCtx->codec_tag);
+        qInfo("sample_rate: %d, channels: %d, sample_fmt: %d",
+              pDecodeCtx->sample_rate,
+              pDecodeCtx->ch_layout.nb_channels,
+              pDecodeCtx->sample_fmt);
+        qInfo("frame_size: %d, frame_number: %d, block_align: %d",
+              pDecodeCtx->frame_size,
+              pDecodeCtx->frame_num,
+              pDecodeCtx->block_align);
+    }
+}
+
+void MainWindowA::save_settings()
+{
+    auto res = m_actionHide_Play_Ctronl->isChecked();
+    m_settings.set_general("hidePlayContrl", int(res));
+    res = m_actionFullscreen->isChecked();
+    m_settings.set_general("fullScreen", int(res));
+
+    res = m_actionHardware_decode->isChecked();
+    m_settings.set_general("openDXVA2", int(res));
+    res = m_actionLoop_Play->isChecked();
+    m_settings.set_general("loopPlay", int(res));
+
+    m_settings.set_general("style", get_selected_style());
+
+    m_settings.set_info("software", "Video player");
+    //m_settings.set_info("version", PLAYER_VERSION);
+    m_settings.set_info("author", "Steven Huang");
+}
+
+void MainWindowA::read_settings()
+{
+    int value;
+    auto values = m_settings.get_general("hidePlayContrl");
+    if (values.isValid()) {
+        value = values.toInt();
+        m_actionHide_Play_Ctronl->setChecked(!!value);
+        hide_play_control(value);
+    }
+
+    values = m_settings.get_general("fullScreen");
+    if (values.isValid()) {
+        value = values.toInt();
+        m_actionFullscreen->setChecked(!!value);
+        show_fullscreen(value);
+    }
+
+    values = m_settings.get_general("openDXVA2");
+    if (values.isValid()) {
+        value = values.toInt();
+        m_actionHardware_decode->setChecked(!!value);
+    }
+
+    values = m_settings.get_general("loopPlay");
+    if (values.isValid()) {
+        value = values.toInt();
+        m_actionLoop_Play->setChecked(!!value);
+    }
+}
+
+float MainWindowA::volume_settings(bool set, float vol)
+{
+    if (set) {
+        m_settings.set_general("volume", QString::number(float(vol), 'f', 1));
+    } else {
+        auto value = 0.2f; // default sound volume
+        auto values = m_settings.get_general("volume");
+        if (values.isValid())
+            value = values.toFloat();
+        return value;
+    }
+    return 0;
+}
+
+QString MainWindowA::get_selected_style() const
+{
+    auto pMenu = m_menuStyle;
+    for (auto action : pMenu->actions()) {
+        if (!(action->isSeparator() || action->menu())) {
+            qDebug("action: %s", qUtf8Printable(action->text()));
+            if (action->isChecked())
+                return action->data().toString();
+        }
+    }
+    return QString("");
+}
+
+void MainWindowA::set_style_action(const QString& style)
+{
+    auto pMenu = m_menuStyle;
+    for (auto action : pMenu->actions()) {
+        if (!(action->isSeparator() || action->menu())) {
+            if (action->data().toString() == style)
+                action->setChecked(true);
+        }
+    }
+}
+
+void MainWindowA::audio_data(const AudioData& data)
+{
+    if (m_audio_effect_wnd)
+        m_audio_effect_wnd->paint_data(data);
+}
+
+void MainWindowA::update_menus()
+{
+    qDebug() << "update_menus: " << m_playerController->isPlaying();
+    enable_menus(m_playerController->isPlaying());
+    enable_v_menus(m_playerController->playingHasVideo());
+    enable_a_menus(m_playerController->playingHasAudio());
+}
+
+void MainWindowA::enable_menus(bool enable)
+{
+    m_actionStop->setEnabled(enable);
+    m_actionMedia_Info->setEnabled(enable);
+}
+
+void MainWindowA::enable_v_menus(bool enable)
+{
+    m_actionAspect_Ratio->setEnabled(enable);
+    m_actionOriginalSize->setEnabled(enable);
+    m_actionHardware_decode->setEnabled(enable);
+
+    for (auto& pAction : m_menuCV->actions()) {
+        if (pAction)
+            pAction->setEnabled(enable);
+    }
+}
+
+void MainWindowA::enable_a_menus(bool enable)
+{
+    m_menuAudioVisualize->setEnabled(enable);
+}
+
+bool MainWindowA::get_avisual_format(BarHelper::VisualFormat& fmt) const
+{
+    if (m_actionLine->isChecked()) {
+        fmt.gType = BarHelper::e_GtLine;
+    } else if (m_actionBar->isChecked()) {
+        fmt.gType = BarHelper::e_GtBar;
+    } else if (m_actionPie->isChecked()) {
+        fmt.gType = BarHelper::e_GtPie;
+    }
+
+    if (m_actionSampling->isChecked()) {
+        fmt.vType = BarHelper::e_VtSampleing;
+    } else if (m_actionFrequency->isChecked()) {
+        fmt.vType = BarHelper::e_VtFrequency;
+    }
+
+    return true;
+}
+
+void MainWindowA::create_playlist_wnd()
+{
+    m_playListWnd = std::make_unique<PlayListWnd>(this);
+    connect(m_playListWnd.get(),
+            &PlayListWnd::play_file,
+            m_playerController,
+            &PlayerController::startToPlay);
+    // connect(m_playListWnd.get(), &PlayListWnd::save_playlist_signal, this, &MainWindowA::save_playlist);
+    connect(m_playListWnd.get(), &PlayListWnd::hiden, this, &MainWindowA::playlist_hiden);
+    connect(m_playListWnd.get(),
+            &PlayListWnd::playlist_file_saved,
+            this,
+            &MainWindowA::playlist_file_saved);
+}
+
+void MainWindowA::on_actionPlayList_triggered()
+{
+    show_playlist(m_actionPlayList->isChecked());
+}
+
+void MainWindowA::show_playlist(bool show)
+{
+    if (!m_playListWnd)
+        return;
+
+    if (show) {
+        m_playListWnd->show();
+        m_playListWnd->set_cur_palyingfile();
+    } else {
+        m_playListWnd->hide();
+    }
+}
+
+void MainWindowA::playlist_hiden()
+{
+    m_actionPlayList->setChecked(false);
+}
+
+void MainWindowA::add_to_playlist(const QString& file)
+{
+    if (m_playListWnd)
+        m_playListWnd->add_file(file);
+}
+
+QString MainWindowA::get_playingfile() const
+{
+    if (m_playerController->isPlaying())
+        return m_videoFile;
+    return QString("");
+}
+
+void MainWindowA::on_actionOpenNetworkUrl_triggered()
+{
+    NetworkUrlDlg dialog(this);
+
+    if (dialog.exec() == QDialog::Accepted) {
+        if (auto url = dialog.get_url(); !url.isEmpty()) {
+            m_playerController->startToPlay(url);
+        } else {
+            show_msg_dlg("Please input a valid youtube url. ");
+        }
+    }
+}
+
+void MainWindowA::hide_cursor(bool bHide)
+{
+    // if (bHide) {
+    //     QApplication::setOverrideCursor(Qt::BlankCursor);
+    // } else {
+    //     QGuiApplication::restoreOverrideCursor();
+    // }
+}
+
+bool MainWindowA::cursor_in_window(QWidget* pWnd)
+{
+    if (!pWnd)
+        return false;
+
+    auto rt = pWnd->rect();
+    return rt.contains(pWnd->mapFromGlobal(QCursor::pos()));
+}
+
+void MainWindowA::create_savedPlaylists_menu()
+{
+    for (int i = 0; i < MaxPlaylist; ++i) {
+        m_savedPlaylists[i] = std::make_unique<QAction>(this);
+        m_savedPlaylists[i]->setVisible(false);
+        connect(m_savedPlaylists[i].get(), SIGNAL(triggered()), this, SLOT(open_playlist()));
+    }
+
+    m_PlaylistsClear = std::make_unique<QAction>(this);
+    m_PlaylistsClear->setText(tr("清除"));
+    connect(m_PlaylistsClear.get(), SIGNAL(triggered()), this, SLOT(clear_savedPlaylists()));
+
+    auto pMenu = m_menuSavedPlaylist;
+    pMenu->clear();
+    for (int i = 0; i < MaxPlaylist; ++i)
+        pMenu->addAction(m_savedPlaylists[i].get());
+    pMenu->addSeparator();
+    pMenu->addAction(m_PlaylistsClear.get());
+
+    update_savedPlaylists_actions();
+}
+
+void MainWindowA::remove_playlist_file(const QString& fileName)
+{
+    auto files = m_settings.get_savedplaylists().toStringList();
+    files.removeAll(fileName);
+    m_settings.set_savedplaylists(files);
+
+    update_savedPlaylists_actions();
+}
+
+void MainWindowA::update_savedPlaylists_actions()
+{
+    auto files = m_settings.get_savedplaylists().toStringList();
+
+    int num = qMin(files.size(), (int) MaxPlaylist);
+
+    m_menuSavedPlaylist->setEnabled(num > 0);
+
+    for (int i = 0; i < num; ++i) {
+        QString text = tr("%1 %2").arg(i + 1).arg(stripped_name(files[i]));
+        m_savedPlaylists[i]->setText(
+            QApplication::translate("MainWindowA", text.toStdString().c_str(), nullptr));
+        m_savedPlaylists[i]->setData(files[i]);
+        m_savedPlaylists[i]->setVisible(true);
+    }
+
+    for (int j = num; j < MaxPlaylist; ++j)
+        m_savedPlaylists[j]->setVisible(false);
+}
+
+void MainWindowA::clear_savedPlaylists()
+{
+    auto files = m_settings.get_savedplaylists().toStringList();
+
+    for (const auto& i : files) {
+        QFile file(i);
+        file.remove();
+    }
+
+    files.clear();
+    m_settings.set_savedplaylists(files);
+
+    update_savedPlaylists_actions();
+}
+
+void MainWindowA::open_playlist()
+{
+    if (!m_playListWnd)
+        return;
+
+    auto action = qobject_cast<QAction*>(sender());
+    if (!action)
+        return;
+
+    auto file = action->data().toString();
+
+    QStringList files;
+    if (read_playlist(file, files)) {
+        m_playListWnd->update_files(files);
+        show_playlist();
+        m_actionPlayList->setChecked(true);
+    } else {
+        remove_playlist_file(file);
+    }
+}
+
+void MainWindowA::playlist_file_saved(const QString& file)
+{
+    auto files = m_settings.get_savedplaylists().toStringList();
+    files.removeAll(file);
+    files.prepend(file);
+
+    if (files.size() > MaxPlaylist) {
+        show_msg_dlg(QString("You can only save %1 playlist files!").arg(MaxPlaylist));
+    }
+
+    while (files.size() > MaxPlaylist)
+        files.removeLast();
+
+    m_settings.set_savedplaylists(files);
+    update_savedPlaylists_actions();
+}
+
+bool MainWindowA::read_playlist(const QString& playlist_file, QStringList& files) const
+{
+    QFile file(playlist_file);
+    if (file.open(QIODevice::ReadOnly)) {
+        QTextStream stream(&file);
+        stream.setCodec("UTF-8");
+
+        files = stream.readAll().split(PLAYLIST_SEPERATE_CHAR);
+        files.removeAll(QString(""));
+        file.close();
+        return true;
+    }
+    return false;
+}
+
+// ========== CV菜单Action统一处理槽 ==========
+void MainWindowA::onCvActionToggled()
+{
+    if (!m_video_widget)
+        return;
+    // 灰度
+    m_video_widget->setGray(m_actionGrayscale && m_actionGrayscale->isChecked());
+    // 二值化
+    m_video_widget->setThreshold(m_actionThreshold && m_actionThreshold->isChecked(), 0.5f);
+    // 模糊
+    m_video_widget->setBlur(m_actionBlur && m_actionBlur->isChecked(), 1.0f);
+    // 反色
+    m_video_widget->setReverse(m_actionReverse && m_actionReverse->isChecked());
+    // 色彩减少
+    m_video_widget->setColorReduce(m_actionColorReduce && m_actionColorReduce->isChecked(), 8);
+    // 伽马
+    m_video_widget->setGamma(m_actionGamma && m_actionGamma->isChecked(), 1.2f);
+    // 对比度/亮度
+    m_video_widget->setContrastBright(m_actionContrastBright && m_actionContrastBright->isChecked(),
+                                      1.2f,
+                                      0.1f);
+    // 镜像
+    m_video_widget->setMirror(m_actionMirro && m_actionMirro->isChecked());
+    // 其它特效可继续扩展
+}
+
+// 新增槽函数实现
+void MainWindowA::onShowMessage(const QString& message,
+                                const QString& windowTitle,
+                                const QString& styleSheet)
+{
+    QMessageBox msgBox;
+    msgBox.setText(message);
+    msgBox.setWindowTitle(windowTitle);
+    msgBox.setStyleSheet(styleSheet);
+    msgBox.show();
+    msgBox.move(frameGeometry().center() - msgBox.rect().center());
+    msgBox.setWindowFlags(msgBox.windowFlags() | Qt::Dialog);
+    msgBox.setModal(true);
+    msgBox.exec();
+}
+
+void MainWindowA::onRequestFullscreen(bool bFullscreen)
+{
+    if (bFullscreen) {
+        showFullScreen();
+    } else {
+        showNormal();
+        update_video_label();
+    }
+    hide_cursor(bFullscreen);
+}
+
+void MainWindowA::onRequestHideStatusBar(bool bHide)
+{
+    statusBar()->setVisible(!bHide);
+    auto sz_status = statusBar()->size();
+    if (isFullScreen()) {
+        centralWidget()->resize(centralWidget()->size());
+    } else {
+        auto sz = size();
+        if (statusBar()->isVisible()) {
+            sz += QSize(0, sz_status.height());
+        } else {
+            sz -= QSize(0, sz_status.height());
+        }
+        resize(sz);
+    }
+}

+ 259 - 0
AvPlayer2/mainwindowa.h

@@ -0,0 +1,259 @@
+#pragma once
+
+#include <QActionGroup>
+#include <QElapsedTimer>
+#include <QFileDialog>
+#include <QMainWindow>
+#include <QMessageBox>
+#include <QMimeData>
+#include <QMouseEvent>
+#include <QMutex>
+#include <QSettings>
+#include <QSizePolicy>
+#include <QThread>
+#include <QTimer>
+
+#include "AVPlayer2/playercontroller.h"
+#include "app_settings.h"
+#include "audio_effect_gl.h"
+#include "network_url_dlg.h"
+#include "play_control_window.h"
+#include "playlist_window.h"
+// #include "read_thread.h"
+// #include "start_play_thread.h"
+// #include "stopplay_waiting_thread.h"
+// #include "subtitle_decode_thread.h"
+// #include "video_decode_thread.h"
+// #include "audio_decode_thread.h"
+// #include "audio_play_thread.h"
+// #include "video_play_thread.h"
+// #include "video_state.h"
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class MainWindowA;
+}
+QT_END_NAMESPACE
+
+class OpenGLVideoWidget;
+
+#define MaxRecentFiles 20 // maximum recent play files
+#define MaxSkinStlyes 20  // maximum sytyles
+#define MaxPlaylist 5     // maximum playlist numbers
+
+class MainWindowA : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindowA(QWidget* parent = Q_NULLPTR);
+    ~MainWindowA();
+
+public:
+    float volume_settings(bool set = true, float vol = 0);
+    void play_mute(bool mute);
+    void play_seek();
+    void play_start_seek();
+
+    void set_volume(int volume);
+    void set_play_speed();
+    void show_msg_dlg(const QString& message,
+                      const QString& windowTitle = "Warning",
+                      const QString& styleSheet = "");
+    QString get_playingfile() const;
+    void show_fullscreen(bool bFullscreen = true);
+
+    PlayerController* m_playerController;
+public slots:
+    void frameReady(AVFrame*);
+    void subtitle_ready(const QString&);
+    void audio_data(const AudioData& data);
+    void open_recentFile();
+    void clear_recentfiles();
+    void update_play_time();
+    void play_failed(const QString& file);
+    void playlist_file_saved(const QString& file);
+
+signals:
+    // 删除所有与底层线程相关的信号
+
+private:
+    void keyPressEvent(QKeyEvent* event) override;
+    void resizeEvent(QResizeEvent* event) override;
+    void moveEvent(QMoveEvent* event) override;
+    bool eventFilter(QObject* obj, QEvent* event) override;
+    void dropEvent(QDropEvent* event) override;
+    void dragEnterEvent(QDragEnterEvent* event) override;
+
+private slots:
+    void on_actionOpen_triggered();
+    void on_actionQuit_triggered();
+    void on_actionHelp_triggered();
+    void on_actionAbout_triggered();
+    void on_actionStop_triggered();
+    void on_actionFullscreen_triggered();
+    void on_actionHide_Play_Ctronl_triggered();
+    void on_actionAspect_Ratio_triggered();
+
+    void on_actionLoop_Play_triggered();
+    void on_actionMedia_Info_triggered();
+    void on_actionKeyboard_Usage_triggered();
+    void on_actionPlayList_triggered();
+    void on_actionOpenNetworkUrl_triggered();
+    void on_actionOriginalSize_triggered();
+
+    void onCvActionToggled();
+    void onShowMessage(const QString& message,
+                       const QString& windowTitle,
+                       const QString& styleSheet);
+    void onRequestFullscreen(bool bFullscreen);
+    void onRequestHideStatusBar(bool bHide);
+
+private:
+    void print_decodeContext(const AVCodecContext* pVideo, bool bVideo = true) const;
+    void about_media_info();
+    void center_window(QRect screen_rec);
+    bool label_fullscreen();
+    void hide_statusbar(bool bHide = true);
+    void hide_menubar(bool bHide = true);
+    void check_hide_menubar(const QPoint& pt);
+    void check_hide_play_control();
+    void auto_hide_play_control(bool bHide = true);
+    void displayStatusMessage(const QString& message);
+    void hide_play_control(bool bHide = true);
+    void set_paly_control_wnd(bool set = true);
+    void update_paly_control_volume();
+    void update_paly_control_status();
+    void update_paly_control_muted();
+    void print_size() const;
+    void print_screen() const;
+    void create_style_menu();
+    inline QScreen* screen() const;
+    QRect screen_rect() const;
+    qreal screen_scale() const;
+    QSize display_video_size(AVCodecContext* pCtxVideo) const;
+    inline OpenGLVideoWidget* get_video_widget() const { return m_video_widget.get(); }
+    inline PlayControlWnd* get_play_control() const { return m_play_control_wnd.get(); }
+    inline QObject* get_object(const QString& name) const { return findChild<QObject*>(name); }
+    void create_play_control();
+    void update_play_control();
+    void set_volume_updown(bool bUp = true, float unit = 0.05);
+    void create_recentfiles_menu();
+    void set_current_file(const QString& fileName);
+    void remove_recentfiles(const QString& fileName);
+    void update_recentfile_actions();
+    QString stripped_name(const QString& fullFileName) const;
+    void save_settings();
+    void read_settings();
+    QString get_selected_style() const;
+    void set_style_action(const QString& style);
+    void clear_subtitle_str();
+    void set_subtitle(const QString& str);
+    void create_cv_action_group();
+    void play_speed_adjust(bool up = true);
+    void create_video_label();
+    void update_video_label();
+    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 video_seek(double pos = 0, double incr = 0);
+    void update_menus();
+    void enable_menus(bool enable = true);
+    void enable_v_menus(bool enable = true);
+    void enable_a_menus(bool enable = true);
+
+    bool get_avisual_format(BarHelper::VisualFormat& fmt) const;
+    void popup_audio_effect();
+    void set_audio_effect_format(const BarHelper::VisualFormat& fmt);
+    void create_playlist_wnd();
+    void add_to_playlist(const QString& file);
+    void show_playlist(bool show = true);
+    void playlist_hiden();
+    void hide_cursor(bool bHide = true);
+    bool cursor_in_window(QWidget* pWnd);
+
+    void create_savedPlaylists_menu();
+    void remove_playlist_file(const QString& fileName);
+    void update_savedPlaylists_actions();
+    bool read_playlist(const QString& playlist_file, QStringList& files) const;
+
+public slots:
+    void clear_savedPlaylists();
+    void open_playlist();
+
+private:
+    // 新增:菜单栏、菜单、Action、状态栏、中央控件等成员变量
+    QMenuBar* m_menuBar = nullptr;
+    QMenu* m_menuFile = nullptr;
+    QMenu* m_menuRecentFiles = nullptr;
+    QMenu* m_menuSavedPlaylist = nullptr;
+    QMenu* m_menuCV = nullptr;
+    QMenu* m_menuStyle = nullptr;
+    QMenu* m_menuAudioVisualize = nullptr;
+    QMenu* m_menuHelp = nullptr;
+
+    QAction* m_actionOpen = nullptr;
+    QAction* m_actionQuit = nullptr;
+    QAction* m_actionHelp = nullptr;
+    QAction* m_actionAbout = nullptr;
+    QAction* m_actionAbout_QT = nullptr;
+    QAction* m_actionStop = nullptr;
+    QAction* m_actionFullscreen = nullptr;
+    QAction* m_actionHide_Play_Ctronl = nullptr;
+    QAction* m_actionAspect_Ratio = nullptr;
+    QAction* m_actionLoop_Play = nullptr;
+    QAction* m_actionMedia_Info = nullptr;
+    QAction* m_actionKeyboard_Usage = nullptr;
+    QAction* m_actionPlayList = nullptr;
+    QAction* m_actionOpenNetworkUrl = nullptr;
+    QAction* m_actionOriginalSize = nullptr;
+    QAction* m_actionHardware_decode = nullptr;
+    QAction* m_actionSampling = nullptr;
+    QAction* m_actionFrequency = nullptr;
+    QAction* m_actionLine = nullptr;
+    QAction* m_actionBar = nullptr;
+    QAction* m_actionPie = nullptr;
+    QAction* m_actionRemoveCV = nullptr;
+    QAction* m_actionRotate = nullptr;
+    QAction* m_actionRepeat = nullptr;
+    QAction* m_actionEqualizeHist = nullptr;
+    QAction* m_actionThreshold = nullptr;
+    QAction* m_actionThreshold_Adaptive = nullptr;
+    QAction* m_actionReverse = nullptr;
+    QAction* m_actionColorReduce = nullptr;
+    QAction* m_actionGamma = nullptr;
+    QAction* m_actionContrastBright = nullptr;
+    QAction* m_actionCanny = nullptr;
+    QAction* m_actionBlur = nullptr;
+    QAction* m_actionSobel = nullptr;
+    QAction* m_actionLaplacian = nullptr;
+    QAction* m_actionScharr = nullptr;
+    QAction* m_actionPrewitt = nullptr;
+    QAction* m_actionTest_CV = nullptr;
+    QAction* m_actionGrayscale = nullptr;
+    QAction* m_actionMirro = nullptr;
+    QAction* m_actionTransform = nullptr;
+
+    QStatusBar* m_statusBar = nullptr;
+    QWidget* m_centralWidget = nullptr;
+
+    QString m_videoFile;
+    QTimer m_timer; // mouse moving checking timer
+    AppSettings m_settings;
+    QString m_subtitle;
+
+    std::unique_ptr<OpenGLVideoWidget> m_video_widget;
+
+    std::unique_ptr<PlayControlWnd> m_play_control_wnd;
+    std::unique_ptr<AudioEffectGL> m_audio_effect_wnd;
+    std::unique_ptr<PlayListWnd> m_playListWnd;
+
+    std::unique_ptr<QAction> m_recentFileActs[MaxRecentFiles];
+    std::unique_ptr<QAction> m_recentClear;
+    QActionGroup* m_CvActsGroup; // cv menus group
+    QActionGroup* m_AVisualTypeActsGroup;
+    QActionGroup* m_AVisualGrapicTypeActsGroup;
+    std::unique_ptr<QAction> m_savedPlaylists[MaxPlaylist];
+    std::unique_ptr<QAction> m_PlaylistsClear;
+};

+ 39 - 0
AvPlayer2/network_url_dlg.cpp

@@ -0,0 +1,39 @@
+// ***********************************************************/
+// network_url_dlg.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// network stream media open dialog
+// ***********************************************************/
+
+#include <QMessageBox>
+#include "network_url_dlg.h"
+
+/* some public test stream video urls
+ * http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
+ * http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4
+ * http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4
+ * http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4
+ * http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4
+ * rtsp://rtsp.stream/pattern
+ * rtsp://rtsp.stream/movie
+ */
+
+NetworkUrlDlg::NetworkUrlDlg(QWidget* parent)
+    : QDialog(parent), ui(std::make_unique<Ui::NetworkUrlDlg>())
+{
+    ui->setupUi(this);
+    setLayout(ui->gridLayout);
+
+    auto flags = windowFlags();
+    flags |= Qt::WindowStaysOnTopHint;
+    flags &= (~Qt::WindowMinMaxButtonsHint);
+    flags &= (~Qt::WindowContextHelpButtonHint);
+
+    setWindowFlags(flags);
+}
+
+QString NetworkUrlDlg::get_url() const
+{
+    return ui->lineEdit->text();
+}

+ 27 - 0
AvPlayer2/network_url_dlg.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include <QDialog>
+#include <memory>
+#include "ui_network_url_dlg.h"
+
+QT_BEGIN_NAMESPACE
+namespace Ui
+{
+class NetworkUrlDlg;
+};
+QT_END_NAMESPACE
+
+class NetworkUrlDlg : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit NetworkUrlDlg(QWidget* parent = Q_NULLPTR);
+    ~NetworkUrlDlg(){};
+
+public:
+    QString get_url() const;
+
+private:
+    std::unique_ptr<Ui::NetworkUrlDlg> ui;
+};

+ 204 - 0
AvPlayer2/network_url_dlg.ui

@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NetworkUrlDlg</class>
+ <widget class="QDialog" name="NetworkUrlDlg">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>100</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>100</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>500</width>
+    <height>100</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Open Network Media</string>
+  </property>
+  <property name="locale">
+   <locale language="English" country="NewZealand"/>
+  </property>
+  <widget class="QWidget" name="gridLayoutWidget">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>301</width>
+     <height>71</height>
+    </rect>
+   </property>
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="2" column="1">
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="btn_Ok">
+        <property name="text">
+         <string>Ok</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="btn_Cancel">
+        <property name="text">
+         <string>Cancel</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </item>
+    <item row="1" column="1">
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <spacer name="horizontalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Preferred</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>10</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="minimumSize">
+         <size>
+          <width>68</width>
+          <height>0</height>
+         </size>
+        </property>
+        <property name="text">
+         <string>Stream media url:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit"/>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_4">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Preferred</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>10</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="1">
+     <spacer name="verticalSpacer">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::Fixed</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>10</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>btn_Ok</sender>
+   <signal>clicked()</signal>
+   <receiver>NetworkUrlDlg</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>120</x>
+     <y>58</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>249</x>
+     <y>50</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>btn_Cancel</sender>
+   <signal>clicked()</signal>
+   <receiver>NetworkUrlDlg</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>180</x>
+     <y>58</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>249</x>
+     <y>50</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 1304 - 0
AvPlayer2/packets_sync.cpp

@@ -0,0 +1,1304 @@
+// ***********************************************************/
+// packets_sync.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// packets A/V synchronization struct and operations definition.
+// This code is referenced from ffplay.c in Ffmpeg library.
+// ***********************************************************/
+
+#include "packets_sync.h"
+
+int framedrop = -1;
+// static int decoder_reorder_pts = -1;
+// static int display_disable = 1;
+// static int64_t audio_callback_time;
+
+int packet_queue_init(PacketQueue* q)
+{
+    memset(q, 0, sizeof(PacketQueue));
+    q->pkt_list = av_fifo_alloc2(1, sizeof(MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
+    if (!q->pkt_list)
+        return AVERROR(ENOMEM);
+    q->mutex = new QMutex();
+    if (!q->mutex)
+    {
+        av_log(nullptr, AV_LOG_FATAL, "new QMutex() error.\n");
+        return AVERROR(ENOMEM);
+    }
+    q->cond = new QWaitCondition();
+    if (!q->cond)
+    {
+        av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() error.\n");
+        return AVERROR(ENOMEM);
+    }
+    q->abort_request = 1;
+    return 0;
+}
+
+void packet_queue_destroy(PacketQueue* q)
+{
+    packet_queue_flush(q);
+    av_fifo_freep2(&q->pkt_list);
+    delete q->mutex;
+    delete q->cond;
+}
+
+void packet_queue_flush(PacketQueue* q)
+{
+    MyAVPacketList pkt1;
+
+    q->mutex->lock();
+    while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
+        av_packet_free(&pkt1.pkt);
+    q->nb_packets = 0;
+    q->size = 0;
+    q->duration = 0;
+    q->serial++;
+    q->mutex->unlock();
+}
+
+void packet_queue_start(PacketQueue* q)
+{
+    q->mutex->lock();
+    q->abort_request = 0;
+    q->serial++;
+    q->mutex->unlock();
+}
+
+void packet_queue_abort(PacketQueue* q)
+{
+    q->mutex->lock();
+    q->abort_request = 1;
+    q->cond->wakeAll();
+    q->mutex->unlock();
+}
+
+int packet_queue_get(PacketQueue* q, AVPacket* pkt, int block, int* serial)
+{
+#if PRINT_PACKETQUEUE_INFO
+    // packet_queue_print(q, pkt, "packet_queue_get");
+#endif
+
+    MyAVPacketList pkt1;
+    int ret = 0;
+
+    q->mutex->lock();
+
+    for (;;)
+    {
+        if (q->abort_request)
+        {
+            ret = -1;
+            break;
+        }
+
+        if (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0)
+        {
+            q->nb_packets--;
+            q->size -= pkt1.pkt->size + sizeof(pkt1);
+            q->duration -= pkt1.pkt->duration;
+            av_packet_move_ref(pkt, pkt1.pkt);
+            if (serial)
+                *serial = pkt1.serial;
+            av_packet_free(&pkt1.pkt);
+            ret = 1;
+            break;
+        }
+        else if (!block)
+        {
+            ret = 0;
+            break;
+        }
+        else
+        {
+            q->cond->wait(q->mutex);
+        }
+    }
+    q->mutex->unlock();
+    return ret;
+}
+
+void packet_queue_print(const PacketQueue* q, const AVPacket* pkt, const QString& prefix)
+{
+    qDebug("[%s]Queue:[%p](nb_packets:%d, size:%d, dur:%d, serial:%d), "
+           "pkt(pts:%lld,dts:%lld,size:%d,s_index:%d,dur:%lld,pos:%lld).",
+           qUtf8Printable(prefix), q, q->nb_packets, q->size, q->duration,
+           q->serial, pkt->pts, pkt->dts, pkt->size, pkt->stream_index,
+           pkt->duration, pkt->pos);
+}
+
+int packet_queue_put(PacketQueue* q, AVPacket* pkt)
+{
+    AVPacket* pkt1;
+    int ret = -1;
+
+    pkt1 = av_packet_alloc();
+    if (!pkt1)
+    {
+        av_packet_unref(pkt);
+        return ret;
+    }
+    av_packet_move_ref(pkt1, pkt);
+
+    q->mutex->lock();
+    ret = packet_queue_put_private(q, pkt1);
+    q->mutex->unlock();
+
+    if (ret < 0)
+        av_packet_free(&pkt1);
+
+#if PRINT_PACKETQUEUE_INFO
+        // packet_queue_print(q, pkt, "packet_queue_put");
+#endif
+    return ret;
+}
+
+int packet_queue_put_nullpacket(PacketQueue* q, AVPacket* pkt, int stream_index)
+{
+    pkt->stream_index = stream_index;
+    return packet_queue_put(q, pkt);
+}
+
+int packet_queue_put_private(PacketQueue* q, AVPacket* pkt)
+{
+    MyAVPacketList pkt1;
+    int ret;
+
+    if (q->abort_request)
+        return -1;
+
+    pkt1.pkt = pkt;
+    pkt1.serial = q->serial;
+
+    ret = av_fifo_write(q->pkt_list, &pkt1, 1);
+    if (ret < 0)
+        return ret;
+    q->nb_packets++;
+    q->size += pkt1.pkt->size + sizeof(pkt1);
+    q->duration += pkt1.pkt->duration;
+    /* XXX: should duplicate packet data in DV case */
+    q->cond->wakeAll();
+    return 0;
+}
+
+int frame_queue_init(FrameQueue* f, PacketQueue* pktq, int max_size, int keep_last)
+{
+    int i;
+    memset(f, 0, sizeof(FrameQueue));
+    if (!(f->mutex = new QMutex()))
+    {
+        av_log(nullptr, AV_LOG_FATAL, "new QMutex() error!\n");
+        return AVERROR(ENOMEM);
+    }
+    if (!(f->cond = new QWaitCondition()))
+    {
+        av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() error\n");
+        return AVERROR(ENOMEM);
+    }
+    f->pktq = pktq;
+    f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
+    f->keep_last = !!keep_last;
+    for (i = 0; i < f->max_size; i++)
+        if (!(f->queue[i].frame = av_frame_alloc()))
+            return AVERROR(ENOMEM);
+    return 0;
+}
+
+void frame_queue_destory(FrameQueue* f)
+{
+    int i;
+    for (i = 0; i < f->max_size; i++)
+    {
+        Frame* vp = &f->queue[i];
+        frame_queue_unref_item(vp);
+        av_frame_free(&vp->frame);
+    }
+
+    delete f->mutex;
+    delete f->cond;
+}
+
+void frame_queue_unref_item(Frame* vp)
+{
+    av_frame_unref(vp->frame); // frame reference number reduce 1
+    avsubtitle_free(&vp->sub); // sub
+}
+
+void frame_queue_signal(FrameQueue* f)
+{
+    f->mutex->lock();
+    f->cond->wakeAll();
+    f->mutex->unlock();
+}
+
+Frame* frame_queue_peek(FrameQueue* f)
+{
+    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
+}
+
+Frame* frame_queue_peek_next(FrameQueue* f)
+{
+    return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
+}
+
+Frame* frame_queue_peek_last(FrameQueue* f) { return &f->queue[f->rindex]; }
+
+Frame* frame_queue_peek_writable(FrameQueue* f)
+{
+    /* wait until we have space to put a new frame */
+    f->mutex->lock();
+    while (f->size >= f->max_size && !f->pktq->abort_request)
+    {
+        f->cond->wait(f->mutex);
+    }
+    f->mutex->unlock();
+
+    if (f->pktq->abort_request)
+        return nullptr;
+
+    return &f->queue[f->windex];
+}
+
+Frame* frame_queue_peek_readable(FrameQueue* f)
+{
+    /* wait until we have a readable a new frame */
+    f->mutex->lock();
+    while (f->size - f->rindex_shown <= 0 && !f->pktq->abort_request)
+    {
+        f->cond->wait(f->mutex);
+    }
+    f->mutex->unlock();
+
+    if (f->pktq->abort_request)
+        return nullptr;
+
+    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
+}
+
+void frame_queue_push(FrameQueue* f)
+{
+    if (++f->windex == f->max_size)
+        f->windex = 0;
+    f->mutex->lock();
+    f->size++;
+    f->cond->wakeAll();
+    f->mutex->unlock();
+}
+
+void frame_queue_next(FrameQueue* f)
+{
+    if (f->keep_last && !f->rindex_shown)
+    {
+        f->rindex_shown = 1;
+        return;
+    }
+    frame_queue_unref_item(&f->queue[f->rindex]);
+    if (++f->rindex == f->max_size)
+        f->rindex = 0;
+    f->mutex->lock();
+    f->size--;
+    f->cond->wakeAll();
+    f->mutex->unlock();
+}
+
+/* return the number of undisplayed frames in the queue */
+int frame_queue_nb_remaining(FrameQueue* f)
+{
+    return f->size - f->rindex_shown;
+}
+
+/* return last shown position */
+int64_t frame_queue_last_pos(FrameQueue* f)
+{
+    Frame* fp = &f->queue[f->rindex];
+    if (f->rindex_shown && fp->serial == f->pktq->serial)
+        return fp->pos;
+    else
+        return -1;
+}
+
+int queue_picture(VideoState* is, AVFrame* src_frame, double pts, double duration, int64_t pos, int serial)
+{
+#if PRINT_PACKETQUEUE_INFO
+    // int64 lld, double lf
+    qDebug("queue picture, w:%d, h:%d, nb:%d, ft:%d(%s), kf:%d, pic_t:%d(%c), "
+           "pts:%lf, duration:%lf, pos:%lld, serial:%d",
+           src_frame->width, src_frame->height, src_frame->nb_samples,
+           src_frame->format,
+           av_get_sample_fmt_name(AVSampleFormat(src_frame->format)),
+           src_frame->key_frame, src_frame->pict_type,
+           av_get_picture_type_char(src_frame->pict_type), pts, duration, pos,
+           serial);
+#endif
+
+    Frame* vp;
+
+    if (!(vp = frame_queue_peek_writable(&is->pictq)))
+        return -1;
+
+    vp->sar = src_frame->sample_aspect_ratio;
+    vp->uploaded = 0;
+
+    vp->width = src_frame->width;
+    vp->height = src_frame->height;
+    vp->format = src_frame->format;
+
+    vp->pts = pts;
+    vp->duration = duration;
+    vp->pos = pos;
+    vp->serial = serial;
+
+    av_frame_move_ref(vp->frame, src_frame);
+    frame_queue_push(&is->pictq);
+    return 0;
+}
+
+int get_video_frame(VideoState* is, AVFrame* frame)
+{
+    int got_picture = -1;
+
+    if ((got_picture = decoder_decode_frame(&is->viddec, frame, nullptr)) < 0)
+        return -1;
+
+    if (got_picture)
+    {
+        double dpts = NAN;
+
+        if (frame->pts != AV_NOPTS_VALUE)
+            dpts = av_q2d(is->video_st->time_base) * frame->pts;
+
+        frame->sample_aspect_ratio =
+            av_guess_sample_aspect_ratio(is->ic, is->video_st, frame);
+
+        if (framedrop > 0 ||
+            (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER))
+        {
+            if (frame->pts != AV_NOPTS_VALUE)
+            {
+                double diff = dpts - get_master_clock(is);
+                if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
+                    diff - is->frame_last_filter_delay < 0 &&
+                    is->viddec.pkt_serial == is->vidclk.serial &&
+                    is->videoq.nb_packets)
+                {
+                    is->frame_drops_early++;
+                    av_frame_unref(frame);
+                    got_picture = 0;
+                }
+            }
+        }
+    }
+
+    return got_picture;
+}
+
+int decoder_init(Decoder* d, AVCodecContext* avctx, PacketQueue* queue, QWaitCondition* empty_queue_cond)
+{
+    memset(d, 0, sizeof(Decoder));
+    d->pkt = av_packet_alloc();
+    if (!d->pkt)
+        return AVERROR(ENOMEM);
+    d->avctx = avctx;
+    d->queue = queue;
+    d->empty_queue_cond = empty_queue_cond;
+    d->start_pts = AV_NOPTS_VALUE;
+    d->pkt_serial = -1;
+    return 0;
+}
+
+int decoder_start(Decoder* d, void* thread, const char* thread_name)
+{
+    packet_queue_start(d->queue);
+    d->decoder_tid = thread;
+    d->decoder_name = av_strdup(thread_name);
+    return 0;
+}
+
+void decoder_destroy(Decoder* d)
+{
+    av_packet_free(&d->pkt);
+    avcodec_free_context(&d->avctx);
+    av_free(d->decoder_name);
+}
+
+void decoder_abort(Decoder* d, FrameQueue* fq)
+{
+    packet_queue_abort(d->queue);
+    frame_queue_signal(fq);
+    // SDL_WaitThread(d->decoder_tid, nullptr);
+    ((QThread*)(d->decoder_tid))->wait();
+    d->decoder_tid = nullptr;
+    packet_queue_flush(d->queue);
+}
+
+int decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub)
+{
+    int ret = AVERROR(EAGAIN);
+    int decoder_reorder_pts = -1;
+    for (;;)
+    {
+        if (d->queue->serial == d->pkt_serial)
+        {
+            do
+            {
+                if (d->queue->abort_request)
+                    return -1;
+
+                switch (d->avctx->codec_type)
+                {
+                    case AVMEDIA_TYPE_VIDEO:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        if (ret >= 0)
+                        {
+                            if (decoder_reorder_pts == -1)
+                            {
+                                frame->pts = frame->best_effort_timestamp;
+                            }
+                            else if (!decoder_reorder_pts)
+                            {
+                                frame->pts = frame->pkt_dts;
+                            }
+                        }
+                        break;
+                    case AVMEDIA_TYPE_AUDIO:
+                        ret = avcodec_receive_frame(d->avctx, frame);
+                        if (ret >= 0)
+                        {
+                            AVRational tb = AVRational{1, frame->sample_rate};
+                            if (frame->pts != AV_NOPTS_VALUE)
+                                frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
+                            else if (d->next_pts != AV_NOPTS_VALUE)
+                                frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
+                            if (frame->pts != AV_NOPTS_VALUE)
+                            {
+                                d->next_pts = frame->pts + frame->nb_samples;
+                                d->next_pts_tb = tb;
+                            }
+                        }
+                        break;
+                }
+                if (ret == AVERROR_EOF)
+                {
+                    d->finished = d->pkt_serial;
+                    avcodec_flush_buffers(d->avctx);
+                    return 0;
+                }
+                if (ret >= 0)
+                    return 1;
+            } while (ret != AVERROR(EAGAIN));
+        }
+
+        do
+        {
+            if (d->queue->nb_packets == 0)
+                d->empty_queue_cond->wakeAll();
+
+            if (d->packet_pending)
+            {
+                d->packet_pending = 0;
+            }
+            else
+            {
+                int old_serial = d->pkt_serial;
+                if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0)
+                    return -1;
+                if (old_serial != d->pkt_serial)
+                {
+                    avcodec_flush_buffers(d->avctx);
+                    d->finished = 0;
+                    d->next_pts = d->start_pts;
+                    d->next_pts_tb = d->start_pts_tb;
+                }
+            }
+            if (d->queue->serial == d->pkt_serial)
+                break;
+            av_packet_unref(d->pkt);
+        } while (1);
+
+        if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        {
+            int got_frame = 0;
+            ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, d->pkt);
+            if (ret < 0)
+            {
+                ret = AVERROR(EAGAIN);
+            }
+            else
+            {
+                if (got_frame && !d->pkt->data)
+                {
+                    d->packet_pending = 1;
+                }
+                ret = got_frame ? 0 : (d->pkt->data ? AVERROR(EAGAIN) : AVERROR_EOF);
+            }
+            av_packet_unref(d->pkt);
+        }
+        else
+        {
+            if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN))
+            {
+                av_log(d->avctx, AV_LOG_ERROR,
+                       "Receive_frame and send_packet both returned EAGAIN, which is "
+                       "an API violation.\n");
+                d->packet_pending = 1;
+            }
+            else
+            {
+                av_packet_unref(d->pkt);
+            }
+        }
+    }
+}
+
+void get_file_info(const char* filename, int64_t& duration)
+{
+    AVFormatContext* pFormatCtx = avformat_alloc_context();
+    if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) == 0)
+    {
+        if (pFormatCtx->duration < 0)
+            avformat_find_stream_info(pFormatCtx, NULL);
+        duration = pFormatCtx->duration;
+    }
+    // etc
+    avformat_close_input(&pFormatCtx);
+    avformat_free_context(pFormatCtx);
+}
+
+void get_duration_time(const int64_t duration_us, int64_t& hours, int64_t& mins, int64_t& secs, int64_t& us)
+{
+    int64_t duration = duration_us + (duration_us <= INT64_MAX - 5000 ? 5000 : 0);
+    duration = duration < 0 ? 0 : duration;
+    secs = duration / AV_TIME_BASE;
+    us = duration % AV_TIME_BASE;
+    us = (100 * us) / AV_TIME_BASE;
+    mins = secs / 60;
+    secs %= 60;
+    hours = mins / 60;
+    mins %= 60;
+}
+
+double get_clock(Clock* c)
+{
+    if (*c->queue_serial != c->serial)
+        return NAN;
+    if (c->paused)
+    {
+        return c->pts;
+    }
+    else
+    {
+        double time = av_gettime_relative() / 1000000.0;
+        return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
+    }
+}
+
+void set_clock_at(Clock* c, double pts, int serial, double time)
+{
+    c->pts = pts;
+    c->last_updated = time;
+    c->pts_drift = c->pts - time;
+    c->serial = serial;
+}
+
+void set_clock(Clock* c, double pts, int serial)
+{
+    double time = av_gettime_relative() / 1000000.0;
+    set_clock_at(c, pts, serial, time);
+}
+
+void set_clock_speed(Clock* c, double speed)
+{
+    set_clock(c, get_clock(c), c->serial);
+    c->speed = speed;
+}
+
+void init_clock(Clock* c, int* queue_serial)
+{
+    c->speed = 1.0;
+    c->paused = 0;
+    c->queue_serial = queue_serial;
+    set_clock(c, NAN, -1);
+}
+
+void sync_clock_to_slave(Clock* c, Clock* slave)
+{
+    double clock = get_clock(c);
+    double slave_clock = get_clock(slave);
+    if (!isnan(slave_clock) &&
+        (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
+        set_clock(c, slave_clock, slave->serial);
+}
+
+int stream_has_enough_packets(AVStream* st, int stream_id, PacketQueue* queue)
+{
+    return stream_id < 0 || queue->abort_request ||
+           (st->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
+           queue->nb_packets > MIN_FRAMES &&
+               (!queue->duration ||
+                av_q2d(st->time_base) * queue->duration > 1.0);
+}
+
+int is_realtime(AVFormatContext* s)
+{
+    if (!strcmp(s->iformat->name, "rtp") || !strcmp(s->iformat->name, "rtsp") ||
+        !strcmp(s->iformat->name, "sdp"))
+        return 1;
+
+    if (s->pb && (!strncmp(s->url, "rtp:", 4) || !strncmp(s->url, "udp:", 4)))
+        return 1;
+    return 0;
+}
+
+int get_master_sync_type(VideoState* is)
+{
+    if (is->av_sync_type == AV_SYNC_VIDEO_MASTER)
+    {
+        if (is->video_st)
+            return AV_SYNC_VIDEO_MASTER;
+        else
+            return AV_SYNC_AUDIO_MASTER;
+    }
+    else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER)
+    {
+        if (is->audio_st)
+            return AV_SYNC_AUDIO_MASTER;
+        else
+            return AV_SYNC_EXTERNAL_CLOCK;
+    }
+    else
+    {
+        return AV_SYNC_EXTERNAL_CLOCK;
+    }
+}
+
+/* get the current master clock value */
+double get_master_clock(VideoState* is)
+{
+    double val;
+
+    switch (get_master_sync_type(is))
+    {
+        case AV_SYNC_VIDEO_MASTER:
+            val = get_clock(&is->vidclk);
+            break;
+        case AV_SYNC_AUDIO_MASTER:
+            val = get_clock(&is->audclk);
+            break;
+        default:
+            val = get_clock(&is->extclk);
+            break;
+    }
+    return val;
+}
+
+void check_external_clock_speed(VideoState* is)
+{
+    if ((is->video_stream >= 0 &&
+         is->videoq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES) ||
+        (is->audio_stream >= 0 &&
+         is->audioq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES))
+    {
+        set_clock_speed(&is->extclk,
+                        FFMAX(EXTERNAL_CLOCK_SPEED_MIN,
+                              is->extclk.speed - EXTERNAL_CLOCK_SPEED_STEP));
+    }
+    else if ((is->video_stream < 0 ||
+              is->videoq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES) &&
+             (is->audio_stream < 0 ||
+              is->audioq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES))
+    {
+        set_clock_speed(&is->extclk,
+                        FFMIN(EXTERNAL_CLOCK_SPEED_MAX,
+                              is->extclk.speed + EXTERNAL_CLOCK_SPEED_STEP));
+    }
+    else
+    {
+        double speed = is->extclk.speed;
+        if (speed != 1.0)
+            set_clock_speed(&is->extclk, speed + EXTERNAL_CLOCK_SPEED_STEP *
+                                                     (1.0 - speed) /
+                                                     fabs(1.0 - speed));
+    }
+}
+
+/* seek in the stream */
+void stream_seek(VideoState* is, int64_t pos, int64_t rel, int seek_by_bytes)
+{
+    if (!is->seek_req)
+    {
+        is->seek_pos = pos;
+        is->seek_rel = rel;
+        is->seek_flags &= ~AVSEEK_FLAG_BYTE;
+        if (seek_by_bytes)
+            is->seek_flags |= AVSEEK_FLAG_BYTE;
+        is->seek_req = 1;
+        // SDL_CondSignal(is->continue_read_thread);
+        is->continue_read_thread->wakeAll();
+    }
+}
+
+/* pause or resume the video */
+// void stream_toggle_pause(VideoState* is, bool pause)
+//{
+//	if (is->paused) {
+//		is->frame_timer += av_gettime_relative() / 1000000.0 -
+//is->vidclk.last_updated; 		if (is->read_pause_return != AVERROR(ENOSYS)) {
+//			is->vidclk.paused = 0;
+//		}
+//		set_clock(&is->vidclk, get_clock(&is->vidclk),
+//is->vidclk.serial);
+//	}
+//	set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
+//	is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused =
+//pause; // !is->paused; 	is->step = 0;
+//}
+
+void toggle_pause(VideoState* is, bool pause)
+{
+    if (is->paused)
+    {
+        is->frame_timer +=
+            av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
+        if (is->read_pause_return != AVERROR(ENOSYS))
+        {
+            is->vidclk.paused = 0;
+        }
+        set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
+    }
+    set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
+    is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused =
+        pause; // !is->paused;
+    is->step = 0;
+}
+
+void toggle_mute(VideoState* is, bool mute)
+{
+    bool muted = !!is->muted;
+    if (muted != mute)
+    {
+        is->muted = mute;
+    }
+}
+
+void update_volume(VideoState* is, int sign, double step)
+{
+    double volume_level =
+        is->audio_volume
+            ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10))
+            : -1000.0;
+    int new_volume =
+        lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
+    is->audio_volume = av_clip(
+        is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume,
+        0, SDL_MIX_MAXVOLUME);
+}
+
+void step_to_next_frame(VideoState* is)
+{
+    /* if the stream is paused unpause it, then step */
+    if (is->paused)
+        toggle_pause(is, !is->paused);
+    is->step = 1;
+}
+
+double compute_target_delay(double delay, VideoState* is)
+{
+    double sync_threshold, diff = 0;
+
+    /* update delay to follow master synchronisation source */
+    if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)
+    {
+        /* if video is slave, we try to correct big delays by
+       duplicating or deleting a frame */
+        diff = get_clock(&is->vidclk) - get_master_clock(is);
+
+        /* skip or repeat frame. We take into account the
+       delay to compute the threshold. I still don't know
+       if it is the best guess */
+        sync_threshold =
+            FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
+        if (!isnan(diff) && fabs(diff) < is->max_frame_duration)
+        {
+            if (diff <= -sync_threshold)
+                delay = FFMAX(0, delay + diff);
+            else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
+                delay = delay + diff;
+            else if (diff >= sync_threshold)
+                delay = 2 * delay;
+        }
+    }
+
+    av_log(nullptr, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", delay, -diff);
+    return delay;
+}
+
+double vp_duration(VideoState* is, Frame* vp, Frame* nextvp)
+{
+    if (vp->serial == nextvp->serial)
+    {
+        double duration = nextvp->pts - vp->pts;
+        if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
+            return vp->duration;
+        else
+            return duration;
+    }
+
+    return 0.0;
+}
+
+void update_video_pts(VideoState* is, double pts, int64_t pos, int serial)
+{
+    /* update current video pts */
+    set_clock(&is->vidclk, pts, serial);
+    sync_clock_to_slave(&is->extclk, &is->vidclk);
+}
+
+#if PRINT_PACKETQUEUE_INFO
+void print_state_info(VideoState* is)
+{
+    if (is)
+    {
+        PacketQueue* pPacket = &is->videoq;
+        qDebug("[VideoState] V PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
+               "abort:%d, serial:%d)",
+               pPacket, pPacket->nb_packets, pPacket->size, pPacket->duration,
+               pPacket->abort_request, pPacket->serial);
+
+        pPacket = &is->audioq;
+        qDebug("[VideoState] A PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
+               "abort:%d, serial:%d)",
+               pPacket, pPacket->nb_packets, pPacket->size, pPacket->duration,
+               pPacket->abort_request, pPacket->serial);
+
+        pPacket = &is->subtitleq;
+        qDebug("[VideoState] S PacketQueue[%p](nb_packets:%d,size:%d,dur:%lld, "
+               "abort:%d, serial:%d)",
+               pPacket, pPacket->nb_packets, pPacket->size, pPacket->duration,
+               pPacket->abort_request, pPacket->serial);
+
+        /*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);
+    qDebug("[VideoState]Clock(v:%p,a:%p,s:%p)",
+            &is->vidclk, &is->audclk, &is->extclk);*/
+    }
+}
+#endif
+
+#if USE_AVFILTER_AUDIO
+void set_audio_playspeed(VideoState* is, double value)
+{
+    if (value < 0 || value > 4)
+        return;
+
+    if (is->audio_speed == value)
+        return;
+
+    is->audio_speed = value;
+
+    const size_t len = 32;
+    if (!is->afilters)
+        is->afilters = (char*)av_malloc(len);
+
+    if (value <= 0.5)
+    {
+        snprintf(is->afilters, len, "atempo=0.5,");
+        char tmp[128];
+        snprintf(tmp, sizeof(tmp), "atempo=%lf", value / 0.5);
+
+        // strncat(is->afilters, tmp, len - strlen(is->afilters) - 1);
+        strncat_s(is->afilters, len, tmp, len - strlen(is->afilters) - 1);
+    }
+    else if (value <= 2.0)
+    {
+        snprintf(is->afilters, len, "atempo=%lf", value);
+    }
+    else
+    {
+        snprintf(is->afilters, len, "atempo=2.0,");
+        char tmp[128];
+        snprintf(tmp, sizeof(tmp), "atempo=%lf", value / 2.0);
+        // strncat(is->afilters, tmp, len - strlen(is->afilters) - 1);
+        strncat_s(is->afilters, len, tmp, len - strlen(is->afilters) - 1);
+    }
+
+    qDebug("changing audio filters to :%s", is->afilters);
+
+#if USE_AVFILTER_VIDEO
+    set_video_playspeed(is);
+#endif
+
+    is->audio_clock_old = is->audio_clock;
+    is->req_afilter_reconfigure = 1;
+}
+
+void set_video_playspeed(VideoState* is)
+{
+    double speed = is->audio_speed;
+
+    size_t len = 32;
+    if (!is->vfilters)
+        is->vfilters = (char*)av_malloc(len);
+
+    snprintf(is->vfilters, len, "setpts=%.4lf*PTS", 1.0 / speed);
+
+    is->req_vfilter_reconfigure = 1;
+
+    qDebug("changing video filters to :%s", is->vfilters);
+}
+
+int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1,
+                   enum AVSampleFormat fmt2, int64_t channel_count2)
+{
+    /* If channel count == 1, planar and non-planar formats are the same */
+    if (channel_count1 == 1 && channel_count2 == 1)
+        return av_get_packed_sample_fmt(fmt1) != av_get_packed_sample_fmt(fmt2);
+    else
+        return channel_count1 != channel_count2 || fmt1 != fmt2;
+}
+
+// int64_t get_valid_channel_layout(int64_t channel_layout, int channels)
+//{
+//	if (channel_layout && av_get_channel_layout_nb_channels(channel_layout)
+//== channels) 		return channel_layout; 	else 		return 0;
+//}
+
+int configure_filtergraph(AVFilterGraph* graph, const char* filtergraph, AVFilterContext* source_ctx, AVFilterContext* sink_ctx)
+{
+    int ret;
+    int nb_filters = graph->nb_filters;
+    AVFilterInOut *outputs = nullptr, *inputs = nullptr;
+
+    if (filtergraph)
+    {
+        outputs = avfilter_inout_alloc();
+        inputs = avfilter_inout_alloc();
+        if (!outputs || !inputs)
+        {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        outputs->name = av_strdup("in");
+        outputs->filter_ctx = source_ctx;
+        outputs->pad_idx = 0;
+        outputs->next = nullptr;
+
+        inputs->name = av_strdup("out");
+        inputs->filter_ctx = sink_ctx;
+        inputs->pad_idx = 0;
+        inputs->next = nullptr;
+
+        if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs,
+                                            nullptr)) < 0)
+            goto fail;
+    }
+    else
+    {
+        if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
+            goto fail;
+    }
+
+    /* Reorder the filters to ensure that inputs of the custom filters are merged
+   * first */
+    for (unsigned int i = 0; i < graph->nb_filters - nb_filters; i++)
+        FFSWAP(AVFilterContext*, graph->filters[i],
+               graph->filters[i + nb_filters]);
+
+    ret = avfilter_graph_config(graph, nullptr);
+fail:
+    avfilter_inout_free(&outputs);
+    avfilter_inout_free(&inputs);
+    return ret;
+}
+
+int configure_audio_filters(VideoState* is, const char* afilters, int force_output_format)
+{
+    static const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S16,
+                                                      AV_SAMPLE_FMT_NONE};
+    int sample_rates[2] = {0, -1};
+    // int64_t channel_layouts[2] = { 0, -1 };
+    // AVChannelLayout channel_layouts[2] = {};
+    // int channels[2] = { 0, -1 };
+    AVFilterContext *filt_asrc = nullptr, *filt_asink = nullptr;
+    // char aresample_swr_opts[512] = "";
+    // const AVDictionaryEntry* e = nullptr;
+    char asrc_args[256];
+    int ret;
+    AVBPrint bp;
+
+    avfilter_graph_free(&is->agraph);
+    if (!(is->agraph = avfilter_graph_alloc()))
+        return AVERROR(ENOMEM);
+    is->agraph->nb_threads = 0;
+
+    /*while ((e = av_dict_get(swr_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
+          av_strlcatf(aresample_swr_opts, sizeof(aresample_swr_opts), "%s=%s:",
+  e->key, e->value); if (strlen(aresample_swr_opts))
+          aresample_swr_opts[strlen(aresample_swr_opts) - 1] = '\0';
+  av_opt_set(is->agraph, "aresample_swr_opts", aresample_swr_opts, 0);*/
+
+    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
+    av_channel_layout_describe_bprint(&is->audio_filter_src.ch_layout, &bp);
+
+    snprintf(asrc_args, sizeof(asrc_args),
+             "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:channel_layout=%s",
+             is->audio_filter_src.freq,
+             av_get_sample_fmt_name(is->audio_filter_src.fmt), 1,
+             is->audio_filter_src.freq, bp.str);
+
+    ret = avfilter_graph_create_filter(
+        &filt_asrc, avfilter_get_by_name("abuffer"), "ffplay_abuffer", asrc_args,
+        nullptr, is->agraph);
+    if (ret < 0)
+        goto end;
+
+    ret = avfilter_graph_create_filter(
+        &filt_asink, avfilter_get_by_name("abuffersink"), "ffplay_abuffersink",
+        nullptr, nullptr, is->agraph);
+    if (ret < 0)
+        goto end;
+
+    if ((ret = av_opt_set_int_list(filt_asink, "sample_fmts", sample_fmts,
+                                   AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) <
+        0)
+        goto end;
+    if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 1,
+                              AV_OPT_SEARCH_CHILDREN)) < 0)
+        goto end;
+
+    if (force_output_format)
+    {
+        sample_rates[0] = is->audio_filter_src.freq;
+        // channel_layouts[0] = is->audio_filter_src.channel_layout;
+        // channels[0] = is->audio_filter_src.channels;
+        if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 0,
+                                  AV_OPT_SEARCH_CHILDREN)) < 0)
+            goto end;
+
+        if ((ret = av_opt_set(filt_asink, "ch_layouts", bp.str,
+                              AV_OPT_SEARCH_CHILDREN)) < 0)
+            goto end;
+        /*if ((ret = av_opt_set_int_list(filt_asink, "channel_layouts",
+       channel_layouts, -1, AV_OPT_SEARCH_CHILDREN)) < 0) goto end;*/
+        /*if ((ret = av_opt_set_int_list(filt_asink, "channel_counts", channels, -1,
+       AV_OPT_SEARCH_CHILDREN)) < 0) goto end;*/
+        if ((ret = av_opt_set_int_list(filt_asink, "sample_rates", sample_rates, -1,
+                                       AV_OPT_SEARCH_CHILDREN)) < 0)
+            goto end;
+    }
+
+    if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc,
+                                     filt_asink)) < 0)
+        goto end;
+
+    is->in_audio_filter = filt_asrc;
+    is->out_audio_filter = filt_asink;
+
+end:
+    if (ret < 0)
+        avfilter_graph_free(&is->agraph);
+    av_bprint_finalize(&bp, NULL);
+    return ret;
+}
+
+int configure_video_filters(AVFilterGraph* graph, VideoState* is, const char* vfilters, AVFrame* frame)
+{
+    enum AVPixelFormat pix_fmts[1]; // FF_ARRAY_ELEMS(sdl_texture_format_map)
+    // char sws_flags_str[512] = "";
+    char buffersrc_args[256];
+    int ret;
+    AVFilterContext *filt_src = nullptr, *filt_out = nullptr,
+                    *last_filter = nullptr;
+    AVCodecParameters* codecpar = is->video_st->codecpar;
+    AVRational fr = av_guess_frame_rate(is->ic, is->video_st, nullptr);
+    // const AVDictionaryEntry* e = nullptr;
+    int nb_pix_fmts = 0;
+
+    /*
+  int i, j;
+  for (i = 0; i < renderer_info.num_texture_formats; i++) {
+          for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map) - 1; j++) {
+                  if (renderer_info.texture_formats[i] ==
+  sdl_texture_format_map[j].texture_fmt) { pix_fmts[nb_pix_fmts++] =
+  sdl_texture_format_map[j].format; break;
+                  }
+          }
+  }*/
+    pix_fmts[nb_pix_fmts] = AV_PIX_FMT_NONE;
+
+    /*while ((e = av_dict_get(sws_dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
+          if (!strcmp(e->key, "sws_flags")) {
+                  av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:",
+  "flags", e->value);
+          }
+          else
+                  av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:",
+  e->key, e->value);
+  }
+  if (strlen(sws_flags_str))
+          sws_flags_str[strlen(sws_flags_str) - 1] = '\0';
+
+  graph->scale_sws_opts = av_strdup(sws_flags_str);*/
+
+    snprintf(buffersrc_args, sizeof(buffersrc_args),
+             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
+             frame->width, frame->height, frame->format,
+             is->video_st->time_base.num, is->video_st->time_base.den,
+             codecpar->sample_aspect_ratio.num,
+             FFMAX(codecpar->sample_aspect_ratio.den, 1));
+    if (fr.num && fr.den)
+        av_strlcatf(buffersrc_args, sizeof(buffersrc_args), ":frame_rate=%d/%d",
+                    fr.num, fr.den);
+
+    if ((ret = avfilter_graph_create_filter(
+             &filt_src, avfilter_get_by_name("buffer"), "ffplay_buffer",
+             buffersrc_args, nullptr, graph)) < 0)
+        goto fail;
+
+    ret = avfilter_graph_create_filter(
+        &filt_out, avfilter_get_by_name("buffersink"), "ffplay_buffersink",
+        nullptr, nullptr, graph);
+    if (ret < 0)
+        goto fail;
+
+    if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts,
+                                   AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
+        goto fail;
+
+    last_filter = filt_out;
+
+#if 0
+	/* Note: this macro adds a filter before the lastly added filter, so the
+	 * processing order of the filters is in reverse */
+#define INSERT_FILT(name, arg)                                                    \
+    do                                                                            \
+    {                                                                             \
+        AVFilterContext* filt_ctx;                                                \
+                                                                                  \
+        ret = avfilter_graph_create_filter(&filt_ctx, avfilter_get_by_name(name), \
+                                           "ffplay_" name, arg, nullptr, graph);  \
+        if (ret < 0)                                                              \
+            goto fail;                                                            \
+                                                                                  \
+        ret = avfilter_link(filt_ctx, 0, last_filter, 0);                         \
+        if (ret < 0)                                                              \
+            goto fail;                                                            \
+                                                                                  \
+        last_filter = filt_ctx;                                                   \
+    } while (0)
+
+	if (autorotate) {
+		int32_t* displaymatrix = (int32_t*)av_stream_get_side_data(is->video_st, AV_PKT_DATA_DISPLAYMATRIX, nullptr);
+		double theta = get_rotation(displaymatrix);
+
+		if (fabs(theta - 90) < 1.0) {
+			INSERT_FILT("transpose", "clock");
+		}
+		else if (fabs(theta - 180) < 1.0) {
+			INSERT_FILT("hflip", nullptr);
+			INSERT_FILT("vflip", nullptr);
+		}
+		else if (fabs(theta - 270) < 1.0) {
+			INSERT_FILT("transpose", "cclock");
+		}
+		else if (fabs(theta) > 1.0) {
+			char rotate_buf[64];
+			snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
+			INSERT_FILT("rotate", rotate_buf);
+		}
+	}
+#endif
+
+    if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
+        goto fail;
+
+    is->in_video_filter = filt_src;
+    is->out_video_filter = filt_out;
+
+fail:
+    return ret;
+}
+
+#if 0
+int audio_open(void* opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams* audio_hw_params)
+{
+	//SDL_AudioSpec wanted_spec, spec;
+	//const char* env;
+	static const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 };
+	static const int next_sample_rates[] = { 0, 44100, 48000, 96000, 192000 };
+	//int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
+
+	/*env = SDL_getenv("SDL_AUDIO_CHANNELS");
+	if (env) {
+		wanted_nb_channels = atoi(env);
+		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
+	}*/
+
+	if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
+		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
+		wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
+	}
+
+	wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
+	/*
+	wanted_spec.channels = wanted_nb_channels;
+	wanted_spec.freq = wanted_sample_rate;
+	if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
+		av_log(nullptr, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
+		return -1;
+	}
+	while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
+		next_sample_rate_idx--;
+	wanted_spec.format = AUDIO_S16SYS;
+	wanted_spec.silence = 0;
+	wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
+	wanted_spec.callback = sdl_audio_callback;
+	wanted_spec.userdata = opaque;
+	while (!(audio_dev = SDL_OpenAudioDevice(nullptr, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {
+		av_log(nullptr, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
+			wanted_spec.channels, wanted_spec.freq, SDL_GetError());
+		wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
+		if (!wanted_spec.channels) {
+			wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
+			wanted_spec.channels = wanted_nb_channels;
+			if (!wanted_spec.freq) {
+				av_log(nullptr, AV_LOG_ERROR,
+					"No more combinations to try, audio open failed\n");
+				return -1;
+			}
+		}
+		wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
+	}
+	if (spec.format != AUDIO_S16SYS) {
+		av_log(nullptr, AV_LOG_ERROR,
+			"SDL advised audio format %d is not supported!\n", spec.format);
+		return -1;
+	}
+	if (spec.channels != wanted_spec.channels) {
+		wanted_channel_layout = av_get_default_channel_layout(spec.channels);
+		if (!wanted_channel_layout) {
+			av_log(nullptr, AV_LOG_ERROR,
+				"SDL advised channel count %d is not supported!\n", spec.channels);
+			return -1;
+		}
+	}*/
+
+	audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
+	audio_hw_params->freq = wanted_sample_rate; // spec.freq;
+	audio_hw_params->channel_layout.nb_channels = wanted_channel_layout;
+	audio_hw_params->channels = wanted_nb_channels; // spec.channels;
+
+	audio_hw_params->frame_size = av_samples_get_buffer_size(nullptr, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
+	audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(nullptr, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
+	if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
+		av_log(nullptr, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
+		return -1;
+	}
+	return 0;// spec.size;
+}
+#endif
+
+#endif

+ 437 - 0
AvPlayer2/packets_sync.h

@@ -0,0 +1,437 @@
+#pragma once
+
+#include <QMutex>
+#include <QThread>
+#include <QWaitCondition>
+
+// only need to open audio filter, video will be synced
+#define USE_AVFILTER_AUDIO 1
+#define USE_AVFILTER_VIDEO 0
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavcodec/avfft.h>
+#include <libavformat/avformat.h>
+#include <libavutil/bprint.h>
+#include <libavutil/fifo.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/samplefmt.h>
+#include <libavutil/time.h>
+#include <libswresample/swresample.h>
+#include <libswscale/swscale.h>
+
+#if USE_AVFILTER_AUDIO
+#include <libavfilter/avfilter.h>
+#include <libavfilter/buffersink.h>
+#include <libavfilter/buffersrc.h>
+#include <libavutil/avstring.h>
+#include <libavutil/macros.h>
+#include <libavutil/opt.h>
+#endif
+}
+
+#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
+#define MIN_FRAMES 25
+#define EXTERNAL_CLOCK_MIN_FRAMES 2
+#define EXTERNAL_CLOCK_MAX_FRAMES 10
+
+#define SDL_MIX_MAXVOLUME 128
+/* Minimum SDL audio buffer size, in samples. */
+#define SDL_AUDIO_MIN_BUFFER_SIZE 512
+/* Calculate actual buffer size keeping in mind not cause too frequent audio
+ * callbacks */
+#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
+
+/* Step size for volume control in dB */
+#define SDL_VOLUME_STEP (0.75)
+
+/* no AV sync correction is done if below the minimum AV sync threshold */
+#define AV_SYNC_THRESHOLD_MIN 0.04
+/* AV sync correction is done if above the maximum AV sync threshold */
+#define AV_SYNC_THRESHOLD_MAX 0.1
+/* If a frame duration is longer than this, it will not be duplicated to
+ * compensate AV sync */
+#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
+/* no AV correction is done if too big error */
+#define AV_NOSYNC_THRESHOLD 10.0
+
+/* maximum audio speed change to get correct sync */
+#define SAMPLE_CORRECTION_PERCENT_MAX 10
+
+/* external clock speed adjustment constants for realtime sources based on
+ * buffer fullness */
+#define EXTERNAL_CLOCK_SPEED_MIN 0.900
+#define EXTERNAL_CLOCK_SPEED_MAX 1.010
+#define EXTERNAL_CLOCK_SPEED_STEP 0.001
+
+/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
+#define AUDIO_DIFF_AVG_NB 20
+
+/* polls for possible required screen refresh at least this often, should be
+ * less than 1/fps */
+#define REFRESH_RATE 0.01
+
+/* NOTE: the size must be big enough to compensate the hardware audio buffersize
+ * size */
+/* TODO: We assume that a decoded and resampled frame fits into this buffer */
+#define SAMPLE_ARRAY_SIZE (8 * 65536)
+
+#define CURSOR_HIDE_DELAY 1000000
+
+#define USE_ONEPASS_SUBTITLE_RENDER 1
+
+typedef struct MyAVPacketList
+{
+    AVPacket* pkt;
+    int serial;
+} MyAVPacketList;
+
+typedef struct PacketQueue
+{
+    AVFifo* pkt_list;
+    int nb_packets;
+    int size;
+    int64_t duration;
+    int abort_request;
+    int serial;
+    QMutex* mutex;
+    QWaitCondition* cond;
+} PacketQueue;
+
+#define VIDEO_PICTURE_QUEUE_SIZE 3
+#define SUBPICTURE_QUEUE_SIZE 16
+#define SAMPLE_QUEUE_SIZE 20
+#define FRAME_QUEUE_SIZE \
+    FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))
+
+typedef struct AudioParams
+{
+    int freq;
+    AVChannelLayout ch_layout;
+    enum AVSampleFormat fmt;
+    int frame_size;
+    int bytes_per_sec;
+} AudioParams;
+
+typedef struct Clock
+{
+    double pts;       /* clock base */
+    double pts_drift; /* clock base minus time at which we updated the clock */
+    double last_updated;
+    double speed;
+    int serial; /* clock is based on a packet with this serial */
+    int paused;
+    int* queue_serial; /* pointer to the current packet queue serial, used for
+                        obsolete clock detection */
+} Clock;
+
+typedef struct FrameData
+{
+    int64_t pkt_pos;
+} FrameData;
+
+/* Common struct for handling all types of decoded data and allocated render
+ * buffers. */
+typedef struct Frame
+{
+    AVFrame* frame;
+    AVSubtitle sub;
+    int serial;
+    double pts;      /* presentation timestamp for the frame */
+    double duration; /* estimated duration of the frame */
+    int64_t pos;     /* byte position of the frame in the input file */
+    int width;
+    int height;
+    int format;
+    AVRational sar;
+    int uploaded;
+    int flip_v;
+} Frame;
+
+typedef struct FrameQueue
+{
+    Frame queue[FRAME_QUEUE_SIZE]; // array queue model, loop queue
+    int rindex;                    // read pointer
+    int windex;                    // write pointer
+    int size;                      // current frame num
+    int max_size;                  // max frame num
+    int keep_last;                 // keep last frame
+    int rindex_shown;              // current frame is shown
+    QMutex* mutex;
+    QWaitCondition* cond;
+    PacketQueue* pktq;
+} FrameQueue;
+
+enum {
+    AV_SYNC_AUDIO_MASTER, /* default choice */
+    AV_SYNC_VIDEO_MASTER,
+    AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
+};
+
+typedef struct Decoder
+{
+    AVPacket* pkt;
+    PacketQueue* queue;
+    AVCodecContext* avctx;
+    int pkt_serial;
+    int finished;
+    int packet_pending;
+    QWaitCondition* empty_queue_cond; // SDL_cond* empty_queue_cond;
+    int64_t start_pts;
+    AVRational start_pts_tb;
+    int64_t next_pts;
+    AVRational next_pts_tb;
+    // SDL_Thread* decoder_tid;
+    void* decoder_tid; // thread pointer
+    char* decoder_name;
+} Decoder;
+
+typedef struct Threads
+{
+    QThread* read_tid{nullptr};
+    QThread* video_decode_tid{nullptr};
+    QThread* audio_decode_tid{nullptr};
+    QThread* video_play_tid{nullptr};
+    QThread* audio_play_tid{nullptr};
+    QThread* subtitle_decode_tid{nullptr};
+} Threads;
+
+typedef struct VideoState
+{
+    const AVInputFormat* iformat;
+    int abort_request;
+    int force_refresh;
+    int paused;
+    int last_paused;
+    int queue_attachments_req;
+    int seek_req;
+    int seek_flags;
+    int64_t seek_pos;
+    int64_t seek_rel;
+    int read_pause_return;
+    AVFormatContext* ic;
+    int realtime;
+
+    Clock vidclk;
+    Clock audclk;
+    Clock extclk;
+
+    PacketQueue videoq;
+    PacketQueue audioq;
+    PacketQueue subtitleq;
+
+    FrameQueue pictq;
+    FrameQueue sampq;
+    FrameQueue subpq;
+
+    Decoder viddec;
+    Decoder auddec;
+    Decoder subdec;
+
+    int audio_stream;
+    int av_sync_type;
+
+    double audio_clock;
+    int audio_clock_serial;
+
+    double audio_clock_old; // add for renew clock after speed changing
+
+    AVStream* audio_st;
+
+    int audio_volume;
+    int muted;
+
+    int frame_drops_early;
+    int frame_drops_late;
+
+#if 0
+	double audio_diff_avg_coef;
+	int audio_diff_avg_count;
+	double audio_diff_cum; /* used for AV difference average computation */
+	double audio_diff_threshold;
+
+	int16_t sample_array[SAMPLE_ARRAY_SIZE];
+	int sample_array_index;
+
+	uint8_t* audio_buf;
+	uint8_t* audio_buf1;
+	unsigned int audio_buf_size; /* in bytes */
+	unsigned int audio_buf1_size;
+	int audio_buf_index; /* in bytes */
+	int audio_write_buf_size;
+
+	int audio_hw_buf_size;
+
+	struct SwrContext* swr_ctx;
+
+	enum ShowMode {
+		SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
+	} show_mode;
+#endif
+
+    int last_i_start;
+    // RDFTContext* rdft;
+    int rdft_bits;
+    // FFTSample* rdft_data;
+    int xpos;
+    double last_vis_time;
+    // SDL_Texture* vis_texture;
+    // SDL_Texture* sub_texture;
+    // SDL_Texture* vid_texture;
+
+    int subtitle_stream;
+    AVStream* subtitle_st;
+
+    double frame_timer;
+    double frame_last_returned_time;
+    double frame_last_filter_delay;
+    int video_stream;
+    AVStream* video_st;
+
+    double max_frame_duration; // maximum duration of a frame - above this, we
+                               // consider the jump a timestamp discontinuity
+    struct SwsContext* img_convert_ctx;
+    struct SwsContext* sub_convert_ctx;
+    int eof;
+    int loop;
+
+    char* filename;
+    int width, height, xleft, ytop;
+    int step;
+
+#if USE_AVFILTER_AUDIO
+    struct AudioParams audio_src;
+    struct AudioParams audio_tgt;
+
+    double audio_speed;
+    char* afilters;
+    int req_afilter_reconfigure;
+    char* vfilters;
+    int req_vfilter_reconfigure;
+
+    struct AudioParams audio_filter_src;
+    int vfilter_idx;
+    AVFilterContext* in_video_filter;  // the first filter in the video chain
+    AVFilterContext* out_video_filter; // the last filter in the video chain
+    AVFilterContext* in_audio_filter;  // the first filter in the audio chain
+    AVFilterContext* out_audio_filter; // the last filter in the audio chain
+    AVFilterGraph* agraph;
+    AVFilterGraph* vgraph;
+#endif
+
+    int last_video_stream, last_audio_stream, last_subtitle_stream;
+
+    QWaitCondition* continue_read_thread;
+    int read_thread_exit;
+    // void* read_tid; //read thread pointer
+
+    Threads threads; // all thread would access VideoState
+} VideoState;
+
+#if !NDEBUG
+#define PRINT_PACKETQUEUE_INFO 0
+#define PRINT_PACKETQUEUE_AUDIO_INFO 0
+#else
+#define PRINT_PACKETQUEUE_INFO 0
+#define PRINT_PACKETQUEUE_AUDIO_INFO 0
+#endif
+
+/***************PacketQueue operations*****************/
+int packet_queue_init(PacketQueue* q);
+void packet_queue_destroy(PacketQueue* q);
+void packet_queue_flush(PacketQueue* q);
+void packet_queue_start(PacketQueue* q);
+void packet_queue_abort(PacketQueue* q);
+int packet_queue_get(PacketQueue* q, AVPacket* pkt, int block, int* serial);
+int packet_queue_put(PacketQueue* q, AVPacket* pkt);
+int packet_queue_put_nullpacket(PacketQueue* q, AVPacket* pkt, int stream_index);
+int packet_queue_put_private(PacketQueue* q, AVPacket* pkt);
+void packet_queue_print(const PacketQueue* q, const AVPacket* pkt, const QString& prefix);
+
+/***************FrameQueue operations*****************/
+int frame_queue_init(FrameQueue* f, PacketQueue* pktq, int max_size, int keep_last);
+void frame_queue_destory(FrameQueue* f);
+void frame_queue_unref_item(Frame* vp);
+void frame_queue_signal(FrameQueue* f);
+Frame* frame_queue_peek_writable(FrameQueue* f);
+Frame* frame_queue_peek(FrameQueue* f);
+Frame* frame_queue_peek_next(FrameQueue* f);
+Frame* frame_queue_peek_last(FrameQueue* f);
+Frame* frame_queue_peek_readable(FrameQueue* f);
+void frame_queue_push(FrameQueue* f);
+void frame_queue_next(FrameQueue* f);
+int frame_queue_nb_remaining(FrameQueue* f);
+int64_t frame_queue_last_pos(FrameQueue* f);
+
+int queue_picture(
+    VideoState* is, AVFrame* src_frame, double pts, double duration, int64_t pos, int serial);
+int get_video_frame(VideoState* is, AVFrame* frame);
+
+/***************Decoder operations*****************/
+int decoder_init(Decoder* d,
+                 AVCodecContext* avctx,
+                 PacketQueue* queue,
+                 QWaitCondition* empty_queue_cond);
+int decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub);
+void decoder_destroy(Decoder* d);
+int decoder_start(Decoder* d, void* thread, const char* thread_name);
+void decoder_abort(Decoder* d, FrameQueue* fq);
+void get_file_info(const char* filename, int64_t& duration);
+void get_duration_time(
+    const int64_t duration_us, int64_t& hours, int64_t& mins, int64_t& secs, int64_t& us);
+
+/***************Clock operations*****************/
+double get_clock(Clock* c);
+void set_clock_at(Clock* c, double pts, int serial, double time);
+void set_clock(Clock* c, double pts, int serial);
+void set_clock_speed(Clock* c, double speed);
+void init_clock(Clock* c, int* queue_serial);
+void sync_clock_to_slave(Clock* c, Clock* slave);
+
+/***************VideoState operations*****************/
+int get_master_sync_type(VideoState* is);
+double get_master_clock(VideoState* is);
+void check_external_clock_speed(VideoState* is);
+void stream_seek(VideoState* is, int64_t pos, int64_t rel, int seek_by_bytes);
+// void stream_toggle_pause(VideoState* is, bool pause = true);
+void toggle_pause(VideoState* is, bool pause = true);
+void toggle_mute(VideoState* is, bool mute = true);
+void update_volume(VideoState* is, int sign, double step);
+void step_to_next_frame(VideoState* is);
+double compute_target_delay(double delay, VideoState* is);
+double vp_duration(VideoState* is, Frame* vp, Frame* nextvp);
+void update_video_pts(VideoState* is, double pts, int64_t pos, int serial);
+
+#if PRINT_PACKETQUEUE_INFO
+void print_state_info(VideoState* is);
+#endif
+
+/****************************************/
+int is_realtime(AVFormatContext* s);
+int stream_has_enough_packets(AVStream* st, int stream_id, PacketQueue* queue);
+
+#if USE_AVFILTER_AUDIO
+void set_audio_playspeed(VideoState* is, double value);
+
+int cmp_audio_fmts(enum AVSampleFormat fmt1,
+                   int64_t channel_count1,
+                   enum AVSampleFormat fmt2,
+                   int64_t channel_count2);
+// int64_t get_valid_channel_layout(int64_t channel_layout, int channels);
+
+int configure_audio_filters(VideoState* is, const char* afilters, int force_output_format);
+int configure_filtergraph(AVFilterGraph* graph,
+                          const char* filtergraph,
+                          AVFilterContext* source_ctx,
+                          AVFilterContext* sink_ctx);
+
+// int audio_open(void* opaque, int64_t wanted_channel_layout, int
+// wanted_nb_channels, int wanted_sample_rate, struct AudioParams*
+// audio_hw_params);
+
+void set_video_playspeed(VideoState* is);
+int configure_video_filters(AVFilterGraph* graph,
+                            VideoState* is,
+                            const char* vfilters,
+                            AVFrame* frame);
+#endif

+ 395 - 0
AvPlayer2/play_control_window.cpp

@@ -0,0 +1,395 @@
+// ***********************************************************/
+// play_control_window.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// video play control panel.
+// ***********************************************************/
+
+#include "play_control_window.h"
+#include "mainwindowa.h"
+
+#define PLAY_SPEED_STEP 0.25
+#define PLAY_SPEED_START 0.5
+#define PLAY_SPEED_STOP 2 // 4 speed multiple from start to stop in step
+
+#include "clickable_slider.h"
+
+PlayControlWnd::PlayControlWnd(PlayerController* playerController, QWidget* parent)
+    : QWidget(parent)
+{
+    // 1. 创建控件
+    btn_pre = new QPushButton("<<", this);
+    btn_play = new QPushButton("Play", this);
+    btn_next = new QPushButton(">>", this);
+    btn_stop = new QPushButton("Stop", this);
+    slider_speed = new QSlider(Qt::Horizontal, this);
+    label_speed = new QLabel("1.0x", this);
+    check_mute = new QCheckBox("Mute", this);
+    slider_vol = new QSlider(Qt::Horizontal, this);
+    label_vol = new QLabel("0", this);
+    progress_slider = new ClickableSlider(this);
+
+    label_curTime = new QLabel("00:00", this);
+    label_totalTime = new QLabel("00:00", this);
+    label_divider = new QLabel("/", this);
+
+    // 2. 设置控件属性
+    slider_speed->setMinimum(1);
+    slider_speed->setMaximum(8);
+    slider_speed->setValue(4);
+    slider_speed->setTickPosition(QSlider::TicksAbove);
+    slider_speed->setTickInterval(1);
+    slider_speed->setFixedHeight(30);
+    label_speed->setFixedWidth(28);
+    label_speed->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+    slider_vol->setMinimum(0);
+    slider_vol->setMaximum(100);
+    slider_vol->setSingleStep(5);
+    slider_vol->setPageStep(5);
+    slider_vol->setFixedHeight(22);
+    label_vol->setFixedWidth(20);
+    label_vol->setAlignment(Qt::AlignCenter);
+    progress_slider->setMinimum(0);
+    progress_slider->setMaximum(100);
+    progress_slider->setSingleStep(1);
+    progress_slider->setPageStep(5);
+    progress_slider->setTracking(true);
+    progress_slider->setFixedHeight(18);
+    label_curTime->setFixedWidth(48);
+    label_totalTime->setFixedWidth(48);
+    label_divider->setText("/");
+    label_divider->setFixedWidth(10);
+
+    // 3. 布局
+    hLayout_progress = new QHBoxLayout;
+    hLayout_progress->setSpacing(2);
+    hLayout_progress->addWidget(progress_slider, 1);
+    hLayout_progress->addWidget(label_curTime);
+    hLayout_progress->addWidget(label_divider);
+    hLayout_progress->addWidget(label_totalTime);
+
+    hLayout_controls = new QHBoxLayout;
+    hLayout_controls->setSpacing(1);
+    hLayout_controls->addWidget(btn_pre);
+    hLayout_controls->addWidget(btn_play);
+    hLayout_controls->addWidget(btn_next);
+    hLayout_controls->addWidget(btn_stop);
+    hLayout_controls->addSpacing(10);
+    hLayout_controls->addWidget(slider_speed);
+    hLayout_controls->addWidget(label_speed);
+    hLayout_controls->addSpacing(10);
+    hLayout_controls->addWidget(check_mute);
+    hLayout_controls->addWidget(slider_vol);
+    hLayout_controls->addWidget(label_vol);
+
+    mainLayout = new QVBoxLayout(this);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+    mainLayout->setSpacing(2);
+    mainLayout->addLayout(hLayout_progress);
+    mainLayout->addLayout(hLayout_controls);
+    setLayout(mainLayout);
+
+    // 4. 信号槽
+    connect(slider_vol, SIGNAL(valueChanged(int)), label_vol, SLOT(setNum(int)));
+    connect(check_mute, &QCheckBox::stateChanged, this, &PlayControlWnd::volume_muted);
+    connect(check_mute, &QCheckBox::stateChanged, playerController, &PlayerController::playMute);
+    connect(btn_stop, &QPushButton::clicked, playerController, &PlayerController::stopPlay);
+    connect(btn_play, &QPushButton::clicked, playerController, &PlayerController::pausePlay);
+    connect(slider_vol,
+            &QSlider::valueChanged,
+            playerController,
+            [this, playerController](int value) {
+                auto maxValue = get_volum_slider_max();
+                playerController->setVolume(value, maxValue);
+            });
+    connect(btn_pre, &QPushButton::pressed, playerController, &PlayerController::playSeekPre);
+    connect(btn_next, &QPushButton::pressed, playerController, &PlayerController::playSeekNext);
+    connect(progress_slider, &QSlider::sliderReleased, playerController, &PlayerController::playStartSeek);
+    connect(progress_slider, &QSlider::sliderPressed, playerController, &PlayerController::pausePlay);
+    connect(progress_slider, &ClickableSlider::onClick, playerController, &PlayerController::playSeek);
+    connect(slider_speed, &QSlider::valueChanged, this, &PlayControlWnd::speed_changed);
+    connect(slider_speed, &QSlider::valueChanged, playerController, [this, playerController]() {
+        auto speed = get_speed();
+        playerController->setPlaySpeed(speed);
+    });
+
+    clear_all();
+}
+
+PlayControlWnd::~PlayControlWnd()
+{
+}
+
+void PlayControlWnd::set_focus_policy()
+{
+    /*
+   * Disable widgets in this window to accept foucs,
+   * so all key event will be transfered to the
+   * parent widget. The mainwindow will handle these keyevent
+   * to control the playing.
+   */
+
+    // setFocusPolicy(Qt::NoFocus);
+    progress_slider->setFocusPolicy(Qt::NoFocus);
+    slider_speed->setFocusPolicy(Qt::NoFocus);
+    slider_vol->setFocusPolicy(Qt::NoFocus);
+
+    btn_pre->setFocusPolicy(Qt::NoFocus);
+    btn_play->setFocusPolicy(Qt::NoFocus);
+    btn_next->setFocusPolicy(Qt::NoFocus);
+    btn_stop->setFocusPolicy(Qt::NoFocus);
+    check_mute->setFocusPolicy(Qt::NoFocus);
+}
+
+void PlayControlWnd::volume_muted(int mute)
+{
+    bool enable = !mute;
+    label_vol->setEnabled(enable);
+    slider_vol->setEnabled(enable);
+    check_mute->setChecked(!enable);
+}
+
+void PlayControlWnd::speed_changed(int value)
+{
+    int max = slider_speed->maximum();
+    value %= (max + 1);
+    double speed = (value - 1) * PLAY_SPEED_STEP + PLAY_SPEED_START;
+    QString str = QString::number(speed, 'f', 2) + "x";
+    // qDebug() << speed << max << "str=" << str;
+    label_speed->setText(str);
+}
+
+double PlayControlWnd::get_speed() const
+{
+    int value = slider_speed->value();
+    return (value - 1) * PLAY_SPEED_STEP + PLAY_SPEED_START;
+}
+
+void PlayControlWnd::speed_adjust(bool up)
+{
+    int value = slider_speed->value();
+    int max = slider_speed->maximum();
+    if (up)
+    {
+        value += 1;
+    }
+    else
+    {
+        value -= 1;
+    }
+
+    value = value > max ? max : value;
+    value = value < 0 ? 0 : value;
+    slider_speed->setValue(value);
+}
+
+void PlayControlWnd::init_slider_speed()
+{
+    int maxSpeed = (PLAY_SPEED_STOP + PLAY_SPEED_STEP - PLAY_SPEED_START) / PLAY_SPEED_STEP; // 8
+    slider_speed->setMaximum(maxSpeed);
+    slider_speed->setValue((1 + PLAY_SPEED_STEP - PLAY_SPEED_START) / PLAY_SPEED_STEP); // set 1x speed
+}
+
+void PlayControlWnd::set_volume_slider(float volume)
+{
+    enable_slider_vol(true);
+    auto max = slider_vol->maximum();
+    slider_vol->setValue(int(volume * max));
+}
+
+inline double PlayControlWnd::get_time_secs(int64_t hours, int64_t mins, int64_t secs)
+{
+    return hours * 60 * 60 + mins * 60 + secs;
+}
+
+inline QSlider* PlayControlWnd::get_progress_slider() const
+{
+    return progress_slider;
+}
+
+inline QSlider* PlayControlWnd::get_volume_slider() const
+{
+    return slider_vol;
+}
+
+inline QSlider* PlayControlWnd::get_speed_slider() const
+{
+    return slider_speed;
+}
+
+int PlayControlWnd::get_volum_slider_max()
+{
+    return get_volume_slider()->maximum();
+}
+
+int PlayControlWnd::get_progress_slider_max()
+{
+    return get_progress_slider()->maximum();
+}
+
+int PlayControlWnd::get_progress_slider_value()
+{
+    return get_progress_slider()->value();
+}
+
+void PlayControlWnd::enable_progressbar(bool enable)
+{
+    get_progress_slider()->setEnabled(enable);
+}
+
+void PlayControlWnd::enable_slider_vol(bool enable)
+{
+    slider_vol->setEnabled(enable);
+}
+
+void PlayControlWnd::enable_slider_speed(bool enable)
+{
+    slider_speed->setEnabled(enable);
+}
+
+void PlayControlWnd::get_play_time_params(int64_t total_secs, int64_t& hours, int64_t& mins, int64_t& secs)
+{
+#if 1
+    mins = total_secs / 60;
+    secs = total_secs % 60;
+    hours = mins / 60;
+    mins %= 60;
+#else
+    hours = int(total_secs / 3600);
+    mins = (total_secs - hours * 3600) / 60;
+    secs = (total_secs - hours * 3600 - mins * 60);
+#endif
+}
+
+void PlayControlWnd::update_play_time(int64_t hours, int64_t mins, int64_t secs)
+{
+    auto time_str = get_play_time(hours, mins, secs);
+    label_curTime->setText(time_str);
+
+    int percent = 0;
+    auto total = get_total_time();
+    auto cur = get_time_secs(hours, mins, secs);
+    cur = cur > total ? total : cur;
+    if (total > 0)
+    {
+        percent = cur * get_progress_slider()->maximum() / total;
+    }
+    get_progress_slider()->setValue(percent);
+}
+
+void PlayControlWnd::update_play_time(int64_t total_secs)
+{
+    int64_t hours = 0, mins = 0, secs = 0;
+    double total = get_total_time();
+
+    total_secs = total_secs < 0 ? 0 : total_secs;
+    total_secs = total_secs > total ? total : total_secs;
+
+    get_play_time_params(total_secs, hours, mins, secs);
+    // qDebug() << "total:" << total_secs << "h:" << hours << "m:" << mins <<
+    // "ses:" << secs;
+    update_play_time(hours, mins, secs);
+}
+
+double PlayControlWnd::get_total_time() const
+{
+    return get_time_secs(m_hours, m_mins, m_secs);
+}
+
+void PlayControlWnd::set_total_time(int64_t hours, int64_t mins, int64_t secs)
+{
+    enable_progressbar();
+    enable_play_buttons();
+    enable_slider_vol();
+    enable_slider_speed();
+    check_mute->setEnabled(true);
+    init_slider_speed();
+
+    m_hours = hours;
+    m_mins = mins;
+    m_secs = secs;
+
+    set_progress_bar(get_total_time());
+    label_totalTime->setText(get_play_time(hours, mins, secs));
+}
+
+void PlayControlWnd::set_progress_bar(double total_secs)
+{
+    get_progress_slider()->setMaximum(total_secs);
+}
+
+QString PlayControlWnd::get_play_time(int64_t hours, int64_t mins, int64_t secs)
+{
+    QString str;
+
+    if (hours == 0)
+    {
+        str = QString("%1:%2")
+                  .arg(QString::number(mins).rightJustified(2, '0'))
+                  .arg(QString::number(secs).rightJustified(2, '0'));
+    }
+    else
+    {
+        str = QString("%1:%2:%3")
+                  .arg(QString::number(hours).rightJustified(2, '0'))
+                  .arg(QString::number(mins).rightJustified(2, '0'))
+                  .arg(QString::number(secs).rightJustified(2, '0'));
+    }
+    return str;
+}
+
+void PlayControlWnd::clear_time()
+{
+    m_hours = 0;
+    m_mins = 0;
+    m_secs = 0;
+
+    get_progress_slider()->setValue(0);
+    label_totalTime->setText("--:--");
+    label_curTime->setText("--:--");
+}
+
+void PlayControlWnd::clear_all()
+{
+    clear_time();
+    enable_progressbar(false);
+    update_btn_play();
+
+    enable_slider_speed(false);
+    enable_slider_vol(false);
+    enable_play_buttons(false);
+    check_mute->setEnabled(false);
+
+    init_slider_speed();
+}
+
+void PlayControlWnd::update_btn_play(bool bPause)
+{
+    if (bPause)
+    {
+        btn_play->setText("Play");
+    }
+    else
+    {
+        btn_play->setText("Pause");
+    }
+}
+
+void PlayControlWnd::enable_play_buttons(bool enable)
+{
+    btn_next->setEnabled(enable);
+    btn_pre->setEnabled(enable);
+    btn_play->setEnabled(enable);
+    btn_stop->setEnabled(enable);
+}
+
+void PlayControlWnd::keyPressEvent(QKeyEvent* event)
+{
+    if (auto pParent = (MainWindowA*) parent()) {
+        QApplication::sendEvent(pParent, event);
+        event->ignore();
+    } else {
+        QWidget::keyPressEvent(event);
+    }
+}

+ 89 - 0
AvPlayer2/play_control_window.h

@@ -0,0 +1,89 @@
+#pragma once
+
+#include <QSlider>
+#include <QWidget>
+#include <memory>
+#include <QPushButton>
+#include <QLabel>
+#include <QCheckBox>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QGridLayout>
+#include "clickable_slider.h"
+
+QT_BEGIN_NAMESPACE
+namespace Ui
+{
+class play_control_window;
+};
+QT_END_NAMESPACE
+class PlayerController;
+class PlayControlWnd : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit PlayControlWnd(PlayerController* playerController, QWidget* parent = Q_NULLPTR);
+    ~PlayControlWnd();
+
+public:
+    void update_play_time(int64_t total_secs);
+    void set_total_time(int64_t hours, int64_t mins, int64_t secs);
+    inline QSlider* get_progress_slider() const;
+    inline QSlider* get_volume_slider() const;
+    inline QSlider* get_speed_slider() const;
+    int get_volum_slider_max();
+    int get_progress_slider_max();
+    int get_progress_slider_value();
+    void set_volume_slider(float volume);
+    void clear_all();
+    void update_btn_play(bool bPause = true);
+    double get_total_time() const;
+    double get_speed() const;
+    void speed_adjust(bool up = true);
+
+public:
+    static void get_play_time_params(int64_t total_secs, int64_t& hours, int64_t& mins, int64_t& secs);
+    static QString get_play_time(int64_t hours, int64_t mins, int64_t secs);
+    static inline double get_time_secs(int64_t hours, int64_t mins, int64_t secs);
+
+public slots:
+    void volume_muted(int mute);
+    void speed_changed(int speed);
+
+private:
+    void enable_progressbar(bool enable = true);
+    void enable_slider_vol(bool enable = true);
+    void enable_slider_speed(bool enable = true);
+    void init_slider_speed();
+    void clear_time();
+    void enable_play_buttons(bool enable = true);
+    void update_play_time(int64_t hours, int64_t mins, int64_t secs);
+    void set_progress_bar(double total_secs);
+    void set_focus_policy();
+    void keyPressEvent(QKeyEvent* event) override;
+
+private:
+    // UI控件成员
+    QPushButton* btn_pre = nullptr;
+    QPushButton* btn_play = nullptr;
+    QPushButton* btn_next = nullptr;
+    QPushButton* btn_stop = nullptr;
+    QSlider* slider_speed = nullptr;
+    QLabel* label_speed = nullptr;
+    QCheckBox* check_mute = nullptr;
+    QSlider* slider_vol = nullptr;
+    QLabel* label_vol = nullptr;
+    ClickableSlider* progress_slider = nullptr;
+    QLabel* label_curTime = nullptr;
+    QLabel* label_totalTime = nullptr;
+    QLabel* label_divider = nullptr;
+    QGridLayout* gridLayout = nullptr;
+    QHBoxLayout* hLayout_controls = nullptr;
+    QHBoxLayout* hLayout_progress = nullptr;
+    QVBoxLayout* mainLayout = nullptr;
+    // ... 其它需要的布局和控件 ...
+    int64_t m_hours{0};
+    int64_t m_mins{0};
+    int64_t m_secs{0};
+};

+ 658 - 0
AvPlayer2/playercontroller.cpp

@@ -0,0 +1,658 @@
+#include "playercontroller.h"
+
+#include "common.h"
+
+#include <QApplication>
+#include <QElapsedTimer>
+#include <QStatusBar>
+#include <cassert>
+#include <memory>
+
+#include "ffmpeg_init.h"
+#include "start_play_thread.h"
+#include "video_state.h"
+
+PlayerController::PlayerController(QWidget* parent)
+    : QObject(parent)
+{
+    ffmpeg_init();
+}
+
+PlayerController::~PlayerController()
+{
+    stopPlay();
+}
+
+// 播放控制接口
+void PlayerController::startToPlay(const QString& file)
+{
+    if (isPlaying()) {
+        if (m_currentFile == file)
+            return;
+
+        waitStopPlay(file);
+        return;
+    }
+
+    m_currentFile = file;
+
+    if (!startPlay()) {
+        playFailed(m_currentFile);
+        return;
+    }
+    emit startToPlaySignal();
+}
+
+void PlayerController::stopPlay()
+{
+    deleteVideoState();
+}
+
+void PlayerController::pausePlay()
+{
+    if (!m_videoState)
+        return;
+
+    if (auto state = m_videoState->get_state())
+        toggle_pause(state, !state->paused);
+
+    emit updatePlayControlStatus();
+}
+
+void PlayerController::playMute(bool mute)
+{
+    if (!m_videoState)
+        return;
+
+    if (auto state = m_videoState->get_state())
+        toggle_mute(state, mute);
+}
+
+void PlayerController::playStartSeek()
+{
+    emit playSeek();
+    pausePlay();
+}
+
+void PlayerController::playSeekPre()
+{
+    videoSeekInc(-2);
+}
+
+void PlayerController::playSeekNext()
+{
+    videoSeekInc(2);
+}
+
+void PlayerController::setVolume(int volume, int maxValue)
+{
+    if (!m_audioPlayThread)
+        return;
+
+    const float vol = static_cast<float>(volume) / maxValue;
+    m_audioPlayThread->set_device_volume(vol);
+}
+
+void PlayerController::setPlaySpeed(double speed)
+{
+    if (m_videoState) {
+        if (auto state = m_videoState->get_state()) {
+#if USE_AVFILTER_AUDIO
+            set_audio_playspeed(state, speed);
+#endif
+        }
+    }
+}
+
+// 状态访问接口
+QString PlayerController::playingFile() const
+{
+    return isPlaying() ? m_currentFile : QString();
+}
+
+bool PlayerController::isPlaying() const
+{
+    return m_videoState || m_packetReadThread || m_decodeVideoThread || m_decodeAudioThread
+           || m_audioPlayThread || m_videoPlayThread || m_decodeSubtitleThread;
+}
+
+bool PlayerController::playingHasVideo()
+{
+    return m_videoState ? m_videoState->has_video() : false;
+}
+
+bool PlayerController::playingHasAudio()
+{
+    return m_videoState ? m_videoState->has_audio() : false;
+}
+
+bool PlayerController::playingHasSubtitle()
+{
+    return m_videoState ? m_videoState->has_subtitle() : false;
+}
+
+VideoState* PlayerController::state()
+{
+    return m_videoState ? m_videoState->get_state() : nullptr;
+}
+
+float PlayerController::deviceVolume() const
+{
+    return m_audioPlayThread ? m_audioPlayThread->get_device_volume() : 0.0f;
+}
+
+void PlayerController::setDeviceVolume(float volume)
+{
+    if (m_audioPlayThread)
+        m_audioPlayThread->set_device_volume(volume);
+}
+
+// 播放状态回调槽函数
+void PlayerController::playStarted(bool success)
+{
+    if (!success) {
+        qWarning("Audio device initialization failed!");
+        return;
+    }
+
+    allThreadStart();
+    setThreads();
+}
+
+void PlayerController::playFailed(const QString& file)
+{
+    emit showMessage(QString("Playback failed: %1").arg(toNativePath(file)), "Warning", "");
+}
+
+// 线程生命周期管理槽函数
+void PlayerController::readPacketStopped()
+{
+    if (m_packetReadThread) {
+        m_packetReadThread.reset();
+        qDebug("************* Read packets thread stopped.");
+    }
+
+    stopPlay();
+}
+
+void PlayerController::decodeVideoStopped()
+{
+    m_decodeVideoThread.reset();
+    qDebug("************* Video decode thread stopped.");
+}
+
+void PlayerController::decodeAudioStopped()
+{
+    m_decodeAudioThread.reset();
+    qDebug("************* Audio decode thread stopped.");
+}
+
+void PlayerController::decodeSubtitleStopped()
+{
+    m_decodeSubtitleThread.reset();
+    qDebug("************* Subtitle decode thread stopped.");
+}
+
+void PlayerController::audioPlayStopped()
+{
+    m_audioPlayThread.reset();
+    qDebug("************* Audio play thread stopped.");
+}
+
+void PlayerController::videoPlayStopped()
+{
+    m_videoPlayThread.reset();
+    qDebug("************* Video play thread stopped.");
+}
+
+// 线程管理槽函数
+void PlayerController::setThreads()
+{
+    if (!m_videoState)
+        return;
+
+    Threads threads;
+    threads.read_tid = m_packetReadThread.get();
+    threads.video_decode_tid = m_decodeVideoThread.get();
+    threads.audio_decode_tid = m_decodeAudioThread.get();
+    threads.video_play_tid = m_videoPlayThread.get();
+    threads.audio_play_tid = m_audioPlayThread.get();
+    threads.subtitle_decode_tid = m_decodeSubtitleThread.get();
+
+    m_videoState->threads_setting(m_videoState->get_state(), threads);
+}
+
+void PlayerController::startSendData(bool send)
+{
+    if (m_audioPlayThread)
+        m_audioPlayThread->send_visual_open(send);
+}
+
+void PlayerController::videoSeek(double position, double increment)
+{
+    if (!m_videoState)
+        return;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return;
+
+    if (state->ic->start_time != AV_NOPTS_VALUE
+        && position < state->ic->start_time / static_cast<double>(AV_TIME_BASE)) {
+        position = state->ic->start_time / static_cast<double>(AV_TIME_BASE);
+    }
+
+    stream_seek(state,
+                static_cast<int64_t>(position * AV_TIME_BASE),
+                static_cast<int64_t>(increment * AV_TIME_BASE),
+                0);
+}
+
+// 核心私有实现
+bool PlayerController::startPlay()
+{
+    QElapsedTimer timer;
+    timer.start();
+
+    if (m_currentFile.isEmpty()) {
+        qWarning("Filename is invalid. Please select a valid media file.");
+        return false;
+    }
+
+    qInfo("Starting playback: %s", qUtf8Printable(toNativePath(m_currentFile)));
+
+    // 创建数据包读取线程
+    if (!createReadThread()) {
+        qWarning("Packet read thread creation failed");
+        return false;
+    }
+
+    // 创建视频状态对象
+    if (!createVideoState(m_currentFile)) {
+        qWarning("Video state creation failed");
+        readPacketStopped();
+        return false;
+    }
+
+    // 检查状态有效性
+    assert(m_videoState);
+    if (!m_videoState) {
+        qWarning("Video state initialization error");
+        return false;
+    }
+
+    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");
+            return false;
+        }
+    }
+
+    // 创建音频相关线程
+    if (hasAudio) {
+        if (!createDecodeAudioThread() || !createAudioPlayThread()) {
+            qWarning("Audio processing setup failed");
+            return false;
+        }
+    }
+
+    // 创建字幕线程
+    if (hasSubtitle && !createDecodeSubtitleThread()) {
+        qWarning("Subtitle processing setup failed");
+        return false;
+    }
+
+    // 开始播放
+    if (hasAudio) {
+        startPlayThread(); // 异步启动(处理音频设备初始化)
+    } else {
+        playStarted(); // 同步启动(无音频流)
+    }
+
+    qDebug("Playback initialized in %lld ms", timer.elapsed());
+    return true;
+}
+
+void PlayerController::waitStopPlay(const QString& file)
+{
+    m_stopPlayWaitingThread = std::make_unique<StopWaitingThread>(this, file);
+    connect(m_stopPlayWaitingThread.get(),
+            &StopWaitingThread::stopPlay,
+            this,
+            &PlayerController::stopPlay);
+    connect(m_stopPlayWaitingThread.get(),
+            &StopWaitingThread::startPlay,
+            this,
+            &PlayerController::startToPlay);
+
+    m_stopPlayWaitingThread->start();
+    qDebug("++++++++++ StopPlay waiting thread started");
+}
+
+void PlayerController::allThreadStart()
+{
+    // 启动所有创建的线程
+    if (m_packetReadThread) {
+        m_packetReadThread->start();
+        qDebug("++++++++++ Read packets thread started");
+    }
+
+    if (m_decodeVideoThread) {
+        m_decodeVideoThread->start();
+        qDebug("++++++++++ Video decode thread started");
+    }
+
+    if (m_decodeAudioThread) {
+        m_decodeAudioThread->start(QThread::Priority::HighPriority);
+        qDebug("++++++++++ Audio decode thread started");
+    }
+
+    if (m_decodeSubtitleThread) {
+        m_decodeSubtitleThread->start();
+        qDebug("++++++++++ Subtitle decode thread started");
+    }
+
+    if (m_videoPlayThread) {
+        m_videoPlayThread->start();
+        qDebug("++++++++++ Video play thread started");
+    }
+
+    if (m_audioPlayThread) {
+        m_audioPlayThread->start();
+        qDebug("++++++++++ Audio play thread started");
+    }
+
+    // 通知UI更新
+    emit setPlayControlWnd(true);
+    emit updatePlayControlVolume();
+    emit updatePlayControlStatus();
+}
+
+// 辅助函数
+void PlayerController::videoSeekInc(double increment)
+{
+    if (!m_videoState)
+        return;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return;
+
+    double position = get_master_clock(state);
+    if (std::isnan(position)) {
+        position = static_cast<double>(state->seek_pos) / AV_TIME_BASE;
+    }
+
+    position += increment;
+    videoSeek(position, increment);
+}
+
+// 线程创建方法
+bool PlayerController::createVideoState(const QString& file)
+{
+    const bool useHardware = false; // 待实现:来自UI设置
+    const bool loop = false;        // 待实现:来自UI设置
+
+    if (m_videoState)
+        return false;
+
+    m_videoState = std::make_unique<VideoStateData>(useHardware, loop);
+    const int ret = m_videoState->create_video_state(file.toUtf8().constData());
+
+    if (ret < 0) {
+        m_videoState.reset();
+        qWarning("Video state creation failed (error: %d)", ret);
+        return false;
+    }
+
+    return true;
+}
+
+void PlayerController::deleteVideoState()
+{
+    m_videoState.reset();
+}
+
+bool PlayerController::createReadThread()
+{
+    if (m_packetReadThread)
+        return false;
+
+    m_packetReadThread = std::make_unique<ReadThread>(this);
+    connect(m_packetReadThread.get(),
+            &ReadThread::finished,
+            this,
+            &PlayerController::readPacketStopped);
+
+    return true;
+}
+
+bool PlayerController::createDecodeVideoThread()
+{
+    if (!m_videoState || m_decodeVideoThread)
+        return false;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return false;
+
+    m_decodeVideoThread = std::make_unique<VideoDecodeThread>(this, state);
+    connect(m_decodeVideoThread.get(),
+            &VideoDecodeThread::finished,
+            this,
+            &PlayerController::decodeVideoStopped);
+
+    auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_VIDEO);
+
+    // 初始化视频解码器
+    int ret = decoder_init(&state->viddec,
+                           codecContext,
+                           &state->videoq,
+                           state->continue_read_thread);
+    if (ret < 0) {
+        qWarning("Video decoder initialization failed (error: %d)", ret);
+        return false;
+    }
+
+    ret = decoder_start(&state->viddec, m_decodeVideoThread.get(), "video_decoder");
+    if (ret < 0) {
+        qWarning("Video decoder start failed (error: %d)", ret);
+        return false;
+    }
+
+    state->queue_attachments_req = 1;
+    return true;
+}
+
+bool PlayerController::createDecodeAudioThread()
+{
+    if (!m_videoState || m_decodeAudioThread)
+        return false;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return false;
+
+    m_decodeAudioThread = std::make_unique<AudioDecodeThread>(this, state);
+    connect(m_decodeAudioThread.get(),
+            &AudioDecodeThread::finished,
+            this,
+            &PlayerController::decodeAudioStopped);
+
+    auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_AUDIO);
+
+    // 初始化音频解码器
+    int ret = decoder_init(&state->auddec,
+                           codecContext,
+                           &state->audioq,
+                           state->continue_read_thread);
+    if (ret < 0) {
+        qWarning("Audio decoder initialization failed (error: %d)", ret);
+        return false;
+    }
+
+    ret = decoder_start(&state->auddec, m_decodeAudioThread.get(), "audio_decoder");
+    if (ret < 0) {
+        qWarning("Audio decoder start failed (error: %d)", ret);
+        return false;
+    }
+
+    return true;
+}
+
+bool PlayerController::createDecodeSubtitleThread()
+{
+    if (!m_videoState || m_decodeSubtitleThread)
+        return false;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return false;
+
+    m_decodeSubtitleThread = std::make_unique<SubtitleDecodeThread>(this, state);
+    connect(m_decodeSubtitleThread.get(),
+            &SubtitleDecodeThread::finished,
+            this,
+            &PlayerController::decodeSubtitleStopped);
+
+    auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_SUBTITLE);
+
+    // 初始化字幕解码器
+    int ret = decoder_init(&state->subdec,
+                           codecContext,
+                           &state->subtitleq,
+                           state->continue_read_thread);
+    if (ret < 0) {
+        qWarning("Subtitle decoder initialization failed (error: %d)", ret);
+        return false;
+    }
+
+    ret = decoder_start(&state->subdec, m_decodeSubtitleThread.get(), "subtitle_decoder");
+    if (ret < 0) {
+        qWarning("Subtitle decoder start failed (error: %d)", ret);
+        return false;
+    }
+
+    return true;
+}
+
+bool PlayerController::createVideoPlayThread()
+{
+    if (!m_videoState || m_videoPlayThread)
+        return false;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return false;
+
+    m_videoPlayThread = std::make_unique<VideoPlayThread>(this, state);
+
+    // 连接信号
+    connect(m_videoPlayThread.get(),
+            &VideoPlayThread::finished,
+            this,
+            &PlayerController::videoPlayStopped);
+    connect(m_videoPlayThread.get(),
+            &VideoPlayThread::frameReady,
+            this,
+            &PlayerController::frameReady);
+    connect(m_videoPlayThread.get(),
+            &VideoPlayThread::subtitle_ready,
+            this,
+            &PlayerController::subtitleReady);
+    connect(this,
+            &PlayerController::stopVideoPlayThread,
+            m_videoPlayThread.get(),
+            &VideoPlayThread::stop_thread);
+
+    // 初始化参数
+    auto videoContext = m_videoState->get_contex(AVMEDIA_TYPE_VIDEO);
+    const bool useHardware = m_videoState->is_hardware_decode();
+
+    if (!m_videoPlayThread->init_resample_param(videoContext, useHardware)) {
+        qWarning("Video resample parameters initialization failed");
+        return false;
+    }
+
+    return true;
+}
+
+bool PlayerController::createAudioPlayThread()
+{
+    if (!m_videoState || m_audioPlayThread)
+        return false;
+
+    auto state = m_videoState->get_state();
+    if (!state)
+        return false;
+
+    m_audioPlayThread = std::make_unique<AudioPlayThread>(this, state);
+
+    // 连接信号
+    connect(m_audioPlayThread.get(),
+            &AudioPlayThread::finished,
+            this,
+            &PlayerController::audioPlayStopped);
+    connect(this,
+            &PlayerController::stopAudioPlayThread,
+            m_audioPlayThread.get(),
+            &AudioPlayThread::stop_thread);
+    connect(m_audioPlayThread.get(),
+            &AudioPlayThread::update_play_time,
+            this,
+            &PlayerController::updatePlayTime);
+    connect(m_audioPlayThread.get(),
+            &AudioPlayThread::data_visual_ready,
+            this,
+            &PlayerController::audioData);
+
+    // 音频设备初始化在独立线程中完成
+    return true;
+}
+
+bool PlayerController::startPlayThread()
+{
+    if (m_beforePlayThread)
+        return false;
+
+    m_beforePlayThread = std::make_unique<StartPlayThread>(this);
+    connect(m_beforePlayThread.get(),
+            &StartPlayThread::audio_device_init,
+            this,
+            &PlayerController::playStarted);
+
+    m_beforePlayThread->start();
+    qDebug("++++++++++ StartPlay thread (audio init) started");
+
+    return true;
+}
+
+// 调试辅助函数
+void PlayerController::printDecodeContext(const AVCodecContext* codecCtx, bool isVideo) const
+{
+    if (!codecCtx)
+        return;
+
+    qInfo("%s codec: %s", isVideo ? "Video" : "Audio", codecCtx->codec->name);
+    qInfo("  Type: %d, ID: %d, Tag: %d",
+          codecCtx->codec_type,
+          codecCtx->codec_id,
+          codecCtx->codec_tag);
+
+    if (isVideo) {
+        qInfo("  Dimensions: %dx%d", codecCtx->width, codecCtx->height);
+    } else {
+        qInfo("  Sample rate: %d Hz, Channels: %d, Format: %d",
+              codecCtx->sample_rate,
+              codecCtx->ch_layout.nb_channels,
+              codecCtx->sample_fmt);
+        qInfo("  Frame size: %d, Block align: %d", codecCtx->frame_size, codecCtx->block_align);
+    }
+}

+ 149 - 0
AvPlayer2/playercontroller.h

@@ -0,0 +1,149 @@
+#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 "audio_decode_thread.h"
+#include "audio_effect_helper.h"
+#include "audio_play_thread.h"
+#include "play_control_window.h"
+#include "read_thread.h"
+#include "start_play_thread.h"
+#include "stopplay_waiting_thread.h"
+#include "subtitle_decode_thread.h"
+#include "video_decode_thread.h"
+#include "video_play_thread.h"
+#include "video_state.h"
+
+class OpenGLVideoWidget;
+
+class PlayerController : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit PlayerController(QWidget* parent = nullptr);
+    ~PlayerController();
+
+    // 播放控制接口
+    void startToPlay(const QString& file);
+    void stopPlay();
+    void pausePlay();
+    void playMute(bool mute);
+    void playStartSeek();
+    void playSeekPre();
+    void playSeekNext();
+    void setVolume(int volume, int maxValue = 100);
+    void setPlaySpeed(double speed);
+    bool isPlaying() const;
+    QString playingFile() const;
+    bool playingHasVideo();
+    bool playingHasAudio();
+    bool playingHasSubtitle();
+
+    // 状态访问接口
+    VideoState* state();
+    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(); }
+
+public slots:
+    // 线程生命周期管理
+    void readPacketStopped();
+    void decodeVideoStopped();
+    void decodeAudioStopped();
+    void decodeSubtitleStopped();
+    void audioPlayStopped();
+    void videoPlayStopped();
+
+    // 播放状态回调
+    void playStarted(bool success = true);
+    void playFailed(const QString& file);
+
+    // 线程管理
+    void setThreads();
+    void startSendData(bool send = true);
+    void videoSeek(double pos = 0, double incr = 0);
+
+signals:
+    // 线程控制信号
+    void startToPlaySignal();
+    void stopAudioPlayThread();
+    void stopVideoPlayThread();
+    void stopDecodeThread();
+    void stopReadPacketThread();
+    void waitStopAudioPlayThread();
+    void waitStopVideoPlayThread();
+
+    // 多媒体数据处理
+    void frameReady(AVFrame*);
+    void audioData(const AudioData& data);
+    void subtitleReady(const QString& text);
+
+    // UI更新信号
+    void setPlayControlWnd(bool set);
+    void updatePlayControlVolume();
+    void updatePlayControlStatus();
+    void updatePlayTime();
+    void playSeek();
+    void showMessage(const QString& message, const QString& windowTitle, const QString& styleSheet);
+    void requestFullscreen(bool fullscreen);
+    void requestHideStatusBar(bool hide);
+
+private:
+    // 核心播放逻辑
+    bool startPlay();
+    void waitStopPlay(const QString& file);
+    void allThreadStart();
+
+    // 线程创建方法
+    bool createVideoState(const QString& file);
+    void deleteVideoState();
+    bool createReadThread();
+    bool createDecodeVideoThread();
+    bool createDecodeAudioThread();
+    bool createDecodeSubtitleThread();
+    bool createVideoPlayThread();
+    bool createAudioPlayThread();
+    bool startPlayThread(); // 避免UI冻结的线程
+
+    // 播放辅助方法
+    void videoSeekInc(double incr);
+    void printDecodeContext(const AVCodecContext* videoCtx, bool isVideo = true) const;
+    void displayStatusMessage(const QString& message);
+    QSize displayVideoSize(AVCodecContext* videoCtx) const;
+    void setVolumeUpdown(bool increase = true, float unit = 0.05f);
+    QString strippedName(const QString& fullPath) const;
+    void hideCursor(bool hide = true);
+    bool cursorInWindow(QWidget* widget);
+
+private:
+    // 多媒体线程
+    std::unique_ptr<ReadThread> m_packetReadThread;
+    std::unique_ptr<VideoDecodeThread> m_decodeVideoThread;
+    std::unique_ptr<AudioDecodeThread> m_decodeAudioThread;
+    std::unique_ptr<SubtitleDecodeThread> m_decodeSubtitleThread;
+    std::unique_ptr<AudioPlayThread> m_audioPlayThread;
+    std::unique_ptr<VideoPlayThread> m_videoPlayThread;
+
+    // 状态管理
+    std::unique_ptr<VideoStateData> m_videoState;
+    std::unique_ptr<StartPlayThread> m_beforePlayThread;
+    std::unique_ptr<StopWaitingThread> m_stopPlayWaitingThread;
+
+    QString m_currentFile; // 更清晰的命名
+};

+ 406 - 0
AvPlayer2/playlist_window.cpp

@@ -0,0 +1,406 @@
+// ***********************************************************/
+// playlist_window.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// playlist window.
+// ***********************************************************/
+
+#include "playlist_window.h"
+#include "common.h"
+#include "mainwindowa.h"
+#include "packets_sync.h"
+#include "play_control_window.h"
+
+PlayListWnd::PlayListWnd(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::PlayList>())
+{
+    ui->setupUi(this);
+    setLayout(ui->gridLayout);
+
+    auto flags = windowFlags();
+    flags |= Qt::Window;
+    flags |= Qt::WindowStaysOnTopHint;
+    flags &= (~Qt::WindowContextHelpButtonHint);
+
+    setWindowFlags(flags);
+    setWindowModality(Qt::NonModal);
+
+    setAcceptDrops(true);
+
+    create_temp_menu();
+    init_list();
+    clear_data_files();
+    update_table_list();
+}
+
+void PlayListWnd::init_list()
+{
+    auto pTable = get_table();
+
+    QStringList headerLabels;
+    // headerLabels << "#" << "Title" << "Duration" << "Path";
+    headerLabels << "Title"
+                 << "Duration"
+                 << "Path";
+    pTable->setColumnCount(headerLabels.size());
+    pTable->setHorizontalHeaderLabels(headerLabels);
+
+    auto header = pTable->horizontalHeader();
+    header->setFixedHeight(35);
+    // header->setSectionResizeMode(QHeaderView::Stretch);
+    // header->setSectionResizeMode(1, QHeaderView::ResizeToContents); //column
+    // duration.
+    header->setStretchLastSection(true);
+
+    pTable->verticalHeader()->setVisible(false);
+    pTable->setShowGrid(false);
+    // pTable->setStyleSheet("QTableView {selection-background-color: red;}");
+    pTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    pTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+    pTable->setSelectionMode(QAbstractItemView::SingleSelection);
+
+    connect(pTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellSelected(int, int)));
+
+    pTable->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(pTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayMenu(const QPoint&)));
+}
+
+bool PlayListWnd::add_data_file(const QString& file)
+{
+    PlayListLine data;
+    data.file = file;
+    if (is_local(data.file))
+    {
+        data.fileName = get_file_name(data.file);
+        data.duration = get_file_duration(data.file);
+    }
+    else
+    {
+        data.fileName = "Unknow"; // not handled
+        data.duration = "--:--";
+
+        // if (auto parent = (MainWindowA*) parentWidget()) {
+        //     YoutubeJsonParser::YtStreamData st_data;
+        //     if (parent->find_yt_list(data.file, st_data))
+        //     {
+        //         data.fileName = st_data.title;
+        //         data.duration = get_file_duration(data.file);
+        //     }
+        // }
+    }
+
+    m_dataItems.insert({file, data});
+    return true;
+}
+
+inline bool PlayListWnd::already_in(const QString& file) const
+{
+    return m_dataItems.find(file) != m_dataItems.end();
+}
+
+void PlayListWnd::del_data_file(const QString& file)
+{
+    if (already_in(file))
+        m_dataItems.erase(file);
+}
+
+inline QString PlayListWnd::get_data_file(int id) const
+{
+    auto it = m_dataItems.begin();
+    std::advance(it, id);
+    return it->first;
+}
+
+void PlayListWnd::clear_data_files()
+{
+    m_dataItems.clear();
+}
+
+void PlayListWnd::add_table_line(const PlayListLine& data)
+{
+    const auto pTable = get_table();
+    const int count = pTable->rowCount();
+    pTable->setRowCount(count + 1);
+
+    int col = 0;
+    pTable->setItem(count, col++, new QTableWidgetItem(data.fileName));
+    pTable->setItem(count, col++, new QTableWidgetItem(data.duration));
+
+    auto file = toNativePath(data.file);
+    pTable->setItem(count, col++, new QTableWidgetItem(file));
+    pTable->setRowHeight(count, 16);
+}
+
+void PlayListWnd::clear_table_list()
+{
+    const auto pTable = get_table();
+    while (pTable->rowCount() > 0)
+        pTable->removeRow(0);
+}
+
+void PlayListWnd::update_table_list()
+{
+    clear_table_list();
+
+    for (auto const& i : m_dataItems)
+        add_table_line(i.second);
+
+    set_cur_palyingfile();
+}
+
+void PlayListWnd::cellSelected(int row, int col)
+{
+    auto file = get_data_file(row);
+    qDebug() << "file clicked:"
+             << "row:" << row << "col:" << col << "file:" << file;
+    emit play_file(file);
+}
+
+QString PlayListWnd::get_cursel_file() const
+{
+    const auto pTable = get_table();
+    return get_data_file(pTable->currentRow());
+}
+
+QString PlayListWnd::get_row_file(int row) const
+{
+    return get_data_file(row);
+}
+
+void PlayListWnd::add_files(const QStringList& files)
+{
+    for (int i = 0; i < files.size(); i++)
+        add_file(files[i]);
+}
+
+void PlayListWnd::add_file(const QString& file)
+{
+    if (file.isEmpty())
+    {
+        qWarning() << "File is empty!\n";
+        return;
+    }
+
+    if (!is_media(file))
+    {
+        qWarning() << "This is not media file, file:" << file;
+        return;
+    }
+
+    add_data_file(file);
+    update_table_list();
+}
+
+void PlayListWnd::closeEvent(QCloseEvent* event)
+{
+    hide();
+    event->ignore();
+    emit hiden();
+}
+
+void PlayListWnd::dropEvent(QDropEvent* event)
+{
+    const auto mimeData = event->mimeData();
+
+    if (!mimeData->hasUrls())
+        return;
+
+    auto urlList = mimeData->urls();
+
+    QStringList files;
+    for (int i = 0; i < urlList.size(); i++)
+        files.append(urlList.at(i).toLocalFile().trimmed());
+
+    add_files(files);
+}
+
+QString PlayListWnd::get_file_name(const QString& path)
+{
+    QFileInfo fileInfo(path);
+    return fileInfo.baseName();
+}
+
+QString PlayListWnd::get_file_duration(const QString& file) const
+{
+    int64_t duration = 0;
+    get_file_info(file.toStdString().c_str(), duration);
+    return get_file_duration(duration);
+}
+
+QString PlayListWnd::get_file_duration(int64_t duration) const
+{
+    int64_t hours = 0, mins = 0, secs = 0, us = 0;
+    get_duration_time(duration, hours, mins, secs, us);
+
+    return PlayControlWnd::get_play_time(hours, mins, secs);
+}
+
+void PlayListWnd::dragEnterEvent(QDragEnterEvent* event)
+{
+    const auto mimeData = event->mimeData();
+    if (mimeData->hasUrls())
+        event->acceptProposedAction();
+    event->accept();
+}
+
+void PlayListWnd::keyPressEvent(QKeyEvent* event)
+{
+    if (event->key() == Qt::Key_Escape)
+    {
+        hide();
+        event->accept();
+    }
+    else
+    {
+        QWidget::keyPressEvent(event);
+    }
+}
+
+void PlayListWnd::set_sel_file(const QString& file)
+{
+    if (const auto pTable = get_table())
+    {
+        pTable->selectRow(0); // default
+        for (int i = 0; i < pTable->rowCount(); i++)
+        {
+            if (get_row_file(i) == file)
+                pTable->selectRow(i);
+        }
+    }
+}
+
+void PlayListWnd::get_files(QStringList& files) const
+{
+    for (auto const& i : m_dataItems)
+        files.append(i.first);
+}
+
+void PlayListWnd::deleteBtn_clicked()
+{
+    if (auto sel = get_cursel_file(); !sel.isEmpty())
+    {
+        del_data_file(sel);
+        update_table_list();
+    }
+}
+
+void PlayListWnd::clearBtn_clicked()
+{
+    clear_data_files();
+    update_table_list();
+}
+
+bool PlayListWnd::saveBtn_clicked()
+{
+    QStringList files;
+    get_files(files);
+    if (files.size() <= 0)
+    {
+        qWarning() << "Nothing in playlist!";
+        return false;
+    }
+
+    auto dir = QDir::currentPath();
+    auto fileName = QFileDialog::getSaveFileName(this, tr("Save Playlist File"), dir, tr("Playlist (*.pl)"));
+    if (fileName.isEmpty())
+        return false;
+
+    dir = QDir(fileName).absolutePath();
+
+    QFile file(fileName);
+    if (file.open(QIODevice::WriteOnly))
+    {
+        QTextStream stream(&file);
+        stream.setCodec("UTF-8");
+        stream << files.join(PLAYLIST_SEPERATE_CHAR) << Qt::endl;
+        stream.flush();
+
+        file.close();
+
+        emit playlist_file_saved(fileName);
+        return true;
+    }
+    return false;
+}
+
+void PlayListWnd::update_files(const QStringList& files)
+{
+    clear_data_files();
+    add_files(files);
+}
+
+void PlayListWnd::displayMenu(const QPoint& pos)
+{
+    if (auto pTable = get_table())
+    {
+        if (pTable->rowCount() <= 0)
+            return;
+
+        if (m_tmpMenu)
+            m_tmpMenu->exec(pTable->viewport()->mapToGlobal(pos));
+    }
+}
+
+void PlayListWnd::create_temp_menu()
+{
+    m_tmpMenu = std::make_unique<QMenu>(this);
+    auto del_act = m_tmpMenu->addAction("Delete");
+    auto clear_act = m_tmpMenu->addAction("Clear");
+    auto save_act = m_tmpMenu->addAction("Save");
+
+    connect(del_act, &QAction::triggered, this, &PlayListWnd::deleteBtn_clicked);
+    connect(clear_act, &QAction::triggered, this, &PlayListWnd::clearBtn_clicked);
+    connect(save_act, &QAction::triggered, this, &PlayListWnd::saveBtn_clicked);
+}
+
+void PlayListWnd::set_cur_palyingfile()
+{
+    if (auto pParent = (MainWindowA*) parent())
+        set_sel_file(pParent->get_playingfile());
+}
+
+bool PlayListWnd::is_local(const QString& file)
+{
+    return QUrl::fromUserInput(file).isLocalFile();
+}
+
+QString PlayListWnd::mimeType(const QString& file)
+{
+    auto mimeType = QMimeDatabase().mimeTypeForFile(file);
+    qDebug() << file << ", MIME info:";
+    qDebug() << "name:" << mimeType.name();
+    qDebug() << "comment:" << mimeType.comment();
+    qDebug() << "genericIconName:" << mimeType.genericIconName();
+    qDebug() << "iconName:" << mimeType.iconName();
+    qDebug() << "globPatterns:" << mimeType.globPatterns();
+    qDebug() << "parentMimeTypes:" << mimeType.parentMimeTypes();
+    qDebug() << "allAncestors:" << mimeType.allAncestors();
+    qDebug() << "aliases:" << mimeType.aliases();
+    qDebug() << "suffixes:" << mimeType.suffixes();
+    qDebug() << "preferredSuffix:" << mimeType.preferredSuffix();
+    qDebug() << "filterString:" << mimeType.filterString();
+
+    return QMimeDatabase().mimeTypeForFile(file).name();
+}
+
+bool PlayListWnd::is_media(const QString& file) const
+{
+    if (!is_local(file)) // assume all network url are media files
+        return true;
+
+    auto mimetype = mimeType(file);
+    auto mimetypes = mimetype.split("/");
+    if (mimetypes[0] == "video" || mimetypes[0] == "audio")
+        return true;
+
+#if 0
+	QStringList mimes = { "video/mp4","video/x-matroska","video/webm","audio/x-wav" };
+	if (mimes.contains(mimetype, Qt::CaseInsensitive)) {
+		return true;
+	}
+	else {
+		qWarning() << "Not handled, MIME type:" << mimetype;
+	}
+#endif
+    return false;
+}

+ 92 - 0
AvPlayer2/playlist_window.h

@@ -0,0 +1,92 @@
+#pragma once
+
+#include <QDebug>
+#include <QDialog>
+#include <QDropEvent>
+#include <QFileInfo>
+#include <QLabel>
+#include <QMenu>
+#include <QMimeData>
+#include <QMimeDatabase>
+#include <memory>
+#include <set>
+#include "ui_playlist_window.h"
+
+QT_BEGIN_NAMESPACE
+namespace Ui
+{
+class PlayList;
+}
+QT_END_NAMESPACE
+
+#define PLAYLIST_SEPERATE_CHAR "\n"
+
+class PlayListWnd : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit PlayListWnd(QWidget* parent = Q_NULLPTR);
+    ~PlayListWnd(){};
+
+public:
+    typedef struct
+    {
+        QString fileName;
+        QString file;
+        QString duration;
+    } PlayListLine;
+
+public:
+    void add_file(const QString& file);
+    void add_files(const QStringList& files);
+    void get_files(QStringList& files) const;
+    void update_files(const QStringList& files);
+    void set_cur_palyingfile();
+
+signals:
+    void play_file(const QString& file);
+    void save_playlist_signal(const QStringList& files);
+    void hiden();
+    void playlist_file_saved(const QString& file);
+
+public slots:
+    void cellSelected(int row, int col);
+    void deleteBtn_clicked();
+    void clearBtn_clicked();
+    bool saveBtn_clicked();
+    void displayMenu(const QPoint& pos);
+
+protected:
+    void closeEvent(QCloseEvent* event) override;
+    void dropEvent(QDropEvent* event) override;
+    void dragEnterEvent(QDragEnterEvent* event) override;
+    void keyPressEvent(QKeyEvent* event) override;
+
+private:
+    inline QTableWidget* get_table() const { return ui->tableWidget; }
+    static QString get_file_name(const QString& path);
+    inline bool already_in(const QString& file) const;
+    QString get_cursel_file() const;
+    QString get_row_file(int row) const;
+    void init_list();
+    void add_table_line(const PlayListLine& data);
+    void update_table_list();
+    void clear_table_list();
+    bool add_data_file(const QString& file);
+    void del_data_file(const QString& file);
+    inline QString get_data_file(int id) const;
+    void clear_data_files();
+    void set_sel_file(const QString& file);
+    QString get_file_duration(const QString& file) const;
+    QString get_file_duration(int64_t duration) const;
+    void create_temp_menu();
+    static QString mimeType(const QString& filePath);
+    static bool is_local(const QString& file);
+    bool is_media(const QString& file) const;
+
+private:
+    std::unique_ptr<Ui::PlayList> ui;
+    std::unique_ptr<QMenu> m_tmpMenu;
+    std::map<QString, PlayListLine> m_dataItems;
+};

+ 51 - 0
AvPlayer2/playlist_window.ui

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PlayList</class>
+ <widget class="QWidget" name="PlayList">
+  <property name="windowModality">
+   <enum>Qt::WindowModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>560</width>
+    <height>360</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>560</width>
+    <height>360</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Playlist</string>
+  </property>
+  <property name="locale">
+   <locale language="English" country="NewZealand"/>
+  </property>
+  <widget class="QWidget" name="layoutWidget">
+   <property name="geometry">
+    <rect>
+     <x>7</x>
+     <y>6</y>
+     <width>331</width>
+     <height>261</height>
+    </rect>
+   </property>
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <widget class="QTableWidget" name="tableWidget">
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 259 - 0
AvPlayer2/qimage_operation.cpp

@@ -0,0 +1,259 @@
+// ***********************************************************/
+// qimage_operation.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// QImage processing for CV effects.
+// ***********************************************************/
+
+#include "qimage_operation.h"
+
+void image_info(const QImage& img)
+{
+    qDebug("QImage: w:%d, h:%d, (format:%d,alpha:%d,grey:%d,null:%d),"
+           "color count:%d, size(%d,%d), sizeinbytes:%lld,"
+           "depth:%d, devicePixelRatio:%f, dotsX:%d, dotsY:%d,"
+           "offset(%d,%d), rect(%d,%d,%d,%d),"
+           "(hMM:%d,wMM:%d,logDpiX:%d,logDpiY:%d,phsDpiX:%d,phsDpiY:%d)",
+           img.width(), img.height(), img.format(), img.hasAlphaChannel(),
+           img.isGrayscale(), img.isNull(), img.colorCount(), img.size().width(),
+           img.size().height(), img.sizeInBytes(), img.depth(),
+           img.devicePixelRatio(), img.dotsPerMeterX(), img.dotsPerMeterY(),
+           img.offset().x(), img.offset().y(), img.rect().x(), img.rect().y(),
+           img.rect().width(), img.rect().height(), img.heightMM(), img.widthMM(),
+           img.logicalDpiX(), img.logicalDpiY(), img.physicalDpiX(),
+           img.physicalDpiY());
+}
+
+void create_image(QImage& img)
+{
+    image_info(img);
+
+    /*int w = img.width();
+  int h = img.height();*/
+
+    // QRgb value;
+    // value = qRgb(237, 187, 51); // 0xffedba31
+    // image.setPixel(2, 1, value);
+
+    int n = 50;
+    QRgb value[50];
+    for (int i = 0; i < n; i++)
+        value[i] = int(0xff000000 + 0xffffff * i / n);
+
+    Q_ASSERT(img.format() == QImage::Format_RGB32);
+    random_image(img);
+}
+
+bool draw_img_text(QImage& img, const QString& str, const QRect rt, QPen pen, QFont font)
+{
+    QPainter p;
+    if (!p.begin(&img))
+        return false;
+
+    p.setPen(pen);
+    p.setFont(font);
+    p.drawText(rt, Qt::AlignCenter, str);
+    return p.end();
+}
+
+bool draw_img_rect(QImage& img, const QRect rt, QPen pen)
+{
+    QPainter p;
+    if (!p.begin(&img))
+        return false;
+
+    p.setPen(pen);
+    p.drawRect(rt);
+    return p.end();
+}
+
+void grey_image(QImage& img)
+{
+#if 1
+    img = img.convertToFormat(QImage::Format_Grayscale8);
+#else
+    int depth = sizeof(QRgb); // 4
+    for (int i = 0; i < img.height(); i++)
+    {
+        uchar* scan = img.scanLine(i);
+        for (int j = 0; j < img.width(); j++)
+        {
+            QRgb* rgbpixel = reinterpret_cast<QRgb*>(scan + j * depth);
+            int gray = qGray(*rgbpixel);
+            *rgbpixel = QColor(gray, gray, gray).rgb();
+        }
+    }
+#endif
+}
+
+void random_image(QImage& img)
+{
+#if 1
+    int depth = img.depth() / 8;
+    for (int i = 0; i < img.height(); i++)
+    {
+        uchar* scan = img.scanLine(i);
+        for (int j = 0; j < img.width(); j++)
+        {
+            QRgb* rgbpixel = reinterpret_cast<QRgb*>(scan + j * depth);
+
+            int r = QRandomGenerator::global()->generate();
+            int g = QRandomGenerator::global()->generate();
+            int b = QRandomGenerator::global()->generate();
+
+            //*rgbpixel = QColor(r % 255, g % 255, b % 255).rgb();
+            *rgbpixel = qRgb(r, g, b);
+        }
+    }
+#else
+    for (int i = 0; i < img.height(); i++)
+    {
+        for (int j = 0; j < img.width(); j++)
+        {
+            // img.setPixel(j, i, value[w % n]);
+            int r = QRandomGenerator::global()->generate();
+            int g = QRandomGenerator::global()->generate();
+            int b = QRandomGenerator::global()->generate();
+            img.setPixel(j, i, qRgb(r % 255, g % 255, b % 255));
+        }
+    }
+#endif
+}
+
+void split_image(QImage& img, QImage& r_img, QImage& b_img, QImage& g_img)
+{
+    Q_ASSERT(img.size() == r_img.size());
+    Q_ASSERT(r_img.size() == b_img.size());
+    Q_ASSERT(r_img.size() == g_img.size());
+
+    int depth = img.depth() / 8;
+    for (int i = 0; i < img.height(); i++)
+    {
+        uchar* scan = img.scanLine(i);
+
+        uchar* r_scan = r_img.scanLine(i);
+        uchar* b_scan = b_img.scanLine(i);
+        uchar* g_scan = g_img.scanLine(i);
+
+        for (int j = 0; j < img.width(); j++)
+        {
+            QRgb* all_rgbpixel = reinterpret_cast<QRgb*>(scan + j * depth);
+
+            QRgb* r_rgbpixel = reinterpret_cast<QRgb*>(r_scan + j * depth);
+            QRgb* g_rgbpixel = reinterpret_cast<QRgb*>(g_scan + j * depth);
+            QRgb* b_rgbpixel = reinterpret_cast<QRgb*>(b_scan + j * depth);
+
+            uint r = qRed(*all_rgbpixel);
+            uint g = qGreen(*all_rgbpixel);
+            uint b = qBlue(*all_rgbpixel);
+
+            *r_rgbpixel = qRgb(r, 0, 0);
+            *g_rgbpixel = qRgb(0, g, 0);
+            *b_rgbpixel = qRgb(0, 0, b);
+        }
+    }
+}
+
+void invert_image(QImage& img)
+{
+    img.invertPixels();
+}
+
+void mirro_image(QImage& img, bool horizontal, bool vertical)
+{
+    img = img.mirrored(horizontal, vertical);
+}
+
+void swap_image(QImage& img)
+{
+    img = img.rgbSwapped();
+}
+
+void scale_image(QImage& img, int width, int height)
+{
+    img =
+        img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
+}
+
+void transform_image(QImage& img, const QTransform& matrix, Qt::TransformationMode mode)
+{
+    img = img.transformed(matrix, mode);
+}
+
+QImage gamma_image(const QImage& img, double exp)
+{
+    QImage retImg(img);
+
+    int depth = img.depth() / 8;
+    for (int i = 0; i < img.height(); i++)
+    {
+        const uchar* scan = img.scanLine(i);
+        uchar* ret_scan = retImg.scanLine(i);
+
+        for (int j = 0; j < img.width(); j++)
+        {
+            const QRgb* rgbpixel = reinterpret_cast<const QRgb*>(scan + j * depth);
+
+            QRgb* ret_rgbpixel = reinterpret_cast<QRgb*>(ret_scan + j * depth);
+
+            const double r = qRed(*rgbpixel) / 255.0;
+            const double g = qGreen(*rgbpixel) / 255.0;
+            const double b = qBlue(*rgbpixel) / 255.0;
+
+            *ret_rgbpixel = QColor(255 * std::pow(r, exp), 255 * std::pow(g, exp),
+                                   255 * std::pow(b, exp))
+                                .rgb();
+        }
+    }
+
+    return retImg;
+}
+
+/*
+QImage applyEffectToImage(QImage& src, QGraphicsEffect* effect, int extent)
+{
+        QGraphicsScene scene;
+        QGraphicsPixmapItem item;
+        item.setPixmap(QPixmap::fromImage(src));
+        item.setGraphicsEffect(effect);
+        scene.addItem(&item);
+        QImage res(src.size() + QSize(extent * 2, extent * 2),
+QImage::Format_ARGB32); res.fill(Qt::transparent); QPainter ptr(&res);
+        scene.render(&ptr, QRectF(), QRectF(-extent, -extent, src.width() +
+extent * 2, src.height() + extent * 2)); return res;
+}
+
+QImage blur_img(QImage& img, int radius, int extent)
+{
+        QGraphicsBlurEffect* e = new QGraphicsBlurEffect();
+        e->setBlurRadius(radius);
+        return applyEffectToImage(img, e, extent);
+}
+
+QImage dropshadow_img(QImage& img, int radius, int offsetX, int offsetY, QColor
+color, int extent)
+{
+        QGraphicsDropShadowEffect* e = new QGraphicsDropShadowEffect();
+        e->setColor(color);
+        e->setOffset(offsetX, offsetY);
+        e->setBlurRadius(radius);
+        return applyEffectToImage(img, e, extent);
+}
+
+QImage colorize_img(QImage& img, QColor color, double strength)
+{
+        QGraphicsColorizeEffect* e = new QGraphicsColorizeEffect();
+        e->setColor(color);
+        e->setStrength(strength);
+        return applyEffectToImage(img, e);
+}
+
+QImage opacity_img(QImage& img, double opacity)
+{
+        QGraphicsOpacityEffect* e = new QGraphicsOpacityEffect();
+        e->setOpacity(opacity);
+        //e->setOpacityMask();
+        return applyEffectToImage(img, e);
+}
+*/

+ 37 - 0
AvPlayer2/qimage_operation.h

@@ -0,0 +1,37 @@
+#ifndef QIMAGE_OPERATION_H
+#define QIMAGE_OPERATION_H
+#include <QImage>
+#include <QRandomGenerator>
+#include <QPainter>
+#include <QPen>
+#include <QFont>
+#include <QGraphicsEffect>
+#include <QGraphicsScene>
+#include <QGraphicsPixmapItem>
+#include <QGraphicsColorizeEffect>
+
+void image_info(const QImage& img);
+void create_image(QImage& img);
+bool draw_img_text(QImage& img, const QString& str, const QRect rt,
+                   QPen pen = QPen(Qt::red), QFont font = QFont("Times", 48, QFont::Bold));
+
+bool draw_img_rect(QImage& img, const QRect rt, QPen pen = QPen(Qt::red));
+void grey_image(QImage& img);
+void random_image(QImage& img);
+void split_image(QImage& img, QImage& r_img, QImage& b_img, QImage& g_img);
+void invert_image(QImage& img);
+void mirro_image(QImage& img, bool horizontal = true, bool vertical = false);
+void swap_image(QImage& img); //rbg<==>bgr
+void scale_image(QImage& img, int width, int height);
+
+void transform_image(QImage& img, const QTransform& matrix, Qt::TransformationMode mode = Qt::FastTransformation);
+QImage gamma_image(const QImage& img, double exp = 1 / 2.0);
+
+/*
+QImage applyEffectToImage(QImage& src, QGraphicsEffect* effect, int extent = 0);
+QImage blur_img(QImage& img, int radius = 5, int extent = 0);
+QImage dropshadow_img(QImage& img, int radius = 5, int offsetX = 0, int offsetY = 0, QColor color = QColor(40, 40, 40, 255), int extent = 0);
+QImage colorize_img(QImage& img, QColor color = QColor(40, 40, 40, 255), double strength = 0.5);
+QImage opacity_img(QImage& img, double opacity = 0.5);
+*/
+#endif // QIMAGE_OPERATION_H

+ 8 - 0
AvPlayer2/qmake_qmake_qm_files.qrc

@@ -0,0 +1,8 @@
+<RCC>
+    <qresource prefix="/i18n">
+        <file alias="VideoPlayer_en_001.qm">VideoPlayer_en_001.qm</file>
+    </qresource>
+    <qresource prefix="/images">
+        <file>res/bkground.png</file>
+    </qresource>
+</RCC>

+ 228 - 0
AvPlayer2/read_thread.cpp

@@ -0,0 +1,228 @@
+// ***********************************************************/
+// read_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// read packets thread.
+// ***********************************************************/
+
+#include "read_thread.h"
+
+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()
+{
+}
+
+void ReadThread::set_video_state(VideoState* pState)
+{
+    assert(pState);
+    m_pPlayData = pState;
+}
+
+int ReadThread::loop_read()
+{
+    int ret = -1;
+    VideoState* is = m_pPlayData;
+    AVPacket* pkt = nullptr;
+    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;
+
+    pkt = av_packet_alloc();
+    if (!pkt)
+    {
+        av_log(nullptr, AV_LOG_FATAL, "Could not allocate packet.\n");
+        ret = AVERROR(ENOMEM);
+        return ret;
+    }
+
+    is->read_thread_exit = 0;
+
+    for (;;)
+    {
+        if (is->abort_request)
+            break;
+
+        if (is->paused != is->last_paused)
+        {
+            is->last_paused = is->paused;
+            if (is->paused)
+                is->read_pause_return = av_read_pause(is->ic);
+            else
+                av_read_play(is->ic);
+        }
+
+        if (is->seek_req)
+        {
+            int64_t seek_target = is->seek_pos;
+            int64_t seek_min =
+                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);
+            if (ret < 0)
+            {
+                av_log(nullptr, AV_LOG_ERROR, "%s: error while seeking\n", is->ic->url);
+            }
+            else
+            {
+                if (is->audio_stream >= 0)
+                    packet_queue_flush(&is->audioq);
+                if (is->subtitle_stream >= 0)
+                    packet_queue_flush(&is->subtitleq);
+                if (is->video_stream >= 0)
+                    packet_queue_flush(&is->videoq);
+                if (is->seek_flags & AVSEEK_FLAG_BYTE)
+                {
+                    set_clock(&is->extclk, NAN, 0);
+                }
+                else
+                {
+                    set_clock(&is->extclk, seek_target / (double)AV_TIME_BASE, 0);
+                }
+            }
+            is->seek_req = 0;
+            is->queue_attachments_req = 1;
+            is->eof = 0;
+            if (is->paused)
+                step_to_next_frame(is);
+        }
+
+        if (is->queue_attachments_req)
+        {
+            if (is->video_st &&
+                is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)
+            {
+                ret = av_packet_ref(pkt, &is->video_st->attached_pic);
+                if (ret < 0)
+                    break;
+                packet_queue_put(&is->videoq, pkt);
+                packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
+            }
+            is->queue_attachments_req = 0;
+        }
+
+        /* if the queue are full, no need to read more */
+        if (infinite_buffer < 1 &&
+            (is->audioq.size + is->videoq.size + is->subtitleq.size >
+                 MAX_QUEUE_SIZE ||
+             (stream_has_enough_packets(is->audio_st, is->audio_stream,
+                                        &is->audioq) &&
+              stream_has_enough_packets(is->video_st, is->video_stream,
+                                        &is->videoq) &&
+              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();
+            continue;
+        }
+
+        ret = av_read_frame(is->ic, pkt);
+        if (ret < 0)
+        {
+            if ((ret == AVERROR_EOF || avio_feof(is->ic->pb)) && !is->eof)
+            {
+                if (is->video_stream >= 0)
+                    packet_queue_put_nullpacket(&is->videoq, pkt, is->video_stream);
+                if (is->audio_stream >= 0)
+                    packet_queue_put_nullpacket(&is->audioq, pkt, is->audio_stream);
+                if (is->subtitle_stream >= 0)
+                    packet_queue_put_nullpacket(&is->subtitleq, pkt, is->subtitle_stream);
+
+                if (is->loop)
+                {
+                    stream_seek(is, 0, 0, 0);
+                }
+                else
+                {
+                    is->eof = 1;
+                    break; // added for auto exit read thread
+                }
+            }
+            if (is->ic->pb && is->ic->pb->error)
+            {
+                break;
+            }
+
+            m_waitMutex.lock();
+            is->continue_read_thread->wait(&m_waitMutex, 10);
+            m_waitMutex.unlock();
+            continue;
+        }
+        else
+        {
+            is->eof = 0;
+        }
+
+        /* check if packet is in play range specified by user, then queue, otherwise
+     * discard */
+        stream_start_time = is->ic->streams[pkt->stream_index]->start_time;
+        pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
+        pkt_in_play_range =
+            duration == AV_NOPTS_VALUE ||
+            (pkt_ts -
+             (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
+                        av_q2d(is->ic->streams[pkt->stream_index]->time_base) -
+                    (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) /
+                        1000000 <=
+                ((double)duration / 1000000);
+        if (pkt->stream_index == is->audio_stream && pkt_in_play_range)
+        {
+            packet_queue_put(&is->audioq, pkt);
+        }
+        else if (pkt->stream_index == is->video_stream && pkt_in_play_range &&
+                 !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC))
+        {
+            packet_queue_put(&is->videoq, pkt);
+        }
+        else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range)
+        {
+            packet_queue_put(&is->subtitleq, pkt);
+        }
+        else
+        {
+            av_packet_unref(pkt);
+        }
+
+        // print_state_info(is);
+    }
+
+    is->read_thread_exit = -1;
+    av_packet_free(&pkt);
+    return 0;
+}
+
+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.");
+    }
+}

+ 28 - 0
AvPlayer2/read_thread.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <QThread>
+#include "packets_sync.h"
+
+class ReadThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    explicit ReadThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    ~ReadThread();
+
+public:
+    void set_video_state(VideoState* pState = nullptr); // call before start
+                                                        // thread
+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;
+};

BIN
AvPlayer2/res/QSS-master.zip


+ 576 - 0
AvPlayer2/res/QSS/AMOLED.qss

@@ -0,0 +1,576 @@
+/*
+AMOLED Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 01/10/2021, 15:49.
+Available at: https://github.com/GTRONICK/QSS/blob/master/AMOLED.qss
+*/
+QMainWindow {
+	background-color:#000000;
+}
+QDialog {
+	background-color:#000000;
+}
+QColorDialog {
+	background-color:#000000;
+}
+QTextEdit {
+	background-color:#000000;
+	color: #a9b7c6;
+}
+QPlainTextEdit {
+	selection-background-color:#f39c12;
+	background-color:#000000;
+	border: 1px solid #FF00FF;
+	color: #a9b7c6;
+}
+QPushButton{
+	border: 1px transparent;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #000000;
+}
+QPushButton::default{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #e67e22;
+	border-width: 1px;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #000000;
+}
+QPushButton:hover{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #FF00FF;
+	border-bottom-width: 1px;
+	border-bottom-radius: 6px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-bottom: 2px;
+	background-color: #000000;
+}
+QPushButton:pressed{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #FF00FF;
+	border-bottom-width: 2px;
+	border-bottom-radius: 6px;
+	border-style: solid;
+	color: #e67e22;
+	padding-bottom: 1px;
+	background-color: #000000;
+}
+QPushButton:disabled{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 2px;
+	border-bottom-radius: 6px;
+	border-style: solid;
+	color: #808086;
+	padding-bottom: 1px;
+	background-color: #000000;
+}
+QToolButton {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #e67e22;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #000000;
+}
+QToolButton:hover{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #e67e22;
+	border-bottom-width: 2px;
+	border-bottom-radius: 6px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-bottom: 1px;
+	background-color: #000000;
+}
+QLineEdit {
+	border-width: 1px; border-radius: 4px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	padding: 0 8px;
+	color: #a9b7c6;
+	background:#000000;
+	selection-background-color:#007b50;
+	selection-color: #FFFFFF;
+}
+QLabel {
+	color: #a9b7c6;
+}
+QLCDNumber {
+	color: #e67e22;
+}
+QProgressBar {
+	text-align: center;
+	color: rgb(240, 240, 240);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	background-color:#000000;
+}
+QProgressBar::chunk {
+	background-color: #e67e22;
+	border-radius: 5px;
+}
+QMenu{
+	background-color:#000000;
+}
+QMenuBar {
+	background:rgb(0, 0, 0);
+	color: #a9b7c6;
+}
+QMenuBar::item {
+  	spacing: 3px; 
+	padding: 1px 4px;
+  	background: transparent;
+}
+QMenuBar::item:selected { 
+  	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #e67e22;
+	border-bottom-width: 1px;
+	border-bottom-radius: 6px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-bottom: 0px;
+	background-color: #000000;
+}
+QMenu::item:selected {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: #e67e22;
+	border-bottom-color: transparent;
+	border-left-width: 2px;
+	color: #FFFFFF;
+	padding-left:15px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+	background-color:#000000;
+}
+QMenu::item {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #a9b7c6;
+	padding-left:17px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+	background-color:#000000;
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:#000000;
+}
+QTabWidget::pane {
+		border-color: rgb(77,77,77);
+		background-color:#000000;
+		border-style: solid;
+		border-width: 1px;
+    	border-radius: 6px;
+}
+QTabBar::tab {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #808086;
+	padding: 3px;
+	margin-left:3px;
+	background-color:#000000;
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #e67e22;
+	border-bottom-width: 2px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-left: 3px;
+	padding-bottom: 2px;
+	margin-left:3px;
+	background-color:#000000;
+}
+
+QCheckBox {
+	color: #a9b7c6;
+	padding: 2px;
+}
+QCheckBox:disabled {
+	color: #808086;
+	padding: 2px;
+}
+
+QCheckBox:hover {
+	border-radius:4px;
+	border-style:solid;
+	padding-left: 1px;
+	padding-right: 1px;
+	padding-bottom: 1px;
+	padding-top: 1px;
+	border-width:1px;
+	border-color: rgb(87, 97, 106);
+	background-color:#000000;
+}
+QCheckBox::indicator:checked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #e67e22;
+	color: #a9b7c6;
+	background-color: #e67e22;
+}
+QCheckBox::indicator:unchecked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #e67e22;
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QRadioButton {
+	color: #a9b7c6;
+	background-color:#000000;
+	padding: 1px;
+}
+QRadioButton::indicator:checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #e67e22;
+	color: #a9b7c6;
+	background-color: #e67e22;
+}
+QRadioButton::indicator:!checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #e67e22;
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QStatusBar {
+	color:#34e8eb;
+}
+QSpinBox {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QDoubleSpinBox {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QTimeEdit {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QDateTimeEdit {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QDateEdit {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QComboBox {
+	color: #a9b7c6;	
+	background: #1e1d23;
+}
+QComboBox:editable {
+	background: #1e1d23;
+	color: #a9b7c6;
+	selection-background-color:#000000;
+}
+QComboBox QAbstractItemView {
+	color: #a9b7c6;	
+	background: #1e1d23;
+	selection-color: #FFFFFF;
+	selection-background-color:#000000;
+}
+QComboBox:!editable:on, QComboBox::drop-down:editable:on {
+	color: #a9b7c6;	
+	background: #1e1d23;
+}
+QFontComboBox {
+	color: #a9b7c6;	
+	background-color:#000000;
+}
+QToolBox {
+	color: #a9b7c6;
+	background-color:#000000;
+}
+QToolBox::tab {
+	color: #a9b7c6;
+	background-color:#000000;
+}
+QToolBox::tab:selected {
+	color: #FFFFFF;
+	background-color:#000000;
+}
+QScrollArea {
+	color: #FFFFFF;
+	background-color:#000000;
+}
+QSlider::groove:horizontal {
+	height: 5px;
+	background: #e67e22;
+}
+QSlider::groove:vertical {
+	width: 5px;
+	background: #e67e22;
+}
+QSlider::handle:horizontal {
+	background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);
+	border: 1px solid #5c5c5c;
+	width: 14px;
+	margin: -5px 0;
+	border-radius: 7px;
+}
+QSlider::handle:vertical {
+	background: qlineargradient(x1:1, y1:1, x2:0, y2:0, stop:0 #b4b4b4, stop:1 #8f8f8f);
+	border: 1px solid #5c5c5c;
+	height: 14px;
+	margin: 0 -5px;
+	border-radius: 7px;
+}
+QSlider::add-page:horizontal {
+    background: white;
+}
+QSlider::add-page:vertical {
+    background: white;
+}
+QSlider::sub-page:horizontal {
+    background: #e67e22;
+}
+QSlider::sub-page:vertical {
+    background: #e67e22;
+}
+QScrollBar:horizontal {
+	max-height: 20px;
+	background: rgb(0,0,0);
+	border: 1px transparent grey;
+	margin: 0px 20px 0px 20px;
+}
+QScrollBar::handle:horizontal {
+	background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(0,0,0);
+	min-width: 25px;
+}
+QScrollBar::handle:horizontal:hover {
+	background: rgb(230, 126, 34);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(0,0,0);
+	min-width: 25px;
+}
+QScrollBar::add-line:horizontal {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+  	background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+  	width: 20px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:hover {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:pressed {
+  	border: 1px solid;
+  	border-color: grey;
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+  	background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+  	width: 20px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:hover {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:pressed {
+  	border: 1px solid;
+  	border-color: grey;
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::left-arrow:horizontal {
+  	border: 1px transparent grey;
+  	border-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+ 	background: rgb(0,0,0);
+}
+QScrollBar::right-arrow:horizontal {
+	border: 1px transparent grey;
+	border-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+ 	background: rgb(0,0,0);
+}
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
+ 	background: none;
+} 
+QScrollBar:vertical {
+	max-width: 20px;
+	background: rgb(0,0,0);
+	border: 1px transparent grey;
+	margin: 20px 0px 20px 0px;
+}
+QScrollBar::add-line:vertical {
+	border: 1px solid;
+  	border-color: rgb(0,0,0);
+  	background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+  	height: 20px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:hover {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:pressed {
+  	border: 1px solid;
+  	border-color: grey;
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+	background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+  	height: 20px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:hover {
+  	border: 1px solid;
+  	border-color: rgb(0,0,0);
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:pressed {
+  	border: 1px solid;
+  	border-color: grey;
+	border-radius: 8px;
+  	background: rgb(230, 126, 34);
+  	height: 16px;
+  	width: 16px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+	QScrollBar::handle:vertical {
+	background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:0.7 rgba(255, 0, 0, 0), stop:0.71 rgb(230, 126, 34), stop:1 rgb(230, 126, 34));
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(0,0,0);
+	min-height: 25px;
+}
+QScrollBar::handle:vertical:hover {
+	background: rgb(230, 126, 34);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(0,0,0);
+	min-heigth: 25px;
+}
+QScrollBar::up-arrow:vertical {
+	border: 1px transparent grey;
+	border-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+ 	background: rgb(0,0,0);
+}
+QScrollBar::down-arrow:vertical {
+  	border: 1px transparent grey;
+  	border-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+ 	background: rgb(0,0,0);
+}
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
+  	background: none;
+}

+ 559 - 0
AvPlayer2/res/QSS/Aqua.qss

@@ -0,0 +1,559 @@
+/*
+Aqua Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 22/01/2019, 07:55.
+Available at: https://github.com/GTRONICK/QSS/blob/master/Aqua.qss
+*/
+QMainWindow {
+	background-color:#ececec;
+}
+QTextEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QPlainTextEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QToolButton {
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QToolButton:hover{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(197, 197, 197), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(197, 197, 197));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QToolButton:pressed{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(142,142,142);
+}
+QPushButton{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QPushButton::default{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QPushButton:hover{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(197, 197, 197), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(197, 197, 197));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QPushButton:pressed{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(142,142,142);
+}
+QPushButton:disabled{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: #808086;
+	padding: 2px;
+	background-color: rgb(142,142,142);
+}
+QLineEdit {
+	border-width: 1px; border-radius: 4px;
+	border-style: solid;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QLabel {
+	color: #000000;
+}
+QLCDNumber {
+	color: rgb(0, 113, 255, 255);
+}
+QProgressBar {
+	text-align: center;
+	color: rgb(240, 240, 240);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(230, 230, 230);
+	border-style: solid;
+	background-color:rgb(207,207,207);
+}
+QProgressBar::chunk {
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
+	border-radius: 10px;
+}
+QMenuBar {
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));
+}
+QMenuBar::item {
+	color: #000000;
+  	spacing: 3px;
+  	padding: 1px 4px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));
+}
+
+QMenuBar::item:selected {
+  	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	color: #FFFFFF;
+}
+QMenu::item:selected {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	border-bottom-color: transparent;
+	border-left-width: 2px;
+	color: #000000;
+	padding-left:15px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+}
+QMenu::item {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 1px;
+	color: #000000;
+	padding-left:17px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:#000000;
+}
+QTabWidget::pane {
+		border-color: rgb(223,223,223);
+		background-color:rgb(226,226,226);
+		border-style: solid;
+		border-width: 2px;
+    	border-radius: 6px;
+}
+QTabBar::tab:first {
+	border-style: solid;
+	border-left-width:1px;
+	border-right-width:0px;
+	border-top-width:1px;
+	border-bottom-width:1px;
+	border-top-color: rgb(209,209,209);
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-bottom-color: rgb(229,229,229);
+	border-top-left-radius: 4px;
+	border-bottom-left-radius: 4px;
+	color: #000000;
+	padding: 3px;
+	margin-left:0px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QTabBar::tab:last {
+	border-style: solid;
+	border-width:1px;
+	border-top-color: rgb(209,209,209);
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-right-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-bottom-color: rgb(229,229,229);
+	border-top-right-radius: 4px;
+	border-bottom-right-radius: 4px;
+	color: #000000;
+	padding: 3px;
+	margin-left:0px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QTabBar::tab {
+	border-style: solid;
+	border-top-width:1px;
+	border-bottom-width:1px;
+	border-left-width:1px;
+	border-top-color: rgb(209,209,209);
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-bottom-color: rgb(229,229,229);
+	color: #000000;
+	padding: 3px;
+	margin-left:0px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	border-style: solid;
+  	border-left-width:1px;
+	border-right-color: transparent;
+	border-top-color: rgb(209,209,209);
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-bottom-color: rgb(229,229,229);
+	color: #FFFFFF;
+	padding: 3px;
+	margin-left:0px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+
+QTabBar::tab:selected, QTabBar::tab:first:selected, QTabBar::tab:hover {
+  	border-style: solid;
+  	border-left-width:1px;
+  	border-bottom-width:1px;
+  	border-top-width:1px;
+	border-right-color: transparent;
+	border-top-color: rgb(209,209,209);
+	border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
+	border-bottom-color: rgb(229,229,229);
+	color: #FFFFFF;
+	padding: 3px;
+	margin-left:0px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+
+QCheckBox {
+	color: #000000;
+	padding: 2px;
+}
+QCheckBox:disabled {
+	color: #808086;
+	padding: 2px;
+}
+
+QCheckBox:hover {
+	border-radius:4px;
+	border-style:solid;
+	padding-left: 1px;
+	padding-right: 1px;
+	padding-bottom: 1px;
+	padding-top: 1px;
+	border-width:1px;
+	border-color: transparent;
+}
+QCheckBox::indicator:checked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	color: #000000;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QCheckBox::indicator:unchecked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	color: #000000;
+}
+QRadioButton {
+	color: 000000;
+	padding: 1px;
+}
+QRadioButton::indicator:checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	color: #a9b7c6;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QRadioButton::indicator:!checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QStatusBar {
+	color:#027f7f;
+}
+QSpinBox {
+	border-style: solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QDoubleSpinBox {
+	border-style: solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QTimeEdit {
+	border-style: solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QDateTimeEdit {
+	border-style: solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+QDateEdit {
+	border-style: solid;
+	border-width: 1px;
+	border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
+}
+
+QToolBox {
+	color: #a9b7c6;
+	background-color:#000000;
+}
+QToolBox::tab {
+	color: #a9b7c6;
+	background-color:#000000;
+}
+QToolBox::tab:selected {
+	color: #FFFFFF;
+	background-color:#000000;
+}
+QScrollArea {
+	color: #FFFFFF;
+	background-color:#000000;
+}
+QSlider::groove:horizontal {
+	height: 5px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
+}
+QSlider::groove:vertical {
+	width: 5px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
+}
+QSlider::handle:horizontal {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	width: 12px;
+	margin: -5px 0;
+	border-radius: 7px;
+}
+QSlider::handle:vertical {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	height: 12px;
+	margin: 0 -5px;
+	border-radius: 7px;
+}
+QSlider::add-page:horizontal {
+    background: rgb(181,181,181);
+}
+QSlider::add-page:vertical {
+    background: rgb(181,181,181);
+}
+QSlider::sub-page:horizontal {
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
+}
+QSlider::sub-page:vertical {
+    background-color: qlineargradient(spread:pad, y1:0.5, x1:1, y2:0.5, x2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
+}
+QScrollBar:horizontal {
+	max-height: 20px;
+	border: 1px transparent grey;
+	margin: 0px 20px 0px 20px;
+}
+QScrollBar:vertical {
+	max-width: 20px;
+	border: 1px transparent grey;
+	margin: 20px 0px 20px 0px;
+}
+QScrollBar::handle:horizontal {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	border-radius: 7px;
+	min-width: 25px;
+}
+QScrollBar::handle:horizontal:hover {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(147, 200, 200);
+	border-radius: 7px;
+	min-width: 25px;
+}
+QScrollBar::handle:vertical {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	border-radius: 7px;
+	min-height: 25px;
+}
+QScrollBar::handle:vertical:hover {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(147, 200, 200);
+	border-radius: 7px;
+	min-height: 25px;
+}
+QScrollBar::add-line:horizontal {
+   border: 2px transparent grey;
+   border-top-right-radius: 7px;
+   border-bottom-right-radius: 7px;
+   background: rgba(34, 142, 255, 255);
+   width: 20px;
+   subcontrol-position: right;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:pressed {
+   border: 2px transparent grey;
+   border-top-right-radius: 7px;
+   border-bottom-right-radius: 7px;
+   background: rgb(181,181,181);
+   width: 20px;
+   subcontrol-position: right;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+   border: 2px transparent grey;
+   border-bottom-left-radius: 7px;
+   border-bottom-right-radius: 7px;
+   background: rgba(34, 142, 255, 255);
+   height: 20px;
+   subcontrol-position: bottom;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:pressed {
+   border: 2px transparent grey;
+   border-bottom-left-radius: 7px;
+   border-bottom-right-radius: 7px;
+   background: rgb(181,181,181);
+   height: 20px;
+   subcontrol-position: bottom;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal {
+   border: 2px transparent grey;
+   border-top-left-radius: 7px;
+   border-bottom-left-radius: 7px;
+   background: rgba(34, 142, 255, 255);
+   width: 20px;
+   subcontrol-position: left;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:pressed {
+   border: 2px transparent grey;
+   border-top-left-radius: 7px;
+   border-bottom-left-radius: 7px;
+   background: rgb(181,181,181);
+   width: 20px;
+   subcontrol-position: left;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical {
+   border: 2px transparent grey;
+   border-top-left-radius: 7px;
+   border-top-right-radius: 7px;
+   background: rgba(34, 142, 255, 255);
+   height: 20px;
+   subcontrol-position: top;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:pressed {
+   border: 2px transparent grey;
+   border-top-left-radius: 7px;
+   border-top-right-radius: 7px;
+   background: rgb(181,181,181);
+   height: 20px;
+   subcontrol-position: top;
+   subcontrol-origin: margin;
+}
+QScrollBar::left-arrow:horizontal {
+   border: 1px transparent grey;
+   border-top-left-radius: 3px;
+   border-bottom-left-radius: 3px;
+   width: 6px;
+   height: 6px;
+   background: white;
+}
+QScrollBar::right-arrow:horizontal {
+   border: 1px transparent grey;
+   border-top-right-radius: 3px;
+   border-bottom-right-radius: 3px;
+   width: 6px;
+   height: 6px;
+   background: white;
+}
+QScrollBar::up-arrow:vertical {
+   border: 1px transparent grey;
+   border-top-left-radius: 3px;
+   border-top-right-radius: 3px;
+   width: 6px;
+   height: 6px;
+   background: white;
+}
+QScrollBar::down-arrow:vertical {
+   border: 1px transparent grey;
+   border-bottom-left-radius: 3px;
+   border-bottom-right-radius: 3px;
+   width: 6px;
+   height: 6px;
+   background: white;
+}
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
+   background: none;
+}
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
+   background: none;
+}

+ 181 - 0
AvPlayer2/res/QSS/ConsoleStyle.qss

@@ -0,0 +1,181 @@
+/*
+Dark Console Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 24/05/2018, 17:12.
+Available at: https://github.com/GTRONICK/QSS/blob/master/ConsoleStyle.qss
+*/
+QWidget {
+	background-color:rgb(0, 0, 0);
+	color: rgb(240, 240, 240);
+	border-color: rgb(58, 58, 58);
+}
+
+QPlainTextEdit {
+	background-color:rgb(0, 0, 0);
+	color: rgb(200, 200, 200);
+	selection-background-color: rgb(255, 153, 0);
+	selection-color: rgb(0, 0, 0);
+}
+
+QTabWidget::pane {
+    	border-top: 1px solid #000000;
+}
+
+QTabBar::tab {
+ 	background-color:rgb(0, 0, 0);
+ 	border-style: outset;
+	border-width: 1px;
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+  border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-top-width: 0px;
+	border-style: solid;
+	color: rgb(255, 153, 0);
+	padding: 4px;
+}
+
+QTabBar::tab:selected, QTabBar::tab:hover {
+   color: rgb(255, 255, 255);
+   background-color:rgb(0, 0, 0);
+   border-color:rgb(42, 42, 42);
+   margin-left: 0px;
+   margin-right: 0px;
+   border-bottom-right-radius:4px;
+   border-bottom-left-radius:4px;
+}
+
+QTabBar::tab:last:selected {
+  background-color:rgb(0, 0, 0);
+	border-color:rgb(42, 42, 42);
+	margin-left: 0px;
+  	margin-right: 0px;
+	border-bottom-right-radius:4px;
+	border-bottom-left-radius:4px;
+}
+
+QTabBar::tab:!selected {
+   margin-bottom: 4px;
+   border-bottom-right-radius:4px;
+   border-bottom-left-radius:4px;
+}
+
+QPushButton{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 6px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
+}
+
+QPushButton:hover{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-bottom-color: rgb(115, 115, 115);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 6px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(107, 107, 107, 255), stop:1 rgba(157, 157, 157, 255));
+}
+
+QPushButton:pressed{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(62, 62, 62, 255), stop:1 rgba(22, 22, 22, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 6px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
+}
+
+QPushButton:disabled{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(0, 0, 0);
+	padding: 6px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(57, 57, 57, 255), stop:1 rgba(77, 77, 77, 255));
+}
+
+QLineEdit {
+	border-width: 1px; border-radius: 4px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	padding: 0 8px;
+	color: rgb(255, 255, 255);
+	background:rgb(101, 101, 101);
+	selection-background-color: rgb(187, 187, 187);
+	selection-color: rgb(60, 63, 65);
+}
+
+QProgressBar {
+	text-align: center;
+	color: rgb(255, 255, 255);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+}
+
+QProgressBar::chunk {
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(0, 200, 0, 255), stop:1 rgba(30, 230, 30, 255));
+	border-radius: 10px;
+}
+
+QMenuBar {
+	background:rgb(0, 0, 0);
+	color: rgb(255, 153, 0);
+}
+
+QMenuBar::item {
+  	spacing: 3px; 
+	padding: 1px 4px;
+  	background: transparent;
+}
+
+QMenuBar::item:selected { 
+  	background:rgb(115, 115, 115);
+}
+
+QMenu {
+	border-width: 2px; 
+	border-radius: 10px;
+	border-color: rgb(255, 153, 0);
+	border-style: outset;
+}
+
+QMenu::item {
+	spacing: 3px; 
+	padding: 3px 15px;
+}
+
+QMenu::item:selected {
+	spacing: 3px; 
+	padding: 3px 15px;
+	background:rgb(115, 115, 115);
+	color:rgb(255, 255, 255);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+}

+ 196 - 0
AvPlayer2/res/QSS/ElegantDark.qss

@@ -0,0 +1,196 @@
+/*
+ElegantDark Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 17/04/2018
+Available at: https://github.com/GTRONICK/QSS/blob/master/ElegantDark.qss
+*/
+QMainWindow {
+	background-color:rgb(82, 82, 82);
+}
+QTextEdit {
+	background-color:rgb(42, 42, 42);
+	color: rgb(0, 255, 0);
+}
+QPushButton{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
+}
+QPushButton:hover{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
+	border-bottom-color: rgb(115, 115, 115);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(107, 107, 107, 255), stop:1 rgba(157, 157, 157, 255));
+}
+QPushButton:pressed{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(62, 62, 62, 255), stop:1 rgba(22, 22, 22, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(255, 255, 255);
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
+}
+QPushButton:disabled{
+	border-style: outset;
+	border-width: 2px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: rgb(0, 0, 0);
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(57, 57, 57, 255), stop:1 rgba(77, 77, 77, 255));
+}
+QLineEdit {
+	border-width: 1px; border-radius: 4px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	padding: 0 8px;
+	color: rgb(255, 255, 255);
+	background:rgb(100, 100, 100);
+	selection-background-color: rgb(187, 187, 187);
+	selection-color: rgb(60, 63, 65);
+}
+QLabel {
+	color:rgb(255,255,255);	
+}
+QProgressBar {
+	text-align: center;
+	color: rgb(240, 240, 240);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	background-color:rgb(77,77,77);
+}
+QProgressBar::chunk {
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(87, 97, 106, 255), stop:1 rgba(93, 103, 113, 255));
+	border-radius: 5px;
+}
+QMenuBar {
+	background:rgb(82, 82, 82);
+}
+QMenuBar::item {
+	color:rgb(223,219,210);
+	spacing: 3px;
+	padding: 1px 4px;
+	background: transparent;
+}
+
+QMenuBar::item:selected {
+	background:rgb(115, 115, 115);
+}
+QMenu::item:selected {
+	color:rgb(255,255,255);
+	border-width:2px;
+	border-style:solid;
+	padding-left:18px;
+	padding-right:8px;
+	padding-top:2px;
+	padding-bottom:3px;
+	background:qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(87, 97, 106, 255), stop:1 rgba(93, 103, 113, 255));
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
+	border-bottom-color: rgb(58, 58, 58);
+	border-bottom-width: 1px;
+}
+QMenu::item {
+	color:rgb(223,219,210);
+	background-color:rgb(78,78,78);
+	padding-left:20px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:10px;
+}
+QMenu{
+	background-color:rgb(78,78,78);
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:rgb(247,246,246);
+}
+QTabWidget::pane {
+		border-color: rgb(77,77,77);
+		background-color:rgb(101,101,101);
+		border-style: solid;
+		border-width: 1px;
+    	border-radius: 6px;
+}
+QTabBar::tab {
+	padding:2px;
+	color:rgb(250,250,250);
+  	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
+	border-style: solid;
+	border-width: 2px;
+  	border-top-right-radius:4px;
+   border-top-left-radius:4px;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
+	border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
+	border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
+	border-bottom-color: rgb(101,101,101);
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	background-color:rgb(101,101,101);
+  	margin-left: 0px;
+  	margin-right: 1px;
+}
+QTabBar::tab:!selected {
+    	margin-top: 1px;
+		margin-right: 1px;
+}
+QCheckBox {
+	color:rgb(223,219,210);
+	padding: 2px;
+}
+QCheckBox:hover {
+	border-radius:4px;
+	border-style:solid;
+	padding-left: 1px;
+	padding-right: 1px;
+	padding-bottom: 1px;
+	padding-top: 1px;
+	border-width:1px;
+	border-color: rgb(87, 97, 106);
+	background-color:qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(87, 97, 106, 150), stop:1 rgba(93, 103, 113, 150));
+}
+QCheckBox::indicator:checked {
+	border-radius:4px;
+	border-style:solid;
+	border-width:1px;
+	border-color: rgb(180,180,180);
+  	background-color:qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(87, 97, 106, 255), stop:1 rgba(93, 103, 113, 255));
+}
+QCheckBox::indicator:unchecked {
+	border-radius:4px;
+	border-style:solid;
+	border-width:1px;
+	border-color: rgb(87, 97, 106);
+  	background-color:rgb(255,255,255);
+}
+QStatusBar {
+	color:rgb(240,240,240);
+}

+ 21 - 0
AvPlayer2/res/QSS/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Jaime Quiroga
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 434 - 0
AvPlayer2/res/QSS/MacOS.qss

@@ -0,0 +1,434 @@
+/*
+ * MacOS Style Sheet for QT Applications
+ * Author: Jaime A. Quiroga P.
+ * Company: GTRONICK
+ * Last updated: 25/12/2020, 23:10.
+ * Available at: https://github.com/GTRONICK/QSS/blob/master/MacOS.qss
+ */
+QMainWindow {
+    background-color:#ececec;
+}
+QPushButton, QToolButton, QCommandLinkButton{
+    padding: 0 5px 0 5px;
+    border-style: solid;
+    border-top-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-right-color: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-bottom-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-left-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-width: 2px;
+    border-radius: 8px;
+    color: #616161;
+    font-weight: bold;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #fbfdfd, stop:0.5 #ffffff, stop:1 #fbfdfd);
+}
+QPushButton::default, QToolButton::default, QCommandLinkButton::default{
+    border: 2px solid transparent;
+    color: #FFFFFF;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #84afe5, stop:1 #1168e4);
+}
+QPushButton:hover, QToolButton:hover, QCommandLinkButton:hover{
+    color: #3d3d3d;
+}
+QPushButton:pressed, QToolButton:pressed, QCommandLinkButton:pressed{
+    color: #aeaeae;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #ffffff, stop:0.5 #fbfdfd, stop:1 #ffffff);
+}
+QPushButton:disabled, QToolButton:disabled, QCommandLinkButton:disabled{
+    color: #616161;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #dce7eb, stop:0.5 #e0e8eb, stop:1 #dee7ec);
+}
+QLineEdit, QTextEdit, QPlainTextEdit, QSpinBox, QDoubleSpinBox, QTimeEdit, QDateEdit, QDateTimeEdit {
+    border-width: 2px;
+    border-radius: 8px;
+    border-style: solid;
+    border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-right-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-left-color: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    background-color: #f4f4f4;
+    color: #3d3d3d;
+}
+QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QTimeEdit:focus, QDateEdit:focus, QDateTimeEdit:focus {
+    border-width: 2px;
+    border-radius: 8px;
+    border-style: solid;
+    border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 #85b7e3, stop:1 #9ec1db);
+    border-right-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #85b7e3, stop:1 #9ec1db);
+    border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #85b7e3, stop:1 #9ec1db);
+    border-left-color: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 #85b7e3, stop:1 #9ec1db);
+    background-color: #f4f4f4;
+    color: #3d3d3d;
+}
+QLineEdit:disabled, QTextEdit:disabled, QPlainTextEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QTimeEdit:disabled, QDateEdit:disabled, QDateTimeEdit:disabled {
+    color: #b9b9b9;
+}
+QSpinBox::up-button, QDoubleSpinBox::up-button, QTimeEdit::up-button, QDateEdit::up-button, QDateTimeEdit::up-button {
+    subcontrol-origin: padding;
+    subcontrol-position: top right;
+    width: 15px;
+    color: #272727;
+    border-left-width: 1px;
+    border-left-color: darkgray;
+    border-left-style: solid;
+    border-top-right-radius: 3px;
+    padding: 3px;
+}
+QSpinBox::down-button, QDoubleSpinBox::down-button, QTimeEdit::down-button, QDateEdit::down-button, QDateTimeEdit::down-button {
+    subcontrol-origin: padding;
+    subcontrol-position: bottom right;
+    width: 15px;
+    color: #272727;
+    border-left-width: 1px;
+    border-left-color: darkgray;
+    border-left-style: solid;
+    border-bottom-right-radius: 3px;
+    padding: 3px;
+}
+QSpinBox::up-button:pressed, QDoubleSpinBox::up-button:pressed, QTimeEdit::up-button:pressed, QDateEdit::up-button:pressed, QDateTimeEdit::up-button:pressed {
+    color: #aeaeae;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #ffffff, stop:0.5 #fbfdfd, stop:1 #ffffff);
+}
+QSpinBox::down-button:pressed, QDoubleSpinBox::down-button:pressed, QTimeEdit::down-button:pressed, QDateEdit::down-button:pressed, QDateTimeEdit::down-button:pressed {
+    color: #aeaeae;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #ffffff, stop:0.5 #fbfdfd, stop:1 #ffffff);
+}
+QSpinBox::up-button:hover, QDoubleSpinBox::up-button:hover, QTimeEdit::up-button:hover, QDateEdit::up-button:hover, QDateTimeEdit::up-button:hover {
+    color: #FFFFFF;
+    border-top-right-radius: 5px;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #84afe5, stop:1 #1168e4);
+    
+}
+QSpinBox::down-button:hover, QDoubleSpinBox::down-button:hover, QTimeEdit::down-button:hover, QDateEdit::down-button:hover, QDateTimeEdit::down-button:hover {
+    color: #FFFFFF;
+    border-bottom-right-radius: 5px;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #84afe5, stop:1 #1168e4);
+}
+QSpinBox::up-arrow, QDoubleSpinBox::up-arrow, QTimeEdit::up-arrow, QDateEdit::up-arrow, QDateTimeEdit::up-arrow {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-up-symbolic.symbolic.png);
+}
+QSpinBox::down-arrow, QDoubleSpinBox::down-arrow, QTimeEdit::down-arrow, QDateEdit::down-arrow, QDateTimeEdit::down-arrow {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-down-symbolic.symbolic.png);
+}
+QProgressBar {
+    max-height: 8px;
+    text-align: center;
+    font: italic bold 11px;
+    color: #3d3d3d;
+    border: 1px solid transparent;
+    border-radius:4px;
+    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #ddd5d5, stop:0.5 #dad3d3, stop:1 #ddd5d5);
+}
+QProgressBar::chunk {
+    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #467dd1, stop:0.5 #3b88fc, stop:1 #467dd1);
+    border-radius: 4px;
+}
+QProgressBar:disabled {
+    color: #616161;
+}
+QProgressBar::chunk:disabled {
+    background-color: #aeaeae;
+}
+QSlider::groove {
+    border: 1px solid #bbbbbb;
+    background-color: #52595d;
+    border-radius: 4px;
+}
+QSlider::groove:horizontal {
+    height: 6px;
+}
+QSlider::groove:vertical {
+    width: 6px;
+}
+QSlider::handle:horizontal {
+    background: #ffffff;
+    border-style: solid;
+    border-width: 1px;
+    border-color: rgb(207,207,207);
+    width: 12px;
+    margin: -5px 0;
+    border-radius: 7px;
+}
+QSlider::handle:vertical {
+    background: #ffffff;
+    border-style: solid;
+    border-width: 1px;
+    border-color: rgb(207,207,207);
+    height: 12px;
+    margin: 0 -5px;
+    border-radius: 7px;
+}
+QSlider::add-page, QSlider::sub-page {
+    border: 1px transparent;
+    background-color: #52595d;
+    border-radius: 4px;
+}
+QSlider::add-page:horizontal {
+    background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #ddd5d5, stop:0.5 #dad3d3, stop:1 #ddd5d5);
+}
+QSlider::sub-page:horizontal {
+    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #467dd1, stop:0.5 #3b88fc, stop:1 #467dd1);
+}
+QSlider::add-page:vertical {
+    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #467dd1, stop:0.5 #3b88fc, stop:1 #467dd1);
+}
+QSlider::sub-page:vertical {
+    background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #ddd5d5, stop:0.5 #dad3d3, stop:1 #ddd5d5);
+}
+QSlider::add-page:horizontal:disabled, QSlider::sub-page:horizontal:disabled, QSlider::add-page:vertical:disabled, QSlider::sub-page:vertical:disabled {
+    background: #b9b9b9;
+}
+QComboBox, QFontComboBox {
+    border-width: 2px;
+    border-radius: 8px;
+    border-style: solid;
+    border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-right-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    border-left-color: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 #c1c9cf, stop:1 #d2d8dd);
+    background-color: #f4f4f4;
+    color: #272727;
+    padding-left: 5px;
+}
+QComboBox:editable, QComboBox:!editable, QComboBox::drop-down:editable, QComboBox:!editable:on, QComboBox::drop-down:editable:on {
+    background: #ffffff;
+}
+QComboBox::drop-down {
+    subcontrol-origin: padding;
+    subcontrol-position: top right;
+    width: 15px;
+    color: #272727;
+    border-left-width: 1px;
+    border-left-color: darkgray;
+    border-left-style: solid;
+    border-top-right-radius: 3px;
+    border-bottom-right-radius: 3px;
+}
+QComboBox::down-arrow {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-down-symbolic.symbolic.png); /*Adawaita icon thene*/
+}
+
+QComboBox::down-arrow:on {
+    top: 1px;
+    left: 1px;
+}
+QComboBox QAbstractItemView {
+    border: 1px solid darkgray;
+    border-radius: 8px;
+    selection-background-color: #dadada;
+    selection-color: #272727;
+    color: #272727;
+    background: white;
+}
+QLabel, QCheckBox, QRadioButton {
+    color: #272727;
+}
+QCheckBox {
+    padding: 2px;
+}
+QCheckBox:disabled, QRadioButton:disabled {
+    color: #808086;
+    padding: 2px;
+}
+
+QCheckBox:hover {
+    border-radius:4px;
+    border-style:solid;
+    padding-left: 1px;
+    padding-right: 1px;
+    padding-bottom: 1px;
+    padding-top: 1px;
+    border-width:1px;
+    border-color: transparent;
+}
+QCheckBox::indicator:checked {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/object-select-symbolic.symbolic.png);
+    height: 15px;
+    width: 15px;
+    border-style:solid;
+    border-width: 1px;
+    border-color: #48a5fd;
+    color: #ffffff;
+    border-radius: 3px;
+    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #48a5fd, stop:0.5 #329cfb, stop:1 #48a5fd);
+}
+QCheckBox::indicator:unchecked {
+    
+    height: 15px;
+    width: 15px;
+    border-style:solid;
+    border-width: 1px;
+    border-color: #48a5fd;
+    border-radius: 3px;
+    background-color: #fbfdfa;
+}
+QLCDNumber {
+    color: #616161;;
+}
+QMenuBar {
+    background-color: #ececec;
+}
+QMenuBar::item {
+    color: #616161;
+    spacing: 3px;
+    padding: 1px 4px;
+    background-color: #ececec;
+}
+
+QMenuBar::item:selected {
+    background-color: #dadada;
+    color: #3d3d3d;
+}
+QMenu {
+    background-color: #ececec;
+}
+QMenu::item:selected {
+    background-color: #dadada;
+    color: #3d3d3d;
+}
+QMenu::item {
+    color: #616161;;
+    background-color: #e0e0e0;
+}
+QTabWidget {
+    color:rgb(0,0,0);
+    background-color:#000000;
+}
+QTabWidget::pane {
+    border-color: #050a0e;
+    background-color: #e0e0e0;
+    border-width: 1px;
+    border-radius: 4px;
+    position: absolute;
+    top: -0.5em;
+    padding-top: 0.5em;
+}
+
+QTabWidget::tab-bar {
+    alignment: center;
+}
+
+QTabBar::tab {
+    border-bottom: 1px solid #c0c0c0;
+    padding: 3px;
+    color: #272727;
+    background-color: #fefefc;
+    margin-left:0px;
+}
+QTabBar::tab:!last {
+    border-right: 1px solid;
+    border-right-color: #c0c0c0;
+    border-bottom-color: #c0c0c0;
+}
+QTabBar::tab:first {
+    border-top-left-radius: 4px;
+    border-bottom-left-radius: 4px;
+}
+QTabBar::tab:last {
+    border-top-right-radius: 4px;
+    border-bottom-right-radius: 4px;
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+    color: #FFFFFF;
+    background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 #84afe5, stop:1 #1168e4);
+}
+QRadioButton::indicator {
+    height: 14px;
+    width: 14px;
+    border-style:solid;
+    border-radius:7px;
+    border-width: 1px;
+}
+QRadioButton::indicator:checked {
+    border-color: #48a5fd;
+    background-color: qradialgradient(cx:0.5, cy:0.5, radius:0.4,fx:0.5, fy:0.5, stop:0 #ffffff, stop:0.5 #ffffff, stop:0.6 #48a5fd, stop:1 #48a5fd);
+}
+QRadioButton::indicator:!checked {
+    border-color: #a9b7c6;
+    background-color: #fbfdfa;
+}
+QStatusBar {
+    color:#027f7f;
+}
+
+QDial {
+    background: #16a085;
+}
+
+QToolBox {
+    color: #a9b7c6;
+    background-color: #222b2e;
+}
+QToolBox::tab {
+    color: #a9b7c6;
+    background-color:#222b2e;
+}
+QToolBox::tab:selected {
+    color: #FFFFFF;
+    background-color:#222b2e;
+}
+QScrollArea {
+    color: #FFFFFF;
+    background-color:#222b2e;
+}
+
+QScrollBar:horizontal {
+	max-height: 10px;
+	border: 1px transparent grey;
+	margin: 0px 20px 0px 20px;
+	background: transparent;
+}
+QScrollBar:vertical {
+	max-width: 10px;
+	border: 1px transparent grey;
+	margin: 20px 0px 20px 0px;
+	background: transparent;
+}
+QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
+	background: #52595d;
+	border-style: transparent;
+	border-radius: 4px;
+	min-height: 25px;
+}
+QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:hover {
+	background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #467dd1, stop:0.5 #3b88fc, stop:1 #467dd1);
+}
+QScrollBar::add-line, QScrollBar::sub-line {
+    border: 2px transparent grey;
+    border-radius: 4px;
+    subcontrol-origin: margin;
+    background: #b9b9b9;
+}
+QScrollBar::add-line:horizontal {
+    width: 20px;
+    subcontrol-position: right;
+}
+QScrollBar::add-line:vertical {
+    height: 20px;
+    subcontrol-position: bottom;
+}
+QScrollBar::sub-line:horizontal {
+    width: 20px;
+    subcontrol-position: left;
+}
+QScrollBar::sub-line:vertical {
+    height: 20px;
+    subcontrol-position: top;
+}
+QScrollBar::add-line:vertical:pressed, QScrollBar::add-line:horizontal:pressed, QScrollBar::sub-line:horizontal:pressed, QScrollBar::sub-line:vertical:pressed {
+    background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #467dd1, stop:0.5 #3b88fc, stop:1 #467dd1);
+}
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
+    background: none;
+}
+QScrollBar::up-arrow:vertical {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-up-symbolic.symbolic.png);
+}
+QScrollBar::down-arrow:vertical {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-down-symbolic.symbolic.png);
+}
+QScrollBar::left-arrow:horizontal {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-previous-symbolic.symbolic.png);
+}
+QScrollBar::right-arrow:horizontal {
+    image: url(/usr/share/icons/Adwaita/16x16/actions/go-next-symbolic.symbolic.png);
+}

+ 531 - 0
AvPlayer2/res/QSS/ManjaroMix.qss

@@ -0,0 +1,531 @@
+/*
+ManjaroMix Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 25/02/2020, 15:42.
+Available at: https://github.com/GTRONICK/QSS/blob/master/ManjaroMix.qss
+*/
+QMainWindow {
+	background-color:#151a1e;
+}
+QCalendar {
+	background-color: #151a1e;
+}
+QTextEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+	background-color: #222b2e;
+	color: #d3dae3;
+}
+QPlainTextEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+	background-color: #222b2e;
+	color: #d3dae3;
+}
+QToolButton {
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: #d3dae3;
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QToolButton:hover{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(197, 197, 197), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(197, 197, 197));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(195, 195, 195), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(255,255,255);
+}
+QToolButton:pressed{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: rgb(0,0,0);
+	padding: 2px;
+	background-color: rgb(142,142,142);
+}
+QPushButton{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #d3dae3;
+	padding: 2px;
+	background-color: #151a1e;
+}
+QPushButton::default{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #FFFFFF;
+	padding: 2px;
+	background-color: #151a1e;;
+}
+QPushButton:hover{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #d3dae3;
+	padding: 2px;
+	background-color: #1c1f1f;
+}
+QPushButton:pressed{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #d3dae3;
+	padding: 2px;
+	background-color: #2c2f2f;
+}
+QPushButton:disabled{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
+	border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
+	border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
+	border-width: 1px;
+	border-radius: 5px;
+	color: #808086;
+	padding: 2px;
+	background-color: rgb(142,142,142);
+}
+QLineEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+	background-color: #222b2e;
+	color: #d3dae3;
+}
+QLabel {
+	color: #d3dae3;
+}
+QLCDNumber {
+	color: #4d9b87;
+}
+QProgressBar {
+	text-align: center;
+	color: #d3dae3;
+	border-radius: 10px;
+	border-color: transparent;
+	border-style: solid;
+	background-color: #52595d;
+}
+QProgressBar::chunk {
+	background-color: #214037	;
+	border-radius: 10px;
+}
+QMenuBar {
+	background-color: #151a1e;
+}
+QMenuBar::item {
+	color: #d3dae3;
+  	spacing: 3px;
+  	padding: 1px 4px;
+	background-color: #151a1e;
+}
+
+QMenuBar::item:selected {
+  	background-color: #252a2e;
+	color: #FFFFFF;
+}
+QMenu {
+	background-color: #151a1e;
+}
+QMenu::item:selected {
+	background-color: #252a2e;
+	color: #FFFFFF;
+}
+QMenu::item {
+	color: #d3dae3;
+	background-color: #151a1e;
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:#000000;
+}
+QTabWidget::pane {
+		border-color: #050a0e;
+		background-color: #1e282c;
+		border-style: solid;
+		border-width: 1px;
+    	border-bottom-left-radius: 4px;
+		border-bottom-right-radius: 4px;
+}
+QTabBar::tab:first {
+	border-style: solid;
+	border-left-width:1px;
+	border-right-width:0px;
+	border-top-width:1px;
+	border-bottom-width:0px;
+	border-top-color: #050a0e;
+	border-left-color: #050a0e;
+	border-bottom-color: #050a0e;
+	border-top-left-radius: 4px;
+	color: #d3dae3;
+	padding: 3px;
+	margin-left:0px;
+	background-color: #151a1e;
+}
+QTabBar::tab:last {
+	border-style: solid;
+	border-top-width:1px;
+	border-left-width:1px;
+	border-right-width:1px;
+	border-bottom-width:0px;
+	border-color: #050a0e;
+	border-top-right-radius: 4px;
+	color: #d3dae3;
+	padding: 3px;
+	margin-left:0px;
+	background-color: #151a1e;
+}
+QTabBar::tab {
+	border-style: solid;
+	border-top-width:1px;
+	border-bottom-width:0px;
+	border-left-width:1px;
+	border-top-color: #050a0e;
+	border-left-color: #050a0e;
+	border-bottom-color: #050a0e;
+	color: #d3dae3;
+	padding: 3px;
+	margin-left:0px;
+	background-color: #151a1e;
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	border-style: solid;
+  	border-left-width:1px;
+	border-bottom-width:0px;
+	border-right-color: transparent;
+	border-top-color: #050a0e;
+	border-left-color: #050a0e;
+	border-bottom-color: #050a0e;
+	color: #FFFFFF;
+	padding: 3px;
+	margin-left:0px;
+	background-color: #1e282c;
+}
+
+QTabBar::tab:selected, QTabBar::tab:first:selected, QTabBar::tab:hover {
+  	border-style: solid;
+  	border-left-width:1px;
+  	border-bottom-width:0px;
+  	border-top-width:1px;
+	border-right-color: transparent;
+	border-top-color: #050a0e;
+	border-left-color: #050a0e;
+	border-bottom-color: #050a0e;
+	color: #FFFFFF;
+	padding: 3px;
+	margin-left:0px;
+	background-color: #1e282c;
+}
+
+QCheckBox {
+	color: #d3dae3;
+	padding: 2px;
+}
+QCheckBox:disabled {
+	color: #808086;
+	padding: 2px;
+}
+
+QCheckBox:hover {
+	border-radius:4px;
+	border-style:solid;
+	padding-left: 1px;
+	padding-right: 1px;
+	padding-bottom: 1px;
+	padding-top: 1px;
+	border-width:1px;
+	border-color: transparent;
+}
+QCheckBox::indicator:checked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #4fa08b;
+	color: #000000;
+	background-color: qradialgradient(cx:0.4, cy:0.4, radius: 1.5,fx:0, fy:0, stop:0 #1e282c, stop:0.3 #1e282c, stop:0.4 #4fa08b, stop:0.5 #1e282c, stop:1 #1e282c);
+}
+QCheckBox::indicator:unchecked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #4fa08b;
+	color: #000000;
+}
+QRadioButton {
+	color: #d3dae3;
+	padding: 1px;
+}
+QRadioButton::indicator:checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #4fa08b;
+	color: #a9b7c6;
+	background-color: qradialgradient(cx:0.5, cy:0.5, radius:0.4,fx:0.5, fy:0.5, stop:0 #4fa08b, stop:1 #1e282c);
+}
+QRadioButton::indicator:!checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #4fa08b;
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QStatusBar {
+	color:#027f7f;
+}
+QSpinBox {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QDoubleSpinBox {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QTimeEdit {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QDateTimeEdit {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QDateEdit {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QFontComboBox {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+QComboBox {
+	color: #d3dae3;
+	background-color: #222b2e;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #4fa08b;
+}
+
+QDial {
+	background: #16a085;
+}
+
+QToolBox {
+	color: #a9b7c6;
+	background-color: #222b2e;
+}
+QToolBox::tab {
+	color: #a9b7c6;
+	background-color:#222b2e;
+}
+QToolBox::tab:selected {
+	color: #FFFFFF;
+	background-color:#222b2e;
+}
+QScrollArea {
+	color: #FFFFFF;
+	background-color:#222b2e;
+}
+QSlider::groove:horizontal {
+	height: 5px;
+	background-color: #52595d;
+}
+QSlider::groove:vertical {
+	width: 5px;
+	background-color: #52595d;
+}
+QSlider::handle:horizontal {
+	background: #1a2224;
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	width: 12px;
+	margin: -5px 0;
+	border-radius: 7px;
+}
+QSlider::handle:vertical {
+	background: #1a2224;
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	height: 12px;
+	margin: 0 -5px;
+	border-radius: 7px;
+}
+QSlider::add-page:horizontal {
+    background: #52595d;
+}
+QSlider::add-page:vertical {
+    background: #52595d;
+}
+QSlider::sub-page:horizontal {
+    background-color: #15433a;
+}
+QSlider::sub-page:vertical {
+    background-color: #15433a;
+}
+QScrollBar:horizontal {
+	max-height: 10px;
+	border: 1px transparent grey;
+	margin: 0px 20px 0px 20px;
+	background: transparent;
+}
+QScrollBar:vertical {
+	max-width: 10px;
+	border: 1px transparent grey;
+	margin: 20px 0px 20px 0px;
+	background: transparent;
+}
+QScrollBar::handle:horizontal {
+	background: #52595d;
+	border-style: transparent;
+	border-radius: 4px;
+	min-width: 25px;
+}
+QScrollBar::handle:horizontal:hover {
+	background: #58a492;
+	border-style: transparent;
+	border-radius: 4px;
+	min-width: 25px;
+}
+QScrollBar::handle:vertical {
+	background: #52595d;
+	border-style: transparent;
+	border-radius: 4px;
+	min-height: 25px;
+}
+QScrollBar::handle:vertical:hover {
+	background: #58a492;
+	border-style: transparent;
+	border-radius: 4px;
+	min-height: 25px;
+}
+QScrollBar::add-line:horizontal {
+   border: 2px transparent grey;
+   border-top-right-radius: 4px;
+   border-bottom-right-radius: 4px;
+   background: #15433a;
+   width: 20px;
+   subcontrol-position: right;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:pressed {
+   border: 2px transparent grey;
+   border-top-right-radius: 4px;
+   border-bottom-right-radius: 4px;
+   background: rgb(181,181,181);
+   width: 20px;
+   subcontrol-position: right;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical {
+   border: 2px transparent grey;
+   border-bottom-left-radius: 4px;
+   border-bottom-right-radius: 4px;
+   background: #15433a;
+   height: 20px;
+   subcontrol-position: bottom;
+   subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:pressed {
+   border: 2px transparent grey;
+   border-bottom-left-radius: 4px;
+   border-bottom-right-radius: 4px;
+   background: rgb(181,181,181);
+   height: 20px;
+   subcontrol-position: bottom;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal {
+   border: 2px transparent grey;
+   border-top-left-radius: 4px;
+   border-bottom-left-radius: 4px;
+   background: #15433a;
+   width: 20px;
+   subcontrol-position: left;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:pressed {
+   border: 2px transparent grey;
+   border-top-left-radius: 4px;
+   border-bottom-left-radius: 4px;
+   background: rgb(181,181,181);
+   width: 20px;
+   subcontrol-position: left;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical {
+   border: 2px transparent grey;
+   border-top-left-radius: 4px;
+   border-top-right-radius: 4px;
+   background: #15433a;
+   height: 20px;
+   subcontrol-position: top;
+   subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:pressed {
+   border: 2px transparent grey;
+   border-top-left-radius: 4px;
+   border-top-right-radius: 4px;
+   background: rgb(181,181,181);
+   height: 20px;
+   subcontrol-position: top;
+   subcontrol-origin: margin;
+}
+
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
+   background: none;
+}
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
+   background: none;
+}

+ 390 - 0
AvPlayer2/res/QSS/MaterialDark.qss

@@ -0,0 +1,390 @@
+/*
+Material Dark Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Inspired on https://github.com/jxfwinter/qt-material-stylesheet
+Company: GTRONICK
+Last updated: 04/12/2018, 15:00.
+Available at: https://github.com/GTRONICK/QSS/blob/master/MaterialDark.qss
+*/
+QMainWindow {
+	background-color:#1e1d23;
+}
+QDialog {
+	background-color:#1e1d23;
+}
+QColorDialog {
+	background-color:#1e1d23;
+}
+QTextEdit {
+	background-color:#1e1d23;
+	color: #a9b7c6;
+}
+QPlainTextEdit {
+	selection-background-color:#007b50;
+	background-color:#1e1d23;
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-width: 1px;
+	color: #a9b7c6;
+}
+QPushButton{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-width: 1px;
+	border-style: solid;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #1e1d23;
+}
+QPushButton::default{
+	border-style: inset;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #04b97f;
+	border-width: 1px;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #1e1d23;
+}
+QToolButton {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #04b97f;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #a9b7c6;
+	padding: 2px;
+	background-color: #1e1d23;
+}
+QToolButton:hover{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #37efba;
+	border-bottom-width: 2px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-bottom: 1px;
+	background-color: #1e1d23;
+}
+QPushButton:hover{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #37efba;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-bottom: 2px;
+	background-color: #1e1d23;
+}
+QPushButton:pressed{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #37efba;
+	border-bottom-width: 2px;
+	border-style: solid;
+	color: #37efba;
+	padding-bottom: 1px;
+	background-color: #1e1d23;
+}
+QPushButton:disabled{
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #808086;
+	border-bottom-width: 2px;
+	border-style: solid;
+	color: #808086;
+	padding-bottom: 1px;
+	background-color: #1e1d23;
+}
+QLineEdit {
+	border-width: 1px; border-radius: 4px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	padding: 0 8px;
+	color: #a9b7c6;
+	background:#1e1d23;
+	selection-background-color:#007b50;
+	selection-color: #FFFFFF;
+}
+QLabel {
+	color: #a9b7c6;
+}
+QLCDNumber {
+	color: #37e6b4;
+}
+QProgressBar {
+	text-align: center;
+	color: rgb(240, 240, 240);
+	border-width: 1px; 
+	border-radius: 10px;
+	border-color: rgb(58, 58, 58);
+	border-style: inset;
+	background-color:#1e1d23;
+}
+QProgressBar::chunk {
+	background-color: #04b97f;
+	border-radius: 5px;
+}
+QMenuBar {
+	background-color: #1e1d23;
+}
+QMenuBar::item {
+	color: #a9b7c6;
+  	spacing: 3px;
+  	padding: 1px 4px;
+  	background: #1e1d23;
+}
+
+QMenuBar::item:selected {
+  	background:#1e1d23;
+	color: #FFFFFF;
+}
+QMenu::item:selected {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: #04b97f;
+	border-bottom-color: transparent;
+	border-left-width: 2px;
+	color: #FFFFFF;
+	padding-left:15px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+	background-color: #1e1d23;
+}
+QMenu::item {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #a9b7c6;
+	padding-left:17px;
+	padding-top:4px;
+	padding-bottom:4px;
+	padding-right:7px;
+	background-color: #1e1d23;
+}
+QMenu{
+	background-color:#1e1d23;
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:#1e1d23;
+}
+QTabWidget::pane {
+		border-color: rgb(77,77,77);
+		background-color:#1e1d23;
+		border-style: solid;
+		border-width: 1px;
+    	border-radius: 6px;
+}
+QTabBar::tab {
+	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: transparent;
+	border-bottom-width: 1px;
+	border-style: solid;
+	color: #808086;
+	padding: 3px;
+	margin-left:3px;
+	background-color: #1e1d23;
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	border-style: solid;
+	border-top-color: transparent;
+	border-right-color: transparent;
+	border-left-color: transparent;
+	border-bottom-color: #04b97f;
+	border-bottom-width: 2px;
+	border-style: solid;
+	color: #FFFFFF;
+	padding-left: 3px;
+	padding-bottom: 2px;
+	margin-left:3px;
+	background-color: #1e1d23;
+}
+
+QCheckBox {
+	color: #a9b7c6;
+	padding: 2px;
+}
+QCheckBox:disabled {
+	color: #808086;
+	padding: 2px;
+}
+
+QCheckBox:hover {
+	border-radius:4px;
+	border-style:solid;
+	padding-left: 1px;
+	padding-right: 1px;
+	padding-bottom: 1px;
+	padding-top: 1px;
+	border-width:1px;
+	border-color: rgb(87, 97, 106);
+	background-color:#1e1d23;
+}
+QCheckBox::indicator:checked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #04b97f;
+	color: #a9b7c6;
+	background-color: #04b97f;
+}
+QCheckBox::indicator:unchecked {
+
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-width: 1px;
+	border-color: #04b97f;
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QRadioButton {
+	color: #a9b7c6;
+	background-color: #1e1d23;
+	padding: 1px;
+}
+QRadioButton::indicator:checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #04b97f;
+	color: #a9b7c6;
+	background-color: #04b97f;
+}
+QRadioButton::indicator:!checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: #04b97f;
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QStatusBar {
+	color:#027f7f;
+}
+QSpinBox {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QDoubleSpinBox {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QTimeEdit {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QDateTimeEdit {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QDateEdit {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QComboBox {
+	color: #a9b7c6;	
+	background: #1e1d23;
+}
+QComboBox:editable {
+	background: #1e1d23;
+	color: #a9b7c6;
+	selection-background-color: #1e1d23;
+}
+QComboBox QAbstractItemView {
+	color: #a9b7c6;	
+	background: #1e1d23;
+	selection-color: #FFFFFF;
+	selection-background-color: #1e1d23;
+}
+QComboBox:!editable:on, QComboBox::drop-down:editable:on {
+	color: #a9b7c6;	
+	background: #1e1d23;
+}
+QFontComboBox {
+	color: #a9b7c6;	
+	background-color: #1e1d23;
+}
+QToolBox {
+	color: #a9b7c6;
+	background-color: #1e1d23;
+}
+QToolBox::tab {
+	color: #a9b7c6;
+	background-color: #1e1d23;
+}
+QToolBox::tab:selected {
+	color: #FFFFFF;
+	background-color: #1e1d23;
+}
+QScrollArea {
+	color: #FFFFFF;
+	background-color: #1e1d23;
+}
+QSlider::groove:horizontal {
+	height: 5px;
+	background: #04b97f;
+}
+QSlider::groove:vertical {
+	width: 5px;
+	background: #04b97f;
+}
+QSlider::handle:horizontal {
+	background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);
+	border: 1px solid #5c5c5c;
+	width: 14px;
+	margin: -5px 0;
+	border-radius: 7px;
+}
+QSlider::handle:vertical {
+	background: qlineargradient(x1:1, y1:1, x2:0, y2:0, stop:0 #b4b4b4, stop:1 #8f8f8f);
+	border: 1px solid #5c5c5c;
+	height: 14px;
+	margin: 0 -5px;
+	border-radius: 7px;
+}
+QSlider::add-page:horizontal {
+    background: white;
+}
+QSlider::add-page:vertical {
+    background: white;
+}
+QSlider::sub-page:horizontal {
+    background: #04b97f;
+}
+QSlider::sub-page:vertical {
+    background: #04b97f;
+}

+ 47 - 0
AvPlayer2/res/QSS/NeonButtons.qss

@@ -0,0 +1,47 @@
+/*
+Neon Style Sheet for QT Applications (QpushButton)
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 24/10/2020, 15:42.
+Available at: https://github.com/GTRONICK/QSS/blob/master/NeonButtons.qss
+*/
+QPushButton{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #d3dae3;
+	padding: 2px;
+	background-color: #100E19;
+}
+QPushButton::default{
+	border-style: solid;
+	border-color: #050a0e;
+	border-width: 1px;
+	border-radius: 5px;
+	color: #FFFFFF;
+	padding: 2px;
+	background-color: #151a1e;
+}
+QPushButton:hover{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:1, stop:0 #C0DB50, stop:0.4 #C0DB50, stop:0.5 #100E19, stop:1 #100E19);
+    border-bottom-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:1, stop:0 #100E19, stop:0.5 #100E19, stop:0.6 #C0DB50, stop:1 #C0DB50);
+    border-left-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #C0DB50, stop:0.3 #C0DB50, stop:0.7 #100E19, stop:1 #100E19);
+    border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #C0DB50, stop:0.3 #C0DB50, stop:0.7 #100E19, stop:1 #100E19);
+	border-width: 2px;
+    border-radius: 1px;
+	color: #d3dae3;
+	padding: 2px;
+}
+QPushButton:pressed{
+	border-style: solid;
+	border-top-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:1, stop:0 #d33af1, stop:0.4 #d33af1, stop:0.5 #100E19, stop:1 #100E19);
+    border-bottom-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:1, stop:0 #100E19, stop:0.5 #100E19, stop:0.6 #d33af1, stop:1 #d33af1);
+    border-left-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #d33af1, stop:0.3 #d33af1, stop:0.7 #100E19, stop:1 #100E19);
+    border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #d33af1, stop:0.3 #d33af1, stop:0.7 #100E19, stop:1 #100E19);
+	border-width: 2px;
+    border-radius: 1px;
+	color: #d3dae3;
+	padding: 2px;
+}

BIN
AvPlayer2/res/QSS/QSS_IMG/go-down-symbolic.symbolic.png


BIN
AvPlayer2/res/QSS/QSS_IMG/go-next-symbolic.symbolic.png


BIN
AvPlayer2/res/QSS/QSS_IMG/go-previous-symbolic.symbolic.png


BIN
AvPlayer2/res/QSS/QSS_IMG/go-up-symbolic.symbolic.png


BIN
AvPlayer2/res/QSS/QSS_IMG/object-select-symbolic.symbolic.png


+ 45 - 0
AvPlayer2/res/QSS/README.md

@@ -0,0 +1,45 @@
+### QT StyleSheet templates ###
+Themes available:
+1. [Ubuntu](https://github.com/GTRONICK/QSS/blob/master/Ubuntu.qss)
+
+![Ubuntu theme screenshot](https://i.imgur.com/i8zVYwL.png)
+    
+2. [ElegantDark](https://github.com/GTRONICK/QSS/blob/master/ElegantDark.qss)
+
+![ElegantDark theme screenshot](https://i.imgur.com/AUb7R7P.png)
+    
+3. [MaterialDark](https://github.com/GTRONICK/QSS/blob/master/MaterialDark.qss)
+
+![MaterialDark theme screenshot](https://i.imgur.com/ViEQxdh.png)
+    
+4. [ConsoleStyle](https://github.com/GTRONICK/QSS/blob/master/ConsoleStyle.qss)
+
+![ConsoleStyle theme screenshot](https://i.imgur.com/E10ukaA.png)
+    
+5. [AMOLED](https://github.com/GTRONICK/QSS/blob/master/AMOLED.qss)
+
+![AMOLED theme screenshot](https://i.imgur.com/M7RIx4c.png)
+    
+6. [Aqua](https://github.com/GTRONICK/QSS/blob/master/Aqua.qss)
+
+![Aqua theme screenshot](https://i.imgur.com/i8zVYwL.png)
+
+## The ManjaroMix Theme!: Includes a radial gradient for Checkboxes, and minimalist arrows for scrollbars. ##
+7. [ManjaroMix](https://github.com/GTRONICK/QSS/blob/master/ManjaroMix.qss)
+
+![ManjaroMix theme screenshot](https://i.imgur.com/7zrMDMH.png)
+
+8. [NeonButtons](https://github.com/GTRONICK/QSS/blob/master/NeonButtons.qss)
+
+![NeonButtons screenshot](https://i.imgur.com/IqTSQG2.png)
+![NeonButtons screenshot](https://i.imgur.com/l4im5Ve.png)
+
+## MacOS Theme!: Reduced code, image integration through URL resources. ##
+9. [MacOS](https://github.com/GTRONICK/QSS/blob/master/MacOS.qss)
+
+![MacOS](https://i.imgur.com/quEgiVe.png)    
+**Added images in QSS_IMG folder**
+ 
+Stay tunned!, this files are being updated frequently.
+*Consider donating :)* **PayPal Account:** gtronick@gmail.com 
+

+ 496 - 0
AvPlayer2/res/QSS/Ubuntu.qss

@@ -0,0 +1,496 @@
+/*
+Ubuntu Style Sheet for QT Applications
+Author: Jaime A. Quiroga P.
+Company: GTRONICK
+Last updated: 01/10/2021 (dd/mm/yyyy), 15:18.
+Available at: https://github.com/GTRONICK/QSS/blob/master/Ubuntu.qss
+*/
+QMainWindow {
+	background-color:#f0f0f0;
+}
+QCheckBox {
+	padding:2px;
+}
+QCheckBox:hover {
+	border:1px solid rgb(255,150,60);
+	border-radius:4px;
+	padding: 1px;
+	background-color:qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(190, 90, 50, 50), stop:1 rgba(250, 130, 40, 50));
+}
+QCheckBox::indicator:checked {
+	border:1px solid rgb(246, 134, 86);
+	border-radius:4px;
+  	background-color:rgb(246, 134, 86)
+}
+QCheckBox::indicator:unchecked {
+	border-width:1px solid rgb(246, 134, 86);
+	border-radius:4px;
+  	background-color:rgb(255,255,255);
+}
+QColorDialog {
+	background-color:#f0f0f0;
+}
+QComboBox {
+	color:rgb(81,72,65);
+	background: #ffffff;
+}
+QComboBox:editable {
+	selection-color:rgb(81,72,65);
+	selection-background-color: #ffffff;
+}
+QComboBox QAbstractItemView {
+	selection-color: #ffffff;
+	selection-background-color: rgb(246, 134, 86);
+}
+QComboBox:!editable:on, QComboBox::drop-down:editable:on {
+	color:  #1e1d23;	
+}
+QDateTimeEdit, QDateEdit, QDoubleSpinBox, QFontComboBox {
+	color:rgb(81,72,65);
+	background-color: #ffffff;
+}
+
+QDialog {
+	background-color:#f0f0f0;
+}
+
+QLabel,QLineEdit {
+	color:rgb(17,17,17);
+}
+QLineEdit {
+	background-color:rgb(255,255,255);
+	selection-background-color:rgb(236,116,64);
+}
+QMenuBar {
+	color:rgb(223,219,210);
+	background-color:rgb(65,64,59);
+}
+QMenuBar::item {
+	padding-top:4px;
+	padding-left:4px;
+	padding-right:4px;
+	color:rgb(223,219,210);
+	background-color:rgb(65,64,59);
+}
+QMenuBar::item:selected {
+	color:rgb(255,255,255);
+	padding-top:2px;
+	padding-left:2px;
+	padding-right:2px;
+	border-top-width:2px;
+	border-left-width:2px;
+	border-right-width:2px;
+	border-top-right-radius:4px;
+	border-top-left-radius:4px;
+	border-style:solid;
+	background-color:rgb(65,64,59);
+	border-top-color: rgb(47,47,44);
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:0, stop:0 rgba(90, 87, 78, 255), stop:1 rgba(47,47,44, 255));
+	border-left-color:  qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(90, 87, 78, 255), stop:1 rgba(47,47,44, 255));
+}
+QMenu {
+	color:rgb(223,219,210);
+	background-color:rgb(65,64,59);
+}
+QMenu::item {
+	color:rgb(223,219,210);
+	padding:4px 10px 4px 20px;
+}
+QMenu::item:selected {
+	color:rgb(255,255,255);
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(225, 108, 54, 255), stop:1 rgba(246, 134, 86, 255));
+	border-style:solid;
+	border-width:3px;
+	padding:4px 7px 4px 17px;
+	border-bottom-color:qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(175,85,48,255), stop:1 rgba(236,114,67, 255));
+	border-top-color:qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+	border-right-color:qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+	border-left-color:qlineargradient(spread:pad, x1:1, y1:0.5, x2:0, y2:0.5, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+}
+QPlainTextEdit {
+	border: 1px solid transparent;
+	color:rgb(17,17,17);
+	selection-background-color:rgb(236,116,64);
+    background-color: #FFFFFF;
+}
+QProgressBar {
+	text-align: center;
+	color: rgb(0, 0, 0);
+	border: 1px inset rgb(150,150,150); 
+	border-radius: 10px;
+	background-color:rgb(221,221,219);
+}
+QProgressBar::chunk:horizontal {
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(225, 108, 54, 255), stop:1 rgba(246, 134, 86, 255));
+	border:1px solid;
+	border-radius:8px;
+	border-bottom-color:qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(175,85,48,255), stop:1 rgba(236,114,67, 255));
+	border-top-color:qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+	border-right-color:qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+	border-left-color:qlineargradient(spread:pad, x1:1, y1:0.5, x2:0, y2:0.5, stop:0 rgba(253,156,113,255), stop:1 rgba(205,90,46, 255));
+}
+QPushButton{
+	color:rgb(17,17,17);
+	border-width: 1px;
+	border-radius: 6px;
+	border-bottom-color: rgb(150,150,150);
+	border-right-color: rgb(165,165,165);
+	border-left-color: rgb(165,165,165);
+	border-top-color: rgb(180,180,180);
+	border-style: solid;
+	padding: 4px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(220, 220, 220, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QPushButton:hover{
+	color:rgb(17,17,17);
+	border-width: 1px;
+	border-radius:6px;
+	border-top-color: rgb(255,150,60);
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 255));
+	border-left-color:  qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 255));
+	border-bottom-color: rgb(200,70,20);
+	border-style: solid;
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(220, 220, 220, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QPushButton:default{
+	color:rgb(17,17,17);
+	border-width: 1px;
+	border-radius:6px;
+	border-top-color: rgb(255,150,60);
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 255));
+	border-left-color:  qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 255));
+	border-bottom-color: rgb(200,70,20);
+	border-style: solid;
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(220, 220, 220, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QPushButton:pressed{
+	color:rgb(17,17,17);
+	border-width: 1px;
+	border-radius: 6px;
+	border-width: 1px;
+	border-top-color: rgba(255,150,60,200);
+	border-right-color: qlineargradient(spread:pad, x1:0, y1:1, x2:1, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 200));
+	border-left-color:  qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(200, 70, 20, 255), stop:1 rgba(255,150,60, 200));
+	border-bottom-color: rgba(200,70,20,200);
+	border-style: solid;
+	padding: 2px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 rgba(220, 220, 220, 255), stop:1 rgba(255, 255, 255, 255));
+}
+QPushButton:disabled{
+	color:rgb(174,167,159);
+	border-width: 1px;
+	border-radius: 6px;
+	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(200, 200, 200, 255), stop:1 rgba(230, 230, 230, 255));
+}
+QRadioButton {
+	padding: 1px;
+}
+QRadioButton::indicator:checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: rgba(246, 134, 86, 255);
+	color: #a9b7c6;
+	background-color:rgba(246, 134, 86, 255);
+}
+QRadioButton::indicator:!checked {
+	height: 10px;
+	width: 10px;
+	border-style:solid;
+	border-radius:5px;
+	border-width: 1px;
+	border-color: rgb(246, 134, 86);
+	color: #a9b7c6;
+	background-color: transparent;
+}
+QScrollArea {
+	color: white;
+	background-color:#f0f0f0;
+}
+QSlider::groove {
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+}
+QSlider::groove:horizontal {
+	height: 5px;
+	background: rgb(246, 134, 86);
+}
+QSlider::groove:vertical {
+	width: 5px;
+	background: rgb(246, 134, 86);
+}
+QSlider::handle:horizontal {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	width: 12px;
+	margin: -5px 0;
+	border-radius: 7px;
+}
+QSlider::handle:vertical {
+	background: rgb(253,253,253);
+	border-style: solid;
+	border-width: 1px;
+	border-color: rgb(207,207,207);
+	height: 12px;
+	margin: 0 -5px;
+	border-radius: 7px;
+}
+QSlider::add-page:horizontal, QSlider::add-page:vertical {
+ 	background: white;
+}
+QSlider::sub-page:horizontal, QSlider::sub-page:vertical {
+	background: rgb(246, 134, 86);
+}
+QStatusBar, QSpinBox {
+	color:rgb(81,72,65);
+}
+QSpinBox {
+	background-color: #ffffff;
+}
+QScrollBar:horizontal {
+	max-height: 20px;
+	border: 1px transparent;
+	margin: 0px 20px 0px 20px;
+}
+QScrollBar::handle:horizontal {
+	background: rgb(253,253,253);
+	border: 1px solid rgb(207,207,207);
+	border-radius: 7px;
+	min-width: 25px;
+}
+QScrollBar::handle:horizontal:hover {
+	background: rgb(253,253,253);
+	border: 1px solid rgb(255,150,60);
+	border-radius: 7px;
+	min-width: 25px;
+}
+QScrollBar::add-line:horizontal {
+  	border: 1px solid rgb(207,207,207);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-right-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	width: 20px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:hover {
+  	border: 1px solid rgb(255,150,60);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-right-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	width: 20px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:horizontal:pressed {
+  	border: 1px solid grey;
+  	border-top-left-radius: 7px;
+  	border-top-right-radius: 7px;
+  	border-bottom-right-radius: 7px;
+  	background: rgb(231,231,231);
+  	width: 20px;
+  	subcontrol-position: right;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal {
+  	border: 1px solid rgb(207,207,207);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-left-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	width: 20px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:hover {
+  	border: 1px solid rgb(255,150,60);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-left-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	width: 20px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:horizontal:pressed {
+  	border: 1px solid grey;
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-left-radius: 7px;
+  	background: rgb(231,231,231);
+  	width: 20px;
+  	subcontrol-position: left;
+  	subcontrol-origin: margin;
+}
+QScrollBar::left-arrow:horizontal {
+  	border: 1px transparent grey;
+  	border-top-left-radius: 3px;
+  	border-bottom-left-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+  	background: rgb(230,230,230);
+}
+QScrollBar::right-arrow:horizontal {
+	border: 1px transparent grey;
+	border-top-right-radius: 3px;
+	border-bottom-right-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+ 	background: rgb(230,230,230);
+}
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
+ 	background: none;
+} 
+QScrollBar:vertical {
+	max-width: 20px;
+	border: 1px transparent grey;
+	margin: 20px 0px 20px 0px;
+}
+QScrollBar::add-line:vertical {
+	border: 1px solid;
+	border-color: rgb(207,207,207);
+	border-bottom-right-radius: 7px;
+	border-bottom-left-radius: 7px;
+	border-top-left-radius: 7px;
+	background: rgb(255, 255, 255);
+  	height: 20px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:hover {
+  	border: 1px solid;
+  	border-color: rgb(255,150,60);
+  	border-bottom-right-radius: 7px;
+  	border-bottom-left-radius: 7px;
+  	border-top-left-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	height: 20px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::add-line:vertical:pressed {
+  	border: 1px solid grey;
+  	border-bottom-left-radius: 7px;
+  	border-bottom-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	background: rgb(231,231,231);
+  	height: 20px;
+  	subcontrol-position: bottom;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical {
+  	border: 1px solid rgb(207,207,207);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-left-radius: 7px;
+  	background: rgb(255, 255, 255);
+  	height: 20px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:hover {
+  	border: 1px solid rgb(255,150,60);
+  	border-top-right-radius: 7px;
+  	border-top-left-radius: 7px;
+  	border-bottom-left-radius: 7px;
+	background: rgb(255, 255, 255);
+  	height: 20px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+QScrollBar::sub-line:vertical:pressed {
+  	border: 1px solid grey;
+  	border-top-left-radius: 7px;
+  	border-top-right-radius: 7px;
+  	background: rgb(231,231,231);
+ 	height: 20px;
+  	subcontrol-position: top;
+  	subcontrol-origin: margin;
+}
+QScrollBar::handle:vertical {
+	background: rgb(253,253,253);
+	border: 1px solid rgb(207,207,207);
+	border-radius: 7px;
+	min-height: 25px;
+}
+QScrollBar::handle:vertical:hover {
+	background: rgb(253,253,253);
+	border: 1px solid rgb(255,150,60);
+	border-radius: 7px;
+	min-height: 25px;
+}
+QScrollBar::up-arrow:vertical {
+	border: 1px transparent grey;
+  	border-top-left-radius: 3px;
+	border-top-right-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+  	background: rgb(230,230,230);
+}
+QScrollBar::down-arrow:vertical {
+  	border: 1px transparent grey;
+  	border-bottom-left-radius: 3px;
+  	border-bottom-right-radius: 3px;
+  	width: 6px;
+  	height: 6px;
+  	background: rgb(230,230,230);
+}
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
+  	background: none;
+}
+QTabWidget {
+	color:rgb(0,0,0);
+	background-color:rgb(247,246,246);
+}
+QTabWidget::pane {
+	border-color: rgb(180,180,180);
+	background-color:rgb(247,246,246);
+	border-style: solid;
+	border-width: 1px;
+  	border-radius: 6px;
+}
+QTabBar::tab {
+	padding-left:4px;
+	padding-right:4px;
+	padding-bottom:2px;
+	padding-top:2px;
+	color:rgb(81,72,65);
+  	background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(221,218,217,255), stop:1 rgba(240,239,238,255));
+	border-style: solid;
+	border-width: 1px;
+  	border-top-right-radius:4px;
+	border-top-left-radius:4px;
+	border-top-color: rgb(180,180,180);
+	border-left-color: rgb(180,180,180);
+	border-right-color: rgb(180,180,180);
+	border-bottom-color: transparent;
+}
+QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
+  	background-color:rgb(247,246,246);
+  	margin-left: 0px;
+  	margin-right: 1px;
+}
+QTabBar::tab:!selected {
+	margin-top: 1px;
+	margin-right: 1px;
+}
+QTextEdit {
+	border-width: 1px;
+	border-style: solid;
+	border-color:transparent;
+	color:rgb(17,17,17);
+	selection-background-color:rgb(236,116,64);
+}
+QTimeEdit, QToolBox, QToolBox::tab, QToolBox::tab:selected {
+	color:rgb(81,72,65);
+	background-color: #ffffff;
+}

BIN
AvPlayer2/res/bkground.png


BIN
AvPlayer2/res/player.png


+ 64 - 0
AvPlayer2/start_play_thread.cpp

@@ -0,0 +1,64 @@
+// ***********************************************************/
+// start_play_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// start play thread. Do the audio device initialization work
+// which is time-consuming to avoid GUI freezing.
+// ***********************************************************/
+
+#include "start_play_thread.h"
+#include "playercontroller.h"
+
+StartPlayThread::StartPlayThread(QObject* parent)
+    : QThread(parent)
+{}
+
+StartPlayThread::~StartPlayThread() {}
+
+void StartPlayThread::run()
+{
+    PlayerController* pParent = (PlayerController*) parent();
+    assert(pParent);
+    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 = pParent->videoStateData();
+    if (pVideoStateData) {
+        AVCodecContext* pAudio = pVideoStateData->get_contex(AVMEDIA_TYPE_AUDIO);
+        VideoState* pState = pVideoStateData->get_state();
+        if (pAudio) {
+            AudioPlayThread* pThread = pParent->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);
+}

+ 17 - 0
AvPlayer2/start_play_thread.h

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <QThread>
+
+class StartPlayThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    explicit StartPlayThread(QObject* parent = Q_NULLPTR);
+    ~StartPlayThread();
+signals:
+    void audio_device_init(bool ret);
+
+protected:
+    void run() override;
+};

+ 30 - 0
AvPlayer2/stopplay_waiting_thread.cpp

@@ -0,0 +1,30 @@
+// ***********************************************************/
+// stopplay_waiting_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Waiting play-stop thread
+// ***********************************************************/
+#include "stopplay_waiting_thread.h"
+#include "playercontroller.h"
+
+StopWaitingThread::StopWaitingThread(QObject* parent, const QString& file)
+    : QThread(parent)
+    , m_file(file)
+{}
+
+StopWaitingThread::~StopWaitingThread() {}
+
+void StopWaitingThread::run()
+{
+    PlayerController* pMainWnd = (PlayerController*) parent();
+    emit stopPlay();
+
+    while (pMainWnd && pMainWnd->isPlaying()) {
+        msleep(2);
+    }
+
+    emit startPlay(m_file);
+    qDebug("-------- stopplay waiting thread exit.");
+    return;
+}

+ 22 - 0
AvPlayer2/stopplay_waiting_thread.h

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

+ 61 - 0
AvPlayer2/subtitle_decode_thread.cpp

@@ -0,0 +1,61 @@
+// ***********************************************************/
+// subtitle_decode_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Subtitles decode thread.
+// ***********************************************************/
+
+#include "subtitle_decode_thread.h"
+
+SubtitleDecodeThread::SubtitleDecodeThread(QObject* parent, VideoState* pState)
+    : QThread(parent), m_pState(pState)
+{
+}
+
+SubtitleDecodeThread::~SubtitleDecodeThread()
+{
+}
+
+void SubtitleDecodeThread::run()
+{
+    assert(m_pState);
+    VideoState* is = m_pState;
+    Frame* sp;
+    int got_subtitle;
+    double pts = 0;
+
+    for (;;)
+    {
+        if (!(sp = frame_queue_peek_writable(&is->subpq)))
+            return;
+
+        if ((got_subtitle = decoder_decode_frame(&is->subdec, nullptr, &sp->sub)) <
+            0)
+            break;
+
+        pts = 0;
+
+        if (got_subtitle && sp->sub.format == 1)
+        {
+            if (sp->sub.pts != AV_NOPTS_VALUE)
+                pts = sp->sub.pts / (double)AV_TIME_BASE;
+            sp->pts = pts;
+            sp->serial = is->subdec.pkt_serial;
+            sp->width = is->subdec.avctx->width;
+            sp->height = is->subdec.avctx->height;
+            sp->uploaded = 0;
+
+            /* now we can update the picture count */
+            frame_queue_push(&is->subpq);
+        }
+        else if (got_subtitle)
+        {
+            qWarning("Not handled subtitle type:%d", sp->sub.format);
+            avsubtitle_free(&sp->sub);
+        }
+    }
+
+    qDebug("-------- subtitle decode thread exit.");
+    return;
+}

+ 19 - 0
AvPlayer2/subtitle_decode_thread.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <QThread>
+#include "packets_sync.h"
+
+class SubtitleDecodeThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    explicit SubtitleDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    ~SubtitleDecodeThread();
+
+protected:
+    void run() override;
+
+private:
+    VideoState* m_pState;
+};

+ 23 - 0
AvPlayer2/version.h

@@ -0,0 +1,23 @@
+// ***********************************************************/
+// version.h
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Version definition of this software.
+// ***********************************************************/
+
+#pragma once
+
+#define VERSION_MAJOR 2
+#define VERSION_MINOR 2
+#define VERSION_MICRO 3
+
+#define VERSION_DOT(a, b, c) a.b.c
+
+#define PLAYER_VERSION_NUMBER \
+    VERSION_DOT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO)
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+#define PLAYER_VERSION STR(PLAYER_VERSION_NUMBER)

+ 169 - 0
AvPlayer2/video_decode_thread.cpp

@@ -0,0 +1,169 @@
+// ***********************************************************/
+// video_decode_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Video decode thread. This section includes queues
+// and dxva2 hardware transmit decoded frame.
+// ***********************************************************/
+
+#include "video_decode_thread.h"
+
+VideoDecodeThread::VideoDecodeThread(QObject* parent, VideoState* pState)
+    : QThread(parent)
+    , m_pState(pState)
+{}
+
+VideoDecodeThread::~VideoDecodeThread() {}
+
+void VideoDecodeThread::run()
+{
+    assert(m_pState);
+    VideoState* is = m_pState;
+    AVFrame* frame = av_frame_alloc();
+    AVFrame* sw_frame = av_frame_alloc();
+    AVFrame* tmp_frame = nullptr;
+    double pts;
+    double duration;
+    int ret;
+    AVRational tb = is->video_st->time_base;
+    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;
+    enum AVPixelFormat last_format = AV_PIX_FMT_NONE;
+    int last_serial = -1;
+    int last_vfilter_idx = 0;
+#endif
+
+    if (!frame)
+        return;
+
+    for (;;) {
+        /*if (is->abort_request)
+            break;*/
+
+        ret = get_video_frame(is, frame);
+        if (ret < 0)
+            goto the_end;
+        if (!ret)
+            continue;
+
+#if USE_AVFILTER_VIDEO
+        if (last_w != frame->width || last_h != frame->height || last_format != frame->format
+            || last_serial != is->viddec.pkt_serial || last_vfilter_idx != is->vfilter_idx
+            || is->req_vfilter_reconfigure) {
+            av_log(nullptr,
+                   AV_LOG_DEBUG,
+                   "Video frame changed from size:%dx%d format:%s serial:%d to "
+                   "size:%dx%d format:%s serial:%d\n",
+                   last_w,
+                   last_h,
+                   (const char*) av_x_if_null(av_get_pix_fmt_name(last_format), "none"),
+                   last_serial,
+                   frame->width,
+                   frame->height,
+                   (const char*) av_x_if_null(av_get_pix_fmt_name((AVPixelFormat) frame->format),
+                                              "none"),
+                   is->viddec.pkt_serial);
+            avfilter_graph_free(&is->vgraph);
+            is->vgraph = avfilter_graph_alloc();
+            if (!is->vgraph) {
+                ret = AVERROR(ENOMEM);
+                goto the_end;
+            }
+            is->vgraph->nb_threads = 0;
+            if ((ret = configure_video_filters(is->vgraph, is, is->vfilters, frame)) < 0) {
+                goto the_end;
+            }
+            filt_in = is->in_video_filter;
+            filt_out = is->out_video_filter;
+            last_w = frame->width;
+            last_h = frame->height;
+            last_format = (AVPixelFormat) frame->format;
+            last_serial = is->viddec.pkt_serial;
+            last_vfilter_idx = is->vfilter_idx;
+            frame_rate = av_buffersink_get_frame_rate(filt_out);
+
+            is->req_vfilter_reconfigure = 0;
+        }
+
+        ret = av_buffersrc_add_frame(filt_in, frame);
+        if (ret < 0)
+            goto the_end;
+
+        while (ret >= 0) {
+            is->frame_last_returned_time = av_gettime_relative() / 1000000.0;
+
+            ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
+            if (ret < 0) {
+                if (ret == AVERROR_EOF)
+                    is->viddec.finished = is->viddec.pkt_serial;
+                ret = 0;
+                break;
+            }
+
+            is->frame_last_filter_delay = av_gettime_relative() / 1000000.0
+                                          - is->frame_last_returned_time;
+            if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
+                is->frame_last_filter_delay = 0;
+            tb = av_buffersink_get_time_base(filt_out);
+#endif
+
+#if 0
+            duration = (frame_rate.num && frame_rate.den ? av_q2d(AVRational{frame_rate.den, frame_rate.num}) : 0);
+            pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
+            ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial);
+            av_frame_unref(frame);
+#else
+
+            tmp_frame = frame;
+            if (frame->format == AV_PIX_FMT_DXVA2_VLD) // DXVA2 hardware decode frame
+            {
+                ret = av_hwframe_transfer_data(sw_frame, frame, 0);
+                if (ret < 0) {
+                    av_log(nullptr,
+                           AV_LOG_WARNING,
+                           "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;
+            }
+
+            duration = (frame_rate.num && frame_rate.den ? av_q2d({frame_rate.den, frame_rate.num})
+                                                         : 0);
+            pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
+            ret = queue_picture(is, tmp_frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial);
+            av_frame_unref(tmp_frame);
+#endif
+
+#if USE_AVFILTER_VIDEO
+            if (is->videoq.serial != is->viddec.pkt_serial)
+                break;
+        }
+#endif
+
+        if (ret < 0)
+            goto the_end;
+    }
+
+the_end:
+
+#if USE_AVFILTER_VIDEO
+    avfilter_graph_free(&is->vgraph);
+    if (is->vfilters) {
+        av_free(is->vfilters);
+        is->vfilters = nullptr;
+    }
+#endif
+    av_frame_free(&frame);
+    av_frame_free(&sw_frame);
+    qDebug("-------- video decode thread exit.");
+    return;
+}

+ 19 - 0
AvPlayer2/video_decode_thread.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <QThread>
+#include "packets_sync.h"
+
+class VideoDecodeThread : public QThread
+{
+    Q_OBJECT
+
+public:
+    explicit VideoDecodeThread(QObject* parent = nullptr, VideoState* pState = nullptr);
+    ~VideoDecodeThread();
+
+protected:
+    void run() override;
+
+private:
+    VideoState* m_pState;
+};

+ 555 - 0
AvPlayer2/video_play_thread.cpp

@@ -0,0 +1,555 @@
+// ***********************************************************/
+// video_play_thread.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// Video play thread. This section includes key code for
+// synchronizing video frames and audio using pts/dts,
+//  as well as subtitle processing.
+// ***********************************************************/
+
+#include "video_play_thread.h"
+
+extern int framedrop;
+
+const QRegularExpression VideoPlayThread::m_assFilter = QRegularExpression("{\\\\.*?}");
+const QRegularExpression VideoPlayThread::m_assNewLineReplacer = QRegularExpression("\\\\n|\\\\N");
+
+Q_DECLARE_METATYPE(AVFrame*)
+
+VideoPlayThread::VideoPlayThread(QObject* parent, VideoState* pState)
+    : QThread(parent)
+    , m_pState(pState)
+{
+    qRegisterMetaType<AVFrame*>();
+}
+
+VideoPlayThread::~VideoPlayThread()
+{
+    stop_thread();
+    final_resample_param();
+}
+
+void VideoPlayThread::run()
+{
+    assert(m_pState);
+    VideoState* is = m_pState;
+    double remaining_time = 0.0;
+
+    for (;;) {
+        if (m_bExitThread)
+            break;
+
+        if (is->abort_request)
+            break;
+
+        if (is->paused) {
+            msleep(10);
+            continue;
+        }
+
+        if (remaining_time > 0.0)
+            av_usleep((int64_t) (remaining_time * 1000000.0));
+
+        remaining_time = REFRESH_RATE;
+        if ((!is->paused || is->force_refresh))
+            video_refresh(is, &remaining_time);
+    }
+
+    qDebug("-------- Video play thread exit.");
+}
+
+void VideoPlayThread::video_refresh(VideoState* is, double* remaining_time)
+{
+    double time;
+    Frame *sp, *sp2;
+
+    if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
+        check_external_clock_speed(is);
+
+    if (is->video_st) {
+    retry:
+        if (frame_queue_nb_remaining(&is->pictq) == 0) {
+            // nothing to do, no picture to display in the queue
+            *remaining_time = REFRESH_RATE;
+        } else {
+            double last_duration, duration, delay;
+            Frame *vp, *lastvp;
+
+            /* dequeue the picture */
+            lastvp = frame_queue_peek_last(&is->pictq);
+            vp = frame_queue_peek(&is->pictq);
+
+            if (vp->serial != is->videoq.serial) {
+                frame_queue_next(&is->pictq);
+                goto retry;
+            }
+
+            if (lastvp->serial != vp->serial)
+                is->frame_timer = av_gettime_relative() / 1000000.0;
+
+            if (is->paused)
+                goto display;
+
+            /* compute nominal last_duration */
+            last_duration = vp_duration(is, lastvp, vp);
+            delay = compute_target_delay(last_duration, is);
+
+            time = av_gettime_relative() / 1000000.0;
+            if (time < is->frame_timer + delay) {
+                *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
+                goto display;
+            }
+
+            is->frame_timer += delay;
+            if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
+                is->frame_timer = time;
+
+            is->pictq.mutex->lock();
+            if (!isnan(vp->pts))
+                update_video_pts(is, vp->pts, vp->pos, vp->serial);
+            is->pictq.mutex->unlock();
+
+            if (frame_queue_nb_remaining(&is->pictq) > 1) {
+                Frame* nextvp = frame_queue_peek_next(&is->pictq);
+                duration = vp_duration(is, vp, nextvp);
+                if (!is->step
+                    && (framedrop > 0
+                        || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER))
+                    && time > is->frame_timer + duration) {
+                    is->frame_drops_late++;
+                    frame_queue_next(&is->pictq);
+                    goto retry;
+                }
+            }
+
+            if (is->subtitle_st) {
+                while (frame_queue_nb_remaining(&is->subpq) > 0) {
+                    sp = frame_queue_peek(&is->subpq);
+
+                    if (frame_queue_nb_remaining(&is->subpq) > 1)
+                        sp2 = frame_queue_peek_next(&is->subpq);
+                    else
+                        sp2 = nullptr;
+
+                    if (sp->serial != is->subtitleq.serial
+                        || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
+                        || (sp2
+                            && is->vidclk.pts
+                                   > (sp2->pts + ((float) sp2->sub.start_display_time / 1000)))) {
+#if 0
+						if (sp->uploaded) {
+							int i;
+							for (i = 0; i < sp->sub.num_rects; i++) {
+								AVSubtitleRect* sub_rect = sp->sub.rects[i];
+
+								/*uint8_t* pixels;
+								int pitch, j;
+
+								if (!SDL_LockTexture(is->sub_texture, (SDL_Rect*)sub_rect, (void**)&pixels, &pitch)) {
+									for (j = 0; j < sub_rect->h; j++, pixels += pitch)
+										memset(pixels, 0, sub_rect->w << 2);
+									SDL_UnlockTexture(is->sub_texture);
+								}*/
+							}
+						}
+#endif
+                        frame_queue_next(&is->subpq);
+                    } else {
+                        break;
+                    }
+                }
+            }
+
+            frame_queue_next(&is->pictq);
+            is->force_refresh = 1;
+
+            if (is->step && !is->paused)
+                toggle_pause(is, !is->step);
+        }
+
+    display:
+        /* display picture */
+        if (is->force_refresh && is->pictq.rindex_shown)
+            video_display(is);
+    }
+
+    is->force_refresh = 0;
+}
+
+void VideoPlayThread::video_display(VideoState* is)
+{
+    if (is->audio_st && false) {
+        // video_audio_display(is);
+    } else if (is->video_st) {
+        video_image_display(is);
+    }
+}
+
+#if 0
+void VideoPlayThread::video_audio_display(VideoState* s)
+{
+	int64_t audio_callback_time = 0;
+	int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
+	int ch, channels, h, h2;
+	int64_t time_diff;
+	int rdft_bits, nb_freq;
+
+	for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++)
+		;
+	nb_freq = 1 << (rdft_bits - 1);
+
+	/* compute display index : center on currently output samples */
+	channels = s->audio_tgt.channels;
+	nb_display_channels = channels;
+	if (!s->paused) {
+		int data_used = (2 * nb_freq);
+		n = 2 * channels;
+		delay = s->audio_write_buf_size;
+		delay /= n;
+
+		/* to be more precise, we take into account the time spent since
+		   the last buffer computation */
+		if (audio_callback_time) {
+			time_diff = av_gettime_relative() - audio_callback_time;
+			delay -= (time_diff * s->audio_tgt.freq) / 1000000;
+		}
+
+		delay += 2 * data_used;
+		if (delay < data_used)
+			delay = data_used;
+
+		i_start = x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
+		/*if (s->show_mode == SHOW_MODE_WAVES) {
+			h = INT_MIN;
+			for (i = 0; i < 1000; i += channels) {
+				int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
+				int a = s->sample_array[idx];
+				int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
+				int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
+				int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
+				int score = a - d;
+				if (h < score && (b ^ c) < 0) {
+					h = score;
+					i_start = idx;
+				}
+			}
+		}*/
+
+		s->last_i_start = i_start;
+	}
+	else {
+		i_start = s->last_i_start;
+	}
+
+#if 0
+	if (s->show_mode == SHOW_MODE_WAVES) {
+		SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
+
+		/* total height for one channel */
+		h = s->height / nb_display_channels;
+		/* graph height / 2 */
+		h2 = (h * 9) / 20;
+		for (ch = 0; ch < nb_display_channels; ch++) {
+			i = i_start + ch;
+			y1 = s->ytop + ch * h + (h / 2); /* position of center line */
+			for (x = 0; x < s->width; x++) {
+				y = (s->sample_array[i] * h2) >> 15;
+				if (y < 0) {
+					y = -y;
+					ys = y1 - y;
+				}
+				else {
+					ys = y1;
+				}
+				fill_rectangle(s->xleft + x, ys, 1, y);
+				i += channels;
+				if (i >= SAMPLE_ARRAY_SIZE)
+					i -= SAMPLE_ARRAY_SIZE;
+			}
+		}
+
+		SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
+
+		for (ch = 1; ch < nb_display_channels; ch++) {
+			y = s->ytop + ch * h;
+			fill_rectangle(s->xleft, y, s->width, 1);
+		}
+	}
+	else {
+		if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)
+			return;
+
+		if (s->xpos >= s->width)
+			s->xpos = 0;
+		nb_display_channels = FFMIN(nb_display_channels, 2);
+		if (rdft_bits != s->rdft_bits) {
+			av_rdft_end(s->rdft);
+			av_free(s->rdft_data);
+			s->rdft = av_rdft_init(rdft_bits, DFT_R2C);
+			s->rdft_bits = rdft_bits;
+			s->rdft_data = av_malloc_array(nb_freq, 4 * sizeof(*s->rdft_data));
+		}
+		if (!s->rdft || !s->rdft_data) {
+			av_log(nullptr, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
+			s->show_mode = SHOW_MODE_WAVES;
+		}
+		else {
+			FFTSample* data[2];
+			SDL_Rect rect = { .x = s->xpos, .y = 0, .w = 1, .h = s->height };
+			uint32_t* pixels;
+			int pitch;
+			for (ch = 0; ch < nb_display_channels; ch++) {
+				data[ch] = s->rdft_data + 2 * nb_freq * ch;
+				i = i_start + ch;
+				for (x = 0; x < 2 * nb_freq; x++) {
+					double w = (x - nb_freq) * (1.0 / nb_freq);
+					data[ch][x] = s->sample_array[i] * (1.0 - w * w);
+					i += channels;
+					if (i >= SAMPLE_ARRAY_SIZE)
+						i -= SAMPLE_ARRAY_SIZE;
+				}
+				av_rdft_calc(s->rdft, data[ch]);
+			}
+			/* Least efficient way to do this, we should of course
+			 * directly access it but it is more than fast enough. */
+			if (!SDL_LockTexture(s->vis_texture, &rect, (void**)&pixels, &pitch)) {
+				pitch >>= 2;
+				pixels += pitch * s->height;
+				for (y = 0; y < s->height; y++) {
+					double w = 1 / sqrt(nb_freq);
+					int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
+					int b = (nb_display_channels == 2) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
+						: a;
+					a = FFMIN(a, 255);
+					b = FFMIN(b, 255);
+					pixels -= pitch;
+					*pixels = (a << 16) + (b << 8) + ((a + b) >> 1);
+				}
+				SDL_UnlockTexture(s->vis_texture);
+			}
+			SDL_RenderCopy(renderer, s->vis_texture, nullptr, nullptr);
+		}
+		if (!s->paused)
+			s->xpos++;
+	}
+#endif
+}
+#endif
+
+void VideoPlayThread::video_image_display(VideoState* is)
+{
+    Frame* sp = nullptr;
+    Frame* vp = frame_queue_peek_last(&is->pictq);
+    Video_Resample* pResample = &m_Resample;
+
+    if (frame_queue_nb_remaining(&is->subpq) > 0) {
+        sp = frame_queue_peek(&is->subpq);
+
+        if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
+            if (!sp->uploaded) {
+                // uint8_t* pixels[4];
+                // int pitch[4];
+
+                if (!sp->width || !sp->height) {
+                    sp->width = vp->width;
+                    sp->height = vp->height;
+                }
+
+                // if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888,
+                // sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0) 	return;
+#if 1
+                for (unsigned int i = 0; i < sp->sub.num_rects; i++) {
+                    AVSubtitleRect* sub_rect = sp->sub.rects[i];
+                    if (sub_rect->type == SUBTITLE_ASS) {
+                        qDebug("subtitle[%d], format:%d, type:%d, text:%s, flags:%d",
+                               i,
+                               sp->sub.format,
+                               sub_rect->type,
+                               sub_rect->text,
+                               sub_rect->flags);
+                        // QString ass = QString::fromUtf8(sub_rect->ass);
+                        QString ass = QString::fromLocal8Bit(
+                            QString::fromStdString(sub_rect->ass).toUtf8());
+                        QStringList assList = ass.split(",");
+                        if (assList.size() > 8) {
+                            ass = assList[8];
+                            qDebug("ass: %s", qUtf8Printable(ass));
+                            parse_subtitle_ass(ass);
+                        }
+                    } else {
+                        qWarning("not handled yet, type:%d", sub_rect->type);
+                    }
+                }
+#else
+                for (i = 0; i < sp->sub.num_rects; i++) {
+                    AVSubtitleRect* sub_rect = sp->sub.rects[i];
+
+                    sub_rect->x = av_clip(sub_rect->x, 0, sp->width);
+                    sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
+                    sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x);
+                    sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+
+                    is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
+                                                               sub_rect->w,
+                                                               sub_rect->h,
+                                                               AV_PIX_FMT_PAL8,
+                                                               sub_rect->w,
+                                                               sub_rect->h,
+                                                               AV_PIX_FMT_BGRA,
+                                                               0,
+                                                               nullptr,
+                                                               nullptr,
+                                                               nullptr);
+                    if (!is->sub_convert_ctx) {
+                        av_log(nullptr, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
+                        return;
+                    }
+#if 1
+                    sws_scale(is->sub_convert_ctx,
+                              (const uint8_t* const*) sub_rect->data,
+                              sub_rect->linesize,
+                              0,
+                              sub_rect->h,
+                              pixels,
+                              pitch);
+#else
+                    if (!SDL_LockTexture(is->sub_texture,
+                                         (SDL_Rect*) sub_rect,
+                                         (void**) pixels,
+                                         pitch)) {
+                        sws_scale(is->sub_convert_ctx,
+                                  (const uint8_t* const*) sub_rect->data,
+                                  sub_rect->linesize,
+                                  0,
+                                  sub_rect->h,
+                                  pixels,
+                                  pitch);
+                        SDL_UnlockTexture(is->sub_texture);
+                    }
+#endif
+                }
+#endif
+                sp->uploaded = 1;
+            }
+        } else {
+            sp = nullptr;
+        }
+    }
+
+    AVFrame* pFrameRGB = pResample->pFrameRGB; // dst
+    AVCodecContext* pVideoCtx = is->viddec.avctx;
+    AVFrame* pFrame = vp->frame;
+
+    // AVPixelFormat fmt = (AVPixelFormat)pFrame->format; // 0
+    // const char* fmt_name = av_get_pix_fmt_name(fmt);
+
+    // AVHWFramesContext* ctx =
+    // (AVHWFramesContext*)pVideoCtx->hw_frames_ctx->data; AVPixelFormat sw_fmt =
+    // ctx->sw_format;
+
+    // qDebug("frame w:%d,h:%d, pts:%lld, dts:%lld", pVideoCtx->width,
+    // pVideoCtx->height, pFrame->pts, pFrame->pkt_dts);
+
+    // TODO: 不转换
+    sws_scale(pResample->sws_ctx,
+              (uint8_t const* const*) pFrame->data,
+              pFrame->linesize,
+              0,
+              pVideoCtx->height,
+              pFrameRGB->data,
+              pFrameRGB->linesize);
+
+    // QImage img(pVideoCtx->width, pVideoCtx->height, QImage::Format_RGB888);
+    // for (int y = 0; y < pVideoCtx->height; ++y) {
+    //     memcpy(img.scanLine(y),
+    //            pFrameRGB->data[0] + y * pFrameRGB->linesize[0],
+    //            pVideoCtx->width * 3);
+    // }
+
+    // emit frame_ready(img);
+
+    emit frameReady(pFrameRGB);
+}
+
+bool VideoPlayThread::init_resample_param(AVCodecContext* pVideo, bool bHardware)
+{
+    Video_Resample* pResample = &m_Resample;
+    if (pVideo) {
+        enum AVPixelFormat pix_fmt = pVideo->pix_fmt; // frame format after decode
+        if (bHardware)
+            pix_fmt = AV_PIX_FMT_NV12;
+
+        struct SwsContext* sws_ctx
+            = sws_getContext(pVideo->width,
+                             pVideo->height,
+                             pix_fmt, // AV_PIX_FMT_YUV420P
+                             pVideo->width,
+                             pVideo->height,
+                             AV_PIX_FMT_RGB24, // sws_scale destination color scheme
+                             SWS_BILINEAR,
+                             nullptr,
+                             nullptr,
+                             nullptr);
+
+        AVFrame* pFrameRGB = av_frame_alloc();
+        if (!pFrameRGB) {
+            printf("Could not allocate rgb frame.\n");
+            return false;
+        }
+
+        pFrameRGB->width = pVideo->width;
+        pFrameRGB->height = pVideo->height;
+        pFrameRGB->format = AV_PIX_FMT_RGB24;
+
+        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideo->width, pVideo->height, 32);
+        uint8_t* const buffer_RGB = (uint8_t*) av_malloc(numBytes * sizeof(uint8_t));
+        if (!buffer_RGB) {
+            printf("Could not allocate buffer.\n");
+            return false;
+        }
+
+        av_image_fill_arrays(pFrameRGB->data,
+                             pFrameRGB->linesize,
+                             buffer_RGB,
+                             AV_PIX_FMT_RGB24,
+                             pVideo->width,
+                             pVideo->height,
+                             32);
+
+        pResample->sws_ctx = sws_ctx;
+        pResample->pFrameRGB = pFrameRGB;
+        pResample->buffer_RGB = buffer_RGB;
+        return true;
+    }
+    return false;
+}
+
+void VideoPlayThread::final_resample_param()
+{
+    Video_Resample* pResample = &m_Resample;
+    // Free video resample context
+    sws_freeContext(pResample->sws_ctx);
+
+    // Free the RGB image
+    av_free(pResample->buffer_RGB);
+    av_frame_free(&pResample->pFrameRGB);
+    av_free(pResample->pFrameRGB);
+}
+
+void VideoPlayThread::stop_thread()
+{
+    m_bExitThread = true;
+    wait();
+}
+
+void VideoPlayThread::parse_subtitle_ass(const QString& text)
+{
+    QString str = text;
+
+    str.remove(m_assFilter);
+    str.replace(m_assNewLineReplacer, "\n");
+    str = str.trimmed();
+
+    emit subtitle_ready(str);
+}

+ 56 - 0
AvPlayer2/video_play_thread.h

@@ -0,0 +1,56 @@
+#pragma once
+
+#include <QDebug>
+#include <QImage>
+#include <QRegularExpression>
+#include <QThread>
+
+#include "packets_sync.h"
+
+#define PRINT_VIDEO_BUFFER_INFO 0
+
+typedef struct Video_Resample
+{
+    AVFrame* pFrameRGB{nullptr};
+    uint8_t* buffer_RGB{nullptr};
+    struct SwsContext* sws_ctx{nullptr};
+} Video_Resample;
+
+class VideoPlayThread : public QThread
+{
+    Q_OBJECT
+public:
+    explicit VideoPlayThread(QObject* parent = nullptr, 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&);
+
+protected:
+    void run() override;
+
+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);
+
+private:
+    VideoState* m_pState{nullptr};
+    Video_Resample m_Resample;
+    bool m_bExitThread{false};
+
+    const static QRegularExpression m_assFilter;
+    const static QRegularExpression m_assNewLineReplacer;
+};

+ 771 - 0
AvPlayer2/video_state.cpp

@@ -0,0 +1,771 @@
+// ***********************************************************/
+// video_state.cpp
+//
+//      Copy Right @ Steven Huang. All rights reserved.
+//
+// A/V synchronization state. This code is referenced
+// from ffplay.c in Ffmpeg library.
+// ***********************************************************/
+#include "video_state.h"
+
+int infinite_buffer = -1;
+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)
+{
+}
+
+VideoStateData::~VideoStateData()
+{
+    close_hardware();
+    delete_video_state();
+}
+
+void VideoStateData::delete_video_state()
+{
+    if (m_pState)
+    {
+        stream_close(m_pState);
+        m_pState = nullptr;
+    }
+}
+
+VideoState* VideoStateData::get_state() const
+{
+    return m_pState;
+}
+
+bool VideoStateData::is_hardware_decode() const
+{
+    return m_bHardwareSuccess;
+}
+
+int VideoStateData::create_video_state(const char* filename)
+{
+    int ret = -1;
+    if (!filename || !filename[0])
+    {
+        qDebug("filename is invalid, please select a valid media file.");
+        return ret;
+    }
+
+    m_pState = stream_open(filename);
+    if (!m_pState)
+    {
+        qDebug("stream_open failed!");
+        return ret;
+    }
+
+    return open_media(m_pState);
+}
+
+void VideoStateData::print_state() const
+{
+    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);
+        qDebug("[VideoState]Clock(v:%p,a:%p,s:%p)", &is->vidclk, &is->audclk, &is->extclk);
+    }
+}
+
+int VideoStateData::open_media(VideoState* is)
+{
+    assert(is);
+    int err;
+    uint i;
+    int ret = -1;
+    int st_index[AVMEDIA_TYPE_NB];
+    AVFormatContext* ic = nullptr;
+    const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
+
+    memset(st_index, -1, sizeof(st_index));
+
+    is->eof = 0;
+
+    ic = avformat_alloc_context();
+    if (!ic)
+    {
+        av_log(nullptr, AV_LOG_FATAL, "Could not allocate context.\n");
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    ic->interrupt_callback.callback = nullptr; // decode_interrupt_cb;
+    ic->interrupt_callback.opaque = is;
+
+    err = avformat_open_input(&ic, is->filename, is->iformat, nullptr);
+    if (err < 0)
+    {
+        av_log(nullptr, AV_LOG_FATAL, "failed to open %s: %d", is->filename, err);
+        ret = -1;
+        goto fail;
+    }
+
+    is->ic = ic;
+
+    //ic->flags |= AVFMT_FLAG_GENPTS; // gen pts
+
+    av_format_inject_global_side_data(ic);
+
+    //AVDictionary** opts = setup_find_stream_info_opts(ic, codec_opts);
+    //int orig_nb_streams = ic->nb_streams;
+
+    err = avformat_find_stream_info(ic, nullptr);
+    if (err < 0)
+    {
+        av_log(nullptr, AV_LOG_WARNING, "%s: could not find codec parameters\n", is->filename);
+        ret = -1;
+        goto fail;
+    }
+
+    if (ic->pb)
+        ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use
+                                 // avio_feof() to test for the end
+
+    // if (seek_by_bytes < 0)
+    //	seek_by_bytes = (ic->iformat->flags & AVFMT_TS_DISCONT) &&
+    //strcmp("ogg", ic->iformat->name);
+
+    //is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
+    is->max_frame_duration = 2.0;
+
+    /* if seeking requested, we execute it */
+    if (start_time != AV_NOPTS_VALUE)
+    {
+        int64_t timestamp;
+
+        timestamp = start_time;
+        /* add the stream start time */
+        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);
+        }
+    }
+
+    is->realtime = is_realtime(ic);
+
+    av_dump_format(ic, 0, is->filename, 0);
+
+    for (i = 0; i < ic->nb_streams; i++)
+    {
+        AVStream* st = ic->streams[i];
+        enum AVMediaType type = st->codecpar->codec_type;
+        st->discard = AVDISCARD_ALL;
+        if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1)
+            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)));
+            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);
+
+    /* open the streams */
+    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0)
+    {
+        stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
+    }
+
+    if (st_index[AVMEDIA_TYPE_AUDIO] >= 0)
+    {
+        stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
+    }
+
+    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);
+        ret = -1;
+        goto fail;
+    }
+
+    if (infinite_buffer < 0 && is->realtime)
+        infinite_buffer = 1;
+
+    return 0;
+
+fail:
+    if (ic && !is->ic)
+        avformat_close_input(&ic);
+    return ret;
+}
+
+VideoState* VideoStateData::stream_open(const char* filename, const AVInputFormat* iformat)
+{
+    VideoState* is = nullptr;
+
+    int startup_volume = 100;
+    int av_sync_type = AV_SYNC_AUDIO_MASTER;
+
+    is = (VideoState*)av_mallocz(sizeof(VideoState));
+    if (!is)
+        return nullptr;
+    is->last_video_stream = is->video_stream = -1;
+    is->last_audio_stream = is->audio_stream = -1;
+    is->last_subtitle_stream = is->subtitle_stream = -1;
+    is->filename = av_strdup(filename);
+    if (!is->filename)
+        goto fail;
+    is->iformat = iformat;
+    is->ytop = 0;
+    is->xleft = 0;
+
+    /* start video display */
+    if (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)
+        goto fail;
+    if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
+        goto fail;
+    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)
+        goto fail;
+
+    if (!(is->continue_read_thread = new QWaitCondition()))
+    {
+        av_log(nullptr, AV_LOG_FATAL, "new QWaitCondition() failed!\n");
+        goto fail;
+    }
+
+    init_clock(&is->vidclk, &is->videoq.serial);
+    init_clock(&is->audclk, &is->audioq.serial);
+    init_clock(&is->extclk, &is->extclk.serial);
+    is->audio_clock_serial = -1;
+    if (startup_volume < 0)
+        av_log(nullptr, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", startup_volume);
+    if (startup_volume > 100)
+        av_log(nullptr, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);
+    startup_volume = av_clip(startup_volume, 0, 100);
+    startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
+    is->audio_volume = startup_volume;
+    is->muted = 0;
+    is->av_sync_type = av_sync_type;
+    // is->read_tid = m_pReadThreadId;
+    is->read_thread_exit = -1;
+    is->loop = int(m_bLoopPlay);
+
+    is->threads = {nullptr};
+
+#if USE_AVFILTER_AUDIO
+    is->audio_speed = 1.0;
+#endif
+    return is;
+fail:
+    stream_close(is);
+    return nullptr;
+}
+
+void VideoStateData::threads_setting(VideoState* is, const Threads& threads)
+{
+    if (!is)
+        return;
+
+    assert(!is->threads.read_tid);
+    assert(!is->threads.video_decode_tid);
+    assert(!is->threads.audio_decode_tid);
+    assert(!is->threads.video_play_tid);
+    assert(!is->threads.audio_play_tid);
+    assert(!is->threads.subtitle_decode_tid);
+
+    is->threads = threads;
+}
+
+void VideoStateData::read_thread_exit_wait(VideoState* is)
+{
+    if (!is)
+        return;
+
+    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->wait();
+        av_log(nullptr, AV_LOG_INFO, "read thread wait after!\n");
+        is->threads.read_tid = nullptr;
+    }
+}
+
+void VideoStateData::threads_exit_wait(VideoState* is)
+{
+    if (!is)
+        return;
+
+    if (is->threads.video_play_tid)
+    {
+        is->threads.video_play_tid->wait();
+        is->threads.video_play_tid = nullptr;
+    }
+
+    if (is->threads.audio_play_tid)
+    {
+        is->threads.audio_play_tid->wait();
+        is->threads.audio_play_tid = nullptr;
+    }
+
+    if (is->threads.video_decode_tid)
+    {
+        is->threads.video_decode_tid->wait();
+        is->threads.video_decode_tid = nullptr;
+    }
+
+    if (is->threads.audio_decode_tid)
+    {
+        is->threads.audio_decode_tid->wait();
+        is->threads.audio_decode_tid = nullptr;
+    }
+
+    if (is->threads.subtitle_decode_tid)
+    {
+        is->threads.subtitle_decode_tid->wait();
+        is->threads.subtitle_decode_tid = nullptr;
+    }
+}
+
+void VideoStateData::stream_close(VideoState* is)
+{
+    assert(is);
+
+    is->abort_request = 1;
+
+    read_thread_exit_wait(is);
+
+    // if (is->read_thread_exit == 0)
+    //{
+    //	// SDL_WaitThread(is->read_tid, nullptr);
+    //	//((QThread*)(is->read_tid))->wait();
+    //	/*if (m_pReadThreadId)
+    //		m_pReadThreadId->wait();*/
+    //}
+
+    /* close each stream */
+    if (is->audio_stream >= 0)
+        stream_component_close(is, is->audio_stream);
+    if (is->video_stream >= 0)
+        stream_component_close(is, is->video_stream);
+    if (is->subtitle_stream >= 0)
+        stream_component_close(is, is->subtitle_stream);
+
+    threads_exit_wait(is); // read and decode threads exit here.
+
+    avformat_close_input(&is->ic);
+
+    packet_queue_destroy(&is->videoq);
+    packet_queue_destroy(&is->audioq);
+    packet_queue_destroy(&is->subtitleq);
+
+    /* free all pictures */
+    frame_queue_destory(&is->pictq);
+    frame_queue_destory(&is->sampq);
+    frame_queue_destory(&is->subpq);
+
+    if (is->continue_read_thread)
+    {
+        delete is->continue_read_thread;
+        is->continue_read_thread = nullptr;
+    }
+
+    // SDL_DestroyCond(is->continue_read_thread);
+    sws_freeContext(is->img_convert_ctx);
+    sws_freeContext(is->sub_convert_ctx);
+    av_free(is->filename);
+    /*if (is->vis_texture)
+          SDL_DestroyTexture(is->vis_texture);
+  if (is->vid_texture)
+          SDL_DestroyTexture(is->vid_texture);
+  if (is->sub_texture)
+          SDL_DestroyTexture(is->sub_texture);*/
+
+    av_free(is);
+}
+
+static enum AVPixelFormat get_hw_format(AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts)
+{
+    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);
+    return AV_PIX_FMT_NONE;
+}
+
+// static int hw_decoder_init(AVCodecContext* ctx, const enum AVHWDeviceType
+// type)
+//{
+//	int err = 0;
+//
+//	if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr,
+//nullptr, 0)) < 0) { 		fprintf(stderr, "Failed to create specified HW
+//device.\n"); 		return err;
+//	}
+//
+//	ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
+//
+//	return err;
+//}
+
+int VideoStateData::hw_decoder_init(AVCodecContext* ctx, const enum AVHWDeviceType type)
+{
+    int err = 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;
+    }
+
+    ctx->hw_device_ctx = av_buffer_ref(m_hw_device_ctx);
+
+    return err;
+}
+
+bool VideoStateData::open_hardware(AVCodecContext* avctx, const AVCodec* codec, const char* device)
+{
+    enum AVHWDeviceType type = get_hwdevice(device);
+    hw_pix_fmt = get_hwdevice_decoder(codec, type);
+
+    avctx->get_format = get_hw_format;
+
+    if (hw_decoder_init(avctx, type) < 0)
+        return false;
+
+    return true;
+}
+
+void VideoStateData::close_hardware()
+{
+    av_buffer_unref(&m_hw_device_ctx);
+}
+
+int VideoStateData::stream_component_open(VideoState* is, int stream_index)
+{
+    assert(is);
+    AVFormatContext* ic = is->ic;
+    AVCodecContext* avctx;
+    const AVCodec* codec;
+    AVDictionary* opts = nullptr;
+    // const AVDictionaryEntry* t = nullptr;
+    int sample_rate, nb_channels;
+    AVChannelLayout ch_layout = {0};
+    // int64_t
+    int format;
+    int ret = 0;
+    int stream_lowres = 0;
+
+    if (stream_index < 0 || ((unsigned int)stream_index) >= ic->nb_streams)
+        return -1;
+
+    avctx = avcodec_alloc_context3(nullptr);
+    if (!avctx)
+        return AVERROR(ENOMEM);
+
+    ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
+    if (ret < 0)
+        goto fail;
+    avctx->pkt_timebase = ic->streams[stream_index]->time_base;
+
+    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;
+            }
+            break;
+    }
+
+    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);
+        stream_lowres = codec->max_lowres;
+    }
+    avctx->lowres = stream_lowres;
+
+    // avctx->flags2 |= AV_CODEC_FLAG2_FAST;
+    /*opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec);
+    if (!av_dict_get(opts, "threads", NULL, 0))
+        av_dict_set(&opts, "threads", "auto", 0);
+    if (stream_lowres)
+        av_dict_set_int(&opts, "lowres", stream_lowres, 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:
+#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);
+        }
+#else
+            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,
+    &is->audio_tgt)) < 0) goto fail;
+
+    is->audio_src = is->audio_tgt;*/
+
+            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;
+            }
+
+            m_bHasAudio = true;
+            m_avctxAudio = avctx;
+            break;
+
+        case AVMEDIA_TYPE_VIDEO:
+            is->video_stream = stream_index;
+            is->video_st = ic->streams[stream_index];
+
+            m_bHasVideo = true;
+            m_avctxVideo = avctx;
+            break;
+
+        case AVMEDIA_TYPE_SUBTITLE:
+            is->subtitle_stream = stream_index;
+            is->subtitle_st = ic->streams[stream_index];
+
+            m_bHasSubtitle = true;
+            m_avctxSubtitle = avctx;
+            break;
+
+        default:
+            break;
+    }
+
+    goto out;
+
+fail:
+    avcodec_free_context(&avctx);
+out:
+    av_dict_free(&opts);
+    return ret;
+}
+
+void VideoStateData::stream_component_close(VideoState* is, int stream_index)
+{
+    assert(is);
+    AVFormatContext* ic = is->ic;
+    AVCodecParameters* codecpar;
+
+    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);
+
+            // swr_free(&is->swr_ctx);
+            // av_freep(&is->audio_buf1);
+            // is->audio_buf1_size = 0;
+            // is->audio_buf = nullptr;
+
+            /*if (is->rdft) {
+            av_rdft_end(is->rdft);
+            av_freep(&is->rdft_data);
+            is->rdft = nullptr;
+            is->rdft_bits = 0;
+            }*/
+            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;
+
+        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;
+    }
+}
+
+bool VideoStateData::has_video() const
+{
+    return m_bHasVideo;
+}
+
+bool VideoStateData::has_audio() const
+{
+    return m_bHasAudio;
+}
+
+bool VideoStateData::has_subtitle() const
+{
+    return m_bHasSubtitle;
+}
+
+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;
+    }
+    return pCtx;
+}
+
+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)
+    {
+        av_log(nullptr, AV_LOG_WARNING, "Device type %s is not supported.\n", device);
+
+        av_log(nullptr, AV_LOG_INFO, "Available device types:");
+        while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
+            av_log(nullptr, AV_LOG_INFO, " %s", av_hwdevice_get_type_name(type));
+        av_log(nullptr, AV_LOG_INFO, "\n");
+        return AV_HWDEVICE_TYPE_NONE;
+    }
+    return type;
+}
+
+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++)
+    {
+        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));
+            return AV_PIX_FMT_NONE;
+        }
+        if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type)
+        {
+            return config->pix_fmt;
+        }
+    }
+    return AV_PIX_FMT_NONE;
+}

+ 59 - 0
AvPlayer2/video_state.h

@@ -0,0 +1,59 @@
+/**
+ * @file video_state.h
+ * @author Steven Huang
+ */
+
+#pragma once
+
+#include "packets_sync.h"
+
+class VideoStateData
+{
+public:
+    explicit VideoStateData(bool use_hardware = false, bool loop_play = false);
+    virtual ~VideoStateData();
+
+public:
+    bool has_video() const;
+    bool has_audio() const;
+    bool has_subtitle() const;
+    AVCodecContext* get_contex(AVMediaType type) const;
+    bool is_hardware_decode() const;
+    int create_video_state(const char* filename);
+    void delete_video_state();
+    VideoState* get_state() const;
+    void print_state() const;
+    void threads_setting(VideoState* is, const Threads& threads);
+
+private:
+    VideoState* stream_open(const char* filename, const AVInputFormat* iformat = NULL);
+    void stream_close(VideoState* is);
+    int stream_component_open(VideoState* is, int stream_index);
+    void stream_component_close(VideoState* is, int stream_index);
+    int open_media(VideoState* is);
+    enum AVHWDeviceType get_hwdevice(const char* device) const;
+    enum AVPixelFormat get_hwdevice_decoder(const AVCodec* decoder, enum AVHWDeviceType type) const;
+    bool open_hardware(AVCodecContext* avctx, const AVCodec* codec, const char* device = "dxva2");
+    void close_hardware();
+    int hw_decoder_init(AVCodecContext* ctx, const enum AVHWDeviceType type);
+    void read_thread_exit_wait(VideoState* is);
+    void threads_exit_wait(VideoState* is);
+
+private:
+    VideoState* m_pState{nullptr};
+
+    bool m_bHasVideo{false};
+    bool m_bHasAudio{false};
+    bool m_bHasSubtitle{false};
+
+    AVCodecContext* m_avctxVideo{nullptr};
+    AVCodecContext* m_avctxAudio{nullptr};
+    AVCodecContext* m_avctxSubtitle{nullptr};
+
+    /**hardware decode**/
+    bool m_bUseHardware{false};
+    bool m_bHardwareSuccess{false};
+    AVBufferRef* m_hw_device_ctx{nullptr};
+    //enum AVPixelFormat m_hw_pix_fmt;
+    bool m_bLoopPlay{false};
+};

+ 120 - 7
AvRecorder/ui/opengl_video_widget.cpp

@@ -12,6 +12,20 @@ OpenGLVideoWidget::OpenGLVideoWidget(QWidget* parent)
     , m_frameUpdated(false)
     , m_initialized(false)
     , m_keepAspectRatio(true)
+    , m_gray(false)
+    , m_threshold(false)
+    , m_thresholdValue(0.5f)
+    , m_blur(false)
+    , m_blurRadius(1.0f)
+    , m_reverse(false)
+    , m_colorReduce(false)
+    , m_colorReduceLevel(0)
+    , m_gamma(false)
+    , m_gammaValue(1.0f)
+    , m_contrastBright(false)
+    , m_contrast(1.0f)
+    , m_brightness(0.0f)
+    , m_mirror(false)
 {
     // 设置顶点坐标
     m_vertices[0] = -1.0f; m_vertices[1] = -1.0f;
@@ -81,7 +95,8 @@ void OpenGLVideoWidget::initializeGL()
     initializeOpenGLFunctions();
     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 
-    // 创建着色器程序
+    // 创建统一shader,支持多特效
+    if (m_program) { delete m_program; m_program = nullptr; }
     m_program = new QOpenGLShaderProgram();
     m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
         "attribute vec2 vertexIn;\n"
@@ -93,12 +108,69 @@ void OpenGLVideoWidget::initializeGL()
         "    textureOut = textureIn;\n"
         "}\n");
     m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
-        "varying vec2 textureOut;\n"
-        "uniform sampler2D texture;\n"
-        "void main(void)\n"
-        "{\n"
-        "    gl_FragColor = texture2D(texture, textureOut);\n"
-        "}\n");
+        R"Raw(
+        varying vec2 textureOut;
+        uniform sampler2D texture;
+        uniform bool uGray;
+        uniform bool uThreshold;
+        uniform float uThresholdValue;
+        uniform bool uBlur;
+        uniform float uBlurRadius;
+        uniform bool uReverse;
+        uniform bool uColorReduce;
+        uniform int uColorReduceLevel;
+        uniform bool uGamma;
+        uniform float uGammaValue;
+        uniform bool uContrastBright;
+        uniform float uContrast;
+        uniform float uBrightness;
+        uniform bool uMirror;
+        void main(void)
+        {
+            vec2 uv = textureOut;
+            if (uMirror) {
+                uv.x = 1.0 - uv.x;
+            }
+            vec4 color = texture2D(texture, uv);
+            // 灰度
+            if (uGray) {
+                float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
+                color = vec4(gray, gray, gray, color.a);
+            }
+            // 二值化
+            if (uThreshold) {
+                float v = dot(color.rgb, vec3(0.299, 0.587, 0.114));
+                float th = v > uThresholdValue ? 1.0 : 0.0;
+                color = vec4(th, th, th, color.a);
+            }
+            // 简单3x3均值模糊
+            if (uBlur) {
+                vec2 tex_offset = vec2(1.0) / vec2(textureSize2D(texture, 0));
+                vec4 sum = vec4(0.0);
+                for (int dx = -1; dx <= 1; ++dx)
+                for (int dy = -1; dy <= 1; ++dy)
+                    sum += texture2D(texture, uv + vec2(dx, dy) * tex_offset * uBlurRadius);
+                color = sum / 9.0;
+            }
+            // 反色
+            if (uReverse) {
+                color.rgb = vec3(1.0) - color.rgb;
+            }
+            // 色彩减少
+            if (uColorReduce) {
+                color.rgb = floor(color.rgb * float(uColorReduceLevel)) / float(uColorReduceLevel);
+            }
+            // 伽马
+            if (uGamma) {
+                color.rgb = pow(color.rgb, vec3(1.0 / uGammaValue));
+            }
+            // 对比度/亮度
+            if (uContrastBright) {
+                color.rgb = color.rgb * uContrast + uBrightness;
+            }
+            gl_FragColor = color;
+        }
+        )Raw");
     m_program->bindAttributeLocation("vertexIn", 0);
     m_program->bindAttributeLocation("textureIn", 1);
     m_program->link();
@@ -131,6 +203,20 @@ void OpenGLVideoWidget::paintGL()
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_frameWidth, m_frameHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_frameData);
     m_program->bind();
     m_program->setUniformValue("texture", 0);
+    m_program->setUniformValue("uGray", m_gray);
+    m_program->setUniformValue("uThreshold", m_threshold);
+    m_program->setUniformValue("uThresholdValue", m_thresholdValue);
+    m_program->setUniformValue("uBlur", m_blur);
+    m_program->setUniformValue("uBlurRadius", m_blurRadius);
+    m_program->setUniformValue("uReverse", m_reverse);
+    m_program->setUniformValue("uColorReduce", m_colorReduce);
+    m_program->setUniformValue("uColorReduceLevel", m_colorReduceLevel);
+    m_program->setUniformValue("uGamma", m_gamma);
+    m_program->setUniformValue("uGammaValue", m_gammaValue);
+    m_program->setUniformValue("uContrastBright", m_contrastBright);
+    m_program->setUniformValue("uContrast", m_contrast);
+    m_program->setUniformValue("uBrightness", m_brightness);
+    m_program->setUniformValue("uMirror", m_mirror);
     m_program->enableAttributeArray(0);
     m_program->enableAttributeArray(1);
     m_program->setAttributeArray(0, m_vertices, 2);
@@ -361,3 +447,30 @@ void OpenGLVideoWidget::clearFrame()
     m_frameUpdated = false;
     update();
 }
+
+void OpenGLVideoWidget::setGray(bool on) {
+    if (m_gray != on) { m_gray = on; update(); }
+}
+void OpenGLVideoWidget::setThreshold(bool on, float value) {
+    if (m_threshold != on || m_thresholdValue != value) { m_threshold = on; m_thresholdValue = value; update(); }
+}
+void OpenGLVideoWidget::setBlur(bool on, float radius) {
+    if (m_blur != on || m_blurRadius != radius) { m_blur = on; m_blurRadius = radius; update(); }
+}
+void OpenGLVideoWidget::setReverse(bool on) {
+    if (m_reverse != on) { m_reverse = on; update(); }
+}
+void OpenGLVideoWidget::setColorReduce(bool on, int level) {
+    if (m_colorReduce != on || m_colorReduceLevel != level) { m_colorReduce = on; m_colorReduceLevel = level; update(); }
+}
+void OpenGLVideoWidget::setGamma(bool on, float gamma) {
+    if (m_gamma != on || m_gammaValue != gamma) { m_gamma = on; m_gammaValue = gamma; update(); }
+}
+void OpenGLVideoWidget::setContrastBright(bool on, float contrast, float brightness) {
+    if (m_contrastBright != on || m_contrast != contrast || m_brightness != brightness) {
+        m_contrastBright = on; m_contrast = contrast; m_brightness = brightness; update();
+    }
+}
+void OpenGLVideoWidget::setMirror(bool on) {
+    if (m_mirror != on) { m_mirror = on; update(); }
+}

+ 40 - 9
AvRecorder/ui/opengl_video_widget.h

@@ -5,20 +5,24 @@
 #include <QOpenGLShaderProgram>
 #include <QOpenGLTexture>
 #include <QOpenGLWidget>
-#include "basic/frame.h" // 添加对 AVFrame 的支持
 
-// 定义 VideoFrame 结构体
-struct VideoFrame {
-    unsigned char* data;
-    int width;
-    int height;
-    int format;
-};
+extern "C" {
+#include <libavutil/frame.h>
+}
 
 class OpenGLVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
 {
     Q_OBJECT
 public:
+    // 定义 VideoFrame 结构体
+    struct VideoFrame
+    {
+        unsigned char* data;
+        int width;
+        int height;
+        int format;
+    };
+
     explicit OpenGLVideoWidget(QWidget* parent = nullptr);
     ~OpenGLVideoWidget();
 
@@ -37,6 +41,16 @@ public:
     void setKeepAspectRatio(bool keep) { m_keepAspectRatio = keep; update(); }
     bool keepAspectRatio() const { return m_keepAspectRatio; }
 
+    // 效果控制接口
+    void setGray(bool on);
+    void setThreshold(bool on, float value = 0.5f);
+    void setBlur(bool on, float radius = 1.0f);
+    void setReverse(bool on);
+    void setColorReduce(bool on, int level = 8);
+    void setGamma(bool on, float gamma = 1.0f);
+    void setContrastBright(bool on, float contrast = 1.0f, float brightness = 0.0f);
+    void setMirror(bool on);
+
 protected:
     void initializeGL() override;
     void paintGL() override;
@@ -61,4 +75,21 @@ private:
     GLfloat m_texCoords[8];
 
     bool m_keepAspectRatio = true;
-};
+
+    // 效果参数
+    bool m_gray = false;
+    bool m_threshold = false;
+    float m_thresholdValue = 0.5f;
+    bool m_blur = false;
+    float m_blurRadius = 1.0f;
+    bool m_reverse = false;
+    bool m_colorReduce = false;
+    int m_colorReduceLevel = 8;
+    bool m_gamma = false;
+    float m_gammaValue = 1.0f;
+    bool m_contrastBright = false;
+    float m_contrast = 1.0f;
+    float m_brightness = 0.0f;
+
+    bool m_mirror = false;
+};

+ 1 - 1
LearningSmartClient.pro

@@ -82,7 +82,7 @@ msvc {
 DEFINES += _SILENCE_CLANG_COROUTINE_MESSAGE
 include($$PWD/AvRecorder/AvRecorder.pri)
 include($$PWD/AvPlayer/AvPlayer.pri)
-
+include($$PWD/AvPlayer2/AvPlayer2.pri)
 
 include($$PWD/qwindowkit/qwindowkit.pri)
 include($$PWD/fmt.pri)

+ 11 - 0
bin/VideoPlayer.ini

@@ -0,0 +1,11 @@
+[%General]
+hidePlayContrl=1
+fullScreen=0
+openDXVA2=0
+loopPlay=1
+style=
+volume=0.8
+
+[Info]
+software=Video player
+author=Steven Huang

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä