| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- #include "ffmpegvideopuller.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); });
- 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;
- // 解码线程
- m_decodeThread = QThread::create([this]() { this->decodeLoop(); });
- m_decodeThread->start();
- // 播放线程
- m_playThread = QThread::create([this]() { this->playLoop(); });
- m_playThread->start();
- }
- void FFmpegVideoPuller::stop()
- {
- m_running = false;
- if (m_decodeThread) {
- m_decodeThread->quit();
- m_decodeThread->wait();
- delete m_decodeThread;
- m_decodeThread = nullptr;
- }
- if (m_playThread) {
- m_playThread->quit();
- m_playThread->wait();
- delete m_playThread;
- m_playThread = 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::decodeLoop()
- {
- AVPacket* pkt = av_packet_alloc();
- AVFrame* frame = av_frame_alloc();
- while (m_running) {
- if (av_read_frame(m_fmtCtx, pkt) < 0) {
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- continue;
- }
- if (pkt->stream_index == m_videoStreamIdx) {
- 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);
- }
- } else if (pkt->stream_index == m_audioStreamIdx) {
- 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);
- //qDebug() << "解码到音频帧, pts=" << pts << " nb_samples=" << frame->nb_samples;
- m_audioBuffer->push(av_frame_clone(frame), pts);
- }
- }
- av_packet_unref(pkt);
- }
- av_frame_free(&frame);
- av_packet_free(&pkt);
- }
- void FFmpegVideoPuller::playLoop()
- {
- // while (m_running) {
- // AVFrame* vFrame = getCurrentVideoFrame();
- // if (!vFrame) {
- // std::this_thread::sleep_for(std::chrono::milliseconds(5));
- // continue;
- // }
- // double vPts = vFrame->best_effort_timestamp
- // * av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base);
- // // 这里你可以直接把vFrame送到OpenGL渲染
- // // 例如: renderFrame(vFrame);
- // // 控制倍速
- // AVRational fr = m_fmtCtx->streams[m_videoStreamIdx]->avg_frame_rate;
- // double frame_delay = 1.0 / (fr.num ? av_q2d(fr) : 25.0);
- // float speed = getSpeed();
- // int delay = int(frame_delay * 1000 / speed);
- // qDebug() << "playLoop, m_speed=" << speed << delay;
- // if (delay > 0)
- // std::this_thread::sleep_for(std::chrono::milliseconds(delay));
- // // 前进
- // nextVideoFrame();
- // // 更新当前pts
- // {
- // std::lock_guard<std::mutex> lock(m_ptsMutex);
- // m_currentPts = vPts;
- // }
- // }
- while (m_running) {
- AVFrame* vFrame = getCurrentVideoFrame();
- double vPts = vFrame ? (vFrame->best_effort_timestamp
- * av_q2d(m_fmtCtx->streams[m_videoStreamIdx]->time_base))
- : m_currentPts.load();
- if (vFrame && m_videoRenderCallback) {
- m_videoRenderCallback(vFrame);
- }
- // 音频同步:只要音频帧PTS小于等于视频帧PTS,就顺序播放
- while (true) {
- AVFrame* aFrame = getCurrentAudioFrame();
- if (!aFrame)
- break;
- double aPts = aFrame->best_effort_timestamp
- * av_q2d(m_fmtCtx->streams[m_audioStreamIdx]->time_base);
- if (aPts <= vPts + 0.02) { // 允许微小误差
- if (m_audioPlayCallback)
- m_audioPlayCallback(aFrame);
- nextAudioFrame();
- } else {
- break;
- }
- }
- // 控制倍速
- AVRational fr = m_fmtCtx->streams[m_videoStreamIdx]->avg_frame_rate;
- double frame_delay = 1.0 / (fr.num ? av_q2d(fr) : 25.0);
- float speed = getSpeed();
- int delay = int(frame_delay * 1000 / speed);
- if (delay > 0)
- std::this_thread::sleep_for(std::chrono::milliseconds(delay));
- // 前进
- nextVideoFrame();
- // 更新当前pts
- {
- std::lock_guard<std::mutex> lock(m_ptsMutex);
- m_currentPts = vPts;
- }
- }
- }
- // 进度相关
- 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_decodeThread) { m_decodeThread->quit(); m_decodeThread->wait(); delete m_decodeThread; m_decodeThread = nullptr; }
- if (m_playThread) { m_playThread->quit(); m_playThread->wait(); delete m_playThread; m_playThread = 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;
- // 重新启动线程
- m_running = true;
- m_decodeThread = QThread::create([this]() { this->decodeLoop(); });
- m_decodeThread->start();
- m_playThread = QThread::create([this]() { this->playLoop(); });
- m_playThread->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;
- }
|