Browse Source

1.优化rtsp播放
2.修复部分视频出现的杂音问题

huihui 6 tháng trước cách đây
mục cha
commit
817df7f461
28 tập tin đã thay đổi với 1313 bổ sung571 xóa
  1. 3 3
      CMakeLists.txt
  2. 1 1
      module/VideoPlayer/src/EventHandle/VideoPlayerEventHandle.h
  3. 135 0
      module/VideoPlayer/src/PcmPlayer/PcmPlayer.cpp
  4. 60 0
      module/VideoPlayer/src/PcmPlayer/PcmPlayer.h
  5. 117 0
      module/VideoPlayer/src/PcmPlayer/PcmPlayer_SDL.cpp
  6. 41 0
      module/VideoPlayer/src/PcmPlayer/PcmPlayer_SDL.h
  7. 0 0
      module/VideoPlayer/src/PcmPlayer/PcmVolumeControl.cpp
  8. 0 0
      module/VideoPlayer/src/PcmPlayer/PcmVolumeControl.h
  9. 0 217
      module/VideoPlayer/src/VideoPlayer/Audio/VideoPlayer_AudioThread.cpp
  10. 191 112
      module/VideoPlayer/src/VideoPlayer/VideoPlayer.cpp
  11. 31 22
      module/VideoPlayer/src/VideoPlayer/VideoPlayer.h
  12. 272 0
      module/VideoPlayer/src/VideoPlayer/VideoPlayer_AudioThread.cpp
  13. 17 6
      module/VideoPlayer/src/VideoPlayer/VideoPlayer_VideoThread.cpp
  14. 39 0
      module/VideoPlayer/src/frame/AudioFrame/AACFrame.cpp
  15. 77 0
      module/VideoPlayer/src/frame/AudioFrame/AACFrame.h
  16. 41 0
      module/VideoPlayer/src/frame/AudioFrame/PCMFrame.cpp
  17. 44 0
      module/VideoPlayer/src/frame/AudioFrame/PCMFrame.h
  18. 0 0
      module/VideoPlayer/src/frame/VideoFrame/VideoFrame.cpp
  19. 0 0
      module/VideoPlayer/src/frame/VideoFrame/VideoFrame.h
  20. 3 2
      module/VideoPlayer/src/types.h
  21. 163 0
      module/VideoPlayer/src/util/thread.cpp
  22. 65 0
      module/VideoPlayer/src/util/thread.h
  23. 1 1
      src/AppConfig.cpp
  24. 0 128
      src/Base/FunctionTransfer.cpp
  25. 0 61
      src/Base/FunctionTransfer.h
  26. 5 10
      src/MainWindow.cpp
  27. 6 6
      src/Widget/ShowVideoWidget.cpp
  28. 1 2
      src/Widget/ShowVideoWidget.h

+ 3 - 3
CMakeLists.txt

@@ -114,9 +114,9 @@ file(GLOB_RECURSE ALL_SOURCE
 #     ${CMAKE_CURRENT_SOURCE_DIR}/module/DragAbleWidget/*.cpp)
 # list (APPEND ALL_SOURCE ${TMP_SOURCE})
 
-file(GLOB_RECURSE ALL_HEAD
-        src/*.hpp
-        src/*.h)
+# file(GLOB_RECURSE ALL_HEAD
+#         src/*.hpp
+#         src/*.h)
 
 qt5_add_resources(qrc_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/resources.qrc)
 message("ALL_SOURCE:"${ALL_SOURCE})

+ 1 - 1
module/VideoPlayer/src/EventHandle/VideoPlayerEventHandle.h

@@ -2,7 +2,7 @@
 #define VIDEOPLAYEREVENTHANDLE_H
 
 #include "types.h"
-#include "VideoPlayer/Video/VideoFrame.h"
+#include "frame/VideoFrame/VideoFrame.h"
 
 class VideoPlayerCallBack
 {

+ 135 - 0
module/VideoPlayer/src/PcmPlayer/PcmPlayer.cpp

@@ -0,0 +1,135 @@
+#include "PcmPlayer.h"
+
+#include "PcmVolumeControl.h"
+
+#include <stdio.h>
+
+PcmPlayer::PcmPlayer()
+{
+    mCond = new Cond();
+}
+
+PcmPlayer::~PcmPlayer()
+{
+
+}
+
+bool PcmPlayer::startPlay()
+{
+    auto now = std::chrono::system_clock::now();
+    auto tick = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
+
+    m_last_try_open_device_time = tick;
+
+    bool is_succeed = openDevice();
+    m_device_opened = is_succeed;
+    return is_succeed;
+}
+
+bool PcmPlayer::stopPlay()
+{
+    bool isSucceed = closeDevice();
+    m_device_opened = false;
+    return isSucceed;
+}
+
+int PcmPlayer::inputPCMFrame(PCMFramePtr framePtr)
+{
+    int frame_size = 0;
+
+    if (m_device_opened)
+    {
+        //音频设备打开的情况下才处理播放
+
+        mCond->Lock();
+        mPcmFrameList.push_back(framePtr);
+        frame_size = mPcmFrameList.size();
+        mCond->Unlock();
+        mCond->Signal();
+
+    //    qDebug()<<m_sample_rate<<m_channel<<m_device_opened<<framePtr->sampleRate()<<framePtr->channels();
+
+        int channels = framePtr->channels();
+        int sample_rate = framePtr->sampleRate();
+
+        ///用于实现 播放队列太大的时候,提高播放采样率,用于直播消延时
+        static int use_sample_rate = framePtr->sampleRate();
+        if (frame_size > 20)
+        {
+        //    use_sample_rate = 32000;
+            use_sample_rate = sample_rate*2;
+            // qDebug()<<__FUNCTION__<<"frame_size="<<frame_size<<"make sample_rate bigger>>"<<use_sample_rate;
+        }
+        else if (frame_size < 10 && use_sample_rate != sample_rate)
+        {
+            use_sample_rate = sample_rate;
+            // qDebug()<<__FUNCTION__<<"frame_size="<<frame_size<<"make sample_rate normal>>"<<use_sample_rate;
+        }
+    //qDebug()<<__FUNCTION__<<"frame_size="<<frame_size<<"make sample_rate bigger>>"<<sample_rate;
+        if (m_sample_rate != use_sample_rate || m_channel != channels || !m_device_opened)
+        {
+            m_sample_rate = use_sample_rate;
+            m_channel = channels;
+
+            stopPlay();
+            startPlay();
+        }
+    }
+    else
+    {
+        auto now = std::chrono::system_clock::now();
+        auto tick = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
+
+        if ((tick - m_last_try_open_device_time) > 3000)
+        {
+            //音频设备未打开,则每隔3秒尝试打开一次
+            stopPlay();
+            startPlay();
+        }
+    }
+    return frame_size;
+}
+
+int PcmPlayer::getPcmFrameSize()
+{
+//    mCond->Lock();
+    int size = mPcmFrameList.size();
+//    mCond->Unlock();
+
+    return size;
+}
+
+void PcmPlayer::playAudioBuffer(void *stream, int len)
+{
+    PCMFramePtr pcmFramePtr = nullptr;
+//fprintf(stderr, "%s %d %d \n", __FUNCTION__, len, mPcmFrameList.size());
+    mCond->Lock();
+    if (!mPcmFrameList.empty())
+    {
+        pcmFramePtr = mPcmFrameList.front();
+        mPcmFrameList.pop_front();
+    }
+    mCond->Unlock();
+
+    if (pcmFramePtr != nullptr)
+    {
+        /// 这里我的PCM数据都是AV_SAMPLE_FMT_S16 44100 双声道的,且采样都是1024,因此pcmFramePtr->getSize()和len值相等都为4096.
+        /// 所以这里直接拷贝过去就好了。
+//        fprintf(stderr, "%s %d %d \n", __FUNCTION__, pcmFramePtr->getSize(), len);
+
+        if (m_is_mute)// || mIsNeedPause) //静音 或者 是在暂停的时候跳转了
+        {
+            memset(stream, 0x0, pcmFramePtr->getSize());
+        }
+        else
+        {
+            PcmVolumeControl::RaiseVolume((char*)pcmFramePtr->getBuffer(), pcmFramePtr->getSize(), 1, m_volume);
+            memcpy(stream, (uint8_t *)pcmFramePtr->getBuffer(), pcmFramePtr->getSize());
+        }
+
+        len -= pcmFramePtr->getSize();
+
+        m_current_pts = pcmFramePtr->pts();
+    }
+
+}

+ 60 - 0
module/VideoPlayer/src/PcmPlayer/PcmPlayer.h

@@ -0,0 +1,60 @@
+#ifndef PCMPLAYER_H
+#define PCMPLAYER_H
+
+#include <thread>
+#include <list>
+
+#include "Mutex/Cond.h"
+#include "frame/AudioFrame/PCMFrame.h"
+
+struct AudioDevice
+{
+    std::string deviceName;
+    uint32_t deviceId;
+};
+
+class PcmPlayer
+{
+public:
+    PcmPlayer();
+    virtual ~PcmPlayer();
+
+    virtual std::list<AudioDevice> getAudiDeviceList() = 0; //获取音频设备列表
+
+    bool startPlay();
+    bool stopPlay();
+
+    int inputPCMFrame(PCMFramePtr framePtr);
+    int getPcmFrameSize();
+
+    uint32_t getCurrentPts(){return m_current_pts;}
+
+    void setMute(const bool is_mute){m_volume = is_mute;}
+    void setVolume(float value){m_volume = value;}
+    float getVolume(){return m_volume;}
+
+    bool deviceOpened(){return m_device_opened;}
+
+protected:
+    Cond *mCond;
+    std::list<PCMFramePtr> mPcmFrameList;
+
+    uint32_t m_current_pts = 0; //当前播放帧的时间戳
+    bool m_device_opened = false; //设备是否已经打开了
+    uint64_t m_last_try_open_device_time = 0; //上一次尝试打开音频设备的时间
+    int m_sample_rate = 0;
+    int m_channel = 0;
+
+    ///音量相关变量
+    bool  m_is_mute = false;
+    float m_volume = 1.0f; //音量 0~1 超过1 表示放大倍数
+
+    virtual bool openDevice()  = 0;
+    virtual bool closeDevice() = 0;
+
+protected:
+    void playAudioBuffer(void *stream, int len);
+
+};
+
+#endif // AUDIOPLAYER_H

+ 117 - 0
module/VideoPlayer/src/PcmPlayer/PcmPlayer_SDL.cpp

@@ -0,0 +1,117 @@
+#include "PcmPlayer_SDL.h"
+
+#include "PcmVolumeControl.h"
+
+#include <stdio.h>
+
+void PcmPlayer_SDL::sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len)
+{
+    PcmPlayer_SDL *player = (PcmPlayer_SDL*)userdata;
+    player->playAudioBuffer(stream, len);
+}
+
+PcmPlayer_SDL::PcmPlayer_SDL()
+{
+//    ///SDL初始化需要放入子线程中,否则有些电脑会有问题。
+//    if (SDL_Init(SDL_INIT_AUDIO))
+//    {
+//        fprintf(stderr, "Could not initialize SDL - %s. \n", SDL_GetError());
+//    }
+}
+
+PcmPlayer_SDL::~PcmPlayer_SDL()
+{
+    SDL_Quit();
+}
+
+std::list<AudioDevice> PcmPlayer_SDL::getAudiDeviceList()
+{
+    std::list<AudioDevice> deviceList;
+
+    int num = SDL_GetNumAudioDevices(0);
+    for (int i=0;i<num;i++)
+    {
+        AudioDevice device;
+        device.deviceId = i;
+        device.deviceName = SDL_GetAudioDeviceName(i, 0);
+
+        deviceList.push_back(device);
+    }
+
+    return deviceList;
+}
+
+bool PcmPlayer_SDL::openDevice()
+{
+    static bool is_inited = false;
+
+//    if (!is_inited)
+    {
+        is_inited = true;
+
+        ///SDL初始化需要放入子线程中,否则有些电脑会有问题。
+        if (SDL_Init(SDL_INIT_AUDIO))
+        {
+            fprintf(stderr, "Could not initialize SDL - %s. \n", SDL_GetError());
+        }
+    }
+
+    ///打开SDL,并设置播放的格式为:AUDIO_S16LSB 双声道,44100hz
+    ///后期使用ffmpeg解码完音频后,需要重采样成和这个一样的格式,否则播放会有杂音
+    SDL_AudioSpec wanted_spec, spec;
+//    int wanted_nb_channels = 2;
+//    int samplerate = 44100;
+    int wanted_nb_channels = m_channel;
+    int samplerate = m_sample_rate;
+
+    wanted_spec.channels = wanted_nb_channels;
+    wanted_spec.freq = samplerate;
+    wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
+    wanted_spec.silence = 0;            // 0指示静音
+    wanted_spec.samples = 1024;  // 自定义SDL缓冲区大小
+    wanted_spec.callback = sdlAudioCallBackFunc;  // 回调函数
+    wanted_spec.userdata = this;                  // 传给上面回调函数的外带数据
+
+    int num = SDL_GetNumAudioDevices(0);
+    for (int i=0;i<num;i++)
+    {
+        mAudioID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i,0), false, &wanted_spec, &spec,0);
+        if (mAudioID > 0)
+        {
+            break;
+        }
+    }
+// qDebug()<<__FUNCTION__<<"NUM="<<num<<" mAudioID="<<mAudioID;
+    /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
+//    if (spec.format != AUDIO_S16SYS)
+    if (mAudioID <= 0)
+    {
+        return false;
+    }
+    else
+    {
+        SDL_LockAudioDevice(mAudioID);
+        SDL_PauseAudioDevice(mAudioID, 0);
+        SDL_UnlockAudioDevice(mAudioID);
+    }
+//fprintf(stderr, "mAudioID=%d\n\n\n\n\n\n", mAudioID);
+    return true;
+}
+
+bool PcmPlayer_SDL::closeDevice()
+{
+    if (mAudioID > 0)
+    {
+        SDL_LockAudioDevice(mAudioID);
+        SDL_PauseAudioDevice(mAudioID, 1);
+        SDL_UnlockAudioDevice(mAudioID);
+
+        SDL_CloseAudioDevice(mAudioID);
+    }
+
+    SDL_Quit();
+
+    mAudioID = 0;
+
+    return true;
+}

+ 41 - 0
module/VideoPlayer/src/PcmPlayer/PcmPlayer_SDL.h

@@ -0,0 +1,41 @@
+#ifndef AUDIOPLAYERSDL_H
+#define AUDIOPLAYERSDL_H
+
+#include <thread>
+#include <list>
+
+extern "C"
+{
+    #include <SDL.h>
+    #include <SDL_audio.h>
+    #include <SDL_types.h>
+    #include <SDL_name.h>
+    #include <SDL_main.h>
+    #include <SDL_config.h>
+}
+
+#include "PcmPlayer/PcmPlayer.h"
+
+class PcmPlayer_SDL : public PcmPlayer
+{
+public:
+    PcmPlayer_SDL();
+    ~PcmPlayer_SDL();
+
+    std::list<AudioDevice> getAudiDeviceList(); //获取音频设备列表
+
+private:
+    ///本播放器中SDL仅用于播放音频,不用做别的用途
+    ///SDL播放音频相关
+    SDL_AudioDeviceID mAudioID = 0;
+    bool openDevice();
+    bool closeDevice();
+
+    void sdlAudioCallBack(Uint8 *stream, int len);
+
+protected:
+    static void sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len);
+
+};
+
+#endif // AUDIOPLAYERSDL_H

+ 0 - 0
module/VideoPlayer/src/VideoPlayer/Audio/PcmVolumeControl.cpp → module/VideoPlayer/src/PcmPlayer/PcmVolumeControl.cpp


+ 0 - 0
module/VideoPlayer/src/VideoPlayer/Audio/PcmVolumeControl.h → module/VideoPlayer/src/PcmPlayer/PcmVolumeControl.h


+ 0 - 217
module/VideoPlayer/src/VideoPlayer/Audio/VideoPlayer_AudioThread.cpp

@@ -1,217 +0,0 @@
-/**
- * 叶海辉
- * QQ群121376426
- * http://blog.yundiantech.com/
- */
-
-#include "VideoPlayer/VideoPlayer.h"
-
-#include "PcmVolumeControl.h"
-
-#include <stdio.h>
-
-#include <QDebug>
-
-void VideoPlayer::sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len)
-{
-    VideoPlayer *player = (VideoPlayer*)userdata;
-    player->sdlAudioCallBack(stream, len);
-}
-
-void VideoPlayer::sdlAudioCallBack(Uint8 *stream, int len)
-{
-    int len1, audio_data_size;
-
-    /*   len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */
-    while (len > 0)
-    {
-        /*  audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
-        /*   这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
-        /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
-         /*   多的桢数据 */
-        if (audio_buf_index >= audio_buf_size)
-        {
-            audio_data_size = decodeAudioFrame();
-
-            /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
-            if (audio_data_size <= 0)
-            {
-                /* silence */
-                audio_buf_size = 1024;
-                /* 清零,静音 */
-                memset(audio_buf, 0, audio_buf_size);
-            }
-            else
-            {
-                audio_buf_size = audio_data_size;
-            }
-            audio_buf_index = 0;
-        }
-        /*  查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
-        len1 = audio_buf_size - audio_buf_index;
-
-        if (len1 > len)
-        {
-            len1 = len;
-        }
-
-        if (audio_buf == NULL) return;
-
-        if (mIsMute || mIsNeedPause) //静音 或者 是在暂停的时候跳转了
-        {
-            memset(audio_buf + audio_buf_index, 0, len1);
-        }
-        else
-        {
-            PcmVolumeControl::RaiseVolume((char*)audio_buf + audio_buf_index, len1, 1, mVolume);
-        }
-
-        memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
-
-//        SDL_memset(stream, 0x0, len);// make sure this is silence.
-//        SDL_MixAudio(stream, (uint8_t *) audio_buf + audio_buf_index, len1, SDL_MIX_MAXVOLUME);
-
-//        SDL_MixAudio(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, len1, 50);
-//        SDL_MixAudioFormat(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, 50);
-
-        len -= len1;
-        stream += len1;
-        audio_buf_index += len1;
-
-    }
-
-}
-
-int VideoPlayer::decodeAudioFrame(bool isBlock)
-{
-    int audioBufferSize = 0;
-
-    while(1)
-    {
-        if (mIsQuit)
-        {
-            mIsAudioThreadFinished = true;
-            clearAudioQuene(); //清空队列
-            break;
-        }
-
-        if (mIsPause == true) //判断暂停
-        {
-            break;
-        }
-
-        mConditon_Audio->Lock();
-
-        if (mAudioPacktList.size() <= 0)
-        {
-            if (isBlock)
-            {
-                mConditon_Audio->Wait();
-            }
-            else
-            {
-                mConditon_Audio->Unlock();
-                break;
-            }
-        }
-
-        AVPacket packet = mAudioPacktList.front();
-        mAudioPacktList.pop_front();
-//qDebug()<<__FUNCTION__<<mAudioPacktList.size();
-        mConditon_Audio->Unlock();
-
-        AVPacket *pkt = &packet;
-
-        /* if update, update the audio clock w/pts */
-        if (pkt->pts != AV_NOPTS_VALUE)
-        {
-            audio_clock = av_q2d(mAudioStream->time_base) * pkt->pts;
-        }
-
-        //收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下
-        if(strcmp((char*)pkt->data,FLUSH_DATA) == 0)
-        {
-            avcodec_flush_buffers(mAudioStream->codec);
-            av_packet_unref(pkt);
-            continue;
-        }
-
-        if (seek_flag_audio)
-        {
-            //发生了跳转 则跳过关键帧到目的时间的这几帧
-           if (audio_clock < seek_time)
-           {
-               continue;
-           }
-           else
-           {
-               seek_flag_audio = 0;
-           }
-        }
-
-        //解码AVPacket->AVFrame
-        int got_frame = 0;
-        int size = avcodec_decode_audio4(aCodecCtx, aFrame, &got_frame, &packet);
-
-//保存重采样之前的一个声道的数据方法
-//size_t unpadded_linesize = aFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat) aFrame->format);
-//static FILE * fp = fopen("out.pcm", "wb");
-//fwrite(aFrame->extended_data[0], 1, unpadded_linesize, fp);
-
-        av_packet_unref(&packet);
-
-        if (got_frame)
-        {
-            /// ffmpeg解码之后得到的音频数据不是SDL想要的,
-            /// 因此这里需要重采样成44100 双声道 AV_SAMPLE_FMT_S16
-
-            ///
-            /// 需要保证重采样后音频的时间是相同的,不同采样率下同样时间采集的数据采样点个数肯定不一样。
-            /// 因此就需要重新计算采样点个数(使用下面的函数)
-            /// 将in_sample_rate的采样次数换算成out_sample_rate对应的采样次数
-            int nb_samples = av_rescale_rnd(swr_get_delay(swrCtx, out_sample_rate) + aFrame->nb_samples, out_sample_rate, in_sample_rate, AV_ROUND_UP);
-
-            if (aFrame_ReSample != nullptr)
-            {
-                if (aFrame_ReSample->nb_samples != nb_samples)
-                {
-                    av_frame_free(&aFrame_ReSample);
-                    aFrame_ReSample = nullptr;
-                }
-            }
-
-            ///解码一帧后才能获取到采样率等信息,因此将初始化放到这里
-            if (aFrame_ReSample == nullptr)
-            {
-                aFrame_ReSample = av_frame_alloc();
-
-                aFrame_ReSample->format = out_sample_fmt;
-                aFrame_ReSample->channel_layout = out_ch_layout;
-                aFrame_ReSample->sample_rate = out_sample_rate;
-                aFrame_ReSample->nb_samples = nb_samples;
-
-                int ret = av_samples_fill_arrays(aFrame_ReSample->data, aFrame_ReSample->linesize, audio_buf, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 0);
-//                int ret = av_frame_get_buffer(aFrame_ReSample, 0);
-                if (ret < 0)
-                {
-                    fprintf(stderr, "Error allocating an audio buffer\n");
-//                        exit(1);
-                }
-            }
-
-            int len2 = swr_convert(swrCtx, aFrame_ReSample->data, aFrame_ReSample->nb_samples, (const uint8_t**)aFrame->data, aFrame->nb_samples);
-
-///下面这两种方法计算的大小是一样的
-#if 0
-            int resampled_data_size = len2 * audio_tgt_channels * av_get_bytes_per_sample(out_sample_fmt);
-#else
-            int resampled_data_size = av_samples_get_buffer_size(NULL, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 1);
-#endif
-//qDebug()<<__FUNCTION__<<resampled_data_size<<aFrame_ReSample->nb_samples<<aFrame->nb_samples;
-            audioBufferSize = resampled_data_size;
-            break;
-        }
-    }
-
-    return audioBufferSize;
-}

+ 191 - 112
module/VideoPlayer/src/VideoPlayer/VideoPlayer.cpp

@@ -6,7 +6,7 @@
 
 #include "VideoPlayer.h"
 
-#include "Audio/PcmVolumeControl.h"
+#include "PcmPlayer/PcmPlayer_SDL.h"
 
 #include <stdio.h>
 
@@ -19,12 +19,16 @@ VideoPlayer::VideoPlayer()
 
     mVideoPlayerCallBack = nullptr;
 
-    mAudioID = 0;
+//    mAudioID = 0;
     mIsMute = false;
 
     mIsNeedPause = false;
 
     mVolume = 1;
+
+    mPcmPlayer = new PcmPlayer_SDL();
+
+    this->setSingleMode(true);
 }
 
 VideoPlayer::~VideoPlayer()
