在上一篇中我们实现了视频的解码、格式转换,但其基本是堆出来的代码,可复用性以及扩展性比较低,现在我们对它进行类的封装。这里我们把它分为四个小部分。

1、重构封装FFMpeg类完成打开和关闭视频接口

2、重构读取视频帧接口

3、重构解码接口

4、重构ToRGB接口

一、重构封装FFMpeg类完成打开和关闭视频接口

我们使用VS的类向导在该项目下添加XFFMpeg类,将上一篇中编辑好的视频打开和关闭的部分代码移植过来,同时进行一些调整,需要注意的时考虑到后面的多线程应用,在打开关闭以及处理时我们都加入了互斥锁,如何处理看下方的代码,注释也都比较清晰。

XFFMpeg.h文件

#pragma once
#include <iostream>
#include <QMutex>
extern "C"
{//调用FFMpeg的头文件
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}class XFFmpeg
{
public:static XFFmpeg *Get()//单件模式{static XFFmpeg ff;return &ff;}///打开视频文件,如果上次已经打开会先关闭
///@para path  视频文件路径
///@return bool 成功失败,失败错误信息通过 GetError获取bool Open(const char *path);//打开视频文件void Close();//关闭文件std::string GetError();//获取错误信息virtual ~XFFmpeg();int totalMs=0;//总时长
protected:char errorbuff[1024];//打开时发生的错误信息XFFmpeg();QMutex mutex;//互斥变量,多线程时避免同时间的读写AVFormatContext *ic = NULL;//解封装上下文};

XFFMpeg.cpp

#include "XFFmpeg.h"//调用FFMpeg的lib库
#pragma comment(lib,"avformat.lib")
#pragma  comment(lib,"avutil.lib")
#pragma  comment(lib,"avcodec.lib")
#pragma  comment(lib,"swscale.lib")XFFmpeg::XFFmpeg()
{errorbuff[0] = '\0';//初始化av_register_all();//注册FFMpeg的库
}XFFmpeg::~XFFmpeg()
{
}bool XFFmpeg::Open(const char *path)
{Close();//打开前先关闭清理mutex.lock();//锁int re = avformat_open_input(&ic, path, 0, 0);//打开解封装器if (re != 0)//打开错误时{mutex.unlock();//解锁av_strerror(re, errorbuff, sizeof(errorbuff));//错误信息printf("open %s failed :%s\n", path, errorbuff);return false;}totalMs = ic->duration / (AV_TIME_BASE);//获取视频的总时间printf("file totalSec is %d-%d\n", totalMs/ 60, totalMs % 60);//以分秒计时mutex.unlock();return true;}void XFFmpeg::Close()
{mutex.lock();//需要上锁,以防多线程中你这里在close,另一个线程中在读取,if (ic) avformat_close_input(&ic);//关闭ic上下文mutex.unlock();}std::string XFFmpeg::GetError()
{mutex.lock();std::string re = this->errorbuff;mutex.unlock();return re;
}

在主函数中这样调用,用来测试是否正确打开文件

#include "aginexplay.h"
#include "XFFmpeg.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{if (XFFmpeg::Get()->Open("1080.mp4"))//是否打开视频{printf("open success!\n");}else{printf("open failed!%s\n",XFFmpeg::Get()->GetError().c_str());getchar();return -1;}QApplication a(argc, argv);agineXplay w;w.show();return a.exec();

二、重构读取视频帧接口

封装读取视频帧,我们在XFFMpeg.h中申明

//读取视频的每一帧,返回每帧后需要清理空间AVPacket Read();

在XFFMpeg中定义

AVPacket XFFmpeg::Read()
{AVPacket pkt;memset(&pkt,0,sizeof(AVPacket));mutex.lock();if (!ic){mutex.unlock();return pkt;}int err = av_read_frame(ic, &pkt);//读取视频帧if (err != 0)//读取失败{av_strerror(err,errorbuff,sizeof(errorbuff));}mutex.unlock();return pkt;}

同样考虑到多线程中,避免在一个线程中刚读取到一帧视频,而在另一个线程中就将其清理里,所以在读取这一部分也需要加入互斥锁,读取完后解锁。

三、重构解码接口

在XFFMpeg.h中申明解码函数以及变量

//读取到每帧数据后需要对其进行解码AVFrame *Decode(const AVPacket *pkt);AVFrame *yuv = NULL;//解码后的视频帧数据int videoStream = 0;//视频流

在XFFMpeg.cpp中定义解码函数

AVFrame * XFFmpeg::Decode(const AVPacket *pkt)
{mutex.lock();if (!ic)//若未打开视频{mutex.unlock();return NULL;}if (yuv == NULL)//申请解码的对象空间{yuv = av_frame_alloc();}int re = avcodec_send_packet(ic->streams[pkt->stream_index]->codec,pkt);//发送之前读取的视频帧pktif (re != 0){mutex.unlock();return NULL;}re = avcodec_receive_frame(ic->streams[pkt->stream_index]->codec,yuv);//解码pkt后存入yuv中if (re != 0){mutex.unlock();return NULL;}mutex.unlock();return yuv;
}

同时在解码时我们需要解码器所以在XFFMpeg.cpp的Open函数中需要打开解码器,代码如下位置

//解码器for (int i = 0; i < ic->nb_streams; i++){AVCodecContext *enc = ic->streams[i]->codec;//解码上下文if (enc->codec_type == AVMEDIA_TYPE_VIDEO)//判断是否为视频{videoStream = i;//videoCtx = enc;AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器if (!codec)//未找到解码器{mutex.unlock();printf("video code not find\n");return false;}int err = avcodec_open2(enc, codec, NULL);//打开解码器if (err != 0)//未打开解码器{mutex.unlock();char buf[1024] = { 0 };av_strerror(err, buf, sizeof(buf));printf(buf);return false;}printf("open codec success!\n");}}//至此为打开解码器过程printf("file totalSec is %d-%d\n", totalMs/ 60, totalMs % 60);//以分秒计时mutex.unlock();return true;

在主函数main中进行测试解码视频帧

for (;;){AVPacket pkt = XFFmpeg::Get()->Read();//每次读取视频得一帧if (pkt.size == 0)break;printf("pts = %lld\n", pkt.pts);AVFrame *yuv = XFFmpeg::Get()->Decode(&pkt);//解码视频帧if (yuv){printf("[D]\n");}av_packet_unref(&pkt);//重新置pkt为0}

同时别忘记在XFFMpeg.cpp的Close时同时还需要释放解码后的空间

if (yuv) av_frame_free(&yuv);//关闭时释放解码后的视频帧空间

四、重构ToRGB接口

在XFFMpeg.h中申明转码函数以及变量

//将解码后的YUV视频帧转化为RGB格式bool ToRGB(const AVFrame *yuv,char *out,int outwidth,int outheight);SwsContext  *cCtx = NULL;//转码器上下文

在XFFMpeg.cpp中定义转码函数

bool XFFmpeg::ToRGB(const AVFrame *yuv, char *out, int outwidth, int outheight)
{mutex.lock();if (!ic)//未打开视频文件{mutex.unlock();return false;}AVCodecContext *videoCtx = ic->streams[this->videoStream]->codec;cCtx = sws_getCachedContext(cCtx, videoCtx->width,//初始化一个SwsContextvideoCtx->height,videoCtx->pix_fmt, //输入像素格式outwidth, outheight,AV_PIX_FMT_BGRA,//输出像素格式SWS_BICUBIC,//转码的算法NULL, NULL, NULL);if (!cCtx){mutex.unlock();printf("sws_getCachedContext  failed!\n");return false;}uint8_t *data[AV_NUM_DATA_POINTERS] = { 0 };data[0] = (uint8_t *)out;//第一位输出RGBint linesize[AV_NUM_DATA_POINTERS] = { 0 };linesize[0] = outwidth * 4;//一行的宽度,32位4个字节int h = sws_scale(cCtx, yuv->data, //当前处理区域的每个通道数据指针yuv->linesize,//每个通道行字节数0, videoCtx->height,//原视频帧的高度data,//输出的每个通道数据指针  linesize//每个通道行字节数);//开始转码if (h > 0){printf("(%d)", h);}mutex.unlock();return true;}

在主函数main中进行测试转码

for (;;){AVPacket pkt = XFFmpeg::Get()->Read();//每次读取视频得一帧if (pkt.size == 0)break;printf("pts = %lld\n", pkt.pts);if (pkt.stream_index != XFFmpeg::Get()->videoStream)//若不为视频流{av_packet_unref(&pkt);//重新置pkt为0continue;}AVFrame *yuv = XFFmpeg::Get()->Decode(&pkt);//解码视频帧if (yuv){printf("[D]\n");XFFmpeg::Get()->ToRGB(yuv, rgb, 800, 600);//视频转码}av_packet_unref(&pkt);//重新置pkt为0}

考虑到一个视频有音频和视频,这里对于音频我们先直接过滤掉,只处理视频,同时在XFFMpeg.cpp的Close中对于转码上下文的空间也需要释放。

if (cCtx){sws_freeContext(cCtx);//释放转码器上下文空间cCtx = NULL;}

至此FFMPEG视频处理原理以及实现基本完成了,下一篇对于QT界面设计和使用opengl绘制视频的总结。

下一篇链接:https://blog.csdn.net/hfuu1504011020/article/details/82686325

基于Qt、FFMpeg的音视频播放器设计二(FFMpeg视频处理之类封装)相关推荐

  1. 基于Qt、FFMpeg的音视频播放器设计一

    前言:整个项目的源代码 https://download.csdn.net/download/hfuu1504011020/10672140 最近刚完成基于Qt.FFMpeg的音视频播放器相关C++程 ...

  2. 基于QT实现简易音视频播放器

    目录:         一.界面布局         二.播放本地音频                 2.1 打开本地音频保存路径                 2.2 选中想要播放的音频加入到播 ...

  3. 基于Qt、FFMpeg的音视频播放器设计四(视频播放进度控制)

    上面介绍了如何使用opengl绘制视频和Qt的界面设计,也比较简单,现在我们看下如何控制视频播放及进度的控制,内容主要分为以下几个部分 1.创建解码线程控制播放速度 2.通过Qt打开外部视频 3.视频 ...

  4. 基于Phonon+QT的音视频播放器设计与实现

    目 录 摘 要 2 第一章 软件需求说明书 1 1.1 引言 1 1.2 业务流程整体说明 1 第二章 需求分析报告 3 2.1 引言 3 2.2 任务概述 3 2.3 功能需求 3 2.4 性能需求 ...

  5. 基于FFmpeg的音视频播放器

    版本信息 AndroidStudio 3.5.2 FFmpeg 4.0.2 背景 AndroidStudio3.5.1下搭建FFmpeg环境 Android使用FFmpeg动态库播放视频 Androi ...

  6. RTSP播放器网页web无插件直播流媒体音视频播放器EasyPlayer-RTSP-Android解码获取视频帧的方法

    应用场景 EasyPlayer-RTSP在多年与VLC的对标过程中,积累了广泛的应用场景,EasyPlayer-RTSP底层与上层全部自主开发,自主知识产权,可实战测试. EasyPlayer-RTS ...

  7. 基于QT的网络音乐播放器(一)

    自学Qt已经有一段时间了,但是始终感觉自己还是很弱(其实并不是感觉自己很弱,是自己本来就很弱,哈哈).自己也照着书上敲了几个例子,但觉得还是要写点东西才能真正运用起来.所以,前段时间就写了个很简单的音 ...

  8. QT + FFmpeg 5.x + x264 + x265 + SDL2 音视频播放器

    QT + FFmpeg 5.x + x264 + x265 + SDL2 音视频播放器 使用了QT的QML设计界面,人机交互; 使用了FFmpeg 5.x + x264 + x265 + SDL2 完 ...

  9. Qt FFmpeg 音视频播放器

    使用FFmpeg库实现 本地和rtp 音视频播放器,使用qt绘制视频. 本demo环境为 qt5.12 vs2019-32位 .pro的qt工程 FFmpeg版本位3.4.8 vs2092-32位 本 ...

  10. 视频教程-基于NDK、C++、FFmpeg的android视频播放器开发实战-Android

    基于NDK.C++.FFmpeg的android视频播放器开发实战 夏曹俊:南京捷帝科技有限公司创始人,南京大学计算机硕士毕业,有15年c++跨平台项目研发的经验,领导开发过大量的c++虚拟仿真,计算 ...

最新文章

  1. 在Linux下轻松玩转Samba服务器
  2. c语言超长编程程序,全国青少年软件编程等级考试C语言经典程序题10道五
  3. android 自定义频谱,android – 如何从实时音频开发频谱分析仪?
  4. 字符串基础类型拓宽的操作,转换成整数值
  5. 图灵2008年12月出版的计算机图书
  6. mac 爱普生打印机驱动_高效打印企业首选 爱普生M2178黑白多功能一体机评测
  7. 服务器放行6in4协议,最简单的接入IPv6网络的方法 – 6in4隧道
  8. CSS选择器必备的3个知识点
  9. Java:jar包和war包区别
  10. Javascript实现完美继承
  11. 主成分分析——SPSS实操
  12. 一文整理常见Java后端面试题系列——Kafka篇(2022最新版)
  13. Aria2基础使用教程
  14. vmware(鼠标移出移入)反复触发numlock问题
  15. 转摘 房地产知识
  16. Cisco2960交换机配置(二)
  17. 在2021年为七夕Python程序与Docker牵线配对
  18. 微信小程序及微信生态圈
  19. elementUI InfiniteScroll 无限滚动 一次加载到底不受禁用限制问题解决
  20. VB.net:VB.net编程语言学习之添加引用打包安装项目的简介、案例应用之详细攻略

热门文章

  1. C++primer——形参、局部变量和静态局部变量的差别
  2. 股票学习-量柱和k线-第三天
  3. 读《虚幻引擎程序设计浅析》笔记
  4. 连续子串最大和——python实现
  5. 揭秘全美第一黑客组织Anonymous(匿名者)的装备库
  6. java以及JavaScript的香港身份证验证方法。
  7. windows简单命令
  8. Vue的双向数据绑定
  9. 设备树slew-rate
  10. springboot 多模块 Found multiple @SpringBootConfiguration annotated classes