目录

1.项目配置

2.显示界面设计

3.视频解码显示

流程描述

4.演示


最近在学习音视频基础知识,在这里感谢雷神留下的一系列指引新手入门的宝贵资源,虽然他英年早逝,但他的硕果永存。不由感慨真是天妒英才,愿雷神在天堂安好

附上学习资料地址:雷霄骅(leixiaohua1020)的专栏

选择学习ffmpeg的原因是,它具有跨平台特性,Windows、Linux、Android、IOS这些主流系统可以通吃

而且它非常全能,从视频采集、视频编码到视频传输都可以直接使用ffmpeg完成,有雷神留下的学习资料加持,学习起来自然是事半功倍

下面简单记录一下自己使用Qt来做图形界面学习ffmpeg的过程

1.项目配置

首先新建一个Qt Widgets项目

选择一个路径,名字不要带有中文

这里使用的是Qt5.6.2版本32位的工具包

新建一个继承QWidget的类

将在官网获取的ffmpeg库解压到与pro文件同级目录下,这里使用的是ffmpeg-4.2.2版本

获取地址:https://www.ffmpeg.org/download.html

其中包含bin动态库,include头文件,lib引入库

修改工程配置文件VideoPlayerDemo.pro,添加使用ffmpeg依赖的库

新建一个C++类

继承QObject

在新建的类中添加头文件,指示编译器这部分代码按C语言进行编译

先编译输出一下,在同级目录下生成debug路径

打开ffmpeg的bin目录,将其中所有文件(主要是dll动态库)拷贝到debug目录下

项目配置方面小小告一段落

2.显示界面设计

打开界面文件videoplayerdemo.ui,将外层widget设置大小为800*600,并设置最小大小为800*600

拖入两个widget控件,wdg_show——用于显示解码得到的图片、wdg_ctrl——用于添加播放控制按钮,在wdg_ctrl中添加一个pb_play按钮,用于播放视频文件

设置wdg_show、wdg_ctrl的最小大小,设置wdg_ctrl的最大高度,在外层widget上进行垂直布局,可以实现拉伸跟随缩放的效果

新建一个类VideoItem,用于重写paintEvent绘图事件(后面会说为什么要新建这个类)

3.视频解码显示

如果使用主线程进行解码,会造成客户端卡顿的现象,因此这里需要使用多线程开启一个线程去执行解码操作

考虑到需要将解码得到的图片传出,并在Qt控件上显示,因此使用Qt封装的线程函数,便于使用信号和槽机制

  1. 在videoshow.h中添加头文件<QThread>
  2. 由这个类继承QThread类
  3. 更改原有的构造函数,添加Qt线程函数run()
  4. 添加信号SIG_GetOneImage(const QImage image),这里不使用引用传递的原因是:由于是多线程操作图片,如果传递引用,可能图片在显示处理前已经被释放,所以需要拷贝一份图片
  5. 添加一会需要打开并进行解码的视频文件

流程描述

  1. 初始化 ffmpeg,调用av_register_all()才能正常使用编码器和解码器注册所用函数 
  2. 需要分配一个 AVFormatContext,ffmpeg所有的操作都要通过这个 AVFormatContext 来进行,可以理解为视频文件指针
  3. avformat_open_input()——打开视频文件,avformat_find_stream_info()——获取视频文件信息
  4. avcodec_find_decoder()——查找解码器,avcodec_open2()——打开解码器
  5. av_read_frame()——循环读取视频帧
  6. AVPacket存放的是解码得到的H.264格式的数据,AVFrame存放的是YUV420p格式的数据
  7. 将解码后的YUV420p 格式视频数据 转换 成 RGB32,抛出信号去控件显示,在Qt控件上绘图显示出来
  8. 回收数据
