浏览代码

添加 音频视频可独立

zhuizhu 6 月之前
父节点
当前提交
3c9ff2415e
共有 3 个文件被更改,包括 211 次插入97 次删除
  1. 83 60
      libs/AVPlayer/av_decoder.cpp
  2. 116 35
      libs/AVPlayer/av_player.cpp
  3. 12 2
      libs/AVPlayer/av_player.h

+ 83 - 60
libs/AVPlayer/av_decoder.cpp

@@ -61,8 +61,13 @@ AVTool::MediaInfo* Decoder::detectMediaInfo(const QString& url)
 
     int videoIndex = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
     if (videoIndex < 0) {
-        qDebug() << "no video stream!";
-        return Q_NULLPTR;
+        // 仅音频:返回时长和占位图
+        AVTool::MediaInfo* info = new AVTool::MediaInfo;
+        info->duration = duration;
+        info->tipImg = QImage(300, 300, QImage::Format_RGB888);
+        info->tipImg.fill(Qt::black);
+        avformat_close_input(&fmtCtx);
+        return info;
     }
     //视频解码初始化
     AVCodecParameters* videoCodecPar = fmtCtx->streams[videoIndex]->codecpar;
@@ -252,91 +257,102 @@ int Decoder::decode(const QString& url)
     AVRational q = {1, AV_TIME_BASE};
     m_duration = (uint32_t) (m_fmtCtx->duration * av_q2d(q));
     av_dict_free(&formatOpts);
-    //qDebug()<<QString("duration: %1:%2").arg(m_duration/60).arg(m_duration%60)<<endl;
 
