项目实战——Qt实现FFmpeg音视频转码器
文章目录
- 前言
- 一、移植 FFmpeg 相关文件
- 二、绘制 ui 界面
- 三、实现简单的转码
- 四、功能优化
- 1、控件布局及美化
- 2、缩放界面
- 3、实现拖拽
- 4、解析文件
- 5、开启独立线程
- 6、开启定时器
- 7、最终运行效果
- 五、附录
- 六、资源自取
前言
本文记录使用 Qt 实现 FFmepg 音视频转码器项目的开发过程。
一、移植 FFmpeg 相关文件
1、首先创建一个 Qt 项目,选择 MSVC2017 32bit 作为其编译器

2、将 FFmpeg 相关库及源文件拷贝到当前目录下

3、注释 prepare_app_arguments 函数(这里方便后面我们运行时可以指定相应的转码参数)

4、将所需的一些 dll 动态库文件拷贝到 debug 目录下

5、将音视频素材文件拷贝到 build-QtVideoConverterFFmpeg431-Desktop_Qt_5_14_2_MinGW_32_bit-Debug目录下(点击运行自动生成的目录)

二、绘制 ui 界面
绘制一个简单的 ui 界面,效果如下:

里面包括 Frame、Push Button、Progress Bar、Label、Table Widget、Combo Box、Line Edit 等相关控件。
三、实现简单的转码
1、在开始转码按键的 clicked 槽函数加入以下代码:
void Widget::on_pushButton_Running_clicked()
{qDebug() << "hello,ffmpeg";QString currentPath = QDir::current().path();qDebug() << "Current path:" << currentPath;char* arrParams[10] = { 0 };for (int k = 0; k < 10; k++) {arrParams[k] = new char[64]();}strcpy(arrParams[0], "QtVideoConverter.exe");strcpy(arrParams[1], "-i");strcpy(arrParams[2], "SampleVideo_1280x720_20mb.mp4");strcpy(arrParams[3], "-vcodec");strcpy(arrParams[4], "libx264");strcpy(arrParams[5], "-acodec");strcpy(arrParams[6], "copy");strcpy(arrParams[7], "-y");strcpy(arrParams[8], "SampleVideo_1280x720_20mb.flv");main_ffmpeg431(9, arrParams);AVGeneralMediaInfo* avmi = new AVGeneralMediaInfo();for (int k = 0; k < 10; k++) {delete[] arrParams[k];avmi = NULL;}
}
2、点击运行,可以看到如下的界面

目前进度条功能还未实现,点击转码可以在 build-QtVideoConverter-Desktop_Qt_5_14_2_MSVC2017_32bit-Debug 目录下看到转码成功的 flv 文件

四、功能优化
1、控件布局及美化
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setStyleSheet("background-color:#F0F0F0;"); // 设置组件窗口的外观// qss,类似于cssui->lblLogoText->setStyleSheet("color:#009100;font-style:italic;font-weight:bold;font-size:30px;");// frame 背景色ui->frameTop->setStyleSheet("background-color:#C4E1FF;");// 按钮背景色ui->pushButton_Running->setStyleSheet("background-color:#C4E1FF;font-weight:bold;font-size:30px;color:#009100;border:2px groove gray;border-radius:10px;padding:2px 4px;");
}// 隐藏栅格线、单元格不可编辑ui->tableWidget_FileList->verticalHeader()->setHidden(true); // 设置行名隐藏(注意是行名,不是整行)ui->tableWidget_FileList->setShowGrid(false); // 控制视图中数据项之间是否显示网格ui->tableWidget_FileList->setEditTriggers(QAbstractItemView::NoEditTriggers); // 让这个表格对用户只读
效果如下:

2、缩放界面
事件过滤器:(双击,全屏)
// 事件过滤器:(双击,全屏)
bool Widget::eventFilter(QObject *obj, QEvent *event)
{// 指定某个控件if (obj == ui->frameTop || obj == ui->lblLogoText || obj == ui->lblLogoImage) {// QEvent::MouseButtonPress,QEvent::MouseButtonDblClickif (event->type() == QEvent::MouseButtonDblClick) {QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);if (mouseEvent->button() == Qt::LeftButton) {// QMessageBox::information(this, "点击", "点击了我", QMessageBox::Yes | QMessageBox::No | QMessageBox::Yes);if (!this->isMaximized()) {this->showMaximized();} else {this->showNormal();}return true;} else {return false;}} else {return false;}} else {// pass the event on to the parent classreturn Widget::eventFilter(obj, event);}
}
效果:

ESC 键退出全屏
// 按键:(esc--退出全屏)
void Widget::keyPressEvent(QKeyEvent *event)
{switch (event->key()) {case Qt::Key_Escape:if (this->isMaximized()) {this->showNormal();}break;default:QWidget::keyPressEvent(event);}
}
3、实现拖拽
鼠标按下不松开,然后移动鼠标实现拖拽,松开鼠标拖拽结束
// 拖拽操作---begin
void Widget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {m_bDrag = true;// 获得鼠标的初始位置mouseStartPoint = event->globalPos(); // 事件发生时鼠标相对于我们整个屏幕的左上角(0,0)的偏移值// mouseStartPoint = event->pos(); // 事件发生时鼠标相对于当前active widget的左上角(0,0)的偏移值// 获得窗口的初始位置windowTopLeftPoint = this->frameGeometry().topLeft(); // 仍然表示整个屏幕的左上角qDebug() << "mouseStartPoint" << mouseStartPoint.x() << mouseStartPoint.y();qDebug() << "windowTopLeftPoint" << windowTopLeftPoint.x() << windowTopLeftPoint.y();}
}void Widget::mouseMoveEvent(QMouseEvent *event)
{if (m_bDrag) {// 获得鼠标移动的距离QPoint distance = event->globalPos() - mouseStartPoint;// QPoint distance = event->pos() - mouseStartPoint;// 改变窗口的位置this->move(windowTopLeftPoint + distance);qDebug() << "move" << windowTopLeftPoint + distance;}
}void Widget::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {m_bDrag = false;}
}
// 拖拽操作--end
效果如下:

4、解析文件
点击 选择文件 按钮,选择待转码的文件,可以将所选文件的相关信息解析出来
void Widget::on_pushButton_AddFile_clicked()
{// 定义文件对话框类QFileDialog *fileDialog = new QFileDialog(this);// 定义文件对话框标题fileDialog->setWindowTitle(tr("打开文件")); // tr()函数:Qt会根据当前的语言环境自动选择相应的翻译文件,并将字符串翻译成对应的语言。// 设置默认路径fileDialog->setDirectory(".");// 设置文件过滤器fileDialog->setNameFilter(tr("video(*.mp4 *.flv *.mkv);;All files(*.*)"));// 设置可以选择多个文件,默认只能选择一个文件 QFileDialog::ExistingFilesfileDialog->setFileMode(QFileDialog::ExistingFile);// 设置视图模式fileDialog->setViewMode(QFileDialog::Detail);if (fileDialog->exec()) {QString strFileName = fileDialog->selectedFiles()[0];qDebug() << strFileName;QFileInfo fileinfo;fileinfo = QFileInfo(strFileName);// 插入数据项ui->tableWidget_FileList->setRowCount(1);ui->tableWidget_FileList->setItem(0, 0, new QTableWidgetItem(fileinfo.fileName())); // 文件名ui->tableWidget_FileList->setItem(0, 1, new QTableWidgetItem(fileinfo.suffix())); // 后缀AVGeneralMediaInfo avmi;std::string str = strFileName.toStdString();const char *chFilename = str.c_str();get_avgeneral_mediainfo(&avmi, chFilename);ui->tableWidget_FileList->setItem(0, 2, new QTableWidgetItem(QString(QLatin1String(avmi.videoCodecName))));ui->tableWidget_FileList->setItem(0, 3, new QTableWidgetItem(QString(QLatin1String(avmi.audioCodecName))));char chDuration[128] = {0};sprintf(chDuration, "%lld", avmi.duration);ui->tableWidget_FileList->setItem(0, 4, new QTableWidgetItem(QString(QLatin1String(chDuration))));ui->tableWidget_FileList->setItem(0, 5, new QTableWidgetItem(strFileName));}
}
效果如下:

5、开启独立线程
tcworkthread.h
#ifndef TCWORKTHREAD_H
#define TCWORKTHREAD_H#include <QThread>
extern "C" {
#include "ffmpeg.h"
}#define MAX_CMDLINE_ARGC_COUNT 100// 转码参数
typedef struct __TCParams {char inFilename[512];char videoCodecName[256];char audioCodecName[256];char muxerName[256];// 定义了一个无参数的构造函数__TCParams(),在该构造函数中调用了一个名为__init()的私有成员函数。// 构造函数在创建结构体实例时会被自动调用,因此当创建TCParams对象时,会自动执行__init()函数。__TCParams() {__init();}void __init() {memset(inFilename, 0, 512);memset(videoCodecName, 0, 256);memset(audioCodecName, 0, 256);memset(muxerName, 0, 256);}} TCParams;class TCWorkThread : public QThread
{
public:TCWorkThread();private:virtual void run(); // 任务处理线程TCParams *m_pTCParams;public:int workCount; // 计数void SetTCParams(TCParams *params);signals:public slots:};
tcworkthread.c
#include "tcworkthread.h"
#include <QDebug>TCWorkThread::TCWorkThread()
{workCount = 0;m_pTCParams = nullptr;
}void TCWorkThread::SetTCParams(TCParams *params)
{m_pTCParams = params;
}// run() 重新实现
void TCWorkThread::run()
{if (m_pTCParams == nullptr) {return;}// by lp,参数都是写死的,仅供参考char* arrParams[MAX_CMDLINE_ARGC_COUNT] = { 0 };for (int k = 0; k < MAX_CMDLINE_ARGC_COUNT; k++) {arrParams[k] = new char[1024]();}char strOutName[512] = {0};strcpy(arrParams[0], "QtVideoConverter.exe");strcpy(arrParams[1], "-i");strcpy(arrParams[2], m_pTCParams->inFilename);strcpy(arrParams[3], "-vcodec");strcpy(arrParams[4], m_pTCParams->videoCodecName);strcpy(arrParams[5], "-acodec");strcpy(arrParams[6], m_pTCParams->audioCodecName);strcpy(arrParams[7], "-y");sprintf(strOutName, "SampleVideo_1280x720_20mb.%s", m_pTCParams->muxerName);strcpy(arrParams[8], strOutName);// 准备参数main_ffmpeg431(9, arrParams);for (int k = 0; k < MAX_CMDLINE_ARGC_COUNT; k++) {delete[] arrParams[k]; // 切记要释放申请的内存arrParams[k] = NULL;}
}
6、开启定时器
// 定时器事件处理函数
// 获取实时转码进度
// 当前进度为 1.00 时,killTimer
void Widget::timerEvent(QTimerEvent *event)
{int nPrg = (int)(get_tc_progress() * 100);qDebug() << "progress:" << nPrg;ui->progressBar_tcprg->setValue(nPrg);if (nPrg >= 100) {killTimer(m_TimerID1);}
}
7、最终运行效果
将本地 mp3 文件转换成 flv 文件

五、附录
附上一个十六进制颜色码的网站:十六进制颜色代码表,图表及调色板
六、资源自取
链接:基于QT和ffmpeg的音视频转码器

我的qq:2442391036,欢迎交流!
相关文章:
项目实战——Qt实现FFmpeg音视频转码器
文章目录 前言一、移植 FFmpeg 相关文件二、绘制 ui 界面三、实现简单的转码四、功能优化1、控件布局及美化2、缩放界面3、实现拖拽4、解析文件5、开启独立线程6、开启定时器7、最终运行效果 五、附录六、资源自取 前言 本文记录使用 Qt 实现 FFmepg 音视频转码器项目的开发过…...
AI数字人-数字人视频创作数字人直播效果媲美真人
在科技的不断革新下,数字人技术正日益融入到人们的生活中。近年来,随着AI技术的进一步发展,数字人视频创作领域出现了一种新的创新方式——AI数字人。数字人视频通过AI算法生成虚拟主播,其外貌、动作、语音等方面可与真实人类媲美…...
初识C语言·动态内存开辟
1 为什么要有动态内存开辟 int a 10; int arr[10] { 0 }; 上述定义了一个整型,开辟了4个字节,定义了一个整型数组,开辟了40个字节,但是是固定开辟的,面对灵活多变的实际问题的时候可能就有点鸡肋,这种开…...
机器学习 | 利用Pandas进入高级数据分析领域
目录 初识Pandas Pandas数据结构 基本数据操作 DataFrame运算 文件读取与存储 高级数据处理 初识Pandas Pandas是2008年WesMcKinney开发出的库,专门用于数据挖掘的开源python库,以Numpy为基础,借力Numpy模块在计算方面性能高的优势&am…...
三、计算机理论-计算机网络-物理层,数据通信的理论基础,物理传输媒体、编码与传输技术及传输系统
物理层概述 物理层为数据链路层提供了一条在物理的传输媒体上传送和接受比特流的能力。物理层提供信道的物理连接,主要任务可以描述为确定与传输媒体的接口有关的一些特性:机械特性、电气特性、功能特性、过程特性 数据通信的理论基础 数据通信的意义 主…...
ERROR Failed to get response from https://registry.npm.taobao.org/ 错误的解决
这个问题最近才出现的。可能跟淘宝镜像的证书到期有关。 解决方式一:更新淘宝镜像(本人测试无效,但建议尝试) 虽然无效,但感觉是有很大关系的。还是设置一下比较好。 淘宝镜像的地址(registry.npm.taobao…...
overflow产生的滚动条样式设置
修改overflow产生的滚动条样式,主要可以通过如下三个伪元素设置: 1)-webkit-scrollbar:设置水平滚动条的高度,垂直滚动的宽度 2)-webkit-scrollbar-thumb:设置滚动条里面的滑块样式 3)-webkit-scrollbar-track&…...
Ubuntu环境vscode配置Log4cplus库
1、下载源码 http://sourceforge.net/projects/log4cplus/ 2、安装 例如我下载的是2.0.8版本压缩包,需要解压缩 log4cplus-2.0.8.7z安装解压工具: apt install p7zip-full解压: 7z x log4cplus-2.0.8.7z -r -o/home/配置及编译安装&#x…...
vue中,使用file-saver导出文件,下载Excel文件、下载图片、下载文本
vue中,使用file-saver导出文件,下载Excel文件、下载图片、下载文本 1、基本介绍 npm地址:file-saver - npm 2、安装 # Basic Node.JS installation npm install file-saver --save bower install file-saver# Additional typescript defin…...
【VUE】v-if 和 v-show 大详解(多角度分析+面试简答版)
多角度分析+面试简答版 一、`v-if` 和 `v-show` 的区别之多角度分析控制手段:编译过程:编译条件:性能消耗:总结使用场景二、 `v-if`、`v-show`、`display:none` 和`visibility: hidden` 的区别三、简洁版回答:`v-show` 与 `v-if` 比较一、v-if 和 v-show 的区别之多角度分…...
mac intel jdk安装与配置
jdk地址下载 https://www.oracle.com/java/technologies/downloads/ https://repo.huaweicloud.com/java/jdk/8u201-b09/ 安装后 下载完成之后打开终端 注意如果是第一次配置环境变量需要创建.bash_profile文件。(注意:touch后面有空格) to…...
Backtrader 文档学习-Bracket Orders
Backtrader 文档学习-Bracket Orders 1. 概述 组合订单类型是一个非常宽泛的订单类别,只要brokder支持的订单类型都可以, 包括(Market, Limit, Close, Stop, StopLimit, StopTrail, StopTrailLimit, OCO)。 该功能用于回测,交互broker Brac…...
Python编程 从入门到实践(项目二:数据可视化)
本篇为实践项目二:数据可视化。 配合文章python编程入门学习,代码附文末。 项目二:数据可视化 1.生成数据1.1 安装Matplotlib1.2 绘制简单的折线图1.2.1 修改标签文字和线条粗细1.2.2 校正图形1.2.3 使用内置样式1.2.4 使用scatter()绘制散点…...
Linux版本下载Centos操作
目录 一、Centos7 二、下载Centos7镜像 三、下载Centos7 买了个硬件安装裸机(一堆硬件) 把安装盘放到虚拟机里面,给机器加电 配置设置 编辑 网络配置 开启网络功能 四、安装linux客户端 Xshell是什么 Xshell使用(连接…...
Offer必备算法_二分查找_八道力扣OJ题详解(由易到难)
目录 二分查找算法原理 ①力扣704. 二分查找 解析代码 ②力扣34. 在排序数组中查找元素的第一个和最后一个位置 解析代码 ③力扣69. x 的平方根 解析代码 ④力扣35. 搜索插入位置 解析代码 ⑤力扣852. 山脉数组的峰顶索引 解析代码 ⑥力扣162. 寻找峰值 解析代码…...
SpringBoot对Bean的管理
Bean扫描 Spring中使用标签扫描或者注解 Springboot中没有使用标签或者注解它是怎么扫描的我的controlelr,service等等 核心在于springboot启动类中的SpringBootApplication注解 此注解其实是一个组合注解 它组合了一个ComponentScan注解,相当于在启…...
体验 AutoGen Studio - 微软推出的友好多智能体协作框架
体验 AutoGen Studio - 微软推出的友好多智能体协作框架 - 知乎 最近分别体验了CrewAI、MetaGPT v0.6、Autogen Studio,了解了AI Agent 相关的知识。 它们的区别 可能有人要问:AutoGen我知道,那Autogen Studio是什么? https://g…...
超简单的正则表达式从入门到精通
正则表达式,又称规则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。 概念 正则表达式是对字…...
webpack常用配置
1.webpack概念 本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 …...
nodejs学习计划--(六)包管理工具
包管理工具 1. 介绍 包是什么 『包』英文单词是 package ,代表了一组特定功能的源码集合包管理工具 管理『包』的应用软件,可以对「包」进行 下载安装 , 更新 , 删除 , 上传 等操作 借助包管理工具,可以快…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
