FFmpeg开发(五)——Qt视频播放器之封装FFmpeg类(参考了暴风影音、迅雷影音)

上一篇介绍了,使用Qt和FFmpeg写的播放器。页面大家可以点进去查看和下载。

FFmpeg开发(四)——Qt实现一个视频播放器(参考了暴风影音、迅雷影音)

实现的效果如下:

本篇分享一下,封装的FFmpeg类,因为使用的是Qt,所以就按照C++的方式进行了封装。

构造函数:

FFmpeg::FFmpeg()
{initData();
}

初始化信息:

void FFmpeg::initData()
{m_errorBuff[0] = '\0';av_register_all(); //注册FFMpeg的库m_pAVFormatCtx = NULL;//解封装上下文m_pYuvFrame = NULL;//解码后的视频帧数据m_pPcmFrame = NULL;//解码后的音频数据m_pSwsCtx = NULL;//视频转码上下文m_pSwrCtx = NULL;//音频重采样上下文m_totalMs = 0;//总时长m_videoStream = 0;//视频流m_audioStream = 1;//音频流m_fps = 0;//每秒的视频帧数m_pts = 0;//获得当前解码帧的时间m_sampleRate = 48000;//样本率m_sampleSize = 16;//样本大小m_channel = 2;///通道数m_isPlay = false;
}

打开文件并解码:

int FFmpeg::openFileCtx(const char *path)
{closeAndFree(); //先关闭并清理.m_mutex.lock();//锁int ret = avformat_open_input(&m_pAVFormatCtx, path, 0, 0);//打开解封装器if (ret != 0)//打开错误时{m_mutex.unlock();//解锁av_strerror(ret, m_errorBuff, sizeof(m_errorBuff));//错误信息printf("open %s failed :%s\n", path, m_errorBuff);return 0;}m_totalMs = m_pAVFormatCtx->duration / (AV_TIME_BASE);//获取视频的总时间//打印视频详细信息av_dump_format(m_pAVFormatCtx, 0, path, 0);//解码器for (int i = 0; i < m_pAVFormatCtx->nb_streams; i++){AVCodecContext *avCodecCtx = m_pAVFormatCtx->streams[i]->codec;//解码上下文if (avCodecCtx->codec_type == AVMEDIA_TYPE_VIDEO)//判断是否为视频{m_videoStream = i;m_fps = r2d(m_pAVFormatCtx->streams[i]->avg_frame_rate);//获得每秒的视频帧数AVCodec *avCodec = avcodec_find_decoder(avCodecCtx->codec_id);//查找解码器if (!avCodec)//未找到解码器{m_mutex.unlock();printf("video code not find\n");return 0;}int err = avcodec_open2(avCodecCtx, avCodec, NULL);//打开解码器if (err != 0)//未打开解码器{m_mutex.unlock();char buf[1024] = { 0 };av_strerror(err, buf, sizeof(buf));printf("not open!  %s",buf);return 0;}printf("open codec success!\n");}else if (avCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO)//若为音频流{m_audioStream = i;//音频流AVCodec *avCodec = avcodec_find_decoder(avCodecCtx->codec_id);//查找解码器if (avcodec_open2(avCodecCtx, avCodec, NULL) < 0){m_mutex.unlock();return 0;}m_sampleRate = avCodecCtx->sample_rate;//样本率m_channel = avCodecCtx->channels;//通道数switch (avCodecCtx->sample_fmt)//样本大小{case AV_SAMPLE_FMT_S16://signed 16 bitsm_sampleSize = 16;break;case  AV_SAMPLE_FMT_S32://signed 32 bitsm_sampleSize = 32;default:break;}//printf("audio sample rate:%d sample size:%d chanle:%d\n",this->sampleRate,this->sampleSize,this->channel);}}//至此为打开解码器过程printf("file totalSec is %d-%d\n", m_totalMs / 60, m_totalMs % 60);//以分秒计时m_mutex.unlock();return m_totalMs;
}

释放相关资源:

void FFmpeg::closeAndFree()
{m_mutex.lock();//需要上锁,以防多线程中你这里在close,另一个线程中在读取,if (m_pAVFormatCtx) avformat_close_input(&m_pAVFormatCtx);//关闭上下文空间if (m_pYuvFrame) av_frame_free(&m_pYuvFrame);//关闭时释放解码后的视频帧空间if (m_pPcmFrame) av_frame_free(&m_pPcmFrame);//关闭时释放解码后的音频空间if (m_pSwsCtx){sws_freeContext(m_pSwsCtx);//释放转码器上下文空间m_pSwsCtx = NULL;}if (m_pSwrCtx){swr_free(&m_pSwrCtx);//释放音频上下文空间}m_mutex.unlock();
}

读取视频帧:

//读取视频的每一帧
AVPacket FFmpeg::readAVPacket()
{AVPacket pkt;memset(&pkt, 0, sizeof(AVPacket));m_mutex.lock();if (!m_pAVFormatCtx){m_mutex.unlock();return pkt;}int err = av_read_frame(m_pAVFormatCtx, &pkt);//读取视频帧if (err != 0)//读取失败{av_strerror(err, m_errorBuff, sizeof(m_errorBuff));}m_mutex.unlock();return pkt;
}

