【FFmpeg+Qt开发】解码流程 详细分析+代码示例

4 篇文章 27 订阅
订阅专栏

目录

一、FFMPEG 概述

​二、FFMPEG 解码

2.1解码流程

2.2解码示例


一、FFMPEG 概述

  • FFmpeg 是一套可以用来记录、转换,数字音频、视频,并能将其转化为流的开源计算机程序。
  • FFmpeg 采用 LGPL GPL 许可证;它提供了录制、转换以及流化音视频的完整解决案;它还包含了非常先进的音频\视频编解码库 libavcodec,为了保证高可移植性和编解码质量,libavcodec 里很多 code 都是从头开发的。
  • FFmpeg Linux 平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括 WindowsMac OS X 等。
  • FFmpeg最早由 Fabrice Bellard 发起,2004 年至 2015 年间由 Michael Niedermayer 主要负责维护。许多 FFmpeg 的开发人员都来自 MPlayer 项目,而且当前 FFmpeg 也是放在MPlayer 项目组的服务器上。
  • FFmpeg名称来自 MPEG 视频编码标准,前面的"FF" 代表"Fast Forward"

下载链接: Download FFmpeg

具体下载详见: Qt+FFmpeg----windows下环境搭建_猿力猪的博客-CSDN博客_ffmpeg开发环境搭建

​二、FFMPEG 解码

2.1解码流程

🔴解码:将带有封装格式的视频解封装后得到的压缩码流数据(编码数据)经过解码得到像素数据的过程。
例如:解码 H.264 压缩码流数据得到 YUV (或者 RGB )的像素数据。
解码的流程图,如下所示:

🔴解码思路分析:

  1. 注册所有的组件  av_register_all()
  2. 打开视频文件  avformat_open_input()  有可能打开失败
  3. 获取视频信息 视频码流、音频码流、文字码流
  4. 查找流信息  avformat_find_stream_info()
  5. 找到解码器  avcodec_find_decoder()  有可能没找到
  6. 打开解码器  avcodec_open2()
  7. 读取码流中的一帧码流数据  av_read_frame()
  8. 解码读到一帧码流数据 得到一帧的像素数据  YUV RGB
  9. 重复7-8的动作 直到视频所有的帧都处理完
  10. 关闭解码器
  11. 关闭视频文件

🔴解码过程中几个重要的结构体:

  • AVFormatContext

封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息。

  • AVInputFormat//AVOutpufFormat

每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。

  • AVStream

视频文件中每个视频(音频)流对应一个该结构体。

  • AVCodecContext

编码器上下文结构体,保存了视频(音频)编解码相关信息。

  • AVCodec

每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。

  • AVPacket

存储一帧压缩编码数据。

  • AVFrame

存储一帧解码后像素(采样)数据。

2.2解码示例

🟢解码类的定义

//ffmpeg使用c语言实现的,引入用c写的代码就要用extern
extern "C"   
{
#include <libavcodec/avcodec.h>   //编码
#include <libavdevice/avdevice.h>  
#include <libavformat/avformat.h>  //封装格式处理
#include <libavutil/error.h>  
#include <libswscale/swscale.h>  //像素处理
#include <libswresample/swresample.h>  //缩放
}
class fdecode
{
public:
    fdecode();
    //注册组件
    void registerFFmpeg();
    //打开视频流
    void openVIdeoStream(QString filename);
    //视频名称
    QString filename;
protected:
private:
};

具体实现如下:

🟢注册所有组件

void fdecode::registerFFmpeg()
{
    //注册所有的组件
    av_register_all();
}

🟢打开视频文件

    AVFormatContext *forContent;//用来保存视频相关信息的结构体
    forContent=avformat_alloc_context();//分配空间

    //打开视频文件
    int res=avformat_open_input(&forContent,filename.toStdString().c_str(),nullptr,nullptr);
    if(res!=0)//判断是否打开视频文件
    {
        qDebug()<<"无法打开视频文件";
        return;
    }

🟢获取视频文件信息

    //打开视频文件成功,获取文件信息
    res = avformat_find_stream_info(forContent,nullptr);//查看有没有相关视频流信息
    if(res<0)
    {
        qDebug()<<"没有流媒体信息"<<endl;
        return;
    }

    //一个视频流有多股码流,存在forContentext中streams数组中
    int videoType=-1;
    //nb_streams代表封装格式里面的结构体信息有几个,正常两个:音频信息、视频信息
    for(int i=0;i<forContent->nb_streams;i++) //i小于流的个数
    {
        if(forContent->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)//视频流
        {
            videoType=i;//标识类型
            break;
        }
    }
    //判断是否有视频流信息
    if(videoType==-1)
    {
        qDebug()<<"没有视频流相关信息"<<endl;
        return;
    }

