这里使用 Qt + FFmpeg 实现了一个简单播放视频的例子。先看下按下按钮播放视频时的效果图:

完整工程下载链接:Github-FFmpeg_demo

注意:一定要将 bin 目录下的 dll 文件拷贝到编译生成的 exe 所在的目录下,否则会提示:程序异常结束,无法运行。

一、开发环境的准备

我所用的开发环境是qt-opensource-windows-x86-msvc2015-5.11.2.exe,这里使用 Qt 自带的 Qt Creator 编译器,也可以使用微软的 VS,安装 Qt 插件即可,具体可以参考:如何在 VS2015 上开发 Qt 程序

(一)下载工具

现在准备 FFmpeg 的开发环境:

FFmpeg 下载地址:https://ffmpeg.zeranoe.com/builds/

点击上面地址后弹出界面如下图 1 所示,然后选择 Windows 32-bit 的 FFmpeg,当然你也可以选择 64 位的,不过我选择的是 32 位。之后我们需要将它右侧 Linking 下的 Shared 和 Dev 下载下来,解压后 Dev 的 include 里是它的头文件、lib 里是他的静态链接库,Shared 里的 bin 是它的 dll 和 .exe 程序。之后我们将它 Dev 里的 include、lib 和 Shared 里的 bin 拷贝出来形成如下图 2 所示。

(二)添加库

然后新建一个 Qt Widgets Application 工程,在 .pro 添加 include目录 和 lib 目录的路径。

INCLUDEPATH +="./include"
LIBS += -L$$PWD/lib -lavutil -lavformat -lavcodec -lavdevice -lavfilter -lpostproc -lswresample -lswscale

-L 后面不用加空格,直接跟着地址,然后空格,-l 后面是去掉 lib 之后的文件名",这样 qmake 编译器就可以通过路径+文件名正确的定位到库文件了,另外 $PWD 表示当前目录。

然后要将 bin 目录下的 dll 文件拷贝到编译生成的 exe 所在的目录下,否则会提示:程序异常结束,无法运行。原因是缺少库文件。编译时,我们可能会把库文件放在与源代码相同的地方或其他地方,只要在 pro 文件中设置路径即可,但运行时的路径和编译时的路径往往不一样,这样就导致运行时找不到库文件,需要将库文件拷贝至运行路径下才行。

二、代码实现播放功能

在界面上放置一个 QLabel 和 QPushButton 控件,当点击按钮时播放视频。程序主要分为以下几方面:

1、打开音视频流并获取音视频流信息;

2、查找视频流位置以及查找并打开视频解码器;

3、视频解码的同时处理图片像素数据;

4、最后要释放申请的内存空间。

完整代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QTime> // 调用FFmpeg的头文件
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
} Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
} Widget::~Widget()
{
delete ui;
} // 延时函数
void delay(int msec)
{
QTime dieTime = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < dieTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
} void Widget::on_pushButton_clicked()
{
AVFormatContext *pFormatCtx; // 存储音视频封装格式中包含的信息
int videoIndex = -1; // 视频帧索引,初始化为-1
AVCodecContext *pCodecCtx; // 视频流编码结构
AVCodec *pCodec; // 视频解码器
AVFrame *pFrame, *pFrameRGB;
unsigned char *out_buffer;
AVPacket *packet;
int ret, got_picture;
struct SwsContext *img_convert_ctx; // 主要用于视频图像的转换 char filepath[] = "../FFmpeg_demo/test.mp4"; // 当前目录为构建目录 // 注册FFMpeg的库
av_register_all(); /*** (一)打开音视频流并获取音视频流信息 ***/
// 初始化AVFormatContext
pFormatCtx = avformat_alloc_context();
// 打开音视频流
if (avformat_open_input(&pFormatCtx, filepath, nullptr, nullptr) != 0)
{
printf("Couldn't open input stream.\n");
return;
}
// 获取音视频流数据信息
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
{
printf("Couldn't find stream information.\n");
return;
} /*** (二)查找视频流位置以及查找并打开视频解码器 ***/
// 查找视频流的起始索引位置(nb_streams表示视音频流的个数)
for (int i = 0; i < (int)pFormatCtx->nb_streams; i++)
{
// 查找到视频流时退出循环
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) // 判断是否为视频流
{
videoIndex = i;
break;
}
}
if (videoIndex == -1)
{
printf("Didn't find a video stream.\n");
return ;
}
// 查找视频解码器
pCodecCtx = pFormatCtx->streams[videoIndex]->codec; // 获取视频流编码结构
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == nullptr)
{
printf("Codec not found.\n");
return ;
}
// 打开解码器
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
printf("Could not open codec.\n");
return ;
}
// 打印视频信息
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx, 0, filepath, 0); // 此函数打印输入或输出的详细信息
printf("-------------------------------------------------\n"); /*** (三)视频解码的同时处理图片像素数据 ***/
// 创建帧结构,此函数仅分配基本结构空间,图像数据空间需通过av_malloc分配
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
// 创建动态内存,创建存储图像数据的空间(av_image_get_buffer_size获取一帧图像需要的大小)
out_buffer = (unsigned char *)av_malloc((size_t)av_image_get_buffer_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1));
// 存储一帧像素数据缓冲区
av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer,
AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);
packet = (AVPacket *)av_malloc(sizeof(AVPacket)); // 初始化img_convert_ctx结构
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
// av_read_frame读取一帧未解码的数据
while (av_read_frame(pFormatCtx, packet) >= 0)
{
// 如果是视频数据
if (packet->stream_index == videoIndex)
{
// 解码一帧视频数据
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0)
{
printf("Decode Error.\n");
return ;
}
if (got_picture)
{
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
QImage img((uchar*)pFrameRGB->data[0],pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
ui->label->setPixmap(QPixmap::fromImage(img)); // 在label上播放视频图片
delay(40);
}
}
av_free_packet(packet);
} /*** (四)最后要释放申请的内存空间 ***/
sws_freeContext(img_convert_ctx); // 释放一个SwsContext
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}