解码:

//读取到每帧数据后需要对其进行解码,返回它的pts
int FFmpeg::decodeAVPacket(const AVPacket *pkt)
{m_mutex.lock();if (!m_pAVFormatCtx)//若未打开视频{m_mutex.unlock();return -1;}if (m_pYuvFrame == NULL)//申请解码的对象空间{m_pYuvFrame = av_frame_alloc();}if (m_pPcmFrame == NULL){m_pPcmFrame = av_frame_alloc();}AVFrame *frame = m_pYuvFrame;//此时的frame是解码后的视频流if (pkt->stream_index == m_audioStream)//若为音频{frame = m_pPcmFrame;//此时frame是解码后的音频流}int ret = avcodec_send_packet(m_pAVFormatCtx->streams[pkt->stream_index]->codec, pkt);//发送之前读取的pktif (ret != 0){m_mutex.unlock();return -1;}ret = avcodec_receive_frame(m_pAVFormatCtx->streams[pkt->stream_index]->codec, frame);//解码pkt后存入yuv中if (ret != 0){m_mutex.unlock();return -1;}//qDebug() << "pts=" << frame->pts;m_mutex.unlock();int p = frame->pts*r2d(m_pAVFormatCtx->streams[pkt->stream_index]->time_base);//当前解码的显示时间if (pkt->stream_index == m_audioStream)//为音频流时设置ptsm_pts = p;return p;
}

转成RGB:

bool FFmpeg::ToRGB(char *out, int outwidth, int outheight)
{m_mutex.lock();if (!m_pAVFormatCtx || !m_pYuvFrame)//未打开视频文件或者未解码{m_mutex.unlock();return false;}AVCodecContext *videoCtx = m_pAVFormatCtx->streams[m_videoStream]->codec;m_pSwsCtx = sws_getCachedContext(m_pSwsCtx, videoCtx->width,//初始化一个SwsContextvideoCtx->height,videoCtx->pix_fmt, //输入像素格式outwidth, outheight,AV_PIX_FMT_BGRA,//输出像素格式SWS_BICUBIC,//转码的算法NULL, NULL, NULL);if (!m_pSwsCtx){m_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(m_pSwsCtx, m_pYuvFrame->data, //当前处理区域的每个通道数据指针m_pYuvFrame->linesize,//每个通道行字节数0, videoCtx->height,//原视频帧的高度data,//输出的每个通道数据指针linesize//每个通道行字节数);//开始转码if (h > 0){printf("(%d)", h);}m_mutex.unlock();return true;
}

转成PCM:

int FFmpeg::ToPCM(char *out)
{m_mutex.lock();if (!m_pAVFormatCtx || !m_pPcmFrame || !out)//文件未打开,解码器未打开,无数据{m_mutex.unlock();return 0;}AVCodecContext *avCodecCtx_Audio = m_pAVFormatCtx->streams[m_audioStream]->codec;//音频解码器上下文if(m_pSwrCtx == NULL){m_pSwrCtx = swr_alloc();//初始化swr_alloc_set_opts(m_pSwrCtx,avCodecCtx_Audio->channel_layout,AV_SAMPLE_FMT_S16,avCodecCtx_Audio->sample_rate,avCodecCtx_Audio->channels,avCodecCtx_Audio->sample_fmt,avCodecCtx_Audio->sample_rate, 0,0);swr_init(m_pSwrCtx);}uint8_t  *data[1];data[0] = (uint8_t *)out;int len = swr_convert(m_pSwrCtx, data, 10000, (const uint8_t **)m_pPcmFrame->data, m_pPcmFrame->nb_samples);if(len <= 0){m_mutex.unlock();return 0;}int outsize = av_samples_get_buffer_size(NULL, avCodecCtx_Audio->channels,m_pPcmFrame->nb_samples,AV_SAMPLE_FMT_S16,0);m_mutex.unlock();return outsize;
}

FFmpeg类的封装,大致就是上面这几步。

之前的文章链接,大家可以点击参考:

FFmpeg开发(四)——Qt实现一个视频播放器(参考了暴风影音、迅雷影音)

FFmpeg开发(五)——Qt视频播放器之封装FFmpeg类(参考了暴风影音、迅雷影音)

FFmpeg开发(六)——Qt视频播放器之封装音频类(参考了暴风影音、迅雷影音)

FFmpeg开发(七)——Qt视频播放器之播放列表类(参考了暴风影音、迅雷影音)

FFmpeg开发(八)——Qt视频播放器之多线程的使用(参考了暴风影音、迅雷影音)

FFmpeg开发(九)——Qt视频播放器之快进滑动条(参考了暴风影音、迅雷影音)

下一篇为大家介绍

FFmpeg开发(六)——Qt视频播放器之封装音频类(参考了暴风影音、迅雷影音)

本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。