🟢根据编解码上下文中的编码ID查找对应解码器

    //编解码器对应的上下文对象结构体:保存解码器信息以及图形的宽高、像素信息
    AVCodecContext *codec=forContent->streams[videoType]->codec;
    //查找对应的视频流解码器
    AVCodec *decoder = avcodec_find_decoder(codec->codec_id);
    if(decoder==nullptr)//判断是否找到解码器
    {
        qDebug()<<"没有对应的解码器"<<endl;
        return;
    }

🟢打开解码器

    //找到了解码器,打开解码器
    res = avcodec_open2(codec,decoder,nullptr);
    if(res!=0)
    {
        qDebug()<<"解码器打开失败"<<endl;
        return;
    }

🟢读取准备 获取YUV和RGB像素数据

    //为准备读取帧数据做准备--AVPacket 用来存储一帧一帧的压缩数据(h264)
    AVPacket *pkt=nullptr;
    //设置缓冲区,开空间
    pkt=(AVPacket *) malloc(sizeof(AVPacket));
    int size=codec->width*codec->height;//计算一张图片数据大小
    av_new_packet(pkt,size);

    /* pictureRGB 保存解码后的RGB像素数据
        * pictureYUV 保存解码后的YUV像素数据
        * picture    保存未处理的像素数据
        */
    AVFrame *pictureRGB,*pictureYUV,*picture=nullptr;

    //内存分配
    pictureRGB=av_frame_alloc();
    pictureYUV=av_frame_alloc();
    picture=av_frame_alloc();

    //大小以及格式设置RGB
    pictureRGB->width=codec->width;//宽度
    pictureRGB->height=codec->height;//高度
    pictureRGB->format=codec->pix_fmt;//格式设置

    //大小以及格式设置YUV
    pictureYUV->width=codec->width;//宽度
    pictureYUV->height=codec->height;//高度
    pictureYUV->format=codec->pix_fmt;//格式设置

    //一帧码流数据解码后得到YUV RGB像素数据有多大
    int numByte_RGB=avpicture_get_size(AV_PIX_FMT_RGB32,codec->width,codec->height);
    int numByte_YUV=avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);

    //开的空间用来保存YUV RGB像素数据大小
    uint8_t *buffer_RGB=(uint8_t *)av_malloc(numByte_RGB*sizeof(uint8_t));
    uint8_t *buffer_YUV=(uint8_t *)av_malloc(numByte_YUV*sizeof(uint8_t));

    //像素数据的填充
    avpicture_fill((AVPicture *)pictureRGB,buffer_RGB,AV_PIX_FMT_RGB32,codec->width,codec->height);
    avpicture_fill((AVPicture *)pictureYUV,buffer_YUV,AV_PIX_FMT_YUV420P,codec->width,codec->height);

    //转换规则
    SwsContext *sws_RGB=nullptr;//保存转换规则的结构体
    SwsContext *sws_YUV=nullptr;//保存转换规则的结构体

    //转换规则的设置              AV_PIX_FMT_YUV420P   AV_PIX_FMT_RGB32
    sws_RGB=sws_getContext(codec->width,codec->height,codec->pix_fmt,
                           codec->width,codec->height,AV_PIX_FMT_RGB32,  //目标格式
                           SWS_BICUBIC,nullptr,nullptr,nullptr);         //转换规则
    sws_YUV=sws_getContext(codec->width,codec->height,codec->pix_fmt,
                           codec->width,codec->height,AV_PIX_FMT_YUV420P,//目标格式
                           SWS_BICUBIC,nullptr,nullptr,nullptr);         //转换规则
    
    //保存h.264压缩码流数据的文件
    FILE *fp=fopen("fileout/alenH264.h264","wb+");//文件名字可以自行定义
    //保存yuv像素数据的文件
    FILE *fp_yuv=fopen("fileout/alenyuv.yuv","wb+");//文件名字可以自行定义

