| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- #include "ffmpegvideopuller.h"
- #include "AvPlayer/AudioPlayer.h"
- #include "AvPlayer/VideoPlayer.h"
- #include "qdatetime.h"
- #include <QDebug>
- #include <chrono>
- #include <thread>
- FFmpegVideoPuller::FFmpegVideoPuller(QObject* parent)
- : QObject(parent)
- {
- // av_register_all();
- avformat_network_init();
- }
- FFmpegVideoPuller::~FFmpegVideoPuller()
- {
- stop();
- }
- bool FFmpegVideoPuller::open(const QString& url, int videoBufferSec, int audioBufferSec)
- {
- m_url = url;
- // 打开流
- if (avformat_open_input(&m_fmtCtx, url.toStdString().c_str(), nullptr, nullptr) < 0)
- return false;
- if (avformat_find_stream_info(m_fmtCtx, nullptr) < 0)
- return false;
- // 查找视频流
- for (unsigned i = 0; i < m_fmtCtx->nb_streams; ++i) {
- if (m_fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
- && m_videoStreamIdx == -1) {
- m_videoStreamIdx = i;
- }
- if (m_fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO
- && m_audioStreamIdx == -1) {
- m_audioStreamIdx = i;
- }
- }
- if (m_videoStreamIdx == -1)
- return false;
- // 打开视频解码器
- const AVCodec* vcodec = avcodec_find_decoder(
- m_fmtCtx->streams[m_videoStreamIdx]->codecpar->codec_id);
- m_videoCodecCtx = avcodec_alloc_context3(vcodec);
- avcodec_parameters_to_context(m_videoCodecCtx, m_fmtCtx->streams[m_videoStreamIdx]->codecpar);
- avcodec_open2(m_videoCodecCtx, vcodec, nullptr);
- // 打开音频解码器
- if (m_audioStreamIdx != -1) {
- const AVCodec* acodec = avcodec_find_decoder(
- m_fmtCtx->streams[m_audioStreamIdx]->codecpar->codec_id);
- m_audioCodecCtx = avcodec_alloc_context3(acodec);
- avcodec_parameters_to_context(m_audioCodecCtx,
- m_fmtCtx->streams[m_audioStreamIdx]->codecpar);
- avcodec_open2(m_audioCodecCtx, acodec, nullptr);
- }
- // 计算缓冲区大小
- int videoFps = 25;
- AVRational fr = m_fmtCtx->streams[m_videoStreamIdx]->avg_frame_rate;
- if (fr.num && fr.den)
- videoFps = int(av_q2d(fr));
- int videoBufferSize = videoBufferSec * videoFps;
- int audioBufferSize = audioBufferSec * 50; // 假设每秒50帧
- // 创建缓冲区
- m_videoBuffer = new RingBuffer<AVFrame*>(videoBufferSize);
- 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;
- m_currentPts = 0.0;
- // 获取总时长
- if (m_fmtCtx->duration > 0) {
- m_totalDuration = m_fmtCtx->duration / (double)AV_TIME_BASE;
- } else {
- m_totalDuration = -1.0;
- }
- return true;
- }
- void FFmpegVideoPuller::setSpeed(float speed)
- {
- std::lock_guard<std::mutex> lock(m_speedMutex);
- m_speed = speed;
- }
- float FFmpegVideoPuller::getSpeed() const
- {
- std::lock_guard<std::mutex> lock(m_speedMutex);
- return m_speed;
- }
- void FFmpegVideoPuller::start()
- {
- if (m_running)
- return;
- m_running = true;
- // 初始化PTS
- m_videoPts = 0.0;
- m_audioPts = 0.0;
- // 启动包读取线程
- 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_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();
- m_videoPlayThread->wait();
- delete m_videoPlayThread;
- m_videoPlayThread = nullptr;
- }
- if (m_audioPlayThread) {
- m_audioPlayThread->quit();
- m_audioPlayThread->wait();
- 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();
- delete m_videoBuffer;
- m_videoBuffer = nullptr;
- }
- if (m_audioBuffer) {
- m_audioBuffer->clear();
- delete m_audioBuffer;
- m_audioBuffer = nullptr;
- }
- // 释放FFmpeg资源
- if (m_videoCodecCtx)
- avcodec_free_context(&m_videoCodecCtx);
- if (m_audioCodecCtx)
- avcodec_free_context(&m_audioCodecCtx);
- if (m_fmtCtx)
- avformat_close_input(&m_fmtCtx);
- }
- void FFmpegVideoPuller::packetReadLoop()
- {
- 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) {
- 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) {
- 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);
- }
- }
- }
- void FFmpegVideoPuller::videoDecodeLoop()
- {
- AVFrame* frame = av_frame_alloc();
- while (m_running) {
- 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::audioDecodeLoop()
- {
- if (m_audioStreamIdx == -1) return;
- AVFrame* frame = av_frame_alloc();
- static qint64 lastDecodeTs = 0;
- while (m_running) {
- 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);
- }
- // 进度相关
- double FFmpegVideoPuller::getFirstPts() const
- {
- return m_videoBuffer ? m_videoBuffer->firstPts() : 0.0;
- }
- double FFmpegVideoPuller::getLastPts() const
- {
- return m_videoBuffer ? m_videoBuffer->lastPts() : 0.0;
- }
- double FFmpegVideoPuller::getCurrentPts() const
- {
- std::lock_guard<std::mutex> lock(m_ptsMutex);
- return m_currentPts;
- }
- void FFmpegVideoPuller::seekToPts(double pts)
- {
- // 停止线程
- m_running = false;
- 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; }
-
- // 清空缓冲区
- if (m_videoBuffer) m_videoBuffer->clear();
- if (m_audioBuffer) m_audioBuffer->clear();
-
- // FFmpeg seek
- int64_t seek_target = pts / av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base);
- av_seek_frame(m_fmtCtx, m_videoStreamIdx, seek_target, AVSEEK_FLAG_BACKWARD);
- avcodec_flush_buffers(m_videoCodecCtx);
- if (m_audioCodecCtx) avcodec_flush_buffers(m_audioCodecCtx);
-
- // 重置索引
- m_videoPlayIndex = 0;
- m_audioPlayIndex = 0;
-
- // 重置PTS
- m_videoPts = pts;
- m_audioPts = pts;
- m_currentPts = pts;
-
- // 重新启动线程
- m_running = true;
- m_packetReadThread = QThread::create([this]() { this->packetReadLoop(); });
- m_packetReadThread->start();
- }
- // 取帧接口
- AVFrame* FFmpegVideoPuller::getCurrentVideoFrame()
- {
- if (!m_videoBuffer)
- return nullptr;
- return m_videoBuffer->get(m_videoPlayIndex);
- }
- AVFrame* FFmpegVideoPuller::getCurrentAudioFrame()
- {
- if (!m_audioBuffer)
- return nullptr;
- return m_audioBuffer->get(m_audioPlayIndex);
- }
- void FFmpegVideoPuller::nextVideoFrame()
- {
- ++m_videoPlayIndex;
- if (m_videoBuffer && m_videoPlayIndex >= m_videoBuffer->size())
- m_videoPlayIndex = m_videoBuffer->size() - 1;
- }
- void FFmpegVideoPuller::nextAudioFrame()
- {
- ++m_audioPlayIndex;
- if (m_audioBuffer && m_audioPlayIndex >= m_audioBuffer->size())
- m_audioPlayIndex = m_audioBuffer->size() - 1;
- }
- size_t FFmpegVideoPuller::videoBufferSize() const
- {
- return m_videoBuffer ? m_videoBuffer->size() : 0;
- }
- 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);
- }
|