FFmpeg开发(五)——Qt视频播放器之封装FFmpeg类(参考了暴风影音、迅雷影音)相关推荐

  1. FFmpeg开发(六)——Qt视频播放器之封装音频类(参考了暴风影音、迅雷影音)

    FFmpeg开发(六)--Qt视频播放器之封装音频类(参考了暴风影音.迅雷影音) 上一篇介绍了,封装FFmpeg类. Qt视频播放器之封装FFmpeg类(参考了暴风影音.迅雷影音) 之前的链接大家可以 ...

  2. FFmpeg开发(八)——Qt视频播放器之多线程的使用(参考了暴风影音、迅雷影音)

    FFmpeg开发(八)--Qt视频播放器之多线程的使用(参考了暴风影音.迅雷影音) 上篇文章介绍了: FFmpeg开发(七)--Qt视频播放器之播放列表类(参考了暴风影音.迅雷影音) 本播放器系列相关 ...

  3. FFmpeg开发(七)——Qt视频播放器之播放列表类(参考了暴风影音、迅雷影音)

    FFmpeg开发(七)--Qt视频播放器之播放列表类(参考了暴风影音.迅雷影音) 上一篇分享了音频类的封装. FFmpeg开发(六)--Qt视频播放器之封装音频类(参考了暴风影音.迅雷影音) 本播放器 ...

  4. FFmpeg开发(九)——Qt视频播放器之快进滑动条(参考了暴风影音、迅雷影音)

    FFmpeg开发(九)--Qt视频播放器之快进滑动条(参考了暴风影音.迅雷影音) 上一篇介绍了使用多线程 FFmpeg开发(八)--Qt视频播放器之多线程的使用(参考了暴风影音.迅雷影音) 本播放器系 ...

  5. FFmpeg开发(四)——Qt实现一个视频播放器(参考了暴风影音、迅雷影音)

    FFmpeg开发(四)--Qt实现一个视频播放器(参考了暴风影音.迅雷影音) 本系列文章目录: FFmpeg开发(一)一Qt Creator配置FFmpeg FFmpeg开发(二)--(FFmpeg基 ...

  6. 从零开始学习音视频编程技术(七) FFMPEG Qt视频播放器之SDL的使用

    从零开始学习音视频编程技术(七) FFMPEG Qt视频播放器之SDL的使用 原文地址:http://blog.yundiantech.com/?log=blog&id=10 前面介绍了使用F ...

  7. 从零开始学习音视频编程技术(六) FFMPEG Qt视频播放器之显示图像

    从零开始学习音视频编程技术(六) FFMPEG Qt视频播放器之显示图像 原文地址:http://blog.yundiantech.com/?log=blog&id=9 前面讲解了如何用FFM ...

  8. FFmpeg开发笔记(七):ffmpeg解码音频保存为PCM并使用软件播放

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108799279 各位读者,知识无穷而人力有穷 ...

  9. 【Android FFMPEG 开发】Android Studio 工程配置 FFMPEG ( 动态库打包 | 头文件与函数库拷贝 | CMake 脚本配置 )

    文章目录 I . FFMPEG 交叉编译后的函数库及头文件 II . FFMPEG 静态库打包动态库 ( 仅做参考 ) III . 创建 Android Studio 工程 IV . FFMPEG 头 ...

最新文章

  1. Protobuf3语言指南
  2. IOS定位核心与地图
  3. opengl加载显示3D模型SIB类型文件
  4. 区分多种类型的输入输出
  5. Exceptionless 本地部署
  6. springboot超级详细的日志配置(基于logback)
  7. PHP如何实现HTML页面静态化
  8. 蓝色起源8月25日进行无人飞行,携带实验设备等上天
  9. mysql对所有id求积_sql 行列式 转换,
  10. 雪花算法生成一个id
  11. Spring AOP原理之动态代理
  12. oracle如何删除用户的表空间,Oracle 删除用户和表空间
  13. python全局变量(模块法和global)
  14. (目录)微气候模型ENVI-met模拟的基本使用教程
  15. C++对齐输出(左对齐和右对齐)
  16. java拆分_Java 拆分 PDF 文档
  17. 【Xmind】Linux
  18. testjs插件类库组织与管理
  19. 冠捷aoc显示器不满屏显示器上没有AUTO键解决
  20. 支持Jolla,支持Linux,支持开放,支持互联,Jolla屌丝逆袭

热门文章

  1. 【转载】PCB通流能力经验计算公式
  2. 有关单引号,双引号,三引号的使用方法
  3. 理化计算程序必备:CoolProp+UnitsNet
  4. Unity 模型尺寸自适应
  5. 归山深浅去,须尽沟壑美
  6. mysql查询 多门课程的平均成绩_Mysql_多表查询练习
  7. YzmCMS跨站脚本漏洞(CVE-2020-22394)复现
  8. 计算机硬盘管理 3t,3T硬盘的使用方法总结与分享
  9. 关于md编辑器就MYSQL保存文件解决方案
  10. 哈希表查找——成功和不成功时的平均查找长度