打印出的音视频流信息如下:

--------------- File Information ----------------
-------------------------------------------------
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '../FFmpeg_demo/test.mp4':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2015-06-30T08:50:41.000000Z
copyright :
copyright-eng :
Duration: 00:00:15.06, start: 0.000000, bitrate: 470 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 418 kb/s, 24 fps, 24 tbr, 24k tbn, 48 tbc (default)
Metadata:
creation_time : 2015-06-30T08:50:40.000000Z
handler_name : TrackHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 49 kb/s (default)
Metadata:
creation_time : 2015-06-30T08:50:40.000000Z
handler_name : Sound Media Handler

参考:

基于Qt、FFMpeg的音视频播放器设计一(准备环境)

QT+FFMPEG实现视频播放

Qt+FFmpeg 简单实现视频播放的更多相关文章

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

    ui 在.pro文件中添加 QT +=phonon 头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> ...

  2. 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...

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

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

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

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

  5. 基于<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>的一些个人总结

    最近因为项目接近收尾阶段,所以变的没有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相关流媒体播放器的例子在看. 同时自己也用FFMPEG2.01,SDL2.01结合MFC以及网上罗 ...

  6. 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”

    FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...

  7. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】

    转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] ...

  8. 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器

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

  9. 音视频处理之FFmpeg+SDL+MFC视频播放器20180411

    一.FFmpeg+SDL+MFC视频播放器 1.MFC知识 1).创建MFC工程的方法 打开VC++ 文件->新建->项目->MFC应用程序 应用程序类型->基于对话框 取消勾 ...

随机推荐

  1. 洛谷 P4999(数位DP)

    ###洛谷 P4999 题目链接 ### 题目大意:给你一个区间,求这段区间中所有数的,数位上的,数字之和. 分析: 这题与 洛谷 P2602 相似,稍微改一下就可以了. 求出 0 ~ 9 的个数,然 ...

  2. 企业如何做好B2C电商平台

    导语本文主要讲了两个方面:1.企业如何定位B2C电商平台:2.企业做B2C遇到的问题. 一.企业如何定位B2C电商平台传统企业做B2C电子商务一般选用品牌.渠道.平台模式这三种,品牌模式是在网上建立一 ...

  3. python 动态语言和协议编程

    动态语言:不需要去定义变量的类型 协议编程:一个类实现了某个魔法函数,这个类就是什么类型,理解为协议

  4. centos7开启,关闭防火墙

    1.查看防火墙状态 firewall-cmd --state 2.停止防火墙 systemctl stop firewalld.service 3.禁止防火墙开机启动 systemctl disabl ...

  5. 用Python程序批量删除excel里面的图片

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Rhinoceros PS:如有需要Python学习资料的小伙伴可以 ...

  6. WebSocket实现Java后台消息推送

    1.什么是WebSocket WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端. 2.实现原理 在实现 ...

  7. 访问rabbitmq-server失败

    测试项目正常运行突然访问不了,各项目启动失败,查看日志发现是RabbitMQ拒绝连接. 重启后依然失败,看var/log/rabbitmq/startup_err 发现什么错误信息也没有,后查看磁盘空 ...

  8. 大话IdentityServer4之使用 IdentityServer4 保护 ASP.NET Core 应用

    这几天一直在研究IdentityServer4在asp.net core3.0中的应用,下面说说我的理解: 我们每一个.net core 项目大家可以理解为我新建了一个动物园或者植物园等,注册用户想要 ...

  9. CCNA基础学习

    OSI七层模型 由国际标准化组织ISO于1984年提出 是目前公认的计算机通信和Internet网络通信的基本结构模型 如见使用的最广泛的TCP/IP协议就是基于OSI(Open Systems In ...

  10. cpu 乱序执行与问题【转】

    转自:https://blog.csdn.net/lizhihaoweiwei/article/details/50562732 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议 ...