当前位置:   article > 正文

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

ffmpeg播放视频

                                                    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路径

          

  1. INCLUDEPATH += $$PWD/ffmpeg/include
  2. LIBS += $$PWD/ffmpeg/lib/libavcodec.dll.a \
  3. $$PWD/ffmpeg/lib/libavfilter.dll.a \
  4. $$PWD/ffmpeg/lib/libavformat.dll.a \
  5. $$PWD/ffmpeg/lib/libavutil.dll.a \
  6. $$PWD/ffmpeg/lib/libswscale.dll.a

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

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

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

 4.2 编译

4.3 运行

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

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

五、播放视频(例子2 )

      主要代码:

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include <QDebug>
  4. #include <QTime>
  5. #include <QIcon>
  6. #include <QPixmap>
  7. #include <QFileDialog>
  8. #include <QMessageBox>
  9. #include <QSlider>
  10. #include <QComboBox>
  11. //包含ffmpeg相关头文件,并告诉编译器以C语言方式处理
  12. extern "C"
  13. {
  14. #include <libavcodec/avcodec.h>
  15. #include <libavformat/avformat.h>
  16. #include <libswscale/swscale.h>
  17. #include <libavdevice/avdevice.h>
  18. #include <libavformat/version.h>
  19. #include <libavutil/time.h>
  20. #include <libavutil/mathematics.h>
  21. #include <libavutil/imgutils.h>
  22. }
  23. MainWindow::MainWindow(QWidget *parent) :
  24. QMainWindow(parent),
  25. ui(new Ui::MainWindow)
  26. {
  27. //输出FFmpeg版本信息
  28. qDebug("ffmpeg versioin is: %u", avcodec_version());
  29. ui->setupUi(this);
  30. //初始化
  31. init();
  32. }
  33. MainWindow::~MainWindow()
  34. {
  35. delete ui;
  36. }
  37. //初始化
  38. void MainWindow::init()
  39. {
  40. mVideoWidth = this->width();
  41. mVideoHeight = this->height();
  42. //设置视频label的宽高为窗体宽高
  43. ui->labelVideo->setGeometry(0, 0, mVideoWidth, mVideoHeight);
  44. //初始化播放速度
  45. mPlaySpdVal = PLAY_SPD_10;
  46. //初播放状态wei
  47. mVideoPlaySta = Video_PlayFinish;
  48. /*****在状态栏添加一个用来显示视频路径的label************/
  49. //用于显示已经选择的视频文件路径
  50. mVideoFile = new QLabel(this);
  51. mVideoFile->setToolTip("视频文件");
  52. ui->statusBar->addWidget(mVideoFile);
  53. /*****在工具栏添加选择动作************/
  54. //创建一个选择视频文件动作
  55. QAction* pActionSelectVideoFile = new QAction(QIcon(QPixmap(":/selectVideo.png")), "选择");
  56. //设置鼠标悬停提示文本
  57. pActionSelectVideoFile->setToolTip("选择视频文件");
  58. //将动作添加到工具栏
  59. ui->mainToolBar->addAction(pActionSelectVideoFile);
  60. //连接动作的点击事件
  61. QObject::connect(pActionSelectVideoFile, SIGNAL(triggered(bool)), this, SLOT(on_selectVideoFile()));
  62. /*****在工具栏添加播放/暂停动作************/
  63. //创建一个播放/暂停视频动作
  64. mActionPlayCtrl = new QAction(QIcon(QPixmap(":/play.png")), "播放");
  65. //设置鼠标悬停提示文本
  66. mActionPlayCtrl->setToolTip("播放视频");
  67. //将动作添加到工具栏
  68. ui->mainToolBar->addAction(mActionPlayCtrl);
  69. //连接动作的点击事件
  70. QObject::connect(mActionPlayCtrl, SIGNAL(triggered(bool)), this, SLOT(on_videoPlayCtrl()));
  71. /*****在工具栏添加停止动作************/
  72. //创建一个停止播放视频动作
  73. QAction* pActionStopPlay = new QAction(QIcon(QPixmap(":/stop.png")), "停止");
  74. //设置鼠标悬停提示文本
  75. pActionStopPlay->setToolTip("停止播放");
  76. //将动作添加到工具栏
  77. ui->mainToolBar->addAction(pActionStopPlay);
  78. //连接动作的点击事件
  79. QObject::connect(pActionStopPlay, SIGNAL(triggered(bool)), this, SLOT(on_stopPlayVideo()));
  80. /*****在工具栏添加窗体大小适配视频宽高动作************/
  81. //创建一个停止播放视频动作
  82. QAction* pActionAutoSize = new QAction(QIcon(QPixmap(":/autoSize.png")), "自适应");
  83. //设置鼠标悬停提示文本
  84. pActionAutoSize->setToolTip("窗体自适应视频宽高");
  85. //将动作添加到工具栏
  86. ui->mainToolBar->addAction(pActionAutoSize);
  87. //连接动作的点击事件
  88. QObject::connect(pActionAutoSize, SIGNAL(triggered(bool)), this, SLOT(on_autoSize()));
  89. //添加分隔符
  90. ui->mainToolBar->addSeparator();
  91. //添加播放速度下拉选择
  92. QStringList plsySpdItems;
  93. plsySpdItems << "0.5";
  94. plsySpdItems << "0.8";
  95. plsySpdItems << "1";
  96. plsySpdItems << "1.2";
  97. plsySpdItems << "1.5";
  98. plsySpdItems << "2";
  99. plsySpdItems << "3";
  100. plsySpdItems << "4";
  101. QComboBox* comboBoxPlaySpd = new QComboBox();
  102. comboBoxPlaySpd->setToolTip("播放速度");
  103. comboBoxPlaySpd->addItems(plsySpdItems);//添加倍速选项
  104. comboBoxPlaySpd->setCurrentIndex(2);//设置默认速度1
  105. comboBoxPlaySpd->setEditable(false);//设置为不可编辑
  106. //设置样式
  107. //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; }");
  108. ui->mainToolBar->addWidget(comboBoxPlaySpd);
  109. QObject::connect(comboBoxPlaySpd, SIGNAL(currentIndexChanged(int)), this, SLOT(on_playSpdChange(int)));
  110. //添加分隔符
  111. ui->mainToolBar->addSeparator();
  112. //添加播放声音滑动条到工具栏
  113. QString sliderStyle = "QSlider::groove:horizontal{ \
  114. height: 6px; \
  115. background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: , stop: 0 rgb(124, 124, 124), stop: 1.0 rgb(72, 71, 71)); \
  116. } \
  117. QSlider::handle:horizontal { \
  118. width: 5px; \
  119. background: rgb(0, 160, 230); \
  120. margin: -6px 0px -6px 0px; \
  121. border-radius: 9px; \
  122. }";
  123. QSlider* slider = new QSlider(Qt::Orientation::Horizontal, this);
  124. slider->setToolTip("音量");
  125. slider->setMaximumWidth(100);//设置最大值
  126. slider->setRange(0, 100);//设置滑动范围
  127. slider->setStyleSheet(sliderStyle);//设置样式
  128. ui->mainToolBar->addWidget(slider);
  129. //连接动作的滑动事件
  130. QObject::connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(on_VolumeChange(int)));
  131. /*****在工具栏添加一个用来显示音量的label************/
  132. mCurVolume = new QLabel("音量60");
  133. mCurVolume->setToolTip("当前音量值");
  134. ui->mainToolBar->addWidget(mCurVolume);
  135. }
  136. //延时, 不能直接sleep延时,UI主线程不能直接被阻塞,不然会有问题的
  137. void delay(int ms)
  138. {
  139. QTime stopTime;
  140. stopTime.start();
  141. //qDebug()<<"start:"<<QTime::currentTime().toString("HH:mm:ss.zzz");
  142. while(stopTime.elapsed() < ms)//stopTime.elapsed()返回从start开始到现在的毫秒数
  143. {
  144. QCoreApplication::processEvents();
  145. }
  146. //qDebug()<<"stop :"<<QTime::currentTime().toString("HH:mm:ss.zzz");
  147. }
  148. //重新设置窗体和视频Label的宽高
  149. void MainWindow::resizeWindow(int width, int height)
  150. {
  151. if(width<1)
  152. {
  153. width = this->width();
  154. }
  155. if(height<1)
  156. {
  157. height = this->height();
  158. }
  159. this->setGeometry(100, 100, width, height);
  160. ui->labelVideo->setGeometry(0, 0, width, height);
  161. }
  162. //窗体变化事件
  163. void MainWindow::resizeEvent(QResizeEvent* event)
  164. {
  165. Q_UNUSED(event);
  166. //让视频Label跟随窗体变化
  167. ui->labelVideo->resize(this->size());
  168. }
  169. //使用FFmpeg播放视频
  170. int MainWindow::playVideo(char* videoPath)
  171. {
  172. unsigned char* buf;
  173. int isVideo = -1;
  174. int ret, gotPicture;
  175. unsigned int i, streamIndex = 0;
  176. AVCodec *pCodec;
  177. AVPacket *pAVpkt;
  178. AVCodecContext *pAVctx;
  179. AVFrame *pAVframe, *pAVframeRGB;
  180. AVFormatContext* pFormatCtx;
  181. struct SwsContext* pSwsCtx;
  182. //创建AVFormatContext
  183. pFormatCtx = avformat_alloc_context();
  184. //初始化pFormatCtx
  185. if (avformat_open_input(&pFormatCtx, videoPath, NULL, NULL) != 0)
  186. {
  187. qDebug("avformat_open_input err.");
  188. return -1;
  189. }
  190. //获取音视频流数据信息
  191. if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
  192. {
  193. avformat_close_input(&pFormatCtx);
  194. qDebug("avformat_find_stream_info err.");
  195. return -2;
  196. }
  197. //找到视频流的索引
  198. for (i = 0; i < pFormatCtx->nb_streams; i++)
  199. {
  200. if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
  201. {
  202. streamIndex = i;
  203. isVideo = 0;
  204. break;
  205. }
  206. }
  207. //没有视频流就退出
  208. if (isVideo == -1)
  209. {
  210. avformat_close_input(&pFormatCtx);
  211. qDebug("nb_streams err.");
  212. return -3;
  213. }
  214. //获取视频流编码
  215. pAVctx = avcodec_alloc_context3(NULL);;
  216. //查找解码器
  217. avcodec_parameters_to_context(pAVctx, pFormatCtx->streams[streamIndex]->codecpar);
  218. pCodec = avcodec_find_decoder(pAVctx->codec_id);
  219. if (pCodec == NULL)
  220. {
  221. avcodec_close(pAVctx);
  222. avformat_close_input(&pFormatCtx);
  223. qDebug("avcodec_find_decoder err.");
  224. return -4;
  225. }
  226. //初始化pAVctx
  227. if (avcodec_open2(pAVctx, pCodec, NULL) < 0)
  228. {
  229. avcodec_close(pAVctx);
  230. avformat_close_input(&pFormatCtx);
  231. qDebug("avcodec_open2 err.");
  232. return -5;
  233. }
  234. //初始化pAVpkt
  235. pAVpkt = (AVPacket *)av_malloc(sizeof(AVPacket));
  236. //初始化数据帧空间
  237. pAVframe = av_frame_alloc();
  238. pAVframeRGB = av_frame_alloc();
  239. //创建图像数据存储buf
  240. //av_image_get_buffer_size一帧大小
  241. buf = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1));
  242. av_image_fill_arrays(pAVframeRGB->data, pAVframeRGB->linesize, buf, AV_PIX_FMT_RGB32, pAVctx->width, pAVctx->height, 1);
  243. //根据视频宽高重新调整窗口大小 视频宽高 pAVctx->width, pAVctx->height
  244. resizeWindow(pAVctx->width, pAVctx->height);
  245. //初始化pSwsCtx
  246. pSwsCtx = sws_getContext(pAVctx->width, pAVctx->height, pAVctx->pix_fmt, pAVctx->width, pAVctx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
  247. //循环读取视频数据
  248. for(;;)
  249. {
  250. if(mVideoPlaySta == Video_Playing)//正在播放
  251. {
  252. if(av_read_frame(pFormatCtx, pAVpkt) >= 0)//读取一帧未解码的数据
  253. {
  254. //如果是视频数据
  255. if (pAVpkt->stream_index == (int)streamIndex)
  256. {
  257. //解码一帧视频数据
  258. ret = avcodec_decode_video2(pAVctx, pAVframe, &gotPicture, pAVpkt);
  259. if(ret < 0)
  260. {
  261. qDebug("Decode Error.\n");
  262. continue;
  263. }
  264. if(gotPicture)
  265. {
  266. sws_scale(pSwsCtx, (const unsigned char* const*)pAVframe->data, pAVframe->linesize, 0, pAVctx->height, pAVframeRGB->data, pAVframeRGB->linesize);
  267. QImage img((uchar*)pAVframeRGB->data[0], pAVctx->width, pAVctx->height, QImage::Format_RGB32);
  268. ui->labelVideo->setPixmap(QPixmap::fromImage(img));
  269. delay(mPlaySpdVal);//播放延时
  270. }
  271. }
  272. av_packet_unref(pAVpkt);
  273. }
  274. else
  275. {
  276. break;
  277. }
  278. }
  279. else if(mVideoPlaySta == Video_PlayFinish)//播放结束
  280. {
  281. break;
  282. }
  283. else//暂停
  284. {
  285. delay(300);
  286. }
  287. }
  288. //释放资源
  289. sws_freeContext(pSwsCtx);
  290. av_frame_free(&pAVframeRGB);
  291. av_frame_free(&pAVframe);
  292. avcodec_close(pAVctx);
  293. avformat_close_input(&pFormatCtx);
  294. mVideoPlaySta = Video_PlayFinish;
  295. qDebug()<<"play finish!";
  296. return 0;
  297. }
  298. //选择视频文件
  299. void MainWindow::on_selectVideoFile()
  300. {
  301. QString path = QFileDialog::getOpenFileName(this,"选择视频文件","D:\\Test\\Video\\","WMV视频(*.wmv);;MP4视频(*.mp4);;AVI视频(*.avi)");
  302. if(!path.isEmpty())
  303. {
  304. mVideoFile->setText(path);
  305. on_videoPlayCtrl();
  306. }
  307. }
  308. //视频播放/暂停控制
  309. void MainWindow::on_videoPlayCtrl()
  310. {
  311. QString videoFilePath = mVideoFile->text();
  312. if(videoFilePath.isEmpty())
  313. {
  314. return;
  315. }
  316. //判断文件是都存在(以防被删除了)
  317. // QFileInfo fileInfo(videoFilePath);
  318. // if(fileInfo.isFile())
  319. // {
  320. // QMessageBox::information(this, "提示", "找不到文件:"+videoFilePath);
  321. // return;
  322. // }
  323. //播放和暂停切换
  324. if(mActionPlayCtrl->text() == "播放")
  325. {
  326. mActionPlayCtrl->setText("暂停");
  327. mActionPlayCtrl->setToolTip("暂停视频");
  328. mActionPlayCtrl->setIcon(QIcon(QPixmap(":/pause.png")));
  329. if(mVideoPlaySta == Video_PlayFinish)
  330. {
  331. mVideoPlaySta = Video_Playing;
  332. playVideo(videoFilePath.toLocal8Bit().data());
  333. on_stopPlayVideo();
  334. }
  335. else
  336. {
  337. mVideoPlaySta = Video_Playing;
  338. }
  339. }
  340. else
  341. {
  342. mActionPlayCtrl->setText("播放");
  343. mActionPlayCtrl->setToolTip("播放视频");
  344. mActionPlayCtrl->setIcon(QIcon(QPixmap(":/play.png")));
  345. if(mVideoPlaySta == Video_Playing)
  346. {
  347. mVideoPlaySta = Video_PlayPause;
  348. }
  349. }
  350. }
  351. //停止播放视频
  352. void MainWindow::on_stopPlayVideo()
  353. {
  354. mVideoPlaySta = Video_PlayFinish;
  355. mActionPlayCtrl->setText("播放");
  356. mActionPlayCtrl->setToolTip("播放视频");
  357. mActionPlayCtrl->setIcon(QIcon(QPixmap(":/play.png")));
  358. }
  359. //窗体自适应视频宽高
  360. void MainWindow::on_autoSize()
  361. {
  362. resizeWindow(mVideoWidth, mVideoHeight);
  363. }
  364. //窗体关闭事件
  365. void MainWindow::closeEvent(QCloseEvent* event)//窗体关闭事件
  366. {
  367. //没有在播放视频时直接退出
  368. if(mVideoPlaySta != Video_PlayFinish)
  369. {
  370. if(QMessageBox::Yes == QMessageBox::information(this, "提示", "确认关闭?", QMessageBox::Yes, QMessageBox::No))
  371. {
  372. mVideoPlaySta = Video_PlayFinish;
  373. //event->accept();
  374. }
  375. else
  376. {
  377. event->ignore();//忽略,不关闭
  378. }
  379. }
  380. }
  381. //播放音量变化
  382. void MainWindow::on_VolumeChange(int volume)
  383. {
  384. mCurVolume->setText("音量"+QString::number(volume));
  385. }
  386. //播放速度变化
  387. void MainWindow::on_playSpdChange(int spdVal)
  388. {
  389. switch(spdVal)
  390. {
  391. case 0://0.5
  392. mPlaySpdVal = PLAY_SPD_05;
  393. break;
  394. case 1://0.8
  395. mPlaySpdVal = PLAY_SPD_08;
  396. break;
  397. case 2://1
  398. mPlaySpdVal = PLAY_SPD_10;
  399. break;
  400. case 3://1.2
  401. mPlaySpdVal = PLAY_SPD_12;
  402. break;
  403. case 4://1.5
  404. mPlaySpdVal = PLAY_SPD_15;
  405. break;
  406. case 5://2
  407. mPlaySpdVal = PLAY_SPD_20;
  408. break;
  409. case 6://3
  410. mPlaySpdVal = PLAY_SPD_30;
  411. break;
  412. case 7://4
  413. mPlaySpdVal = PLAY_SPD_40;
  414. break;
  415. default:
  416. break;
  417. }
  418. }

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

       

       其它视频格式尚未测试。

六、查看接口变更信息

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

         

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/354313
推荐阅读
相关标签
  

闽ICP备14008679号