VideoShow::VideoShow()
{m_fileName = "D:/Kugou/华语群星 - 少林英雄.mp4";}void VideoShow::run()
{//1.初始化 FFMPEG 调用了这个才能正常使用编码器和解码器 注册所用函数av_register_all();//2.需要分配一个 AVFormatContext,FFMPEG 所有的操作都要通过这个 AVFormatContext 来进行可以理解为视频文件指针AVFormatContext *pFormatCtx = avformat_alloc_context();//中文兼容std::string path = m_fileName.toStdString();const char* file_path = path.c_str();//3. 打开视频文件if( avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0 ){qDebug()<<"can't open file";return;}//3.1 获取视频文件信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0){qDebug()<<"Could't find stream infomation.";return;}//4.读取视频流int videoStream = -1;int i;for (i = 0; i < pFormatCtx->nb_streams; i++){if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){videoStream = i;}}//如果 videoStream 为-1 说明没有找到视频流if (videoStream == -1){printf("Didn't find a video stream.");return;}//5.查找解码器auto pCodecCtx = pFormatCtx->streams[videoStream]->codec;auto pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (pCodec == NULL){printf("Codec not found.");return;}//打开解码器if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0){printf("Could not open codec.");return;}//6.申请解码需要的结构体 AVFrame 视频缓存的结构体AVFrame *pFrame, *pFrameRGB;pFrame = av_frame_alloc();pFrameRGB = av_frame_alloc();//分配一个 packetAVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket));//7.这里我们将解码后的 YUV 数据转换成 RGB32 YUV420p 格式视频数据-->RGB32--> 图片显示出来static struct SwsContext *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, NULL, NULL, NULL);//计算RGB一帧数据大小auto numBytes = avpicture_get_size(AV_PIX_FMT_RGB32,pCodecCtx->width ,pCodecCtx->height);//申请RGB一帧画面大小对应的空间uint8_t * out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));//pFrameRGB与out_buffer绑定avpicture_fill( (AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,pCodecCtx->width, pCodecCtx->height);//8.循环读取视频帧, 转换为 RGB 格式, 抛出信号去控件显示int ret, got_picture;while(1){//可以看出 av_read_frame 读取的是一帧视频,并存入一个 AVPacket 的结构中if (av_read_frame(pFormatCtx, packet) < 0){break; //这里认为视频读取完了}//生成图片if (packet->stream_index == videoStream){// 解码 packet(H264) 存在 pFrame(yuv) 里面ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);if (ret < 0){printf("decode error");return ;}//有解码器解码之后得到的图像数据都是 YUV420 的格式,而这里需要将其保存成图片文件//因此需要将得到的 YUV420 数据转换成 RGB 格式if (got_picture){//YUV420转换RGBsws_scale(img_convert_ctx,(uint8_t const * const *) pFrame->data,pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,pFrameRGB->linesize);//out_buffer 与 pFrameRGB 是捆绑的,将 out_buffer 里面的数据存在 QImage 里面QImage tmpImg((uchar*)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);//把图像复制一份 传递给界面显示QImage image = tmpImg.copy();//显示到控件 多线程 无法控制控件 所以要发射信号emit SIG_GetOneImage( image );}}av_free_packet(packet);msleep(5); // 停一停}//9.回收数据av_free(out_buffer);av_free(pFrame);av_free(pFrameRGB);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);
}

上述流程解码得到图片image,将解码得到的每一帧图片通过SIG_GetOneImage(image)信号发送出去

给pb_play按钮添加一个处理函数,在VideoPlayer类中实现,点击按钮开启线程,获取图片显示到控件

包含VideoShow类的头文件,添加一个对象,用于连接槽函数

在构造和析构中添加槽函数和回收资源的代码

可以看到接收信号处理的控件是ui->wdg_show,而处理函数slot_setImage并没有定义,这时就需要用到刚才我们新添加的类VideoItem

由于我们不是在最外层的widget上进行绘图,所以需要对控件进行重写paintEvent事件进行绘图(或者使用eventFilter,这里没有用这种方法)

因此我们添加类VideoItem,在其中添加槽函数slot_setImage进行处理,并重写paintEvent事件

槽函数中将image图片保存,并调用repaint重绘