🟢一帧一帧的读取压缩数据 保存码流数据和YUV、RGB像素数据

    int count=0;//存图片
    //一帧一帧的读取压缩数据
    while(av_read_frame(forContent,pkt)==0)//读到数据
    {
        if(pkt->stream_index == videoType)//判断一帧码流数据是不是需要得到的视频流
        {
            fwrite(pkt->data,pkt->size,1,fp);//写入文件中
            int got_picture_ptr=-1;
            //解码得到YUV
            res = avcodec_decode_video2(codec,picture,&got_picture_ptr,pkt);
            if(res < 0)
            {
                qDebug()<<"解码错误"<<endl;
                return;
            }
            //压缩码流数据,解码后的像素数据,判断有没有数据可以解码,对谁进行解码
            if(got_picture_ptr!=0)//解码操作
            {
                //把解码得到的坏数据剔除
                sws_scale(sws_RGB,picture->data,picture->linesize,0,picture->height,
                          pictureRGB->data,pictureRGB->linesize);//RGB
                sws_scale(sws_YUV,picture->data,picture->linesize,0,picture->height,
                          pictureYUV->data,pictureYUV->linesize);//YUV

                //输出的YUV文件
                //AVFrame像素帧写入文件
                fwrite(pictureYUV->data[0],size,1,fp_yuv);  //y数据
                fwrite(pictureYUV->data[1],size/4,1,fp_yuv);//u数据
                fwrite(pictureYUV->data[2],size/4,1,fp_yuv);//v数据

                //设置每25帧输出一张图片
                count++;
                if(count%25==0)
                {
                    QImage image((uchar *)pictureRGB->data[0],pictureRGB->width,pictureRGB->height,QImage::Format_RGB32);//像素数据
                    QString imageName = QString("image/test%1.jpg").arg(count);
                    image.save(imageName);//保存图片函数
                }
            }
        }
        //释放包以及AVFrame资源
        av_packet_unref(pkt);
        av_frame_unref(picture);
    }
    qDebug()<<"保存码流数据成功"<<endl;

🟢资源释放

    //关闭h.264
    fclose(fp);
    //关闭YUV文件
    fclose(fp_yuv);
    //释放AVFrame
    av_frame_free(&picture);
    //关闭解码器
    avcodec_close(codec);
    //释放视频信息结构体
    avformat_free_context(forContent);

🟢测试主函数代码,如下所示:

int main(int argc, char *argv[])
{
    //QApplication a(argc, argv);
    //Widget w;
    //w.show();
    fdecode *decode = new fdecode;
    decode->registerFFmpeg();
    decode->openVIdeoStream("filein/alen.avi");
    //return a.exec();
}

保存的H.264、yuv文件以及图片数据,如下所示:

🔵H.264、yuv文件

🔵图片数据(每25帧截图一张)

🔵 利用以下应用程序可以播放我们得到的H.264YUV文件

 

🔵来播放博主喜欢的日漫吧!

效果如下:

 

🚀​FFMPEG技术---环境配置,详见:

FFmpeg+Qt开发(一):Windows下 环境搭建 详细步骤_猿力猪的博客-CSDN博客_ffmpeg库

🚀​FFMPEG技术---编码流程,详见:

【FFmpeg+Qt开发】编码流程 普通视频编码+示例详解 一学就会_猿力猪的博客-CSDN博客

🚀​FFMPEG技术---转码流程,详见:

【FFmpeg+Qt开发】转码流程 H.264 转(mov、mp4、avi、flv)等视频格式 示例详解_猿力猪的博客-CSDN博客

✍ 本文主要介绍了FFmpeg技术中的解码部分,如有疑问,欢迎各位评论区学习交流!     

✍  觉得博主写的不错的,麻烦!😀点赞!😀评论!😀收藏!支持一下哈!蟹蟹你们! 

windows下Qt+ffmpeg+qsv解码显示
08-05
Qt+ffmpeg+qsv解码,包含所有依赖的库以及运行所需的库,以及解码+显示源代码整个工程。项目在Qt5.9.3下编译,64位,默认用Release编译。需要注意硬件是否支持QSV解码
基于qt+ffmpeg设计的推流软件(支持rtsp.rtmp)(推流桌面与摄像头)
11-03
这是Qt设计的推流软件,核心全部由ffmpeg实现,QT只是做了个界面没有做其他重要的事情。如果要利用ffmpeg开发推流软件,这个一份必看的好东西。目前实现了桌面捕获、摄像头捕获推流,全部由ffmpeg的API实现。
QT+ffmpeg+多线程的视频播放器的代码
01-15
QT+ffmpeg+多线程的视频播放器,实现边解码视频边在窗口播放的效果
(四)qt中使用ffmpeg播放视频,可暂停恢复
最新发布
m0_67254672的博客
04-11 561
qtffmpeg解码并播放视频
基于CUDA+ffmpeg+QT+QOpenGL的硬解码解决方案demo
12-18
使用户ffmpeg拉流,NVIDIA显卡进行视频解码,opengl做视频帧显示的硬解码demo
Qt开发ffmpeg流媒体播放器,支持截图、录像,支持音视频播放,支持本地文件播放、网络流播放
linyibin_123的博客
02-10 7466
本工程qt用的版本是5.8-32位,ffmpeg用的版本是较新的5.1版本。它支持TCP或UDP方式拉取实时流,实时流我采用的是监控摄像头的RTSP流。音频播放采用的是QAudioOutput,视频ffmpeg解码并由YUV转RGB后是在QOpenGLWidget下进行渲染显示。本工程的代码有注释,可以通过本博客查看代码或者在播放最后的链接处下载工程demo。
Qt+FFMPEG+SDL实现的视频播放器
03-08
Qt+FFMPEG+SDL实现的视频播放器 包含源代码和可执行程序
基于Qtffmpeg实现视频播放
06-02
基于Qtffmpeg实现视频播放,根据PaintEvent事件处理函数不停在组件中绘制视频帧数据
QtFFmpeg联合开发指南(一)——解码(1):功能实现
weixin_30924087的博客
04-29 379
前言:对于从未接触过音视频编解码的同学来说,使用FFmpeg的学习曲线恐怕略显陡峭。本人由于工作需要,正好需要在项目中使用。因此特地将开发过程总结下来。只当提供给有兴趣的同学参考和学习。 由于FFmpeg是使用C语言开发,所有和函数调用都是面向过程的。以我目前的学习经验来说,通常我会把一个功能的代码全部放在main函数中实现。经过测试和修改认为功能正常,再以C++面向对象的方式逐步将代码分解和封...
ffmpeg转码过程分析
xiaocao9903的专栏
02-04 1576
转 一,ffmpeg架构 1. 简介 FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG、 DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码. TCPM
FFmpeg API2
qq_37933895的博客
08-22 261
解码数据结构及函数总结 数据读取,主要关注在解码过程中所用到的FFmpeg中的结构体。 解码过程中所调用的函数 数据的读取 一个多媒体文件包含有多个流(视频流 video stream,音频流 audio stream,字幕等); 流是一种抽象的概念,表示一连串的数据元素; 流中的数据元素称为帧Frame。 也就是说多媒体文件中,主要有两种数据:流Stream 及其数据元素 帧Frame...
基于qtffmpeg视频播放器开发实战下载和目录
07-04
基于qtffmpeg视频播放器开发实战下载和目录 基于qtffmpeg视频播放器开发实战下载和目录
Qt+FFMPEG 实现简单的视频播放器
11-28
这是Qt的工程,建议使用Qt Creator 打开 从零开始学习音视频编程技术(二十二) 录屏软件开发之BUG修复 FFMPEG的版本是2.5.2 SDL的版本是2.04 记得将ffmpeg/bin目录下的dll文件拷贝到编译生成的exe所在的目录下,否则会无法运行。
Qt+FFMPEG解码视频并显示
09-07
是一个Windows下使用FFMPEG解码视频 并使用QPaint绘制到QWidget上 FFMPEG的版本是2.5.2 记得将ffmpeg/bin目录下的dll文件拷贝到编译生成的exe所在的目录下,否则会无法运行。 关于代码的解释 请参考: http://blog.yundiantech.com/?log=blog&id=9
QT使用FFmpeg播放视频
12-24
QT5.8加载 ffmpeg,并有详细注释,一个最简单的是使用ffmpeg的DEMO
ffmpeg+qt代码示例
03-13
ffmpeg+qt代码示例
ffmpeg+QT+多线程视频播放器
06-13
一个自己做的基于ffmpeg+QT+多线程知识的播放器代码开发环境为VS2017,解压后双击sln文件后编译成功可以直接运行,该播放器为入门代码,大佬勿喷
Qt+FFmpeg】 - FFmpeg解码详细流程
m0_60259116的博客
09-28 2792
FFmpeg解码详细流程
Qt实现 基于ffmpeg拉流播放视频
c_shell_python的博客
11-15 4670
1、前言 ffmpeg作为开源库,具备跨平台性,被广泛使用于各大视频软件和网站,在视音频开发中占有极其重要的地位。 Qt同样支持跨平台,因此结合qt+ffmpeg制作跨平台视频播放器是比较合适的做法。 2、思路 目前有一个界面类和解码类,其中界面类负责视频的显示工作,解码类的解码过程运行在子线程中,避免解码在主线程中进行,影响界面播放效果。二者的信息交互通过qt的信号槽关联,视频的绘制则采用QPainter绘制。 3、采用技术如下 1、QThread线程类 2、QPainter绘制类 3、F
qt ffmpeg
08-02
Qt是一个跨平台的C++应用程序开发框架,而FFmpeg是一个开源的音视频处理库。在Qt中使用FFmpeg可以实现音视频的录制、转换和流化等功能。通过引入FFmpeg的头文件和库文件,可以在Qt中调用FFmpeg的函数来实现相关的功能。在Qt中使用FFmpeg需要先注册FFmpeg组件,然后可以打开视频流进行处理。具体的代码实现可以参考引用[2]和引用[3]中的示例代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 【OpenCV】车辆识别 C++ OpenCV 原理介绍 + 案例实现 57669
  • 【ROS】OpenCV+ROS 实现人脸识别(Ubantu16.04) 16702
  • 【Linux学习】虚拟机VMware 安装Qt5 一条龙讲解 14557
  • 【OpenCV】Qt + OpenCV 开发配置 + 入门知识(代码示例) 12333
  • 【ROS】虚拟机VMware 安装ROS 一条龙教程+部分报错解决 11799

