| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- #ifndef FFMPEGVIDEOPULLER_H
- #define FFMPEGVIDEOPULLER_H
- #pragma once
- extern "C" {
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- }
- #include <QObject>
- #include <QString>
- #include <QThread>
- #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
- public:
- explicit FFmpegVideoPuller(QObject* parent = nullptr);
- ~FFmpegVideoPuller();
- bool open(const QString& url,
- int videoBufferSec = 300,
- int audioBufferSec = 300);
- void setSpeed(float speed); // 设置倍速
- float getSpeed() const;
- void start();
- void stop();
- // 进度相关
- double getFirstPts() const;
- double getLastPts() const;
- double getCurrentPts() const;
- void seekToPts(double pts); // 跳转到指定pts
- // 取帧接口
- AVFrame* getCurrentVideoFrame();
- AVFrame* getCurrentAudioFrame();
- void nextVideoFrame();
- void nextAudioFrame();
- size_t videoBufferSize() const;
- size_t audioBufferSize() const;
- double getTotalDuration() const { return m_totalDuration; }
- // 主时钟驱动同步播放
- void startSyncPlay();
- void stopSyncPlay();
- AVFrame* popNearestAudioFrame(double pts);
- AVFrame* popNearestVideoFrame(double pts);
- private:
- 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};
- std::mutex m_syncMutex;
- QString m_url;
- std::atomic<bool> m_running{false};
- mutable std::mutex m_speedMutex;
- float m_speed = 1.0f;
- // FFmpeg相关
- AVFormatContext* m_fmtCtx = nullptr;
- AVCodecContext* m_videoCodecCtx = nullptr;
- AVCodecContext* m_audioCodecCtx = nullptr;
- int m_videoStreamIdx = -1;
- int m_audioStreamIdx = -1;
- // 缓冲区
- RingBuffer<AVFrame*>* m_videoBuffer = nullptr;
- RingBuffer<AVFrame*>* m_audioBuffer = nullptr;
- // 线程
- QThread* m_videoPlayThread = nullptr;
- QThread* m_audioPlayThread = nullptr;
- // 播放指针
- 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;
- double m_totalDuration = -1.0;
- };
- #endif // FFMPEGVIDEOPULLER_H
|