Qt開源作品1-視頻流播放ffmpeg內核


一、前言

好久以前就寫過這個工具,后來因為Qt版本的不斷升級以及ffmpeg也經歷過好多次的迭代,可能從官網下載的ffmpeg搭配原來的代碼不能正確編譯,因為很多api已經變了,所以這次特意抽空全部整理重寫一遍,只求最精簡最好用,同時兼容了ffmpeg3和ffmpeg4,並且同時支持32位的庫和64位的庫,這樣任何小白拿過去直接編譯就能用。

  1. 多線程實時繪制
  2. 同時解碼視頻流和音頻流
  3. 支持任意Qt版本任意系統任意編譯器
  4. 解碼和窗體分離,拓展性強
  5. 可選ffmpeg3和ffmpeg4兩個版本
  6. 可選32位和64位的ffmpeg庫
  7. 注釋絕對詳細,包你滿意

二、代碼思路

第一步:引入ffmpeg的頭文件

//必須加以下內容,否則編譯不能通過,為了兼容C和C99標准
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif

//引入ffmpeg頭文件
extern "C" {
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/frame.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/ffversion.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"

#ifdef ffmpegdevice
#include "libavdevice/avdevice.h"
#endif

#ifndef gcc45
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#endif
}

第二步:注冊ffmpeg的庫
這里發現很多人每個類都注冊一次,搞得內存每次增加很多,不是不可以,而是沒有必要,其實ffmpeg的庫和解碼器等,在一個程序中只需要注冊一次即可,沒必要每個視頻類都注冊一次。

//一個軟件中只需要初始化一次就行
void FFmpegThread::initlib()
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    static bool isInit = false;
    if (!isInit) {
        //注冊庫中所有可用的文件格式和解碼器
        av_register_all();
        //注冊所有設備,主要用於本地攝像機播放支持
#ifdef ffmpegdevice
        avdevice_register_all();
#endif
        //初始化網絡流格式,使用網絡流時必須先執行
        avformat_network_init();

        isInit = true;
        qDebug() << TIMEMS << "init ffmpeg lib ok" << " version:" << FFMPEG_VERSION;
#if 0
        //輸出所有支持的解碼器名稱
        QStringList listCodeName;
        AVCodec *code = av_codec_next(NULL);
        while (code != NULL) {
            listCodeName << code->name;
            code = code->next;
        }

        qDebug() << TIMEMS << listCodeName;
#endif
    }
}

第三步:設置參數

//在打開碼流前指定各種參數比如:探測時間/超時時間/最大延時等
//設置緩存大小,1080p可將值調大
av_dict_set(&options, "buffer_size", "8192000", 0);
//以tcp方式打開,如果以udp方式打開將tcp替換為udp
av_dict_set(&options, "rtsp_transport", "tcp", 0);
//設置超時斷開連接時間,單位微秒,3000000表示3秒
av_dict_set(&options, "stimeout", "3000000", 0);
//設置最大時延,單位微秒,1000000表示1秒
av_dict_set(&options, "max_delay", "1000000", 0);
//自動開啟線程數
av_dict_set(&options, "threads", "auto", 0);

第四步:打開視頻流
具體代碼比較多,詳細代碼請自行開源主頁下載。

第五步:解碼圖像

void FFmpegThread::run()
{
    //計時
    QTime time;
    while (!stopped) {
        //根據標志位執行初始化操作
        if (isPlay) {
            this->init();
            isPlay = false;
            continue;
        }

        time.restart();
        if (av_read_frame(avFormatContext, avPacket) >= 0) {
            //判斷當前包是視頻還是音頻
            int packetSize = avPacket->size;
            int index = avPacket->stream_index;
            if (index == videoStreamIndex) {
                //解碼視頻流
                avcodec_decode_video2(videoCodec, avFrame2, &frameFinish, avPacket);

                if (frameFinish) {
                    //將數據轉成一張圖片
                    sws_scale(swsContext, (const uint8_t *const *)avFrame2->data, avFrame2->linesize, 0, videoHeight, avFrame3->data, avFrame3->linesize);

                    //以下兩種方法都可以
                    //QImage image(avFrame3->data[0], videoWidth, videoHeight, QImage::Format_RGB32);
                    QImage image((uchar *)buffer, videoWidth, videoHeight, QImage::Format_RGB32);
                    if (!image.isNull()) {
                        emit receiveImage(image);
                    }

                    msleep(1);
                }
            } else if (index == audioStreamIndex) {
                //解碼音頻流,這里暫不處理,以后交給sdl播放
            }
        }

        av_packet_unref(avPacket);
        av_freep(avPacket);
        msleep(1);
    }

    //線程結束后釋放資源
    free();
    stopped = false;
    isPlay = false;
    qDebug() << TIMEMS << "stop ffmpeg thread";
}

三、效果圖

四、開源主頁

以上作品完整源碼下載都在開源主頁,會持續不斷更新作品數量和質量,歡迎各位關注。

  1. 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
  3. 個人主頁:https://blog.csdn.net/feiyangqingyun
  4. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM