Qt使用FFmpeg播放视频
一、使用场景
因为项目中需要加载MP4播放开机视频,而我们的设备所使用的架构为arm架构,其中缺乏一些多媒体库。安装这些插件库比较麻烦,所以最终决定使用FFmpeg播放视频。
二、下载编译ffmpeg库
2.1 下载源码
源码下载路径:https://www.ffmpeg.org/download.html#build-windows
2.2 编译源码
1) 解压:将源码放到指定目录,并运行"tar -jxvf ffmpeg-snapshot.tar.bz2"。若是xxx.tar.gz源文件,则用"tar -zxvf ffmpeg-xxx.tar.gz"。
2) 创建构建目录,"cd ffmpeg", "mkdir build";
3)编译:
a) ubuntu编译: "./configure --enable-static --prefix=./build"
b)arm交叉编译: "./configure --cc=xxx/aarch64-linux-gnu-gcc(QT指定的gcc路径) --cxx=xxx/aarch64-linux-gnu-g++ --enable-staticc(QT指定的g++路径) --prefix=./build --enable-cross-compile --arch=arm64 --target-os=linux"。
4) make安装: "make && make install"。
5) 运行:若需要运行ffmpeg则需要增加--enable-shared参数,并且添加环境变量"export LD_LIBRARY_PATH=xxx/build/lib/"。
6)使用帮助: 在xxx/build/bin路径下运行"./ffmpeg --help"。
2.3 常见报错
1) 交叉编译需要指定对应的gcc和g++编译器和其它的如平台参数,Linux的QTCreator可以通过如下选项查看对应的编译器路径,工程-》管理构建-》编译器-》指南(Manual)-》双击gcc或g++。注意在操作之前需要先选择工程当前的运行和构建平台为arrch64。
2) make install 报错:"strip: Unable to recognise the format of the input file":将config.mak中的"Trip=strip"改为"Trip=arm -linux-strip"。
三、使用源码
1.在工程中导入头文件和库,注意库顺序。eg:
INCLUDEPATH += xxx/build/include
LIBS += -Lxxx/xxx -lavformat\-lavdevice \-lavcodec \-lswresample \-lavfilter \ -lavutil \-lswscale
2.头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QDebug>
#include <QTimer>
#include <QTime>
#include <QAudioOutput>
extern "C"
{#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libswscale/swscale.h>#include <libavdevice/avdevice.h>#include <libavformat/version.h>#include <libavutil/time.h>#include <libavutil/mathematics.h>#include <libavfilter/buffersink.h>#include <libavfilter/buffersrc.h>#include <libavutil/avutil.h>#include <libavutil/imgutils.h>#include <libavutil/pixfmt.h>#include <libswresample/swresample.h>
}#define MAX_AUDIO_FRAME_SIZE 192000namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();public slots:void timeCallback(void);void on_play_clicked();void resizeEvent(QResizeEvent* );private:Ui::MainWindow *ui;int playVedio(void);QTimer *timer; // 定时播放,根据帧率来int vedioW,vedioH; // 图像宽高QList<QPixmap> vedioBuff; // 图像缓存区QString myUrl = QString("E:/workspace/Qt_workspace/ffmpeg/三国之战神无双.mp4"); // 视频地址AVFormatContext *pFormatCtx;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame, *pFrameRGB;int ret, got_picture,got_audio; // 视频解码标志int videoindex; // 视频序号// 音频int audioindex; // 音频序号AVCodecParameters *aCodecParameters;AVCodec *aCodec;AVCodecContext *aCodecCtx;QByteArray byteBuf;//音频缓冲QAudioOutput *audioOutput;QIODevice *streamOut;
};#endif // MAINWINDOW_H
3.源文件:
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);
// qDebug(avcodec_configuration());
// unsigned version = avcodec_version();
// QString ch = QString::number(version,10);
// qDebug()<<"version:"<<version;timer = new QTimer(this);timer->setTimerType(Qt::PreciseTimer); // 精准定时设置connect(timer,SIGNAL(timeout()),this,SLOT(timeCallback()));
}void MainWindow::timeCallback(void)
{// 视频缓存播放if(!vedioBuff.isEmpty()){ui->label->setPixmap(vedioBuff.at(0));vedioBuff.removeAt(0);}else {timer->stop();}// 音频缓存播放if(audioOutput && audioOutput->state() != QAudio::StoppedState && audioOutput->state() != QAudio::SuspendedState){int writeBytes = qMin(byteBuf.length(), audioOutput->bytesFree());streamOut->write(byteBuf.data(), writeBytes);byteBuf = byteBuf.right(byteBuf.length() - writeBytes);}
}void Delay_MSec(unsigned int msec)
{QTime _Timer = QTime::currentTime().addMSecs(msec);while( QTime::currentTime() < _Timer )QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}int MainWindow::playVedio(void)
{QAudioFormat fmt;fmt.setSampleRate(44100);fmt.setSampleSize(16);fmt.setChannelCount(2);fmt.setCodec("audio/pcm");fmt.setByteOrder(QAudioFormat::LittleEndian);fmt.setSampleType(QAudioFormat::SignedInt);audioOutput = new QAudioOutput(fmt);streamOut = audioOutput->start();char *filepath = myUrl.toUtf8().data();av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();// 打开视频文件,初始化pFormatCtx结构if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){qDebug("视频文件打开失败.\n");return -1;}// 获取音视频流if(avformat_find_stream_info(pFormatCtx,NULL)<0){qDebug("媒体流获取失败.\n");return -1;}videoindex = -1;audioindex = -1;//nb_streams视音频流的个数,这里当查找到视频流时就中断了。for(int i=0; i<pFormatCtx->nb_streams; i++)if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;break;}if(videoindex==-1){qDebug("找不到视频流.\n");return -1;}//nb_streams视音频流的个数,这里当查找到音频流时就中断了。for(int i=0; i<pFormatCtx->nb_streams; i++)if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){audioindex=i;break;}if(audioindex==-1){qDebug("找不到音频流.\n");return -1;}//获取视频流编码结构pCodecCtx=pFormatCtx->streams[videoindex]->codec;float frameNum = pCodecCtx->framerate.num; // 每秒帧数if(frameNum>100) frameNum = frameNum/1001;int frameRate = 1000/frameNum; //qDebug("帧/秒 = %f 播放间隔是时间=%d\n",frameNum,frameRate);//查找解码器pCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){qDebug("找不到解码器.\n");return -1;}//使用解码器读取pCodecCtx结构if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){qDebug("打开视频码流失败.\n");return -1;}//获取音频流编码结构-------------------------------------------------------------aCodecParameters = pFormatCtx->streams[audioindex]->codecpar;aCodec = avcodec_find_decoder(aCodecParameters->codec_id);if (aCodec == 0) {qDebug("找不到解码器.\n");return -1;}aCodecCtx = avcodec_alloc_context3(aCodec);avcodec_parameters_to_context(aCodecCtx, aCodecParameters);//使用解码器读取aCodecCtx结构if (avcodec_open2(aCodecCtx, aCodec, 0) < 0) {qDebug("打开视频码流失败.\n");return 0;}// 清空缓存区byteBuf.clear();vedioBuff.clear();//创建帧结构,此函数仅分配基本结构空间,图像数据空间需通过av_malloc分配pFrame = av_frame_alloc();pFrameRGB = av_frame_alloc();// 获取音频参数uint64_t out_channel_layout = aCodecCtx->channel_layout;AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;int out_sample_rate = aCodecCtx->sample_rate;int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);uint8_t *audio_out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);SwrContext *swr_ctx = swr_alloc_set_opts(NULL, out_channel_layout, out_sample_fmt,out_sample_rate, aCodecCtx->channel_layout, aCodecCtx->sample_fmt, aCodecCtx->sample_rate, 0, 0);swr_init(swr_ctx);//创建动态内存,创建存储图像数据的空间unsigned char *out_buffer = (unsigned char *)av_malloc(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);AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));//初始化img_convert_ctx结构struct SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);timer->start(frameRate); //定时间隔播放while (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == audioindex){int ret = avcodec_decode_audio4(aCodecCtx, pFrame, &got_audio, packet);if ( ret < 0){qDebug("解码失败.\n");return 0;}if (got_audio){int len = swr_convert(swr_ctx, &audio_out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);if (len <= 0){continue;}int dst_bufsize = av_samples_get_buffer_size(0, out_channels, len, out_sample_fmt, 1);QByteArray atemp = QByteArray((const char *)audio_out_buffer, dst_bufsize);byteBuf.append(atemp);}}//如果是视频数据else if (packet->stream_index == videoindex){//解码一帧视频数据ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0){qDebug("解码失败.\n");return 0;}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);img = img.scaled(vedioW, vedioH);QPixmap temp = QPixmap::fromImage(img);vedioBuff.append(temp);Delay_MSec(frameRate-5); // 这里需要流出时间来显示,如果不要这个延时界面回卡死到整个视频解码完成才能播放显示//ui->label->setPixmap(temp);}}av_free_packet(packet);}sws_freeContext(img_convert_ctx);av_frame_free(&pFrameRGB);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);
}void MainWindow::resizeEvent(QResizeEvent* )
{vedioW = ui->label->width();vedioH = ui->label->height();
}
MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_play_clicked()
{vedioW = ui->label->width();vedioH = ui->label->height();if(timer->isActive()) timer->stop();playVedio();
}
相关文章:
Qt使用FFmpeg播放视频
一、使用场景 因为项目中需要加载MP4播放开机视频,而我们的设备所使用的架构为arm架构,其中缺乏一些多媒体库。安装这些插件库比较麻烦,所以最终决定使用FFmpeg播放视频。 二、下载编译ffmpeg库 2.1 下载源码 源码下载路径:http…...
Win32 ListBox控件
Win32 ListBox控件 创建ListBox控件 创建窗口函数 HWND CrateWindowEx(DWORD dwExStyle , // 窗口的扩展风格,基本没用LPCTSTR lpClassName, // 已经注册的窗口类名称LPCTSTR lpWindowName, // 窗口标题栏的名字DWORD dwStyle, // 窗口的基本风格int x, // 左上角水平坐标int …...
最大值池化与均值池化比较分析
1 问题在深度学习的卷积网络过程中,神经网络有卷积层,池化层,全连接层。而池化层有最大值池化和均值池化两种情况,而我们组就在思考,最大值池化和均值池化有什么区别呢?两者的模型准确率是否有所不同&#…...
统计学 多元线性回归
文章目录统计学 多元线性回归多元线性回归模型拟合优度显著性检验线性关系检验回归系数检验多重共线性及其处理多重共线性的问题多重共线性的识别与处理变量选择利用回归方程进行预测哑变量回归统计学 多元线性回归 多元线性回归模型 多元线性回归模型:设因变量为…...
tar和gzip压缩和解压
打包和压缩的区别:打包:将多文件 封装在一起压缩:将多文件 封装在一起 通过特定的算法 将冗余的数据 进行删除tar默认是打包命令,如果想用tar进行压缩 必须加选项1、gzip格式压缩:tar zcvf 压缩包包名 文件1 文件2 文件…...
搭建Docker企业私有仓库
什么是仓库 仓库(Repository)是存储和分发 Docker 镜像的地方。镜像仓库类似于代码仓库,Docker Hub 的命名来自 GitHub,Github 是我们常用的代码存储和分发的地方。同样 Docker Hub 是用来提供 Docker 镜像存储和分发的地方。 谈…...
[NOIP2009 提高组] 最优贸易(C++,tarjan,topo,DP)
题目描述 $C 国有国有国有 n 个大城市和个大城市和个大城市和 m$ 条道路,每条道路连接这 nnn个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 mmm 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的…...
计算机网络:移动IP
移动IP相关概念 移动IP技术是移动结点(计算机/服务器)以固体的网络IP地址,实现跨越不同网段的漫游功能,并保证了基于网络IP的网络权限在漫游中不发生任何改变。移动结点:具有永久IP地址的设备。归属代理(本…...
binutils工具集——GNU binutils工具集简介
以下内容源于网络资源的学习与整理,如有侵权请告知删除。 GNU binutils是一个二进制工具集,主要包括: ld,GNU链接器。as,GNU汇编器。addr2line,把地址转化为文件名和行号。nm,列出目标文件的符…...
Golang编译选项(ldflags)有趣应用
本文介绍如何在构建时使用ldflags选项给Golang应用程序注入变量,用于给Go可执行文件增加版本标识或GIT提交摘要等信息。 应用程序的版本信息 我们首先查看Docker Cli 包含的提交信息: docker version 返回结果: Server: Docker Engine - Co…...
AIR32F103(十一) 在AIR32F103上移植微雪墨水屏驱动
目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告AIR32F103(二) Linux环境和LibOpenCM3项目模板AIR32F103(三) Linux环境基于标准外设库的项目模板AIR32F103(四) 27倍频216MHz,CoreMark跑分测试AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码AIR32F103(六) ADC,I2S…...
Uipath Excel 自动化基础系列文章
Uipath Excel 自动化基础系列文章已发布到CSDN,网址:https://blog.csdn.net/Marshaljun?typeblog (3月份会在CSDN博客发布Uipath Excel 实战课程及经验分享) Uipath Studio流程设计器介绍 https://blog.csdn.net/Marshaljun/article/details/128699022 Uipath St…...
神经网络优化器之随机梯度下降法的理解
随机梯度下降法(SGD)随机梯度下降方法,在每次更新时用1个样本,随机也就是说我们用样本中的一个例子来近似我所有的样本,由于计算得到的并不是准确的一个梯度,因而不是全局最优的。但是相比于批量梯度&#…...
记录一次WIN11开机在登录页面循环的问题
记录一次由于未进行win密码设置,导致开机后卡在登录界面无法登录进去的问题。最后完美解决了。 1. 背景 开机后,显示用户登录界面,但是和以往不同,没有了密码输入框,只有一个“登录”按钮孤零零地显示在屏幕中间&…...
始终从最不易改变的方面开始
在你刚开始新工作、转换职业或者是加入新项目时,始终从最不易改变的方面开始。 在工作中,这可能意味着与团队成员建立关系,了解公司的流程和文化,或者熟悉公司的产品或服务。 在一项新项目中,这可能意味着了解项目范…...
4、Httpclient源码解析之HTTP协议
初始化CloseableHttpClient过程中涉及ExecChainHandler & DefaultHttpProcessor,即典型客户端责任链中的请求执行处理器。 责任链中各节点涉及请求处理器【ExecChainHandler】顺序如下:RedirectExec、ContentCompressionExec、HttpRequestRetryExec…...
浏览器并发行为记录
使用nodejs koa起一个服务,使请求延时返回。 服务端代码 /** 延时 */ exports.timeoutTestData async function (ctx) {console.log(get query:, ctx.request.query);const query ctx.request.query;let timeout query.timeout || 2000;await new Promise(res…...
工厂模式与抽象工厂
原理:逻辑和业务全部封装 不需要细节 只要结果 示例: # 简单工厂 class SimpleFactory:# 产品staticmethoddef product(name):return nameif __name__ "__main__":product SimpleFactory.product("Gitee")print(product) 装饰器…...
什么?你不知道 ConcurrentHashMap 的 kv 不能为 null?
一、背景 最近设计某个类库时使用了 ConcurrentHashMap 最后遇到了 value 为 null 时报了空指针异常的坑。 本文想探讨下以下几个问题: (1) Map接口的常见子类的 kv 对 null 的支持情况。 (2)为什么 ConcurrentHashM…...
SQL复习04 | 复杂查询
1. 视图 视图和表的区别: 表保存的是实际的数据视图保存的是SELECT语句 视图的优点: 视图无需保存数据,可节省存储设备的容量可以将频繁使用的SELECT语句保存成视图,可大大提高效率 1.1 创建视图 CREATE VIEW 视图名称&…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
