Qt 基于FFmpeg的视频播放器 - QtFFmpegPlayer
引言
本文基于FFmpeg,使用Qt制作了一个极简的视频播放器. 如上所示:
- FFmpeg版本信息:
ffmpeg-n7.0-latest-win64-lgpl-shared-7.0
FFmpeg
是一个开源的跨平台音视频处理工具,它提供了音视频编解码、格式转换、流媒体处理等功能。FFmpeg可以在命令行中使用,也可以通过API集成到其他应用程序中使用
。FFmpeg支持众多音视频编码格式,如MP3、AAC、AC3、H.264、MPEG-4等。它可以将不同格式的音视频文件转换为其他格式,从而满足不同设备和平台的需求。除了转换格式,FFmpeg还可以进行音视频的剪切、合并、裁剪、旋转等操作。它可以提取音频或视频流,并且支持添加字幕、水印等特效。在流媒体处理方面,FFmpeg可以通过RTMP、HLS、UDP等协议进行直播推流和播放。它可以将本地音视频流推送到流媒体服务器,也可以从流媒体服务器拉取音视频流进行播放。FFmpeg是一个功能强大且灵活的音视频处理工具,被广泛应用于视频编辑、媒体转换、流媒体服务等领域。它的开源特性使得开发者可以自由定制和扩展功能,同时也极大地方便了用户在不同平台上进行音视频处理。
一、设计思路
参考以下博客:
【Qt+FFmpeg】解码播放本地视频(一):https://blog.youkuaiyun.com/logani/article/details/127233337
版本信息如下:
1.1 FFmpegVideo类
FFmpeg视频类,主要针对视频文件 (暂不包含音频):设计用来调用FFmpeg视频相关API
,编解码以及各式转换等
- 设计一些变量存储视频信息,主要涉及视频的读取、播放等
- 设计函数进行视频处理 (解码、转换等) - 封装FFmpeg API
1.2 视频打开与播放函数 (本文重点)
1. 视频打开(获取相关信息):
- 首先获取
AVFormatContext
,视频的上下文格式,里面包含视频的各种信息 - 通过
AVFormatContext
获取视频流编号和编码器idcodec_id
- 根据编码器id获取相应的解码器
AVCodec
,初始化编解码器AVCodecContext
- 通过编解码器
AVCodecContext
获取视频帧率m_fps
2. 视频播放 (循环解码即可):
- 变量以及缓冲区初始化
- 使用
av_read_frame
顺序读取AVFormatContext
中一帧数据AVPacket
- 如果是视频流即用
avcodec_send_packet
解码,avcodec_receive_frame
获取解码输出 - 使用
sws_scale
进行将一帧图片转换为RGB格式,赋值到QImage
- 发送赋值后的
QImage
到相应的显示窗口 (信号和槽) - 根据视频帧率
m_fps
,使用QThread::msleep
延时 (或使用QTimer
等实现延时效果)
/todo 视频格式的转换等
1.3 播放线程
在主窗体里创建播放线程:QThread * m_PlayThread;
- 初始化播放线程,将
FFmpegVideo
移动到线程中 - 绑定线程的
开始信号
和FFmpegVideo
的播放槽函数
- 启动线程即可播放视频 (需要先读取视频文件)
还可以直接将
FFmpegVideo
设计为继承QThead,将视频播放的代码放到run()
函数即可
1.4 播放窗口
新建一个窗口,继承QWidget
- 创建接收图片的槽函数,保存接收的图片,然后刷新界面
update()
- 重新实现
paintEvent
窗体重绘函数,将槽函数接收到的图片画在界面上drawImage
需要绑定
FFmpeg
发送图片的信号 和界面
中接收图片的槽函数
二、核心源码
2.1 FFmpegVideo类
ffmpegvideo.h
#ifndef FFMPEGVIDEO_H
#define FFMPEGVIDEO_H
#include <QString>
#include <QObject>
#include <QImage>
// FFmpeg头文件
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include "libswresample/swresample.h"
}
class FFmpegVideo : public QObject
{
Q_OBJECT
public:
/**
* @brief FFmpegVideo构造函数
*/
FFmpegVideo();
~FFmpegVideo();
public: // 值
QString m_filename; ///< 文件名
AVFormatContext* avformat_context; ///< 视频文件上下文格式
AVCodecContext* avcodec_context; ///< 编解码器上下文格式
int av_stream_index; ///< 保存视频流的索引
int m_fps; ///< 帧率
int m_frame_id; ///< 当前帧id
bool m_stop; ///< 是否暂停
public: // 函数
/**
* @brief 加载视频
* @param filename
* @return
*/
bool loadVideoFile(QString filename);
public slots:
/**
* @brief 播放视频
*/
void play();
signals:
void sig_SendOneFrame(QImage image); ///< 发送一帧
};
#endif // FFMPEGVIDEO_H
ffmpegvideo.cpp
#include "ffmpegvideo.h"
#include <QDebug>
#include <QThread>
FFmpegVideo::FFmpegVideo(){
}
FFmpegVideo::~FFmpegVideo(){
}
bool FFmpegVideo::loadVideoFile(QString filename){
this->m_filename = filename;
char* error_info = new char[32]; //异常信息
// 获取视频文件格式
avformat_context = a