Qt开发 系列文章 - FFmpeg-Player(十七)
前言
Qt进行播放器设计,采用其自带的多媒体模块QMediaPlayer设计时,其底层操作系统提供的解码器有些格式不一定支持,且解码效果有限。这时我们一般采用第三方音视频解码器库进行操作,这里推荐的是FFmpeg库,用的人比较多,采用纯C编写,保证高可移植性和编解码质量,且支持多种音频和视频格式,提供了录制、转换以及流化音视频的完整解决方案。在Qt平台上开发涉及FFmpeg的应用程序,需要将FFmpeg库集成到你的Qt项目中,并编写代码来利用FFmpeg的功能进行音视频处理,本篇文章将在Qt平台上利用FFmpeg实现视频、视频流播放。
一、FFmpeg
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序,它采用LGPL或GPL许可证,提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。ffmpeg_百度百科 (baidu.com)
FFmpeg具有强大的功能,包括但不限于视频采集、视频格式转换、视频抓图、视频水印、音频提取、视频压缩、流媒体处理等等。
FFmpeg框架的基本组成包括AVFormat、AVCodec、AVFilter、AVDevice、AVUtil、libavdevice等模块库。
FFmpeg的主要工作流程相对简单,具体如下:读取输入源、进行音视频的解封装、解码每一帧音视频数据、编码每一帧音视频数据、进行音视频的重新封装、输出到目标。
凭借其丰富的功能和高效的性能,FFmpeg被广泛应用于多个领域,如直播类:音视频会议、教育直播、娱乐/游戏直播等;短视频类:抖音、快手等;网络视频类:优酷、腾讯视频、爱奇艺等;音视频通话:微信、QQ等实时互动软件;视频监控类:摄像头监控等。
用户需要根据自己的操作系统选择相应的FFmpeg安装方法,可以访问官网下载 FFmpeg,选择不同的版本进行安装。
关于ffmpeg 命令使用介绍可以参考Windows环境 ffmpeg 命令使用介绍-优快云博客写的挺详细的。
以上只是粗略介绍了下FFmpeg的功能、构成框架、工作流程、应用领域、下载安装,后面会有文章详细介绍,下面将介绍Qt平台上采用FFmpeg实现视频播放。
二、实现方式
1.效果演示
动画效果演示,如下所示。
可跟随全屏播放,效果如下。
打开界面上的监控按钮,效果如下。
视频采集画面动画效果演示,如下所示。
打开1#播放,可以输入视频地址,可以输入是本地视频目录地址、也可以是网络视频流地址,效果如下。
其它几号视频窗口操作可在画面上设置,效果如下。
因CSND上传视频不能超过5M,所以截成多个图片。
2.代码实现
代码比较多,给出主要关键实现代码,项目工程实现方式及全部代码见文末链接处。
1.添加ffmpng模块
在Qt项目上添加一个ffmpng.pri模块文件,具体实现如下。
2.ffmpng功能模块
基于Qt,依靠FFmpeg内核驱动,通过代码自建一个需要视频播放窗口FFmpegWidget,大小可以自己定义,代码如下(示例)。
#include "ffmpeg.h"
//实时视频显示窗体类
FFmpegWidget::FFmpegWidget(QWidget *parent) : QWidget(parent)
{
thread = new FFmpegThread(this);
image = QImage();
connect(thread, SIGNAL(receiveImage(QImage)), this, SLOT(updateImage(QImage)));
QString ImagePath = "images/video.jpeg";
initWidget(ImagePath);
}
FFmpegWidget::~FFmpegWidget()
{
close();
}
void FFmpegWidget::initWidget(QString str)
{
image = QImage(str);
this->update();
}
void FFmpegWidget::paintEvent(QPaintEvent *)
{
if (image.isNull()) {
return;
}
//qDebug() << TIMEMS << "paintEvent" << objectName();
QPainter painter(this);
#if 1
//image = image.scaled(this->size(), Qt::KeepAspectRatio);
//按照比例自动居中绘制
int pixX = rect().center().x() - image.width() / 2;
int pixY = rect().center().y() - image.height() / 2;
QPoint point(pixX, pixY);
painter.drawImage(point, image);
#else
painter.drawImage(this->rect(), image);
#endif
}
void FFmpegWidget::updateImage(const QImage &image)
{
//this->image = image.copy();
this->image = image;
this->update();
}
void FFmpegWidget::open()
{
//qDebug() << TIMEMS << "open video" << objectName();
//clear();
QString ImagePath = "images/video.jpeg";
initWidget(ImagePath);
thread->play();
}
void FFmpegWidget::pause()
{
thread->pause();
}
void FFmpegWidget::next()
{
thread->next();
}
void FFmpegWidget::close()
{
if (thread->isRunning())
thread->stop();
//QString ImagePath = "images/video.jpeg";
//QTimer::singleShot(1, this, SLOT(initWidget(ImagePath)));
}
void FFmpegWidget::clear()
{
image = QImage();
update();
}
FFmpegThread::FFmpegThread(QObject *parent) : QThread(parent)
{
setObjectName("FFmpegThread