FFmpeg 简单的视频播放例子(Qt)

一、简述

记--直接在FFmpeg官网下载已经编译好的库(也可以自己用源码编译),并在Qt程序中引用并使用,例子知识简单的播放,待完善。

例子代码和FFmpeg相关库文件:https://lanzoux.com/b0c8c02qb 密码:3wdn

二、下载FFmpeg库(也可以自己用源码编译)

官网:https://ffmpeg.zeranoe.com/builds/

三、 在工程中引用FFmpeg

       3.1 使用QtCreator创建一个“Qt Widgts Application”

3.2 将编译相关文件放到工程目录下

3.2.1 在工程目录下创建ffmpeg目录,以统一存放ffmpeg相关文件

3.2.2 解压ffmpeg-4.2.2-win32-dev.zip和ffmpeg-4.2.2-win32-shared.zip

3.2.3 将相关文件拷贝到 3.2.1步骤创建的目录下

将ffmpeg-4.2.2-win32-dev目录下的include整个目录拷贝到ffmpeg目录下

将ffmpeg-4.2.2-win32-dev的lib目录下的所有.dll.a文件拷贝到ffmpeg/lib目录下

3.3 在项目中添加引用

3.3.1 在.pro文件添加头文件路径和lib路径

INCLUDEPATH += $$PWD/ffmpeg/include
LIBS += $$PWD/ffmpeg/lib/libavcodec.dll.a \$$PWD/ffmpeg/lib/libavfilter.dll.a \$$PWD/ffmpeg/lib/libavformat.dll.a \$$PWD/ffmpeg/lib/libavutil.dll.a \$$PWD/ffmpeg/lib/libswscale.dll.a

3.3.2  在代码中添加头文件引用          

四、简单测试FFmpeg是否可用 (例子1)

          4.1 编写代码输出FFmpeg的配置信息和版本信息         

4.2 编译

4.3 运行

因为是动态编译,在运行前需要将程序所需要的动态库拷贝到程序的同级目录,或者是将动态库路径设置到环境变量。

例子是debug版本,就将动态库拷贝到debug目录

五、播放视频(例子2 )

主要代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTime>
#include <QIcon>
#include <QPixmap>
#include <QFileDialog>
#include <QMessageBox>
#include <QSlider>
#include <QComboBox>//包含ffmpeg相关头文件,并告诉编译器以C语言方式处理
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 <libavutil/imgutils.h>
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{//输出FFmpeg版本信息qDebug("ffmpeg versioin is: %u", avcodec_version());ui->setupUi(this);//初始化init();
}MainWindow::~MainWindow()
{delete ui;
}//初始化
void MainWindow::init()
{mVideoWidth = this->width();mVideoHeight = this->height();//设置视频label的宽高为窗体宽高ui->labelVideo->setGeometry(0, 0, mVideoWidth, mVideoHeight);//初始化播放速度mPlaySpdVal = PLAY_SPD_10;//初播放状态weimVideoPlaySta = Video_PlayFinish;/*****在状态栏添加一个用来显示视频路径的label************///用于显示已经选择的视频文件路径mVideoFile = new QLabel(this);mVideoFile->setToolTip("视频文件");ui->statusBar->addWidget(mVideoFile);/*****在工具栏添加选择动作************///创建一个选择视频文件动作QAction* pActionSelectVideoFile = new QAction(QIcon(QPixmap(":/selectVideo.png")), "选择");//设置鼠标悬停提示文本pActionSelectVideoFile->setToolTip("选择视频文件");//将动作添加到工具栏ui->mainToolBar->addAction(pActionSelectVideoFile);//连接动作的点击事件QObject::connect(pActionSelectVideoFile, SIGNAL(triggered(bool)), this, SLOT(on_selectVideoFile()));/*****在工具栏添加播放/暂停动作************///创建一个播放/暂停视频动作mActionPlayCtrl = new QAction(QIcon(QPixmap(":/play.png")), "播放");//设置鼠标悬停提示文本mActionPlayCtrl->setToolTip("播放视频");//将动作添加到工具栏ui->mainToolBar->addAction(mActionPlayCtrl);//连接动作的点击事件QObject::connect(mActionPlayCtrl, SIGNAL(triggered(bool)), this, SLOT(on_videoPlayCtrl()));/*****在工具栏添加停止动作************///创建一个停止播放视频动作QAction* pActionStopPlay = new QAction(QIcon(QPixmap(":/stop.png")), "停止");//设置鼠标悬停提示文本pActionStopPlay->setToolTip("停止播放");//将动作添加到工具栏ui->mainToolBar->addAction(pActionStopPlay);//连接动作的点击事件QObject::connect(pActionStopPlay, SIGNAL(triggered(bool)), this, SLOT(on_stopPlayVideo()));/*****在工具栏添加窗体大小适配视频宽高动作************///创建一个停止播放视频动作QAction* pActionAutoSize = new QAction(QIcon(QPixmap(":/autoSize.png")), "自适应");//设置鼠标悬停提示文本pActionAutoSize->setToolTip("窗体自适应视频宽高");//将动作添加到工具栏ui->mainToolBar->addAction(pActionAutoSize);//连接动作的点击事件QObject::connect(pActionAutoSize, SIGNAL(triggered(bool)), this, SLOT(on_autoSize()));//添加分隔符ui->mainToolBar->addSeparator();//添加播放速度下拉选择QStringList plsySpdItems;plsySpdItems << "0.5";plsySpdItems << "0.8";plsySpdItems << "1";plsySpdItems << "1.2";plsySpdItems << "1.5";plsySpdItems << "2";plsySpdItems << "3";plsySpdItems << "4";QComboBox* comboBoxPlaySpd = new QComboBox();comboBoxPlaySpd->setToolTip("播放速度");comboBoxPlaySpd->addItems(plsySpdItems);//添加倍速选项comboBoxPlaySpd->setCurrentIndex(2);//设置默认速度1comboBoxPlaySpd->setEditable(false);//设置为不可编辑//设置样式//comboBoxPlaySpd->setStyleSheet("QComboBox { border:1px solid black; min-width: 30px;} QComboBox QAbstractItemView::item {min-height:20px; background-color: rgb(0, 0, 0);} QFrame { border: 1px solid black; }");ui->mainToolBar->addWidget(comboBoxPlaySpd);QObject::connect(comboBoxPlaySpd, SIGNAL(currentIndexChanged(int)), this, SLOT(on_playSpdChange(int)));//添加分隔符ui->mainToolBar->addSeparator();//添加播放声音滑动条到工具栏QString sliderStyle = "QSlider::groove:horizontal{ \height: 6px; \background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: , stop: 0 rgb(124, 124, 124), stop: 1.0 rgb(72, 71, 71)); \} \QSlider::handle:horizontal { \width: 5px; \background: rgb(0, 160, 230); \margin: -6px 0px -6px 0px; \border-radius: 9px; \}";QSlider* slider = new QSlider(Qt::Orientation::Horizontal, this);slider->setToolTip("音量");slider->setMaximumWidth(100);//设置最大值slider->setRange(0, 100);//设置滑动范围slider->setStyleSheet(sliderStyle);//设置样式ui->mainToolBar->addWidget(slider);//连接动作的滑动事件QObject::connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(on_VolumeChange(int)));/*****在工具栏添加一个用来显示音量的label************/mCurVolume = new QLabel("音量60");mCurVolume->setToolTip("当前音量值");ui->mainToolBar->addWidget(mCurVolume);}//延时, 不能直接sleep延时,UI主线程不能直接被阻塞,不然会有问题的
void delay(int ms)
{QTime stopTime;stopTime.start();//qDebug()<<"start:"<<QTime::currentTime().toString("HH:mm:ss.zzz");while(stopTime.elapsed() < ms)//stopTime.elapsed()返回从start开始到现在的毫秒数{QCoreApplication::processEvents();}//qDebug()<<"stop :"<<QTime::currentTime().toString("HH:mm:ss.zzz");
}//重新设置窗体和视频Label的宽高
void MainWindow::resizeWindow(int width, int height)
{if(width<1){width = this->width();}if(height<1){height = this->height();}this->setGeometry(100, 100, width, height);ui->labelVideo->setGeometry(0, 0, width, height);
}//窗体变化事件
void MainWindow::resizeEvent(QResizeEvent* event)
{Q_UNUSED(event);//让视频Label跟随窗体变化ui->labelVideo->resize(this->size());
}//使用FFmpeg播放视频
int MainWindow::playVideo(char* videoPath)
{unsigned char* buf;int isVideo = -1;int ret, gotPicture;unsigned int i, streamIndex = 0;AVCodec *pCodec;AVPacket *pAVpkt;AVCodecContext *pAVctx;AVFrame *pAVframe, *pAVframeRGB;AVFormatContext* pFormatCtx;struct SwsContext* pSwsCtx;//创建AVFormatContextpFormatCtx = avformat_alloc_context();//初始化pFormatCtxif (avformat_open_input(&pFormatCtx, videoPath, NULL, NULL) != 0){qDebug("avformat_open_input err.");return -1;}//获取音视频流数据信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0){avformat_close_input(&pFormatCtx);qDebug("avformat_find_stream_info err.");return -2;}//找到视频流的索引for (i = 0; i < pFormatCtx->nb_streams; i++){if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){streamIndex = i;isVideo = 0;break;}}//没有视频流就退出if (isVideo == -1){avformat_close_input(&pFormatCtx);qDebug("nb_streams err.");return -3;}//获取视频流编码pAVctx = avcodec_alloc_context3(NULL);;//查找解码器avcodec_parameters_to_context(pAVctx, pFormatCtx->streams[streamIndex]->codecpar);pCodec = avcodec_find_decoder(pAVctx->codec_id);if (pCodec == NULL){avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);qDebug("avcodec_find_decoder err.");return -4;}//初始化pAVctxif (avcodec_open2(pAVctx, pCodec, NULL) < 0){avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);qDebug("avcodec_open2 err.");return -5;}//初始化pAVpktpAVpkt = (AVPacket *)av_malloc(sizeof(AVPacket));//初始化数据帧空间pAVframe = av_frame_alloc();pAVframeRGB = av_frame_alloc();//创建图像数据存储buf//av_image_get_buffer_size一帧大小buf = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1));av_image_fill_arrays(pAVframeRGB->data, pAVframeRGB->linesize, buf, AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1);//根据视频宽高重新调整窗口大小 视频宽高 pAVctx->width, pAVctx->heightresizeWindow(pAVctx->width, pAVctx->height);//初始化pSwsCtxpSwsCtx = sws_getContext(pAVctx->width, pAVctx->height, pAVctx->pix_fmt, pAVctx->width, pAVctx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);//循环读取视频数据for(;;){if(mVideoPlaySta == Video_Playing)//正在播放{if(av_read_frame(pFormatCtx, pAVpkt) >= 0)//读取一帧未解码的数据{//如果是视频数据if (pAVpkt->stream_index == (int)streamIndex){//解码一帧视频数据ret = avcodec_decode_video2(pAVctx, pAVframe, &gotPicture, pAVpkt);if(ret < 0){qDebug("Decode Error.\n");continue;}if(gotPicture){sws_scale(pSwsCtx, (const unsigned char* const*)pAVframe->data, pAVframe->linesize, 0, pAVctx->height, pAVframeRGB->data, pAVframeRGB->linesize);QImage img((uchar*)pAVframeRGB->data[0], pAVctx->width, pAVctx->height, QImage::Format_RGB32);ui->labelVideo->setPixmap(QPixmap::fromImage(img));delay(mPlaySpdVal);//播放延时}}av_packet_unref(pAVpkt);}else{break;}}else if(mVideoPlaySta == Video_PlayFinish)//播放结束{break;}else//暂停{delay(300);}}//释放资源sws_freeContext(pSwsCtx);av_frame_free(&pAVframeRGB);av_frame_free(&pAVframe);avcodec_close(pAVctx);avformat_close_input(&pFormatCtx);mVideoPlaySta = Video_PlayFinish;qDebug()<<"play finish!";return 0;
}//选择视频文件
void MainWindow::on_selectVideoFile()
{QString path = QFileDialog::getOpenFileName(this,"选择视频文件","D:\\Test\\Video\\","WMV视频(*.wmv);;MP4视频(*.mp4);;AVI视频(*.avi)");if(!path.isEmpty()){mVideoFile->setText(path);on_videoPlayCtrl();}
}//视频播放/暂停控制
void MainWindow::on_videoPlayCtrl()
{QString videoFilePath = mVideoFile->text();if(videoFilePath.isEmpty()){return;}//判断文件是都存在(以防被删除了)
//    QFileInfo fileInfo(videoFilePath);
//    if(fileInfo.isFile())
//    {
//        QMessageBox::information(this, "提示", "找不到文件:"+videoFilePath);
//        return;
//    }//播放和暂停切换if(mActionPlayCtrl->text() == "播放"){mActionPlayCtrl->setText("暂停");mActionPlayCtrl->setToolTip("暂停视频");mActionPlayCtrl->setIcon(QIcon(QPixmap(":/pause.png")));if(mVideoPlaySta == Video_PlayFinish){mVideoPlaySta = Video_Playing;playVideo(videoFilePath.toLocal8Bit().data());on_stopPlayVideo();}else{mVideoPlaySta = Video_Playing;}}else{mActionPlayCtrl->setText("播放");mActionPlayCtrl->setToolTip("播放视频");mActionPlayCtrl->setIcon(QIcon(QPixmap(":/play.png")));if(mVideoPlaySta == Video_Playing){mVideoPlaySta = Video_PlayPause;}}
}//停止播放视频
void MainWindow::on_stopPlayVideo()
{mVideoPlaySta = Video_PlayFinish;mActionPlayCtrl->setText("播放");mActionPlayCtrl->setToolTip("播放视频");mActionPlayCtrl->setIcon(QIcon(QPixmap(":/play.png")));
}//窗体自适应视频宽高
void MainWindow::on_autoSize()
{resizeWindow(mVideoWidth, mVideoHeight);
}//窗体关闭事件
void MainWindow::closeEvent(QCloseEvent* event)//窗体关闭事件
{//没有在播放视频时直接退出if(mVideoPlaySta != Video_PlayFinish){if(QMessageBox::Yes == QMessageBox::information(this, "提示", "确认关闭?", QMessageBox::Yes, QMessageBox::No)){mVideoPlaySta = Video_PlayFinish;//event->accept();}else{event->ignore();//忽略,不关闭}}
}//播放音量变化
void MainWindow::on_VolumeChange(int volume)
{mCurVolume->setText("音量"+QString::number(volume));
}//播放速度变化
void MainWindow::on_playSpdChange(int spdVal)
{switch(spdVal){case 0://0.5mPlaySpdVal = PLAY_SPD_05;break;case 1://0.8mPlaySpdVal = PLAY_SPD_08;break;case 2://1mPlaySpdVal = PLAY_SPD_10;break;case 3://1.2mPlaySpdVal = PLAY_SPD_12;break;case 4://1.5mPlaySpdVal = PLAY_SPD_15;break;case 5://2mPlaySpdVal = PLAY_SPD_20;break;case 6://3mPlaySpdVal = PLAY_SPD_30;break;case 7://4mPlaySpdVal = PLAY_SPD_40;break;default:break;}
}

5.1 播放.wmv视频、.mp4视频、播放avi视频下效果。

其它视频格式尚未测试。

六、查看接口变更信息

请下载ffmpeg源码或者是在github上查看。在doc/APIchanges

         

FFmpeg 简单的视频播放例子(Qt)相关推荐

  1. VLC实现简单的视频播放器——Qt

    前言 VideoLAN 既是一个项目组,也是一家非盈利组织.它开发了很多项目,有适合所有用户的(例如:VLC.VLMC),也有适合专业人士的(例如:DVBlast.Multicat),更有适合开发人员 ...

  2. 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  3. 最简单的基于FFmpeg的移动端例子:Windows Phone HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  4. 最简单的基于FFmpeg的移动端例子附件:IOS自带播放器

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  5. 最简单的基于FFmpeg的移动端例子:IOS 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  6. 最简单的基于FFmpeg的移动端例子:IOS 视频解码器

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  7. 最简单的基于FFmpeg的移动端例子:IOS HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  8. 最简单的基于FFmpeg的移动端例子附件:SDL Android HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  9. 最简单的基于FFmpeg的移动端例子:Android 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

最新文章

  1. python加上子类的特性_Python--面向对象三大特性
  2. python 函数参数类型检查
  3. Spring - 父容器与子容器
  4. Windows Phone中Wallet钱包的使用
  5. oracle静默安装集群,静默安装Oracle数据库10g篇
  6. git 提交遇到error:fail to push some refer to 远程地址(url)
  7. 系统架构师学习笔记-数据库系统
  8. 教你怎样做好计划 将愿望慢慢实现
  9. makefile语法_Makefile的语法
  10. 解决Tomcat catalina.out 不断成长导致档案过大的问题
  11. 如何查看Linux系统的带宽流量
  12. ARMv8-A Generic Interrupt Controller(GIC)
  13. OKR怎么写?100个OKR案例模板
  14. 计算机科学cs50_世界顶尖CS大学提供500门免费计算机科学课程
  15. c语言中罗马字母数字,罗马数字转整数C语言实现
  16. Elasticsearch查询数据总条数1万条限制
  17. 瑞典皇家理工学院计算机科学世界排名,瑞典皇家理工学院世界排名
  18. Python:什么叫自定义模块?
  19. 【每日一练】56—CSS实现一款实用的响应式卡片悬停效果
  20. Acwing 2944. 回家的路

热门文章

  1. 多核程序设计 linux,多核程序设计Linux多线程编程.ppt
  2. IEEE754数据生成 (附C++代码)
  3. 电子商务B2C的新动态 - 个性化、专业化定制网站已经平民化
  4. FANUC机器人的镜像备份操作及U盘格式化具体步骤
  5. 仓央嘉措 ❤《见与不见》的全文 ❤
  6. 计算机在F1的应用,电脑入门,F1到F12有什么用
  7. assert利用蚁剑登录
  8. android 9华为p20,华为P20PRO升级EMUI9.0(安卓9.0)的一些感受
  9. 从Solidworks中导出的.wrl文件无法打开?
  10. Python获取阿里云产品云监控数据指标