#include "ffmpegvideopuller.h" #include "AvPlayer/AudioPlayer.h" #include "AvPlayer/VideoPlayer.h" #include "qdatetime.h" #include #include #include 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(videoBufferSize); m_audioBuffer = new RingBuffer(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 lock(m_speedMutex); m_speed = speed; } float FFmpegVideoPuller::getSpeed() const { std::lock_guard 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 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(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); }