+    // 查找音频/视频流,允许缺失
     m_audioIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
     if (m_audioIndex < 0) {
         qDebug() << "no audio stream!";
-        return 0;
     }
 
     m_videoIndex = av_find_best_stream(m_fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
     if (m_videoIndex < 0) {
         qDebug() << "no video stream!";
-        return 0;
     }
 
-    //音频解码初始化
-    AVCodecParameters* audioCodecPar = m_fmtCtx->streams[m_audioIndex]->codecpar;
-    if (!audioCodecPar) {
-        qDebug() << "audio par is nullptr!";
+    if (m_audioIndex < 0 && m_videoIndex < 0) {
+        qDebug() << "no audio and video stream!";
         return 0;
     }
 
-    m_audioPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
+    // 音频解码初始化(可选)
+    if (m_audioIndex >= 0) {
+        AVCodecParameters* audioCodecPar = m_fmtCtx->streams[m_audioIndex]->codecpar;
+        if (!audioCodecPar) {
+            qDebug() << "audio par is nullptr!";
+            return 0;
+        }
 
-    ret = avcodec_parameters_to_context(m_audioPktDecoder.codecCtx, audioCodecPar);
-    if (ret < 0) {
-        av_strerror(ret, m_errBuf, sizeof(m_errBuf));
-        qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
-        return 0;
-    }
+        m_audioPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
 
-    const AVCodec* audioCodec = avcodec_find_decoder(m_audioPktDecoder.codecCtx->codec_id);
-    if (!audioCodec) {
-        qDebug() << "avcodec_find_decoder failed!";
-        return 0;
-    }
-    m_audioPktDecoder.codecCtx->codec_id = audioCodec->id;
+        ret = avcodec_parameters_to_context(m_audioPktDecoder.codecCtx, audioCodecPar);
+        if (ret < 0) {
+            av_strerror(ret, m_errBuf, sizeof(m_errBuf));
+            qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
+            return 0;
+        }
 
-    ret = avcodec_open2(m_audioPktDecoder.codecCtx, audioCodec, nullptr);
-    if (ret < 0) {
-        av_strerror(ret, m_errBuf, sizeof(m_errBuf));
-        qDebug() << "error info_avcodec_open2:" << m_errBuf;
-        return 0;
-    }
+        const AVCodec* audioCodec = avcodec_find_decoder(m_audioPktDecoder.codecCtx->codec_id);
+        if (!audioCodec) {
+            qDebug() << "avcodec_find_decoder failed!";
+            return 0;
+        }
+        m_audioPktDecoder.codecCtx->codec_id = audioCodec->id;
 
-    //视频解码初始化
-    AVCodecParameters* videoCodecPar = m_fmtCtx->streams[m_videoIndex]->codecpar;
-    if (!videoCodecPar) {
-        qDebug() << "videocodecpar is nullptr!";
-        return 0;
+        ret = avcodec_open2(m_audioPktDecoder.codecCtx, audioCodec, nullptr);
+        if (ret < 0) {
+            av_strerror(ret, m_errBuf, sizeof(m_errBuf));
+            qDebug() << "error info_avcodec_open2:" << m_errBuf;
+            return 0;
+        }
     }
 
-    m_videoPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
+    // 视频解码初始化(可选)
+    if (m_videoIndex >= 0) {
+        AVCodecParameters* videoCodecPar = m_fmtCtx->streams[m_videoIndex]->codecpar;
+        if (!videoCodecPar) {
+            qDebug() << "videocodecpar is nullptr!";
+            return 0;
+        }
 
-    ret = avcodec_parameters_to_context(m_videoPktDecoder.codecCtx, videoCodecPar);
-    if (ret < 0) {
-        av_strerror(ret, m_errBuf, sizeof(m_errBuf));
-        qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
-        return 0;
-    }
+        m_videoPktDecoder.codecCtx = avcodec_alloc_context3(nullptr);
 
-    const AVCodec* videoCodec = avcodec_find_decoder(m_videoPktDecoder.codecCtx->codec_id);
-    if (!videoCodec) {
-        qDebug() << "avcodec_find_decoder failed!";
-        return 0;
-    }
-    m_videoPktDecoder.codecCtx->codec_id = videoCodec->id;
+        ret = avcodec_parameters_to_context(m_videoPktDecoder.codecCtx, videoCodecPar);
+        if (ret < 0) {
+            av_strerror(ret, m_errBuf, sizeof(m_errBuf));
+            qDebug() << "error info_avcodec_parameters_to_context:" << m_errBuf;
+            return 0;
+        }
 
-    ret = avcodec_open2(m_videoPktDecoder.codecCtx, videoCodec, nullptr);
-    if (ret < 0) {
-        av_strerror(ret, m_errBuf, sizeof(m_errBuf));
-        qDebug() << "error info_avcodec_open2:" << m_errBuf;
-        return 0;
-    }
+        const AVCodec* videoCodec = avcodec_find_decoder(m_videoPktDecoder.codecCtx->codec_id);
+        if (!videoCodec) {
+            qDebug() << "avcodec_find_decoder failed!";
+            return 0;
+        }
+        m_videoPktDecoder.codecCtx->codec_id = videoCodec->id;
 
-    //记录视频帧率
-    m_vidFrameRate = av_guess_frame_rate(m_fmtCtx, m_fmtCtx->streams[m_videoIndex], nullptr);
+        ret = avcodec_open2(m_videoPktDecoder.codecCtx, videoCodec, nullptr);
+        if (ret < 0) {
+            av_strerror(ret, m_errBuf, sizeof(m_errBuf));
+            qDebug() << "error info_avcodec_open2:" << m_errBuf;
+            return 0;
+        }
+
+        //记录视频帧率
+        m_vidFrameRate = av_guess_frame_rate(m_fmtCtx, m_fmtCtx->streams[m_videoIndex], nullptr);
+    }
 
     setInitVal();
 
     ThreadPool::addTask(std::bind(&Decoder::demux, this, std::placeholders::_1),
                         std::make_shared<int>(1));
-    ThreadPool::addTask(std::bind(&Decoder::audioDecode, this, std::placeholders::_1),
-                        std::make_shared<int>(2));
-    ThreadPool::addTask(std::bind(&Decoder::videoDecode, this, std::placeholders::_1),
-                        std::make_shared<int>(3));
+    if (m_audioIndex >= 0) {
+        ThreadPool::addTask(std::bind(&Decoder::audioDecode, this, std::placeholders::_1),
+                            std::make_shared<int>(2));
+    }
+    if (m_videoIndex >= 0) {
+        ThreadPool::addTask(std::bind(&Decoder::videoDecode, this, std::placeholders::_1),
+                            std::make_shared<int>(3));
+    }
 
     return 1;
 }
@@ -472,8 +488,15 @@ void Decoder::demux(std::shared_ptr<void> par)
     av_packet_free(&pkt);
     //是读到文件末尾退出的才清空,强制退出不重复此操作
     if (!m_exit) {
-        while (m_audioFrameQueue.size)
-            QThread::msleep(50);
+        // 根据实际存在的流等待对应帧队列清空
+        if (m_audioIndex >= 0) {
+            while (m_audioFrameQueue.size)
+                QThread::msleep(50);
+        }
+        if (m_videoIndex >= 0) {
+            while (m_videoFrameQueue.size)
+                QThread::msleep(50);
+        }
         exit();
     }
     //qDebug() << "demuxthread exit";

+ 116 - 35
libs/AVPlayer/av_player.cpp

@@ -20,6 +20,7 @@
 
 AVPlayer::AVPlayer()
     : m_decoder(new Decoder)
+    , m_fmtCtx(nullptr)
     , m_audioFrame(av_frame_alloc())
     , m_imageWidth(300)
     , m_imageHeight(300)
@@ -32,7 +33,9 @@ AVPlayer::AVPlayer()
     , m_exit(0)
     , m_pause(0)
     , m_playSpeed(1.0)
-{}
+{
+    m_sonicStream = nullptr;
+}
 
 AVPlayer::~AVPlayer()
 {
@@ -64,12 +67,36 @@ int AVPlayer::play(const QString& url)
     m_pause = 0;
     m_clockInitFlag = -1;
 
-    if (!initSDL()) {
-        qDebug() << "init sdl failed!";
-        return 0;
+    // 判断是否存在音/视频流
+    m_audioIndex = m_decoder->audioIndex();
+    m_videoIndex = m_decoder->videoIndex();
+    m_hasAudio = (m_audioIndex >= 0);
+    m_hasVideo = (m_videoIndex >= 0);
+
+    bool ok = false;
+
+    if (m_hasAudio) {
+        if (initSDL()) {
+            ok = true;
+        } else {
+            qDebug() << "init sdl failed!";
+        }
     }
-    initVideo();
-    return 1;
+
+    if (m_hasVideo) {
+        if (initVideo()) {
+            ok = true;
+        } else {
+            qDebug() << "init video failed!";
+        }
+    }
+
+    // 仅音频时,主动初始化时钟
+    if (!m_hasVideo && m_hasAudio) {
+        initAVClock();
+    }
+
+    return ok ? 1 : 0;
 }
 
 void fillAStreamCallback(void* userdata, uint8_t* stream, int len)
@@ -196,6 +223,10 @@ void fillAStreamCallback(void* userdata, uint8_t* stream, int len)
 
 int AVPlayer::initSDL()
 {
+    // 无音频流直接返回失败,调用方根据 m_hasAudio 控制
+    if (m_decoder->audioIndex() < 0)
+        return 0;
+
     if (SDL_Init(SDL_INIT_AUDIO) != 0) {
         qDebug() << "SDL_Init failed";
         return 0;
@@ -248,6 +279,7 @@ int AVPlayer::initVideo()
 
     m_videoCodecPar = m_decoder->videoCodecPar();
     m_videoIndex = m_decoder->videoIndex();
+    m_fmtCtx = m_decoder->formatContext();
 
     m_imageWidth = m_videoCodecPar->width;
     m_imageHeight = m_videoCodecPar->height;
@@ -269,19 +301,35 @@ int AVPlayer::initVideo()
 
 void AVPlayer::pause(bool isPause)
 {
-    if (SDL_GetAudioStatus() == SDL_AUDIO_STOPPED)
-        return;
-    if (isPause) {
-        if (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) {
-            SDL_PauseAudio(1);
-            m_pauseTime = av_gettime_relative() / 1000000.0;
-            m_pause = 1;
+    if (m_hasAudio) {
+        if (SDL_GetAudioStatus() == SDL_AUDIO_STOPPED)
+            return;
+        if (isPause) {
+            if (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) {
+                SDL_PauseAudio(1);
+                m_pauseTime = av_gettime_relative() / 1000000.0;
+                m_pause = 1;
+            }
+        } else {
+            if (SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
+                SDL_PauseAudio(0);
+                m_frameTimer += av_gettime_relative() / 1000000.0 - m_pauseTime;
+                m_pause = 0;
+            }
         }
-    } else {
-        if (SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
-            SDL_PauseAudio(0);
-            m_frameTimer += av_gettime_relative() / 1000000.0 - m_pauseTime;
-            m_pause = 0;
+    } else if (m_hasVideo) {
+        // 仅视频:通过标志控制回放线程
+        double now = av_gettime_relative() / 1000000.0;
+        if (isPause) {
+            if (!m_pause) {
+                m_pauseTime = now;
+                m_pause = 1;
+            }
+        } else {
+            if (m_pause) {
+                m_frameTimer += now - m_pauseTime;
+                m_pause = 0;
+            }
         }
     }
 }
@@ -290,17 +338,20 @@ void AVPlayer::clearPlayer()
 {
     if (playState() != AV_STOPPED) {
         m_exit = 1;
-        if (playState() == AV_PLAYING)
+        if (m_hasAudio && playState() == AV_PLAYING)
             SDL_PauseAudio(1);
         m_decoder->exit();
-        SDL_CloseAudio();
+        if (m_hasAudio)
+            SDL_CloseAudio();
         if (m_swrCtx)
             swr_free(&m_swrCtx);
         if (m_swsCtx)
             sws_freeContext(m_swsCtx);
         m_swrCtx = nullptr;
         m_swsCtx = nullptr;
-        sonicDestroyStream(m_sonicStream);
+        if (m_sonicStream)
+            sonicDestroyStream(m_sonicStream);
+        m_sonicStream = nullptr;
     }
 }
 
@@ -311,21 +362,34 @@ AVTool::MediaInfo* AVPlayer::detectMediaInfo(const QString& url)
 
 AVPlayer::PlayState AVPlayer::playState()
 {
-    AVPlayer::PlayState state;
-    switch (SDL_GetAudioStatus()) {
-    case SDL_AUDIO_PLAYING:
-        state = AVPlayer::AV_PLAYING;
-        break;
-    case SDL_AUDIO_PAUSED:
-        state = AVPlayer::AV_PAUSED;
-        break;
-    case SDL_AUDIO_STOPPED:
-        state = AVPlayer::AV_STOPPED;
-        break;
-    default:
-        break;
+    if (m_hasAudio) {
+        AVPlayer::PlayState state;
+        switch (SDL_GetAudioStatus()) {
+        case SDL_AUDIO_PLAYING:
+            state = AVPlayer::AV_PLAYING;
+            break;
+        case SDL_AUDIO_PAUSED:
+            state = AVPlayer::AV_PAUSED;
+            break;
+        case SDL_AUDIO_STOPPED:
+            state = AVPlayer::AV_STOPPED;
+            break;
+        default:
+            state = AVPlayer::AV_STOPPED;
+            break;
+        }
+        return state;
+    }
+
+    if (m_hasVideo) {
+        if (m_exit)
+            return AV_STOPPED;
+        if (m_pause)
+            return AV_PAUSED;
+        return AV_PLAYING;
     }
-    return state;
+
+    return AV_STOPPED;
 }
 
 void AVPlayer::initAVClock()
@@ -437,6 +501,16 @@ void AVPlayer::videoCallback(std::shared_ptr<void> par)
             }
 
             displayImage(&frame->frame);
+
+            // 无音频时,基于视频时钟更新对外进度
+            if (!m_hasAudio) {
+                uint32_t _pts = (uint32_t) m_videoClock.getClock();
+                if (m_lastAudPts != _pts) {
+                    emit AVPtsChanged(_pts);
+                    m_lastAudPts = _pts;
+                }
+            }
+
             //读索引后移
             m_decoder->setNextVFrame();
         } else {
@@ -444,10 +518,17 @@ void AVPlayer::videoCallback(std::shared_ptr<void> par)
         }
     } while (true);
     //qDebug()<<"videoCallBack exit"<<endl;
+    if (m_decoder->isExit()) {
+        emit AVTerminate();
+    }
 }
 
 double AVPlayer::computeTargetDelay(double delay)
 {
+    // 无音频流时,不进行音视频同步,直接按视频节奏播放
+    if (!m_hasAudio)
+        return delay;
+
     //视频当前显示帧与当前播放音频帧时间戳差值
     double diff = m_videoClock.getClock() - m_audioClock.getClock();
 

+ 12 - 2
libs/AVPlayer/av_player.h

@@ -53,7 +53,12 @@ public:
         m_volume = (volumePer * SDL_MIX_MAXVOLUME / 100) % (SDL_MIX_MAXVOLUME + 1);
     }
 
-    inline void seekBy(int32_t time_s) { seekTo((int32_t) m_audioClock.getClock() + time_s); }
+    // 修改:根据可用主时钟进行seek
+    inline void seekBy(int32_t time_s)
+    {
+        double base = m_hasAudio ? m_audioClock.getClock() : m_videoClock.getClock();
+        seekTo((int32_t) base + time_s);
+    }
 
     inline void seekTo(int32_t time_s)
     {
@@ -61,7 +66,8 @@ public:
             return;
         if (time_s < 0)
             time_s = 0;
-        m_decoder->seekTo(time_s, time_s - (int32_t) m_audioClock.getClock());
+        double base = m_hasAudio ? m_audioClock.getClock() : m_videoClock.getClock();
+        m_decoder->seekTo(time_s, time_s - (int32_t) base);
     }
 
     inline uint32_t avDuration() { return m_duration; }
@@ -157,6 +163,10 @@ private:
 
     uint8_t* m_pixels[4];
     int m_pitch[4];
+
+    // 新增:是否存在音频/视频流
+    bool m_hasAudio = false;
+    bool m_hasVideo = false;
 };
 
 #endif