一、前言
好久以前就寫過這個工具,後來因為Qt版本的不斷更新以及ffmpeg也經曆過好多次的疊代,可能從官網下載下傳的ffmpeg搭配原來的代碼不能正确編譯,因為很多api已經變了,是以這次特意抽空全部整理重寫一遍,隻求最精簡最好用,同時相容了ffmpeg3和ffmpeg4,并且同時支援32位的庫和64位的庫,這樣任何小白拿過去直接編譯就能用。
- 多線程實時繪制
- 同時解碼視訊流和音頻流
- 支援任意Qt版本任意系統任意編譯器
- 解碼和窗體分離,拓展性強
- 可選ffmpeg3和ffmpeg4兩個版本
- 可選32位和64位的ffmpeg庫
- 注釋絕對詳細,包你滿意
二、代碼思路
第一步:引入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";
}
三、效果圖

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