赞
踩
本文基于FFmpeg,使用Qt制作了一个极简的视频播放器. 如上所示:
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.csdn.net/logani/article/details/127233337
版本信息如下:
FFmpeg视频类,主要针对视频文件 (暂不包含音频):设计用来调用FFmpeg视频相关API
,编解码以及各式转换等
1. 视频打开(获取相关信息):
AVFormatContext
,视频的上下文格式,里面包含视频的各种信息AVFormatContext
获取视频流编号和编码器idcodec_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 视频格式的转换等
在主窗体里创建播放线程:QThread * m_PlayThread;
FFmpegVideo
移动到线程中开始信号
和 FFmpegVideo
的播放槽函数
还可以直接将
FFmpegVideo
设计为继承QThead,将视频播放的代码放到run()
函数即可
新建一个窗口,继承QWidget
update()
paintEvent
窗体重绘函数,将槽函数接收到的图片画在界面上drawImage
需要绑定
FFmpeg
发送图片的信号 和界面
中接收图片的槽函数
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 = avformat_alloc_context(); // 开辟空间 int avformat_open_result = avformat_open_input(&avformat_context, filename.toStdString().c_str(), nullptr, nullptr); if (avformat_open_result != 0){ av_strerror(avformat_open_result, error_info, 100); qDebug()<<QString("获取视频文件格式失败 %1").arg(error_info); return false; }; // 获取视频流 int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, nullptr); if (avformat_find_stream_info_result < 0){ av_strerror(avformat_find_stream_info_result, error_info, 100); qDebug()<<QString("获取音视频流失败 %1").arg(error_info); } av_stream_index = -1; for (quint8 i = 0; i < avformat_context->nb_streams; i++){ //循环遍历每一流找到视频流 if (avformat_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){ av_stream_index = i; // 则streams[av_stream_index]是视频流 break; } } if (av_stream_index == -1){ qDebug()<<QString("没有找到视频流"); return false; } // 获取解码器 const AVCodec* avcodec = avcodec_find_decoder(avformat_context->streams[av_stream_index]->codecpar->codec_id); if (avcodec == nullptr){ qDebug()<<QString("没有找到视频解码器"); return false; } avcodec_context = avcodec_alloc_context3(avcodec); if (avcodec_parameters_to_context(avcodec_context, avformat_context->streams[av_stream_index]->codecpar) < 0){ qDebug()<<"解码器拷贝参数失败"; return false; } int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec, nullptr); if (avcodec_open2_result != 0){ av_strerror(avformat_find_stream_info_result, error_info, 100); qDebug()<<QString("打开解码器失败 %1").arg(error_info); return false; } /// 视频信息输出 AVRational framerate = avcodec_context->framerate; this->m_fps = framerate.num / framerate.den; qDebug()<<"帧率"<<this->m_fps; qDebug()<<"视频详细信息输出"; qDebug()<<"视频时长/s"<<avformat_context->duration/1000000; qDebug()<<QString("视频分辨率%1x%2").arg(avcodec_context->width).arg(avcodec_context->height); return true; } void FFmpegVideo::play(){ // 初始化临时变量 AVPacket* av_packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))); AVFrame *pFramein = av_frame_alloc(); //输入和输出的帧数据 AVFrame *pFrameRGB = av_frame_alloc(); uint8_t * pOutbuffer = static_cast<uint8_t *>(av_malloc( //缓冲区分配内存 static_cast<quint64>( av_image_get_buffer_size(AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height, 1)))); // 初始化缓冲区 av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, pOutbuffer, AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height, 1); // 格式转换 SwsContext* pSwsContext = sws_getContext(avcodec_context->width, // 输入宽 avcodec_context->height, // 输入高 avcodec_context->pix_fmt, // 输入格式 avcodec_context->width, // 输出宽 avcodec_context->height, // 输出高 AV_PIX_FMT_RGB32, // 输出格式 SWS_BICUBIC, ///todo nullptr, nullptr, nullptr); int ret=0; this->m_stop = false; this->m_frame_id = 0; // 开始循环 while (m_stop == false){ //从视频文件上下文中读取包--- 有数据就一直读取 if (av_read_frame(avformat_context, av_packet) >= 0){ //解码什么类型流(视频流、音频流、字幕流等等...) if (av_packet->stream_index == av_stream_index){ avcodec_send_packet(avcodec_context, av_packet); // 解码 ret = avcodec_receive_frame(avcodec_context,pFramein); // 获取解码输出 if (ret == 0){ sws_scale(pSwsContext, //图片格式的转换 static_cast<const uint8_t* const*>(pFramein->data), pFramein->linesize, 0, avcodec_context->height, pFrameRGB->data, pFrameRGB->linesize); QImage *tmpImg = new QImage(static_cast<uchar *>(pOutbuffer), avcodec_context->width, avcodec_context->height, QImage::Format_RGB32); QImage image = tmpImg->copy(); this->m_frame_id++; //计数第几帧 emit sig_SendOneFrame(image); //发送图片信号 QThread::msleep(quint32(1000/this->m_fps)); //延时操作 1秒显示fps帧 qDebug()<<QString("当前遍历第 %1 帧").arg(m_frame_id); } } } else{ qDebug()<<"播放完毕"; this->m_frame_id = 0; m_stop = true; } av_packet_unref(av_packet); } }
qwidget_playvideo.h
#ifndef QWIDGET_PLAYVIDEO_H #define QWIDGET_PLAYVIDEO_H #include <QWidget> class QWidget_PlayVideo : public QWidget { Q_OBJECT public: explicit QWidget_PlayVideo(QWidget *parent = nullptr); QImage image; void paintEvent(QPaintEvent *); ///< 窗体重绘 signals: public slots: void slot_RecvOneFrame(QImage im); ///< 接收一张图片 }; #endif // QWIDGET_PLAYVIDEO_H
qwidget_playvideo.cpp
#include "qwidget_playvideo.h" #include <QPainter> #include <QDebug> QWidget_PlayVideo::QWidget_PlayVideo(QWidget *parent) : QWidget(parent) { } void QWidget_PlayVideo::slot_RecvOneFrame(QImage im){ //qDebug()<<"slot_RecvOneFrame"; this->image = im; this->update(); //刷新界面 } void QWidget_PlayVideo::paintEvent(QPaintEvent *) { QPainter painter(this); if(!this->image.isNull()){ //不为空则刷新 painter.drawImage(QRect(0, 0, this->width(), this->height()), this->image); } }
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QPainter> #include <QDebug> #include <QFileDialog> #include <QString> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // 1. 初始化界面相关 m_Widget_PlayVideo = new QWidget_PlayVideo(this); this->setCentralWidget(m_Widget_PlayVideo); connect(ui->action_open, &QAction::triggered, this, &MainWindow::openVideo); // 2. 初始化视频 m_FFmpegVideo = new FFmpegVideo(); connect(m_FFmpegVideo, SIGNAL(sig_SendOneFrame(QImage)), // 视频发送 绑定 播放界面的接收 m_Widget_PlayVideo, SLOT(slot_RecvOneFrame(QImage)), Qt::AutoConnection); // 3. 初始化播放线程 m_PlayThread = new QThread(this); m_FFmpegVideo->moveToThread(m_PlayThread); // 移动到线程中 connect(m_PlayThread, SIGNAL(started()), // 播放线程的开始信号 绑定 播放函数 实现异步 m_FFmpegVideo, SLOT(play()), Qt::AutoConnection); } MainWindow::~MainWindow() { if(m_PlayThread->isRunning()){ m_PlayThread->quit(); m_PlayThread->wait(); } delete m_PlayThread; delete ui; } void MainWindow::openVideo() { QString filePath = QFileDialog::getOpenFileName(this, QObject::tr("Open File"), nullptr, // QDir::homePath(), QObject::tr("mp4 (*.mp4) ;; All Files (*)")); if (!filePath.isEmpty()) { m_FFmpegVideo->loadVideoFile(filePath); m_PlayThread->start(); } }
Qt实现 基于ffmpeg拉流播放视频:https://blog.csdn.net/c_shell_python/article/details/109699033
FFmpeg音视频编解码 (详细分析以及Qt代码示例):https://blog.csdn.net/m0_61745661/category_11741735.html
基于Microsoft Visual Studio2019环境编写ffmpeg视频解码代码:https://blog.csdn.net/CHYabc123456hh/article/details/125274785
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。