Qt——部署FFmpeg
字数统计:925 阅读时长 ≈ 3分钟前言
项目需要在Qt写的ui里加入视频播放功能,所以准备用FFmpeg整一个Qt视频播放器。
使用FFmpeg 的原因:FFmpeg 的开发环境部署比较简单:
- 在官网下载库文件,然后在QT里面指定路径
- 使用Qt编译生成.exe可执行文件,但此时会提示“程序异常结束”
- 把相关dll文件放到exe目录下,重新编译即可正常运行。
开发环境
MinGW
Qt5.9
FFmpeg 20200510
环境部署
1.下载工具
在 https://ffmpeg.zeranoe.com/builds/ 下载对应版本。链接方式有三种,
Static:只包含ffmpeg.exe、ffplay.exe、ffprobe.exe三个可执行程序,没有头文件和库文件。
Shared:包含ffmpeg.exe、ffplay.exe、ffprobe.exe三个可执行程序和相关动态库文件。
Dev:开发版,包含了头文件和库文件。
我们需要下载Shared和Dev两个版本,Dev有我们程序开发需要的头文件和库文件,这里面包含的库是动态调用的,所依赖的动态库在Shared这个版本里面,所以两个版本都要下载。
2.添加库
打开下载的Dev文件
,将include
和lib
文件夹拷贝到Qt工程目录中:
打开下载的Shared文件
,将动态库.dll
文件拷贝到Qt工程目录的lib文件夹下,拷贝后的bin
文件夹截图:
拷贝后的Qt工程目录截图:
在.pro
添加如下代码:
INCLUDEPATH +="E:\\Lib\\ffmpeg\\include"
LIBS += -LE:\Lib\ffmpeg\lib -lavutil
-lavformat
-lavcodec
-lavdevice
-lavfilter
-lpostproc
-lswresample
-lswscale
以下代码是上面的进阶版本,可以实现对ffmpeg版本的选择:
#ffmpeg3表示用ffmpeg3版本 可以改成ffmpeg4表示用ffmpeg4版本
#qt32表示采用32位的ffmpeg 可以改成qt64表示采用64位的ffmpeg
DEFINES += ffmpeg4 qt64
SOURCES += main.cpp widget.cpp
HEADERS += widget.h
FORMS += widget.ui
CONFIG += warn_off
INCLUDEPATH += $$PWD/ffmpeg
include ($$PWD/ffmpeg/ffmpeg.pri)
在ffmpeg.pri文件中添加如下代码:
#ffmpeg4则使用ffmpeg4的目录
contains(DEFINES, ffmpeg4) {
strPath = ffmpeg4
} else {
strPath = ffmpeg3
}
#32位的库则使用32位的库的头文件和lib文件
contains(DEFINES, qt32) {
strLib = winlib
strInclude = include
} else {
strLib = winlib64
strInclude = include64
}
INCLUDEPATH += $$PWD/$$strPath/$$strInclude
INCLUDEPATH += $$PWD/$$strPath/$$strInclude/plugins
win32 {
LIBS += -L$$PWD/$$strPath/$$strLib/ -lavcodec -lavfilter -lavformat
-lswscale -lavutil -lswresample -lavdevice
}
#请自行替换
unix {
LIBS += -L$$PWD/linuxlib/ -lavfilter -lavformat -lavdevice
-lavcodec -lswscale -lavutil
-lswresample -lavdevice -lpthread
-lm -lz -lrt -ldl
}
然后编译工程,将shared下的动态库添加到生成的.exe可执行文件目录下。
实现播放功能的核心代码:
在界面上放置一个 QLabel 和 QPushButton 控件,当点击按钮时播放视频。程序主要分为以下几方面:
1、打开音视频流并获取音视频流信息;
2、查找视频流位置以及查找并打开视频解码器;
3、视频解码的同时处理图片像素数据;
4、最后要释放申请的内存空间。
#include "widget.h"
#include "ui_widget.h"
#include <QTime>
// 调用FFmpeg的头文件
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
// 延时函数
void delay(int msec)
{
QTime dieTime = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < dieTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
void Widget::on_pushButton_clicked()
{
AVFormatContext *pFormatCtx; // 存储音视频封装格式中包含的信息
int videoIndex = -1; // 视频帧索引,初始化为-1
AVCodecContext *pCodecCtx; // 视频流编码结构
AVCodec *pCodec; // 视频解码器
AVFrame *pFrame, *pFrameRGB;
unsigned char *out_buffer;
AVPacket *packet;
int ret, got_picture;
struct SwsContext *img_convert_ctx; // 主要用于视频图像的转换
char filepath[] = "../FFmpeg_demo/test.mp4"; // 当前目录为构建目录
// 注册FFMpeg的库
av_register_all();
/*** (一)打开音视频流并获取音视频流信息 ***/
// 初始化AVFormatContext
pFormatCtx = avformat_alloc_context();
// 打开音视频流
if (avformat_open_input(&pFormatCtx, filepath, nullptr, nullptr) != 0)
{
printf("Couldn't open input stream.\n");
return;
}
// 获取音视频流数据信息
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
{
printf("Couldn't find stream information.\n");
return;
}
/*** (二)查找视频流位置以及查找并打开视频解码器 ***/
// 查找视频流的起始索引位置(nb_streams表示视音频流的个数)
for (int i = 0; i < (int)pFormatCtx->nb_streams; i++)
{
// 查找到视频流时退出循环
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) // 判断是否为视频流
{
videoIndex = i;
break;
}
}
if (videoIndex == -1)
{
printf("Didn't find a video stream.\n");
return ;
}
// 查找视频解码器
pCodecCtx = pFormatCtx->streams[videoIndex]->codec; // 获取视频流编码结构
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == nullptr)
{
printf("Codec not found.\n");
return ;
}
// 打开解码器
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
printf("Could not open codec.\n");
return ;
}
// 打印视频信息
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx, 0, filepath, 0); // 此函数打印输入或输出的详细信息
printf("-------------------------------------------------\n");
/*** (三)视频解码的同时处理图片像素数据 ***/
// 创建帧结构,此函数仅分配基本结构空间,图像数据空间需通过av_malloc分配
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
// 创建动态内存,创建存储图像数据的空间(av_image_get_buffer_size获取一帧图像需要的大小)
out_buffer = (unsigned char *)av_malloc((size_t)av_image_get_buffer_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1));
// 存储一帧像素数据缓冲区
av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer,
AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);
packet = (AVPacket *)av_malloc(sizeof(AVPacket));
// 初始化img_convert_ctx结构
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
// av_read_frame读取一帧未解码的数据
while (av_read_frame(pFormatCtx, packet) >= 0)
{
// 如果是视频数据
if (packet->stream_index == videoIndex)
{
// 解码一帧视频数据
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0)
{
printf("Decode Error.\n");
return ;
}
if (got_picture)
{
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
QImage img((uchar*)pFrameRGB->data[0],pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
ui->label->setPixmap(QPixmap::fromImage(img)); // 在label上播放视频图片
delay(40);
}
}
av_free_packet(packet);
}
/*** (四)最后要释放申请的内存空间 ***/
sws_freeContext(img_convert_ctx); // 释放一个SwsContext
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
打印出的音视频流信息如下:
--------------- File Information ----------------
-------------------------------------------------
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '../FFmpeg_demo/test.mp4':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2015-06-30T08:50:41.000000Z
copyright :
copyright-eng :
Duration: 00:00:15.06, start: 0.000000, bitrate: 470 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 418 kb/s, 24 fps, 24 tbr, 24k tbn, 48 tbc (default)
Metadata:
creation_time : 2015-06-30T08:50:40.000000Z
handler_name : TrackHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 49 kb/s (default)
Metadata:
creation_time : 2015-06-30T08:50:40.000000Z
handler_name : Sound Media Handler
参考
本文由simyng创作,
采用知识共享署名4.0 国际许可协议进行许可,转载前请务必署名
文章最后更新时间为:May 13th , 2020 at 09:58 pm