@@ -38,7 +42,7 @@ bool VideoPlayer::initPlayer()
 
     if (!isInited)
     {
-        av_register_all(); //初始化FFMPEG  调用了这个才能正常使用编码器和解码器
+//        av_register_all(); //初始化FFMPEG  调用了这个才能正常使用编码器和解码器
         avformat_network_init(); //支持打开网络文件
 
         isInited = true;
@@ -68,19 +72,20 @@ bool VideoPlayer::startPlay(const std::string &filePath)
         mFilePath = filePath;
 
     //启动新的线程实现读取视频文件
-    std::thread([&](VideoPlayer *pointer)
-    {
-        pointer->readVideoFile();
+//    std::thread([&](VideoPlayer *pointer)
+//    {
+//        pointer->readVideoFile();
 
-    }, this).detach();
+//    }, this).detach();
+    this->start();
 
     return true;
 
 }
 
-bool VideoPlayer::replay()
+bool VideoPlayer::replay(bool isWait)
 {
-    stop();
+    stop(isWait);
 
     startPlay(mFilePath);
 
@@ -140,7 +145,8 @@ bool VideoPlayer::stop(bool isWait)
     {
         while(!mIsReadThreadFinished)
         {
-            mSleep(3);
+            fprintf(stderr, "mIsReadThreadFinished=%d \n", mIsReadThreadFinished);
+            mSleep(100);
         }
     }
 
@@ -156,9 +162,16 @@ void VideoPlayer::seek(int64_t pos)
     }
 }
 
+void VideoPlayer::setMute(bool isMute)
+{
+    mIsMute = isMute;
+    mPcmPlayer->setMute(isMute);
+}
+
 void VideoPlayer::setVolume(float value)
 {
     mVolume = value;
+    mPcmPlayer->setVolume(value);
 }
 
 double VideoPlayer::getCurrentTime()
@@ -171,71 +184,100 @@ int64_t VideoPlayer::getTotalTime()
     return pFormatCtx->duration;
 }
 
-int VideoPlayer::openSDL()
+//int VideoPlayer::openSDL()
+//{
+//    ///打开SDL,并设置播放的格式为:AUDIO_S16LSB 双声道,44100hz
+//    ///后期使用ffmpeg解码完音频后,需要重采样成和这个一样的格式,否则播放会有杂音
+//    SDL_AudioSpec wanted_spec, spec;
+//    int wanted_nb_channels = 2;
+////    int samplerate = 44100;
+//    int samplerate = m_out_sample_rate;
+
+//    wanted_spec.channels = wanted_nb_channels;
+//    wanted_spec.freq = samplerate;
+//    wanted_spec.samples = FFMAX(512, 2 << av_log2(wanted_spec.freq / 30));
+//    wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
+//    wanted_spec.silence = 0;            // 0指示静音
+////    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  // 自定义SDL缓冲区大小
+//    wanted_spec.callback = sdlAudioCallBackFunc;  // 回调函数
+//    wanted_spec.userdata = this;                  // 传给上面回调函数的外带数据
+
+//    int num = SDL_GetNumAudioDevices(0);
+//    for (int i=0;i<num;i++)
+//    {
+//        mAudioID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i,0), false, &wanted_spec, &spec,0);
+//        if (mAudioID > 0)
+//        {
+//            break;
+//        }
+//    }
+
+//    /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
+////    if (spec.format != AUDIO_S16SYS)
+//    if (mAudioID <= 0)
+//    {
+//        mIsAudioThreadFinished = true;
+//        return -1;
+//    }
+//fprintf(stderr, "mAudioID=%d\n\n\n\n\n\n", mAudioID);
+//    return 0;
+//}
+
+//void VideoPlayer::closeSDL()
+//{
+//    if (mAudioID > 0)
+//    {
+//        SDL_LockAudioDevice(mAudioID);
+//        SDL_PauseAudioDevice(mAudioID, 1);
+//        SDL_UnlockAudioDevice(mAudioID);
+
+//        SDL_CloseAudioDevice(mAudioID);
+//    }
+
+//    mAudioID = 0;
+//}
+
+
+static int CheckInterrupt(void* ctx)
 {
-    ///打开SDL,并设置播放的格式为:AUDIO_S16LSB 双声道,44100hz
-    ///后期使用ffmpeg解码完音频后,需要重采样成和这个一样的格式,否则播放会有杂音
-    SDL_AudioSpec wanted_spec, spec;
-    int wanted_nb_channels = 2;
-//    int samplerate = 44100;
-    int samplerate = out_sample_rate;
-
-    wanted_spec.channels = wanted_nb_channels;
-    wanted_spec.samples = FFMAX(512, 2 << av_log2(wanted_spec.freq / 30));
-    wanted_spec.freq = samplerate;
-    wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
-    wanted_spec.silence = 0;            // 0指示静音
-//    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  // 自定义SDL缓冲区大小
-    wanted_spec.callback = sdlAudioCallBackFunc;  // 回调函数
-    wanted_spec.userdata = this;                  // 传给上面回调函数的外带数据
-
-    int num = SDL_GetNumAudioDevices(0);
-    for (int i=0;i<num;i++)
-    {
-        mAudioID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i,0), false, &wanted_spec, &spec,0);
-        if (mAudioID > 0)
-        {
-            break;
-        }
-    }
+    VideoPlayer *pointer = (VideoPlayer*)ctx;
 
-    /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
-//    if (spec.format != AUDIO_S16SYS)
-    if (mAudioID <= 0)
+    int ret = 0;
+
+    if (pointer->mIsOpenStream)
     {
-        mIsAudioThreadFinished = true;
-        return -1;
+        ///打开超时10秒
+        if ((av_gettime()-pointer->mCallStartTime) >= 10000000)
+        {
+            ret = 1;
+        }
     }
-fprintf(stderr, "mAudioID=%d\n\n\n\n\n\n", mAudioID);
-    return 0;
-}
-
-void VideoPlayer::closeSDL()
-{
-    if (mAudioID > 0)
+    else
     {
-        SDL_LockAudioDevice(mAudioID);
-        SDL_PauseAudioDevice(mAudioID, 1);
-        SDL_UnlockAudioDevice(mAudioID);
-
-        SDL_CloseAudioDevice(mAudioID);
+        ///读取超时10秒
+        if ((av_gettime()-pointer->mCallStartTime) >= 10000000)
+        {
+            ret = 1;
+        }
     }
+//fprintf(stderr,"%s mIsOpenStream=%d ret=%d \n", __FUNCTION__, pointer->mIsOpenStream, ret);
 
-    mAudioID = 0;
+    return ret;
 }
 
-void VideoPlayer::readVideoFile()
+void VideoPlayer::run()
 {
-    ///SDL初始化需要放入子线程中,否则有些电脑会有问题。
-    if (SDL_Init(SDL_INIT_AUDIO))
-    {
-        doOpenSdlFailed(-100);
-        fprintf(stderr, "Could not initialize SDL - %s. \n", SDL_GetError());
-        return;
-    }
+//    ///SDL初始化需要放入子线程中,否则有些电脑会有问题。
+//    if (SDL_Init(SDL_INIT_AUDIO))
+//    {
+//        doOpenSdlFailed(-100);
+//        fprintf(stderr, "Could not initialize SDL - %s. \n", SDL_GetError());
+//        return;
+//    }
 
     mIsReadThreadFinished = false;
     mIsReadFinished = false;
+    mIsReadError = false;
 
     const char * file_path = mFilePath.c_str();
 
@@ -260,13 +302,20 @@ void VideoPlayer::readVideoFile()
     //Allocate an AVFormatContext.
     pFormatCtx = avformat_alloc_context();
 
+    pFormatCtx->interrupt_callback.callback = CheckInterrupt;//超时回调
+    pFormatCtx->interrupt_callback.opaque = this;
+
     AVDictionary* opts = NULL;
-    av_dict_set(&opts, "rtsp_transport", "tcp", 0); //设置tcp or udp,默认一般优先tcp再尝试udp
-    av_dict_set(&opts, "stimeout", "60000000", 0);//设置超时3秒
+    av_dict_set(&opts, "rtsp_transport", "udp", 0); //设置tcp or udp,默认一般优先tcp再尝试udp
+//    av_dict_set(&opts, "rtsp_transport", "tcp", 0); //设置tcp or udp,默认一般优先tcp再尝试udp
+    av_dict_set(&opts, "stimeout", "5000000", 0);//设置超时5秒
 
+    mCallStartTime = av_gettime();
+    mIsOpenStream  = true;
     if (avformat_open_input(&pFormatCtx, file_path, nullptr, &opts) != 0)
     {
         fprintf(stderr, "can't open the file. \n");
+        mIsReadError = true;
         doOpenVideoFileFailed();
         goto end;
     }
@@ -274,6 +323,7 @@ void VideoPlayer::readVideoFile()
     if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
     {
         fprintf(stderr, "Could't find stream infomation.\n");
+        mIsReadError = true;
         doOpenVideoFileFailed();
         goto end;
     }
@@ -284,11 +334,11 @@ void VideoPlayer::readVideoFile()
     ///循环查找视频中包含的流信息,
     for (int i = 0; i < pFormatCtx->nb_streams; i++)
     {
-        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
         {
             videoStream = i;
         }
-        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO  && audioStream < 0)
+        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO  && audioStream < 0)
         {
             audioStream = i;
         }