void VideoItem::slot_setImage(const QImage  image)
{m_image = image;this->repaint();//立即刷新绘图
}
//绘图事件
void VideoItem::paintEvent(QPaintEvent *event)
{QPainter painter(this);//先画黑色背景painter.setBrush( Qt::black );painter.drawRect( 0,0, this->width() , this->height() );//等比例缩放图片  等比例变成控件这么大if( m_image.size().width()<= 0 ) return;QPixmap img = QPixmap::fromImage(m_image.scaled(this->size(), Qt::KeepAspectRatio));//调整贴图位置,使其居中//x = (widget_show的宽 - 图片宽) / 2//y = (widget_show的高 - 图片高) / 2int x =  this->width() - img.width() ;int y =  this->height() - img.height();  x = x/2;y = y/2;painter.drawPixmap(x,y,img);}

还差最后一步,就是将VideoItem类与控件wdg_show关联起来,在videoPlayer.ui中,将wdg_show提升为VideoItem(继承VideoItem)

这样就可以在wdg_show上进行绘图了

4.演示

点击Play按钮,开启线程在VideoShow类中进行解码,得到的图片通过信号SIG_GetOneImage发送出去,在VideoItem中进行重绘,显示在wdg_show控件上

音视频基础学习之【01.基于ffmpeg的简单播放器demo实现】相关推荐

  1. 音视频开发系列(62)基于FFmpeg实现简单的视频解码器

    一.FFmpeg解码过程流程图和关键的数据结构 FFmpeg解码涉及的知识点比较多,很容易被函数和结构体搞定不知所错,我们先从整体上对解码流程有个认知,画了张解码流程图,如下 1.1 解码流程如下 a ...

  2. 【Android-Service】基于MVP的音乐播放器demo实现思路(附源码)

    最近在学习service相关的内容,在该部分的学习过程中,根据学习视频中的内容进行了总结归纳,以下是音乐播放器demo的开发思路,具体步骤及源码: 有关MVP框架的内容可看: link. 实现效果: ...

  3. 音视频基础学习之【07.仿优酷界面的UI设计】

    目录 界面设计 主页界面设计 QScrollArea 添加地方电视台 bug修复 注册登录客户端 界面设计 注册 协议设计 注册按键 发注册请求包 处理注册回复包 注册密码字段使用MD5进行加密,加固 ...

  4. 音视频开发刚入门该如何实现一个录音/播放器

    如果你从未接触过音视频开发,但有实现一个录音器.播放器的需求或想法,本文会是一个比较好的入门内容. 本博客是从基础内容到具体的实践,再展现一个简易的整体框架,内容主要有: 1.音频基础知识,可以了解音 ...

  5. 【Qt 开源音视频框架模块QtAV】02:QTAV多功能播放器

    前言 在[Qt 开源音视频框架模块QtAV]01:介绍.编译以及简单使用中我们已经完成了QTAV的项目部署和简单案例的测试,下面我再分享下QTAV提供的播放器例程. 程序功能效果展示 演示的项目在QT ...

  6. 仿迅雷播放器教程 -- 基于ffmpeg的C++播放器 (1)

    2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最后说会开源一个播放器,没想到快两年了,才 ...

  7. Android 基于ffmpeg开发简易播放器 - EGL和OpenGLESGLES显示YUV视频

    EGL和OpenGLESGLES显示YUV视频 1.EGL EGL是OpenGL ES与系统原始窗口的适配层: Display:用于与原生窗口建立连接. Surface:用于渲染的区域. Contex ...

  8. 基于DirectShow的简单播放器

    一 个简单的基于DirectShow的打开本地视频文件的例子. 参考了各种资材,网上很多,也比较简单,但有个问题就是在调用" RenderFile "函数时只有在x86下都会返回S ...

  9. FFMPEG开源音视频项目学习汇总

    ~非常感谢雷霄骅老师的无私帮助,本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/42658139~       本文汇总一下自己视音 ...

最新文章

  1. AI大觉醒:图灵奖得主Bengio称AI将产生意识,未来机器学习核心是注意力机制
  2. Hadoop2.6.0子项目hadoop-mapreduce-examples的简单介绍
  3. 阿里DataV案例:制作实时销售大屏流程
  4. PyCharm个性化设置及注意事项
  5. 在SqlServer存储过程中使用Cursor(游标)操作记录
  6. 鸿蒙十系统更新机型,高歌猛进,鸿蒙系统升级机型再次确认,花粉:终等到!...
  7. 工业级光电转换器产品介绍
  8. semihost/ITM机制浅析以及使用JLINK通过ITM调试stm32单片机
  9. 60秒语音有救了?曝微信测试语音进度条,内部人士一句话打回原形
  10. 物联网:不要幻想今年会突飞猛进,但行业依然亢奋
  11. 难道我真的只是你生命中的过客?
  12. static library libs/libvpx/libvpx.a is not portable!
  13. AD选择板边覆铜教程
  14. 固高运动控制卡学习8 --高速硬件捕获
  15. 让你一目了然的商业计划书
  16. 加载mysql的jdbc驱动_JDBC驱动加载
  17. UE编辑器重要快捷键总结
  18. 传说中的蝴蝶效应?--MAC地址克隆竟然惹祸了!
  19. [现代诗]情诗——给网恋中人
  20. 小米id锁状态查询_揭秘:苹果隐藏ID到底是什么?你可能就被坑了!

热门文章

  1. python中match的六种用法,python 正则函数match()和search()用法示例
  2. 多功能日期查询小工具
  3. 小米r2d做nas_零基础也可以打造智能家居,利用群晖docker将小米全家桶接入ios Homekit...
  4. STM32CubeIDE使用
  5. 汉堡式折叠html,3种超酷汉堡包菜单按钮变形动画特效
  6. origin画图软件 多个子图融合
  7. 旷世科技面试题-三个均匀分布x>y>z的概率
  8. 2022年一级建造师考试《市政公用工程》练习题及答案(多选题)
  9. python柱状图标注均值标准差_OpenCV Python 图像矩阵的均值和标准差
  10. android 配置aspect_Android全面屏适配