分类专栏

  • 面试题葵花宝典 2篇
  • Python学习
  • OpenCV 5篇
  • ROS学习 2篇
  • Linux网络编程 6篇
  • FFmpeg音视频编解码 4篇
  • Linux基础 3篇
  • Qt学习 2篇
  • SQLite学习 1篇
  • 牛客刷题

最新评论

  • 【FFmpeg+Qt开发】解码流程 详细分析+代码示例

    事在人wёi: 博主你好,可以分享一下eseye_u.exe这个软件吗?

  • 【OpenCV】车辆识别 目标检测 级联分类器 C++ 案例实现

    老朱自强不息: 这个级联分类器怎么定义的,方便分享一下cars.xml吗?

  • 【ROS】OpenCV+ROS 实现人脸识别(Ubantu16.04)

    重C开始: 我也想问~^~

  • 【ROS】OpenCV+ROS 实现人脸识别(Ubantu16.04)

    淮南385: 启动摄像头后输入了source 还是报错

  • 【ROS】OpenCV+ROS 实现人脸识别(Ubantu16.04)

    一李导: 最后执行三条命令,没有框子怎么搞表情包

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • 【秋招面试题】C++ 网络编程 面试题库(二)
  • 【秋招面试题】C++ 网络编程 面试题库(一)
  • 【OpenCV】红绿灯识别 轮廓识别 C++ OpenCV 案例实现
2022年24篇

目录

目录

评论 62
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿力猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

聚圣源欧洲杯半决赛企业起名测算俊与浚哪个起名好重生日本做阴阳师公司起名忌讳什么系列产品起名荷花怎么画有限合伙起名金牛座今日运势查询子女起名字飞夺泸定桥电影天通苑西三区关于起名字的俏江南加盟社区物业公司起名南方有乔木小说蔡起名字石原夏织百草园干果坊牛宝宝起名字男孩2021免费67岁孕妇产下女婴假如给我三天光明简介身无分文一天赚200099re6在线视频精品免费全国qq等级排行榜中银协回应银行员工拒喝敬酒被打昌字起名男孩水产企业起名大全免费司马迁史记产业园起名淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男孩疑遭霸凌 家长讨说法被踢出群国产伟哥去年销售近13亿网友建议重庆地铁不准乘客携带菜筐雅江山火三名扑火人员牺牲系谣言代拍被何赛飞拿着魔杖追着打月嫂回应掌掴婴儿是在赶虫子山西高速一大巴发生事故 已致13死高中生被打伤下体休学 邯郸通报李梦为奥运任务婉拒WNBA邀请19岁小伙救下5人后溺亡 多方发声王树国3次鞠躬告别西交大师生单亲妈妈陷入热恋 14岁儿子报警315晚会后胖东来又人满为患了倪萍分享减重40斤方法王楚钦登顶三项第一今日春分两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?周杰伦一审败诉网易房客欠租失踪 房东直发愁男子持台球杆殴打2名女店员被抓男子被猫抓伤后确诊“猫抓病”“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火男孩8年未见母亲被告知被遗忘恒大被罚41.75亿到底怎么缴网友洛杉矶偶遇贾玲杨倩无缘巴黎奥运张立群任西安交通大学校长黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发妈妈回应孩子在校撞护栏坠楼考生莫言也上北大硕士复试名单了韩国首次吊销离岗医生执照奥巴马现身唐宁街 黑色着装引猜测沈阳一轿车冲入人行道致3死2伤阿根廷将发行1万与2万面值的纸币外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万手机成瘾是影响睡眠质量重要因素春分“立蛋”成功率更高?胖东来员工每周单休无小长假“开封王婆”爆火:促成四五十对专家建议不必谈骨泥色变浙江一高校内汽车冲撞行人 多人受伤许家印被限制高消费

聚圣源 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化