@@ -299,9 +349,14 @@ void VideoPlayer::readVideoFile()
     ///打开视频解码器,并启动视频线程
     if (videoStream >= 0)
     {
+        AVStream *video_stream = pFormatCtx->streams[videoStream];
+
         ///查找视频解码器
-        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
-        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+        pCodec = (AVCodec*)avcodec_find_decoder(video_stream->codecpar->codec_id);
+        pCodecCtx = avcodec_alloc_context3(pCodec);
+        pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
+
+        avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
 
         if (pCodec == nullptr)
         {
@@ -318,7 +373,7 @@ void VideoPlayer::readVideoFile()
             goto end;
         }
 
-        mVideoStream = pFormatCtx->streams[videoStream];
+        mVideoStream = video_stream;
 
         ///创建一个线程专门用来解码视频
         std::thread([&](VideoPlayer *pointer)
@@ -331,9 +386,10 @@ void VideoPlayer::readVideoFile()
 
     if (audioStream >= 0)
     {
+        AVStream *audio_stream = pFormatCtx->streams[audioStream];
+
         ///查找音频解码器
-        aCodecCtx = pFormatCtx->streams[audioStream]->codec;
-        aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
+        aCodec = (AVCodec*)avcodec_find_decoder(audio_stream->codecpar->codec_id);
 
         if (aCodec == NULL)
         {
@@ -342,6 +398,10 @@ void VideoPlayer::readVideoFile()
         }
         else
         {
+            /* Allocate a codec context for the decoder */
+            aCodecCtx = avcodec_alloc_context3(aCodec);
+            avcodec_parameters_to_context(aCodecCtx, audio_stream->codecpar);
+
             ///打开音频解码器
             if (avcodec_open2(aCodecCtx, aCodec, nullptr) < 0)
             {
@@ -377,13 +437,13 @@ void VideoPlayer::readVideoFile()
             //输出的采样格式 16bit PCM
             out_sample_fmt = AV_SAMPLE_FMT_S16;
             //输入的采样率
-            in_sample_rate = aCodecCtx->sample_rate;
+            m_in_sample_rate = aCodecCtx->sample_rate;
             //输入的声道布局
             in_ch_layout = aCodecCtx->channel_layout;
 
             //输出的采样率
-//            out_sample_rate = 44100;
-            out_sample_rate = aCodecCtx->sample_rate;
+//            m_out_sample_rate = 44100;
+            m_out_sample_rate = aCodecCtx->sample_rate;
 
             //输出的声道布局
 
@@ -399,8 +459,8 @@ void VideoPlayer::readVideoFile()
                 in_ch_layout = av_get_default_channel_layout(aCodecCtx->channels);
             }
 
-            swrCtx = swr_alloc_set_opts(nullptr, out_ch_layout, out_sample_fmt, out_sample_rate,
-                                                 in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
+            swrCtx = swr_alloc_set_opts(nullptr, out_ch_layout, out_sample_fmt, m_out_sample_rate,
+                                                 in_ch_layout, in_sample_fmt, m_in_sample_rate, 0, nullptr);
 
             /** Open the resampler with the specified parameters. */
             int ret = swr_init(swrCtx);
@@ -417,7 +477,7 @@ void VideoPlayer::readVideoFile()
             }
 
             //存储pcm数据
-            int out_linesize = out_sample_rate * audio_tgt_channels;
+            int out_linesize = m_out_sample_rate * audio_tgt_channels;
 
     //        out_linesize = av_samples_get_buffer_size(NULL, audio_tgt_channels, av_get_bytes_per_sample(out_sample_fmt), out_sample_fmt, 1);
             out_linesize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
@@ -425,32 +485,32 @@ void VideoPlayer::readVideoFile()
 
             mAudioStream = pFormatCtx->streams[audioStream];
 
-            ///打开SDL播放声音
-            int code = openSDL();
+//            ///打开SDL播放声音
+//            int code = openSDL();
 
-            if (code == 0)
-            {
-                SDL_LockAudioDevice(mAudioID);
-                SDL_PauseAudioDevice(mAudioID,0);
-                SDL_UnlockAudioDevice(mAudioID);
+//            if (code == 0)
+//            {
+//                SDL_LockAudioDevice(mAudioID);
+//                SDL_PauseAudioDevice(mAudioID,0);
+//                SDL_UnlockAudioDevice(mAudioID);
 
                 mIsAudioThreadFinished = false;
-            }
-            else
-            {
-                doOpenSdlFailed(code);
-            }
+//            }
+//            else
+//            {
+//                doOpenSdlFailed(code);
+//            }
         }
 
     }
 
-//    av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息
+    av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息
 
     mPlayerState = VideoPlayer_Playing;
     doPlayerStateChanged(VideoPlayer_Playing, mVideoStream != nullptr, mAudioStream != nullptr);
 
     mVideoStartTime = av_gettime();
-fprintf(stderr, "%s mIsQuit=%d mIsPause=%d \n", __FUNCTION__, mIsQuit, mIsPause);
+//fprintf(stderr, "%s mIsQuit=%d mIsPause=%d file_path=%s \n", __FUNCTION__, mIsQuit, mIsPause, file_path);
     while (1)
     {
         if (mIsQuit)
@@ -477,7 +537,7 @@ fprintf(stderr, "%s mIsQuit=%d mIsPause=%d \n", __FUNCTION__, mIsQuit, mIsPause)
 
             if (av_seek_frame(pFormatCtx, stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0)
             {
-                fprintf(stderr, "%s: error while seeking\n",pFormatCtx->filename);
+                fprintf(stderr, "%s: error while seeking\n", file_path);
             }
             else
             {
@@ -531,25 +591,31 @@ fprintf(stderr, "%s mIsQuit=%d mIsPause=%d \n", __FUNCTION__, mIsQuit, mIsPause)
         }
 
         AVPacket packet;
+//qDebug("%s av_read_frame .... \n", __FUNCTION__);
+
+        mCallStartTime = av_gettime();
+        mIsOpenStream  = false;
+
         if (av_read_frame(pFormatCtx, &packet) < 0)
         {
             mIsReadFinished = true;
-
-            if (mIsQuit)
-            {
-                break; //解码线程也执行完了 可以退出了
-            }
+            mIsReadError = true;
+// qDebug("%s av_read_frame failed \n", __FUNCTION__);
+//            if (mIsQuit)
+//            {
+//                break; //解码线程也执行完了 可以退出了
+//            }
 
             mSleep(10);
             continue;
         }
-
+//qDebug("%s mIsQuit=%d mIsPause=%d packet.stream_index=%d \n", __FUNCTION__, mIsQuit, mIsPause, packet.stream_index);
         if (packet.stream_index == videoStream)
         {
             inputVideoQuene(packet);
             //这里我们将数据存入队列 因此不调用 av_free_packet 释放
         }
-        else if( packet.stream_index == audioStream )
+        else if(packet.stream_index == audioStream)
         {
             if (mIsAudioThreadFinished)
             { ///SDL没有打开,则音频数据直接释放
@@ -559,6 +625,9 @@ fprintf(stderr, "%s mIsQuit=%d mIsPause=%d \n", __FUNCTION__, mIsQuit, mIsPause)
             {
                 inputAudioQuene(packet);
                 //这里我们将数据存入队列 因此不调用 av_free_packet 释放
+
+                //音频解码不开线程,因此这里直接调用解码操作
+                decodeAudioFrame(false);
             }
 
         }
@@ -583,15 +652,18 @@ end:
 
     if (mPlayerState != VideoPlayer_Stop) //不是外部调用的stop 是正常播放结束
     {
-        stop();
+        stop(false);
     }
 
+    mIsAudioThreadFinished = true;
     while((mVideoStream != nullptr && !mIsVideoThreadFinished) || (mAudioStream != nullptr && !mIsAudioThreadFinished))
     {
+fprintf(stderr, "%s:%d mIsVideoThreadFinished=%d mIsAudioThreadFinished=%d \n", __FILE__, __LINE__, mIsVideoThreadFinished, mIsAudioThreadFinished);
         mSleep(10);
     } //确保视频线程结束后 再销毁队列
 
-    closeSDL();
+//    closeSDL();
+    mPcmPlayer->stopPlay();
 
     if (swrCtx != nullptr)
     {
@@ -628,7 +700,14 @@ end:
 
     SDL_Quit();
 
-    doPlayerStateChanged(VideoPlayer_Stop, mVideoStream != nullptr, mAudioStream != nullptr);
+    if (mIsReadError && !mIsQuit)
+    {
+        doPlayerStateChanged(VideoPlayer_ReadError, mVideoStream != nullptr, mAudioStream != nullptr);
+    }
+    else
+    {
+        doPlayerStateChanged(VideoPlayer_Stop, mVideoStream != nullptr, mAudioStream != nullptr);
+    }
 
     mIsReadThreadFinished = true;
 
@@ -637,10 +716,10 @@ fprintf(stderr, "%s finished \n", __FUNCTION__);
 
 bool VideoPlayer::inputVideoQuene(const AVPacket &pkt)
 {
-    if (av_dup_packet((AVPacket*)&pkt) < 0)
-    {
-        return false;
-    }
+//    if (av_dup_packet((AVPacket*)&pkt) < 0)
+//    {
+//        return false;
+//    }
 
     mConditon_Video->Lock();
     mVideoPacktList.push_back(pkt);
@@ -664,10 +743,10 @@ void VideoPlayer::clearVideoQuene()
 
 bool VideoPlayer::inputAudioQuene(const AVPacket &pkt)
 {
-    if (av_dup_packet((AVPacket*)&pkt) < 0)
-    {
-        return false;
-    }
+//    if (av_dup_packet((AVPacket*)&pkt) < 0)
+//    {
+//        return false;
+//    }
 
     mConditon_Audio->Lock();
     mAudioPacktList.push_back(pkt);
@@ -728,7 +807,7 @@ void VideoPlayer::doTotalTimeChanged(const int64_t &uSec)
 ///播放器状态改变的时候回调此函数
 void VideoPlayer::doPlayerStateChanged(const VideoPlayerState &state, const bool &hasVideo, const bool &hasAudio)
 {
-    fprintf(stderr, "%s \n", __FUNCTION__);
+    fprintf(stderr, "%s state=%d\n", __FUNCTION__, state);
 
     if (mVideoPlayerCallBack != nullptr)
     {
@@ -740,7 +819,7 @@ void VideoPlayer::doPlayerStateChanged(const VideoPlayerState &state, const bool
 ///显示视频数据,此函数不宜做耗时操作,否则会影响播放的流畅性。
 void VideoPlayer::doDisplayVideo(const uint8_t *yuv420Buffer, const int &width, const int &height)
 {
-//    fprintf(stderr, "%s \n", __FUNCTION__);
+//    fprintf(stderr, "%s width=%d height=%d \n", __FUNCTION__, width, height);
     if (mVideoPlayerCallBack != nullptr)
     {
         VideoFramePtr videoFrame = std::make_shared<VideoFrame>();

+ 31 - 22
module/VideoPlayer/src/VideoPlayer/VideoPlayer.h

@@ -28,12 +28,12 @@ extern "C"
     #include <libavfilter/buffersink.h>
     #include <libavfilter/buffersrc.h>
 
-    #include <SDL.h>
-    #include <SDL_audio.h>
-    #include <SDL_types.h>
-    #include <SDL_name.h>
-    #include <SDL_main.h>
-    #include <SDL_config.h>
+//    #include <SDL.h>
+//    #include <SDL_audio.h>
+//    #include <SDL_types.h>
+//    #include <SDL_name.h>
+//    #include <SDL_main.h>
+//    #include <SDL_config.h>
 }
 
 ///启用滤镜,用于旋转带角度的视频
@@ -42,6 +42,8 @@ extern "C"
 #include "types.h"
 #include "Mutex/Cond.h"
 #include "EventHandle/VideoPlayerEventHandle.h"
+#include "PcmPlayer/PcmPlayer.h"
+#include "util/thread.h"
 
 #define SDL_AUDIO_BUFFER_SIZE 1024
 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
@@ -57,7 +59,7 @@ extern "C"
  * 播放器类,纯c++实现,方便移植,与界面的交互通过回调函数的方式实现
  */
 
-class VideoPlayer
+class VideoPlayer : public Thread
 {
 public:
     VideoPlayer();
@@ -74,27 +76,31 @@ public:
 
     bool startPlay(const std::string &filePath);
 
-    bool replay(); //重新播放
+    bool replay(bool isWait = false); //重新播放
 
     bool play(); //播放(用于暂停后,重新开始播放)
     bool pause(); //暂停播放
-    bool stop(bool isWait = false); //停止播放-参数表示是否等待所有的线程执行完毕再返回
+    bool stop(bool isWait = true); //停止播放-参数表示是否等待所有的线程执行完毕再返回
 
     void seek(int64_t pos); //单位是微秒
 
-    void setMute(bool isMute){mIsMute = isMute;}
+    void setMute(bool isMute);
     void setVolume(float value);
     float getVolume(){return mVolume;}
 
     int64_t getTotalTime(); //单位微秒
     double getCurrentTime(); //单位秒
 
+    ///用于判断是否打开超时或读取超时
+    bool mIsOpenStream; //是否正在打开流(用于回调函数中判断是打开流还是读取流)
+    int64_t mCallStartTime = 0;
+
 protected:
-    void readVideoFile(); //读取视频文件
+    void run(); //读取视频文件
     void decodeVideoThread();
 
-    static void sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len);
-    void sdlAudioCallBack(Uint8 *stream, int len);
+//    static void sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len);
+//    void sdlAudioCallBack(Uint8 *stream, int len);
     int decodeAudioFrame(bool isBlock = false);
 
 private:
@@ -121,11 +127,12 @@ private:
     bool mIsReadThreadFinished;
     bool mIsVideoThreadFinished; //视频解码线程
     bool mIsAudioThreadFinished; //音频播放线程
+    bool mIsReadError = false; //是否读取失败
 
     ///音视频同步相关
     uint64_t mVideoStartTime; //开始播放视频的时间
     uint64_t mPauseStartTime; //暂停开始的时间
-    double audio_clock; ///音频时钟
+    double audio_clock; ///音频时钟(秒-小数)
     double video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame
     AVStream *mVideoStream; //视频流
     AVStream *mAudioStream; //音频流
@@ -148,13 +155,14 @@ private:
 
     enum AVSampleFormat in_sample_fmt; //输入的采样格式
     enum AVSampleFormat out_sample_fmt;//输出的采样格式 16bit PCM
-    int in_sample_rate;//输入的采样率
-    int out_sample_rate;//输出的采样率
+    int m_in_sample_rate;//输入的采样率
+    int m_out_sample_rate;//输出的采样率
     int audio_tgt_channels; ///av_get_channel_layout_nb_channels(out_ch_layout);
     int out_ch_layout;
     unsigned int audio_buf_size;
     unsigned int audio_buf_index;
-    DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
+//    DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
+    uint8_t audio_buf[AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
 
     int autorotate = 1;
     int find_stream_info = 1;
@@ -185,11 +193,12 @@ private:
     bool inputAudioQuene(const AVPacket &pkt);
     void clearAudioQuene();
 
-    ///本播放器中SDL仅用于播放音频,不用做别的用途
-    ///SDL播放音频相关
-    SDL_AudioDeviceID mAudioID;
-    int openSDL();
-    void closeSDL();
+//    ///本播放器中SDL仅用于播放音频,不用做别的用途
+//    ///SDL播放音频相关
+//    SDL_AudioDeviceID mAudioID;
+//    int openSDL();
+//    void closeSDL();
+    PcmPlayer *mPcmPlayer = nullptr;
 
     int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph, AVFilterContext *source_ctx, AVFilterContext *sink_ctx);
     int configure_video_filters(AVFilterGraph *graph, const char *vfilters, AVFrame *frame);

+ 272 - 0
module/VideoPlayer/src/VideoPlayer/VideoPlayer_AudioThread.cpp

@@ -0,0 +1,272 @@
+/**
+ * 叶海辉
+ * QQ群121376426
+ * http://blog.yundiantech.com/
+ */
+
+#include "VideoPlayer/VideoPlayer.h"
+
+#include "PcmPlayer/PcmVolumeControl.h"
+
+#include <stdio.h>
+
+//void VideoPlayer::sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len)
+//{
+//    VideoPlayer *player = (VideoPlayer*)userdata;
+//    player->sdlAudioCallBack(stream, len);
+//}
+
+//void VideoPlayer::sdlAudioCallBack(Uint8 *stream, int len)
+//{
+//    int len1, audio_data_size;
+////fprintf(stderr, "len=%d \n", len);
+//    /*   len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */
+//    while (len > 0)
+//    {
+//        /*  audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
+//        /*   这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
+//        /*   们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
+//         /*   多的桢数据 */
+//        if (audio_buf_index >= audio_buf_size)
+//        {
+//            audio_data_size = decodeAudioFrame();
+
+//            /* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
+//            if (audio_data_size <= 0)
+//            {
+//                /* silence */
+//                audio_buf_size = 1024;
+//                /* 清零,静音 */
+//                memset(audio_buf, 0, audio_buf_size);
+//            }
+//            else
+//            {
+//                audio_buf_size = audio_data_size;
+//            }
+//            audio_buf_index = 0;
+//        }
+//        /*  查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
+//        len1 = audio_buf_size - audio_buf_index;
+
+//        if (len1 > len)
+//        {
+//            len1 = len;
+//        }
+
+//        if (audio_buf == NULL) return;
+
+//        if (mIsMute || mIsNeedPause) //静音 或者 是在暂停的时候跳转了
+//        {
+//            memset(audio_buf + audio_buf_index, 0, len1);
+//        }
+//        else
+//        {
+//            PcmVolumeControl::RaiseVolume((char*)audio_buf + audio_buf_index, len1, 1, mVolume);
+//        }
+
+//        memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
+
+////        SDL_memset(stream, 0x0, len);// make sure this is silence.
+////        SDL_MixAudio(stream, (uint8_t *) audio_buf + audio_buf_index, len1, SDL_MIX_MAXVOLUME);
+
+////        SDL_MixAudio(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, len1, 50);
+////        SDL_MixAudioFormat(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, 50);
+
+//        len -= len1;
+//        stream += len1;
+//        audio_buf_index += len1;
+
+//    }
+
+//}
+
+int VideoPlayer::decodeAudioFrame(bool isBlock)
+{
+    int audioBufferSize = 0;
+    float pts_s = 0.0f; //时间戳(秒)
+
+    while(1)
+    {
+        if (mIsQuit)
+        {
+            mIsAudioThreadFinished = true;
+            clearAudioQuene(); //清空队列
+            break;
+        }
+
+        if (mIsPause == true) //判断暂停
+        {
+            break;
+        }
+
+        mConditon_Audio->Lock();
+int audio_list_size = mAudioPacktList.size();
+//fprintf(stderr, "mAudioPacktList.size()=%d \n", audio_list_size);
+        if (mAudioPacktList.size() <= 0)
+        {
+            if (isBlock)
+            {
+                mConditon_Audio->Wait();
+            }
+            else
+            {
+                mConditon_Audio->Unlock();
+                break;
+            }
+        }
+
+        AVPacket packet = mAudioPacktList.front();
+        mAudioPacktList.pop_front();
+//qDebug()<<__FUNCTION__<<mAudioPacktList.size();
+        mConditon_Audio->Unlock();
+
+        AVPacket *pkt = &packet;
+
+        /* if update, update the audio clock w/pts */
+        if (pkt->pts != AV_NOPTS_VALUE)
+        {
+            pts_s = av_q2d(mAudioStream->time_base) * pkt->pts;
+        }
+
+        //收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下
+        if(strcmp((char*)pkt->data,FLUSH_DATA) == 0)
+        {
+            avcodec_flush_buffers(aCodecCtx);
+            av_packet_unref(pkt);
+            continue;
+        }
+
+        if (seek_flag_audio)
+        {
+            //发生了跳转 则跳过关键帧到目的时间的这几帧
+           if (pts_s < seek_time)
+           {
+               continue;
+           }
+           else
+           {
+               seek_flag_audio = 0;
+           }
+        }
+
+        int got_frame = 0;
+
+        //解码AVPacket->AVFrame
+        if (int ret = avcodec_send_packet(aCodecCtx, &packet) && ret != 0)
+        {
+           char buffer[1024] = {0};
+           av_strerror(ret, buffer, 1024);
+           fprintf(stderr, "input AVPacket to decoder failed! ret = %d %s\n", ret, buffer);
+        }
+        else
+        {
+            while(1)
+            {
+                int ret = avcodec_receive_frame(aCodecCtx, aFrame);
+                if (ret != 0)
+                {
+        //            char buffer[1024] = {0};
+        //            av_strerror(ret, buffer, 1024);
+        //            fprintf(stderr, "avcodec_receive_frame = %d %s\n", ret, buffer);
+                    break;
+                }
+
+                /* if update, update the audio clock w/pts */
+                if (packet.pts != AV_NOPTS_VALUE)
+                {
+                    audio_clock = 1000 * av_q2d(mAudioStream->time_base) * packet.pts;
+                }
+
+                int out_sample_rate = m_out_sample_rate;
+
+                /// ffmpeg解码之后得到的音频数据不是SDL想要的,
+                /// 因此这里需要重采样成44100 双声道 AV_SAMPLE_FMT_S16
+
+                /// 需要保证重采样后音频的时间是相同的,不同采样率下同样时间采集的数据采样点个数肯定不一样。
+                /// 因此就需要重新计算采样点个数(使用下面的函数)
+                /// 将in_sample_rate的采样次数换算成out_sample_rate对应的采样次数
+                int nb_samples = av_rescale_rnd(swr_get_delay(swrCtx, out_sample_rate) + aFrame->nb_samples, out_sample_rate, m_in_sample_rate, AV_ROUND_UP);
+    //qDebug()<<swr_get_delay(swrCtx, out_sample_rate) + aFrame->nb_samples<<aFrame->nb_samples<<nb_samples;
+    //            int nb_samples = av_rescale_rnd(aFrame->nb_samples, out_sample_rate, m_in_sample_rate, AV_ROUND_INF);
+                if (aFrame_ReSample != nullptr)
+                {
+                    if (aFrame_ReSample->nb_samples != nb_samples || aFrame_ReSample->sample_rate != out_sample_rate)
+                    {
+                        av_frame_free(&aFrame_ReSample);
+                        aFrame_ReSample = nullptr;
+                    }
+                }
+
+                ///解码一帧后才能获取到采样率等信息,因此将初始化放到这里
+                if (aFrame_ReSample == nullptr)
+                {
+                    aFrame_ReSample = av_frame_alloc();
+
+                    aFrame_ReSample->format = out_sample_fmt;
+                    aFrame_ReSample->channel_layout = out_ch_layout;
+                    aFrame_ReSample->sample_rate = out_sample_rate;
+                    aFrame_ReSample->nb_samples = nb_samples;
+
+                    int ret = av_samples_fill_arrays(aFrame_ReSample->data, aFrame_ReSample->linesize, audio_buf, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 0);
+    //                int ret = av_frame_get_buffer(aFrame_ReSample, 0);
+                    if (ret < 0)
+                    {
+                        fprintf(stderr, "Error allocating an audio buffer\n");
+    //                        exit(1);
+                    }
+                }
+
+                ///2024-09-16
+                //当重采样的采样率不一致的时候,原先的方法会存在杂音问题,解决方法如下:
+                // 1) 如果没有提供足够的空间用于保存输出数据,采样数据会缓存在swr中。可以通过 swr_get_out_samples()来获取下一次调用swr_convert在给定输入样本数量下输出样本数量的上限,来提供足够的空间。
+                // 2)如果是采样频率转换,转换完成后采样数据可能会缓存在swr中,它希望你能给它更多的输入数据。
+                // 3)如果实际上并不需要更多输入数据,通过调用swr_convert(),其中参数in_count设置为0来获取缓存在swr中的数据。
+                // 4)转换结束之后需要冲刷swr_context的缓冲区,通过调用swr_convert(),其中参数in设置为NULL,参数in_count设置为0。
+                for(int i=0;i<2;i++)
+                {
+                    ///执行重采样
+                    int len2;
+
+                    if (i == 0)
+                    {
+                        len2 = swr_convert(swrCtx, aFrame_ReSample->data, aFrame_ReSample->nb_samples, (const uint8_t**)aFrame->data, aFrame->nb_samples);
+                    }
+                    else
+                    {
+                        len2 = swr_convert(swrCtx, aFrame_ReSample->data, aFrame_ReSample->nb_samples, NULL, 0);
+                    }
+
+                    if (len2 <= 0)
+                    {
+                        break;
+                    }
+
+                    ///必须根据swr_convert实际返回的值计算数据大小
+                    int resampled_data_size = len2 * audio_tgt_channels * av_get_bytes_per_sample(out_sample_fmt);
+                    int OneChannelDataSize = resampled_data_size / audio_tgt_channels;
+
+                    audioBufferSize = resampled_data_size;
+
+                    PCMFramePtr pcmFramePtr = std::make_shared<PCMFrame>();
+                    pcmFramePtr->setFrameBuffer(audio_buf, resampled_data_size);
+                    pcmFramePtr->setFrameInfo(m_out_sample_rate, audio_tgt_channels, pts_s*1000);
+
+                    int audio_frame_size = mPcmPlayer->inputPCMFrame(pcmFramePtr);
+//qDebug()<<resampled_data_size<<audio_frame_size;
+                    audio_clock = mPcmPlayer->getCurrentPts() / 1000.0;
+                }
+
+                got_frame = 1;
+            }
+        }
+
+        av_packet_unref(&packet);
+
+        if (got_frame)
+        {
+            break;
+        }
+    }
+
+    return audioBufferSize;
+}

+ 17 - 6
module/VideoPlayer/src/VideoPlayer/Video/VideoPlayer_VideoThread.cpp → module/VideoPlayer/src/VideoPlayer/VideoPlayer_VideoThread.cpp

@@ -231,7 +231,7 @@ void VideoPlayer::decodeVideoThread()
     uint8_t *yuv420pBuffer = nullptr; //解码后的yuv数据
     struct SwsContext *imgConvertCtx = nullptr;  //用于解码后的视频格式转换
 
-    AVCodecContext *pCodecCtx = mVideoStream->codec; //视频解码器
+//    AVCodecContext *pCodecCtx = mVideoStream->codec; //视频解码器
 
     pFrame = av_frame_alloc();
 
@@ -246,6 +246,8 @@ void VideoPlayer::decodeVideoThread()
     int last_vfilter_idx = 0;
 #endif
 
+    bool is_key_frame_getted = false;
+
     while(1)
     {
         if (mIsQuit)
@@ -261,7 +263,7 @@ void VideoPlayer::decodeVideoThread()
         }
 
         mConditon_Video->Lock();
-
+//fprintf(stderr, "mVideoPacktList.size()=%d \n", mVideoPacktList.size());
         if (mVideoPacktList.size() <= 0)
         {
             mConditon_Video->Unlock();
@@ -287,7 +289,7 @@ void VideoPlayer::decodeVideoThread()
         //收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下
         if(strcmp((char*)packet->data, FLUSH_DATA) == 0)
         {
-            avcodec_flush_buffers(mVideoStream->codec);
+            avcodec_flush_buffers(pCodecCtx);
             av_packet_unref(packet);
             continue;
         }
@@ -339,7 +341,7 @@ void VideoPlayer::decodeVideoThread()
                     break;
                 }
 
-                if (mAudioStream != NULL && !mIsAudioThreadFinished)
+                if (mAudioStream != NULL && !mIsAudioThreadFinished && mPcmPlayer->deviceOpened())
                 {
                     if (mIsReadFinished && mAudioPacktList.size() <= 0)
                     {//读取完了 且音频数据也播放完了 就剩下视频数据了  直接显示出来了 不用同步了
@@ -352,7 +354,7 @@ void VideoPlayer::decodeVideoThread()
                 }
                 else
                 {
-                    ///没有音频的情况下,直接同步到外部时钟
+                    ///没有音频或者音频设备打开失败的情况下,直接同步到外部时钟
                     audio_pts = (av_gettime() - mVideoStartTime) / 1000000.0;
                     audio_clock = audio_pts;
                 }
@@ -486,7 +488,16 @@ void VideoPlayer::decodeVideoThread()
                     pFrame->linesize, 0, videoHeight, pFrameYUV->data,
                     pFrameYUV->linesize);
 
-            doDisplayVideo(yuv420pBuffer, videoWidth, videoHeight);
+//qDebug()<<"(packet->flags & AV_PKT_FLAG_KEY)"<<(packet->flags & AV_PKT_FLAG_KEY);
+            if (!is_key_frame_getted && (packet->flags & AV_PKT_FLAG_KEY)) // is keyframe
+            {
+                is_key_frame_getted = true;
+            }
+
+            if (is_key_frame_getted)
+            {
+                doDisplayVideo(yuv420pBuffer, videoWidth, videoHeight);
+            }
 
 #if CONFIG_AVFILTER
 //            if (is->videoq.serial != is->viddec.pkt_serial)

+ 39 - 0
module/VideoPlayer/src/frame/AudioFrame/AACFrame.cpp

@@ -0,0 +1,39 @@
+#include "AACFrame.h"
+
+AACFrame::AACFrame()
+{
+    mFrameBuffer = nullptr;
+    mFrameBufferSize = 0;
+}
+
+AACFrame::~AACFrame()
+{
+    if (mFrameBuffer != nullptr)
+    {
+        free(mFrameBuffer);
+
+        mFrameBuffer = nullptr;
+        mFrameBufferSize = 0;
+    }
+}
+
+void AACFrame::setAdtsHeader(const ADTS_HEADER &adts)
+{
+    mAdtsHeader = adts;
+}
+
+void AACFrame::setFrameBuffer(const uint8_t * const buffer, const unsigned int &size)
+{
+    if (mFrameBufferSize < size)
+    {
+        if (mFrameBuffer != nullptr)
+        {
+            free(mFrameBuffer);
+        }
+
+        mFrameBuffer = static_cast<uint8_t*>(malloc(size));
+    }
+
+    memcpy(mFrameBuffer, buffer, size);
+    mFrameBufferSize = size;
+}

+ 77 - 0
module/VideoPlayer/src/frame/AudioFrame/AACFrame.h

@@ -0,0 +1,77 @@
+#ifndef AACFRAME_H
+#define AACFRAME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <memory>
+
+/*
+sampling_frequency_index sampling frequeny [Hz]
+0x0                           96000
+0x1                           88200
+0x2                           64000
+0x3                           48000
+0x4                           44100
+0x5                           32000
+0x6                           24000
+0x7                           22050
+0x8                           16000
+0x9                           2000
+0xa                           11025
+0xb                           8000
+0xc                           reserved
+0xd                           reserved
+0xe                           reserved
+0xf                           reserved
+*/
+typedef struct
+{
+    unsigned int syncword;  //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
+    unsigned int id;        //1 bslbf   MPEG 标示符, 设置为1
+    unsigned int layer;     //2 uimsbf Indicates which layer is used. Set to ‘00’
+    unsigned int protection_absent;  //1 bslbf  表示是否误码校验
+    unsigned int profile;            //2 uimsbf  表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC
+    unsigned int sf_index;           //4 uimsbf  表示使用的采样率下标
+    unsigned int private_bit;        //1 bslbf
+    unsigned int channel_configuration;  //3 uimsbf  表示声道数
+    unsigned int original;               //1 bslbf
+    unsigned int home;                   //1 bslbf
+    /*下面的为改变的参数即每一帧都不同*/
+    unsigned int copyright_identification_bit;   //1 bslbf
+    unsigned int copyright_identification_start; //1 bslbf
+    unsigned int aac_frame_length;               // 13 bslbf  一个ADTS帧的长度包括ADTS头和raw data block
+    unsigned int adts_buffer_fullness;           //11 bslbf     0x7FF 说明是码率可变的码流
+
+    /*no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
+    所以说number_of_raw_data_blocks_in_frame == 0
+    表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
+    */
+    unsigned int no_raw_data_blocks_in_frame;    //2 uimsfb
+} ADTS_HEADER;
+
+
+#define AACFramePtr std::shared_ptr<AACFrame>
+
+class AACFrame
+{
+public:
+    AACFrame();
+    ~AACFrame();
+
+    void setAdtsHeader(const ADTS_HEADER &adts);
+    void setFrameBuffer(const uint8_t * const buffer, const unsigned int &size);
+
+    uint8_t *getBuffer(){return mFrameBuffer;}
+    unsigned int getSize(){return  mFrameBufferSize;}
+
+private:
+    ADTS_HEADER mAdtsHeader;
+
+    uint8_t *mFrameBuffer; //aac数据(包括adts头)
+    unsigned int mFrameBufferSize; //aac数据长度(包括adts头的大小)
+
+};
+
+#endif // AACFRAME_H

+ 41 - 0
module/VideoPlayer/src/frame/AudioFrame/PCMFrame.cpp

@@ -0,0 +1,41 @@
+#include "PCMFrame.h"
+
+PCMFrame::PCMFrame()
+{
+    mFrameBuffer = nullptr;
+    mFrameBufferSize = 0;
+}
+
+PCMFrame::~PCMFrame()
+{
+    if (mFrameBuffer != nullptr)
+    {
+        free(mFrameBuffer);
+
+        mFrameBuffer = nullptr;
+        mFrameBufferSize = 0;
+    }
+}
+
+void PCMFrame::setFrameInfo(int sample_rate, int channels, uint32_t pts)
+{
+    m_sample_rate = sample_rate;
+    m_channels = channels;
+    m_pts = pts;
+}
+
+void PCMFrame::setFrameBuffer(const uint8_t * const buffer, const unsigned int &size)
+{
+    if (mFrameBufferSize < size)
+    {
+        if (mFrameBuffer != nullptr)
+        {
+            free(mFrameBuffer);
+        }
+
+        mFrameBuffer = static_cast<uint8_t*>(malloc(size));
+    }
+
+    memcpy(mFrameBuffer, buffer, size);
+    mFrameBufferSize = size;
+}

+ 44 - 0
module/VideoPlayer/src/frame/AudioFrame/PCMFrame.h

@@ -0,0 +1,44 @@
+#ifndef PCMFRAME_H
+#define PCMFRAME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <memory>
+
+//extern "C"
+//{
+//    #include <libavutil/samplefmt.h>
+//}
+
+#define PCMFramePtr std::shared_ptr<PCMFrame>
+
+///本程序固定使用AV_SAMPLE_FMT_S16 44100 双声道
+class PCMFrame
+{
+public:
+    PCMFrame();
+    ~PCMFrame();
+
+    void setFrameBuffer(const uint8_t * const buffer, const unsigned int &size);
+    uint8_t *getBuffer(){return mFrameBuffer;}
+    unsigned int getSize(){return  mFrameBufferSize;}
+
+    void setFrameInfo(int sample_rate, int channels, uint32_t pts);
+    uint32_t pts(){return m_pts;}
+    int sampleRate(){return m_sample_rate;}
+    int channels(){return m_channels;}
+
+private:
+    uint8_t *mFrameBuffer = nullptr; //pcm数据
+    unsigned int mFrameBufferSize = 0; //pcm数据长度
+
+    uint32_t m_pts = 0; //时间戳
+//    enum AVSampleFormat mSampleFmt = AV_SAMPLE_FMT_S16;//输出的采样格式
+    int m_sample_rate = 0;//采样率
+    int m_channels = 0; //声道数
+
+};
+
+#endif // PCMFRAME_H

+ 0 - 0
module/VideoPlayer/src/VideoPlayer/Video/VideoFrame.cpp → module/VideoPlayer/src/frame/VideoFrame/VideoFrame.cpp


+ 0 - 0
module/VideoPlayer/src/VideoPlayer/Video/VideoFrame.h → module/VideoPlayer/src/frame/VideoFrame/VideoFrame.h


+ 3 - 2
module/VideoPlayer/src/types.h

@@ -17,9 +17,10 @@
 
 enum VideoPlayerState
 {
-    VideoPlayer_Playing,
+    VideoPlayer_Playing = 0,
     VideoPlayer_Pause,
-    VideoPlayer_Stop
+    VideoPlayer_Stop,
+    VideoPlayer_ReadError,
 };
 
 #if defined(WIN32)

+ 163 - 0
module/VideoPlayer/src/util/thread.cpp

@@ -0,0 +1,163 @@
+#include "thread.h"
+#include <iostream>
+#include <stdio.h>
+
+using namespace std;
+
+Thread::Thread()
+    : m_thread(nullptr),
+      m_is_pause(false),
+      m_is_stop(false),
+      m_state(Stoped)
+{
+    setSingleMode(false);
+}
+
+Thread::Thread(bool single_mode)
+    : m_thread(nullptr),
+      m_is_pause(false),
+      m_is_stop(false),
+      m_state(Stoped)
+{
+    setSingleMode(single_mode);
+}
+
+Thread::~Thread()
+{
+    stop();
+}
+
+Thread::State Thread::state() const
+{
+    return m_state;
+}
+
+void Thread::start()
+{
+    m_mutex_thread.lock();
+
+    //线程已经退出了,需要销毁再重新new一个
+    if (m_state == Stoped && m_thread != nullptr)
+    {
+        m_thread->join(); // wait for thread finished
+        delete m_thread;
+        m_thread = nullptr;
+    }
+
+    if (m_thread == nullptr)
+    {
+        m_is_pause = false;
+        m_is_stop = false;
+        m_state = Running;
+        m_thread = new thread(&Thread::threadFunc, this);
+    }
+    else
+    {
+        resume();
+    }
+    m_mutex_thread.unlock();
+}
+
+void Thread::stop()
+{
+    m_mutex_thread.lock();
+    if (m_thread != nullptr)
+    {
+        m_is_pause = false;
+        m_is_stop = true;
+        m_condition.notify_all();  // Notify one waiting thread, if there is one.
+        m_thread->join(); // wait for thread finished
+        delete m_thread;
+        m_thread = nullptr;
+        m_state = Stoped;
+    }
+    m_mutex_thread.unlock();
+}
+
+void Thread::pause()
+{
+    m_mutex_thread.lock();
+    if (m_thread != nullptr)
+    {
+        m_is_pause = true;
+        m_state = Paused;
+    }
+    m_mutex_thread.unlock();
+}
+
+void Thread::resume()
+{
+    m_mutex_thread.lock();
+    if (m_thread != nullptr)
+    {
+        m_is_pause = false;
+        m_condition.notify_all();
+        m_state = Running;
+    }
+    m_mutex_thread.unlock();
+}
+
+void Thread::setSingleMode(const bool &single_mode)
+{
+    if (single_mode)
+    {
+        m_sleep_time = 0;
+    }
+    else if (m_sleep_time == 0)
+    {
+        m_sleep_time = -1;
+    }
+
+}
+
+void Thread::run()
+{
+    if (m_run_func)
+    {
+        m_run_func();
+    }
+}
+
+void Thread::threadFunc()
+{
+    // cout << "enter thread:" << this_thread::get_id() << endl;
+
+    while (!m_is_stop)
+    {
+        m_state = Running;
+        run();
+        if (m_is_stop)
+        {
+            break;
+        }
+        else if (m_sleep_time > 0)
+        {
+            unique_lock<mutex> locker(m_mutex);
+            m_state = Paused;
+            if (m_condition.wait_for(locker, std::chrono::milliseconds(m_sleep_time)) == std::cv_status::timeout)
+            {
+
+            }
+        }
+        else if (m_is_pause)// || m_sleep_time == 0)
+        {
+            unique_lock<mutex> locker(m_mutex);
+            m_state = Paused;
+            do
+            {
+                m_condition.wait(locker); // Unlock _mutex and wait to be notified
+            }while (m_is_pause);
+
+            locker.unlock();
+        }
+        else if (m_sleep_time == 0)
+        {
+            break;
+        }
+    }
+    m_is_pause = false;
+    m_is_stop = false;
+    m_state = Stoped;
+    
+    // cout << "exit thread:" << this_thread::get_id() << endl;
+}

+ 65 - 0
module/VideoPlayer/src/util/thread.h

@@ -0,0 +1,65 @@
+#ifndef THREAD_H
+#define THREAD_H
+
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+
+class Thread
+{
+public:
+    Thread();
+    Thread(bool single_mode);
+    virtual ~Thread();
+
+    enum State
+    {
+        Stoped,     ///<停止状态,包括从未启动过和启动后被停止
+        Running,    ///<运行状态
+        Paused      ///<暂停状态
+    };
+
+    State state() const;
+
+    void start();
+    void stop();
+    void pause();
+    void resume();
+
+    void setSingleMode(const bool &single_mode); //设置线程只执行一次
+
+    //每执行完一次run函数,休眠多久(毫秒),
+    //<0表示run函数会一直触发,=0表示run函数触发一次就会进入休眠
+    void setSleepTime(const int time){m_sleep_time = time;}
+
+    //设置线程执行函数
+    //可以重载run也可以传入这个run_func,二者选一
+    void setThreadFunc(std::function<void()> run_func){m_run_func = run_func;}
+
+    bool stopFlag(){return m_is_stop;}
+protected:
+    virtual void run();
+
+private:
+    void threadFunc();
+
+protected:
+    std::recursive_mutex m_mutex_thread;
+    std::thread* m_thread = nullptr;
+
+    std::mutex m_mutex;
+    std::condition_variable m_condition;
+    std::atomic_bool m_is_pause;   ///<暂停标识
+    std::atomic_bool m_is_stop;   ///<停止标识
+    State m_state;
+
+    //每执行完一次run函数,休眠多久(毫秒),
+    //<0表示run函数会一直触发,=0表示run函数触发一次就会进入休眠
+    int m_sleep_time = 0; 
+
+    std::function<void()> m_run_func = nullptr; //可以重载上面的run也可以传入这个run_func,二者选一
+
+};
+
+#endif // THREAD_H

+ 1 - 1
src/AppConfig.cpp

@@ -37,7 +37,7 @@
 
 QString AppConfig::APPID = "{a1db97ad-b8ed-11e9-a297-0235d2b38928}";
 int AppConfig::VERSION = 1;
-QString AppConfig::VERSION_NAME = "2.1.8";
+QString AppConfig::VERSION_NAME = "3.0.0";
 
 MainWindow *AppConfig::gMainWindow = NULL;
 QRect AppConfig::gMainWindowRect;

+ 0 - 128
src/Base/FunctionTransfer.cpp

@@ -1,128 +0,0 @@
-#include "FunctionTransfer.h"
-
-#include <QThread>
-#include <QDebug>
-
-Qt::HANDLE FunctionTransfer::gMainThreadId = nullptr;
-FunctionTransfer *FunctionTransfer::main_thread_forward = nullptr;
-
-Q_DECLARE_METATYPE(std::function<void()>)
-
-FunctionTransfer::FunctionTransfer(QObject *parent) :
-    QObject(parent)
-{
-    //因为std::function<void()>是自定义的类型 要跨线程传递需要先注册一下
-    qRegisterMetaType<std::function<void()>>();
-
-    mOnlyRunOneceFunc = nullptr;
-
-    mTimer = new QTimer;
-    connect(mTimer, &QTimer::timeout, this, &FunctionTransfer::slotTimerTimeOut);
-
-    connect(this, SIGNAL(comming(std::function<void()>)), this, SLOT(slotExec(std::function<void()>)), Qt::BlockingQueuedConnection);
-    connect(this, SIGNAL(comming_noBlock(std::function<void()>)), this, SLOT(slotExec(std::function<void()>)), Qt::QueuedConnection);
-}
-
-FunctionTransfer::~FunctionTransfer()
-{
-
-}
-
-void FunctionTransfer::init()
-{
-    init(QThread::currentThreadId());
-}
-
-void FunctionTransfer::init(Qt::HANDLE id)
-{
-    gMainThreadId = id;
-    FunctionTransfer::main_thread_forward = new FunctionTransfer();
-}
-
-bool FunctionTransfer::isMainThread()
-{
-    if (gMainThreadId == nullptr)
-    {
-        qDebug()<<__FILE__<<__LINE__<<__FUNCTION__<<"the main thread id is not set!";
-        return false;
-    }
-
-    if (QThread::currentThreadId() == gMainThreadId)
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
-}
-
-void FunctionTransfer::runInMainThread(std::function<void()> f, bool isBlock)
-{
-    runInMainThread(FunctionTransfer::main_thread_forward, f, isBlock);
-}
-
-void FunctionTransfer::runInMainThread(FunctionTransfer *pointer, std::function<void()> f, bool isBlock)
-{
-//    FunctionTransfer::main_thread_forward->exec(f, isBlock);
-    if(FunctionTransfer::isMainThread())
-    {
-        f();
-    }
-    else
-    {
-        FunctionTransfer *p = pointer;
-
-        if (p == nullptr)
-        {
-            p = FunctionTransfer::main_thread_forward;
-        }
-
-        if (isBlock)
-        {
-            Q_EMIT pointer->comming(f);
-        }
-        else
-        {
-            Q_EMIT pointer->comming_noBlock(f);
-        }
-    }
-}
-
-void FunctionTransfer::runOnece(std::function<void()> f, const int &time)
-{
-    runOnece(FunctionTransfer::main_thread_forward, f, time);
-}
-
-void FunctionTransfer::runOnece(FunctionTransfer *pointer, std::function<void()> f, const int &time)
-{
-    if (time < 0) return;
-
-    FunctionTransfer::runInMainThread(pointer, [=]
-    {
-        ///检测升级
-        FunctionTransfer *p = pointer;
-        if (p == nullptr)
-        {
-            p = FunctionTransfer::main_thread_forward;
-        }
-
-        p->mOnlyRunOneceFunc = f;
-        p->mTimer->stop();
-        p->mTimer->start(time);
-    });
-}
-
-void FunctionTransfer::slotExec(std::function<void()> f)
-{
-    f();
-}
-
-void FunctionTransfer::slotTimerTimeOut()
-{
-    if (QObject::sender() == mTimer)
-    {
-        mTimer->stop();
-        mOnlyRunOneceFunc();
-    }
-}

+ 0 - 61
src/Base/FunctionTransfer.h

@@ -1,61 +0,0 @@
-#ifndef FUNCTIONTRANSFER_H
-#define FUNCTIONTRANSFER_H
-
-#include  <functional>
-
-#include <QThread>
-#include <QObject>
-#include <QTimer>
-#include <QMutex>
-
-//#ifdef QT_NO_KEYWORDS
-//#define signals Q_SIGNALS
-//#define slots Q_SLOTS
-//#define emit Q_EMIT
-//#endif
-
-class FunctionTransfer : public QObject
-{
-    Q_OBJECT
-public:
-
-    ///@brief 构造函数
-    explicit FunctionTransfer(QObject *parent = 0);
-    ~FunctionTransfer();
-
-    static void init(); //此函数必须在主函数运行
-    static void init(Qt::HANDLE id);
-    static bool isMainThread();
-
-public:
-    ///@brief 指定函数f在主线程中中执行
-    static void runInMainThread(std::function<void()> f, bool isBlock = false);
-    static void runInMainThread(FunctionTransfer *pointer, std::function<void()> f, bool isBlock = false);
-
-    ///@brief time时间内在主线程中仅执行一次
-    static void runOnece(std::function<void()> f, const int &time);
-    static void runOnece(FunctionTransfer *pointer, std::function<void()> f, const int &time);
-
-private:
-    static Qt::HANDLE gMainThreadId;
-
-    //在全局数据区实例化一个FunctionTransfer的实例,该实例所在的线程就是主线程。
-    static FunctionTransfer *main_thread_forward;
-
-public:
-    ///用于实现,一定时间内执行一次函数(有且仅有一次)
-    QTimer *mTimer;
-    std::function<void()> mOnlyRunOneceFunc;
-
-Q_SIGNALS:
-    ///@brief 在别的线程有函数对象传来
-    void comming(std::function<void()> f);
-    void comming_noBlock(std::function<void()> f);
-
-private Q_SLOTS:
-    ///@brief 执行函数对象
-    void slotExec(std::function<void()> f);
-    void slotTimerTimeOut();
-};
-
-#endif // FUNCTIONTRANSFER_H

+ 5 - 10
src/MainWindow.cpp

@@ -17,7 +17,6 @@
 #include <QMessageBox>
 
 #include "AppConfig.h"
-#include "Base/FunctionTransfer.h"
 
 #include "Widget/SetVideoUrlDialog.h"
 #include "Widget/mymessagebox_withTitle.h"
@@ -30,8 +29,6 @@ MainWindow::MainWindow(QWidget *parent) :
 {
     ui->setupUi(this->getContainWidget());
 
-    FunctionTransfer::init(QThread::currentThreadId());
-
     ///初始化播放器
     VideoPlayer::initPlayer();
 
@@ -114,7 +111,7 @@ MainWindow::MainWindow(QWidget *parent) :
 
             static QPoint lastPoint = QPoint(0, 0);
 
-            FunctionTransfer::runInMainThread([=]()
+            QMetaObject::invokeMethod(this, [=]()
             {
                 QPoint point = QCursor::pos();
 
@@ -141,8 +138,6 @@ MainWindow::MainWindow(QWidget *parent) :
 
 MainWindow::~MainWindow()
 {
-qDebug()<<__FUNCTION__;
-
     AppConfig::saveConfigInfoToFile();
     AppConfig::removeDirectory(AppConfig::AppDataPath_Tmp);
 
@@ -587,7 +582,7 @@ void MainWindow::slotActionClick()
 ///打开文件失败
 void MainWindow::onOpenVideoFileFailed(const int &code)
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         QMessageBox::critical(NULL, "tips", QString("open file failed %1").arg(code));
     });
@@ -596,7 +591,7 @@ void MainWindow::onOpenVideoFileFailed(const int &code)
 ///打开SDL失败的时候回调此函数
 void MainWindow::onOpenSdlFailed(const int &code)
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         QMessageBox::critical(NULL, "tips", QString("open Sdl failed %1").arg(code));
     });
@@ -605,7 +600,7 @@ void MainWindow::onOpenSdlFailed(const int &code)
 ///获取到视频时长的时候调用此函数
 void MainWindow::onTotalTimeChanged(const int64_t &uSec)
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         qint64 Sec = uSec/1000000;
 
@@ -631,7 +626,7 @@ void MainWindow::onTotalTimeChanged(const int64_t &uSec)
 ///播放器状态改变的时候回调此函数
 void MainWindow::onPlayerStateChanged(const VideoPlayerState &state, const bool &hasVideo, const bool &hasAudio)
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
 qDebug()<<__FUNCTION__<<state<<mIsNeedPlayNext;
         if (state == VideoPlayer_Stop)

+ 6 - 6
src/Widget/ShowVideoWidget.cpp

@@ -109,7 +109,7 @@ void ShowVideoWidget::setIsPlaying(bool value)
 {
     mIsPlaying = value;
 
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         if (!mIsPlaying)
         {
@@ -123,7 +123,7 @@ void ShowVideoWidget::setIsPlaying(bool value)
 void ShowVideoWidget::setPlayFailed(bool value)
 {
     mPlayFailed = value;
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         update();
     });
@@ -132,7 +132,7 @@ void ShowVideoWidget::setPlayFailed(bool value)
 void ShowVideoWidget::setCameraName(QString name)
 {
     mCameraName = name;
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         update();
     });
@@ -148,7 +148,7 @@ qDebug()<<__FUNCTION__<<w<<h<<this->isHidden();
 
     if (mIsOpenGLInited)
     {
-        FunctionTransfer::runInMainThread([=]()
+        QMetaObject::invokeMethod(this, [=]()
         {
             resetGLVertex(this->width(), this->height());
         });
@@ -163,7 +163,7 @@ void ShowVideoWidget::setCloseAble(bool isCloseAble)
 
 void ShowVideoWidget::clear()
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         mVideoFrame.reset();
 
@@ -229,7 +229,7 @@ void ShowVideoWidget::mouseMoveEvent(QMouseEvent *event)
 
 void ShowVideoWidget::inputOneFrame(VideoFramePtr videoFrame)
 {
-    FunctionTransfer::runInMainThread([=]()
+    QMetaObject::invokeMethod(this, [=]()
     {
         int width = videoFrame.get()->width();
         int height = videoFrame.get()->height();

+ 1 - 2
src/Widget/ShowVideoWidget.h

@@ -11,8 +11,7 @@
 #include <QOpenGLTexture>
 #include <QFile>
 
-#include "Base/FunctionTransfer.h"
-#include "VideoPlayer/Video/VideoFrame.h"
+#include "frame/VideoFrame/VideoFrame.h"
 
 namespace Ui {
 class ShowVideoWidget;