【FFmpeg】 音视频解码详细流程

2023-10-26

目录

一:视频解码流程

二:FFMPEG解码流程

三:FFmpeg解码函数 

四:FFmpeg解码的数据结构 

五:FFmpeg数据结构简介

六:FFmpeg数据结构分析

七:像素数据转换 

八:FFMPEG解码

九:FFMPEG解码-视频播放 


一:视频解码流程

1.1 纯净的视频解码流程

压缩编码数据->像素数据。例如解码H.264,就是“H.264码流->YUV”

1.2 一般的视频解码流程

视频码流一般存储在一定的封装格式(例如MP4、AVI等)中。

封装格式中通常还包含音频码流等内容。

对于封装格式中的视频,需要先从封装格式中提取中视频码流,然后再进行解码。

例如解码MKV格式的视频文件,就是“MKV->H.264码流->YUV”。

二:FFMPEG解码流程

三:FFmpeg解码函数 

av_register_all():注册所有组件
avformat_open_input():打开输入视频文件
avformat_find_stream_info():获取视频文件信息
avcodec_find_decoder():查找解码器
avcodec_open2():打开解码器
av_read_frame():从输入文件读取一帧压缩数据
avcodec_decode_video2():解码一帧压缩数据
avcodec_close():关闭解码器
avformat_close_input():关闭输入视频文件

四:FFmpeg解码的数据结构 

五:FFmpeg数据结构简介

AVFormatContext

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

AVInputFormat

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

AVStream[2]

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

 AVCodecContext

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

AVCodec

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

AVPacket

存储一帧压缩编码数据。

AVFrame

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

六:FFmpeg数据结构分析

七:像素数据转换 

解码后YUV格式的视频像素数据保存在AVFrame的data[0]、data[1]、data[2]中。

但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素 。

以亮度 Y 数据为例 , data[0] 中一共包含了linesize[0]* height个数据。

但是出于优化等方面的考虑,linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。

因此需要使用sws_scale()进行转换。转换后去除了无效数据,width和linesize[0]取值相等。

八:FFMPEG解码

实现步骤

1.注册所有组件

av_register_all();

2.打开视频输入文件 

    //文件路径设置                        程序运行当前路径-exe所存在的路径
    QString filename = QCoreApplication::applicationDirPath();
    qDebug()<<"获取程序运行目录 "<<filename;
    //文件名称中文乱码-建议使用英文
    QString cinputFilePath = "test.avi";  //本地视频文件放入程序运行目录
    //指针开空间
    avformat_context = avformat_alloc_context();
    //参数一:封装格式上下文->AVFormatContext->包含了视频信息(视频格式、大小等等...)
    //参数二:打开文件(入口文件)->url
    qDebug()<<"打开"<<videoname<<"视频文件进行播放";
    //打开文件把文件详细信息传入avformat_context
    //形参代表什么 返回值什么含义                                       标准C++的String类型转C的char*类型
    int avformat_open_result = avformat_open_input(&avformat_context,videoname.toStdString().c_str(),NULL,NULL);
    if (avformat_open_result != 0)
    {
        //获取异常信息--打开视频文件失败--具体失败原因
        char* error_info = new char[32];
        av_strerror(avformat_open_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    };

3.查找视频流信息 

    //参数一:封装格式上下文->AVFormatContext
    //参数二:配置
    //返回值:0>=返回OK,否则失败                  查找流信息
    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, NULL);
    if (avformat_find_stream_info_result < 0){
        //获取失败--没有找到流信息
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }

4.查找解码器 

    //第一点:获取当前解码器是属于什么类型解码器->找到了视频流
    //音频解码器、视频解码器、字幕解码器等等...
    //获取视频解码器流引用
    av_stream_index = -1;
    //循环遍历流信息
    for (int i = 0; i < avformat_context->nb_streams; ++i) {
        //循环遍历每一流
        //视频流、音频流、字幕流等等...                            查找视频流
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            //找到了
            av_stream_index = i;
            break;
        }
    }
    if (av_stream_index == -1)
    {
        qDebug()<<QString("没有找到视频流");
    }
    //第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    //编解码器上下文---是否有合适的编解码器
    avcodec_context = avformat_context->streams[av_stream_index]->codec;

    //第三点:根据解码器上下文->获取解码器ID
    avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    if (avcodec == NULL)
    {
        qDebug()<<QString("没有找到视频解码器");
    }

5.打开解码器 

    int avcodec_open2_result = avcodec_open2(avcodec_context,avcodec,NULL);
    if (avcodec_open2_result != 0)
    {
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }

    qDebug()<<"视频详细信息输出";
    //此函数自动打印输入或输出的详细信息--退出时候才会有信息显示
    av_dump_format(avformat_context, 0, cinputFilePath.toStdString().c_str(), 0);
    qDebug()<<"----------------解码准备工作完成-----------------";

6.循环解码 

    //读取帧数据换成到哪里->缓存到packet里面
    av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    //输入->环境一帧数据->缓冲区->类似于一张图
    pFramein = av_frame_alloc();
    //输出->帧数据->数据格式->RGB
    pFrameRGB = av_frame_alloc();
    //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
    //缓冲区分配内存
    pOutbuffer = (uint8_t *)av_malloc(avpicture_get_size(
                                          AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height));
    //初始化缓冲区 类似于内存的memset-开辟完清理操作
    avpicture_fill((AVPicture *)pFrameRGB, pOutbuffer,
                   AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height);

    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)
    //    int current_frame_index = 0;

    //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
    //准备一个视频像素数据格式上下文
    //参数一:输入帧数据宽
    //参数二:输入帧数据高
    //参数三:输入帧数据格式
    //参数四:输出帧数据宽
    //参数五:输出帧数据高
    //参数六:输出帧数据格式->AV_PIX_FMT_RGB32
    //参数七:视频像素数据格式转换算法类型
    //参数八:字节对齐类型(C/C++里面)->提高读取效率
    SwsContext* pSwsContext = sws_getContext(avcodec_context->width,
                                             avcodec_context->height,
                                             avcodec_context->pix_fmt,
                                             avcodec_context->width,
                                             avcodec_context->height,
                                             AV_PIX_FMT_RGB32,
                                             SWS_BICUBIC,NULL,NULL,NULL);
    int ret;//解码结果--处理出来的图片

    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)--在循环体外进行操作
    int current_frame_index = 0;

    //线程标志位
    while (m_stop == false)
    {
        //>=0:说明有数据,继续读取   <0:说明读取完毕,结束
        //从视频文件上下文中读取包--- 有数据就一直读取
        //判断--有数据的话才会一直读取
        if (av_read_frame(avformat_context,av_packet) >= 0)
        {
            //解码什么类型流(视频流、音频流、字幕流等等...)
            if (av_packet->stream_index == av_stream_index)
            {
                //解码一帧视频数据
                avcodec_send_packet(avcodec_context, av_packet);

                //接收一帧数据->解码一帧      处理出来的图片存储到pFramein
                ret = avcodec_receive_frame(avcodec_context,pFramein);
                //处理出来的图片是否可行
                if (ret == 0)//解码成功
                {
                    //图片的转换的相关操作  输入pFramein 输出pFrameRGB
                    sws_scale(pSwsContext, (const unsigned char* const*)pFramein->data, pFramein->linesize, 0, avcodec_context->height,
                              pFrameRGB->data,  pFrameRGB->linesize);

                    QImage  *tmpImg  = new QImage((uchar *)pOutbuffer, avcodec_context->width,
                                                  avcodec_context->height,QImage::Format_RGB32);

                    QImage image=tmpImg->copy();

                    qDebug()<<"接收图片信号"<<image;
                    //解码得到的
                    //一部分做显示  发送信号(图片)--emit
                    emit sigGetOneFrame(image);
                    //一部分做编码
                    //循环 编码一帧数据
                    pvideoCode->codeingOneFrame(pFramein);

                    //遍历每一帧的信息进行打印
                    current_frame_index++;
                    //发送信号-emit 解码信息放在窗口中进行显示
                    emit SendOneData(current_frame_index);
                    //延时操作  1秒显示25帧--1000/25=40
                    QThread::msleep(40);
                    //获取的视频信息
                    qDebug()<<QString("当前遍历第 %1 帧").arg(current_frame_index);
                }
            }
        }

        av_free_packet(av_packet);

7.关闭所有解码组件 

    av_packet_free(&av_packet);
    //关闭流
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);

九:FFMPEG解码-视频播放 

线程简介

线程(英语:thread)是操作系统能够进行运算调度的最小单位。

它被包含在进程之中,是进程中的实际运作单位。

一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程状态 

线程启动和停止 

线程启动调用start()函数。

RUN函数执行完表示线程退出。

QThread 使用测试

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【FFmpeg】 音视频解码详细流程 的相关文章

  • 如何使用meta-toolchain-qt5构建Qt(带有QtWebEngine支持)?

    我正在尝试使用构建 Qtmeta toolchain qt5 但是当我通过这样做时poky glibc x86 64 meta toolchain qt5 cortexa7hf vfp vfpv4 neon toolchain 2 0 1
  • 从 Qt4 中的文本文件中逐字读取

    我想在 Qt4 中逐字读取一个文本文件 说实话我对它很陌生 我想在另一个文件中每行一个字写入 我可以在 C 中做到这一点 没有任何问题 但是当我尝试在 Qt4 中使用 ifstream 和 ofstream 时 我遇到了错误 这是我的 Qt
  • 静态变量中的 qt tr()

    我在 qt 中的翻译方面遇到问题 我的项目中的所有翻译都工作正常 但有一个翻译位于类的静态变量中 相应部分代码如下 头文件类似于这样 typedef struct int type QString problematicString inf
  • 安装J语言的JQt IDE,出现错误

    我一直按照这里的说明进行操作 http code jsoftware com wiki System Installation Linux http code jsoftware com wiki System Installation L
  • 如何消除 QTableWidget 中的空白?

    How do I get rid of the whitespace in my application 我想摆脱 QTableWidget 中的空白 蓝色箭头 我该怎么做 这是我的应用程序的代码 gridLayout QGridLayou
  • 如何在qt中创建正确的退出按钮

    我正在尝试创建一个退出按钮来正确关闭我在 QT 中制作的 GUI 我尝试通过以下方式执行此操作 include
  • 如何从 matlab 调用 Qtproject?

    我在 matlab 中有一个函数可以写入一个 file txt 我在 qt 项目中使用它 So 当我使用 unix 获取要运行的 qt 编译可执行文件时 我有一个 Matlab 文件 但出现错误 代码 unix home matt Desk
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • 如何在 Qt simple 上解密/加密某些字符串(例如密码)

    这是我得到的 Qt SDK版本4 6 2 视窗XP 问题 我怎样才能简单地加密和简单地加密QString价值 我需要它能够将一些加密的字符串保存到 INI 文件中 并在重新打开应用程序后将字符串加密为正常的密码字符串值 PS 我正在寻找简单
  • 将 C++ 代码(本机客户端)移植到浏览器(Web 应用程序)

    我有一个使用 Qt creator SDK 编写的 C 模块 我想将此代码移植到任何网页上运行 而不会对最终用户损害源代码 用户应该能够在任何浏览器 Chrome Firefox Safari Explorer 上看到此模块的输出 而无需安
  • QPainterPath::arcTo 上的角度如何解释?

    我正在开发图形编辑器的功能 在其中编辑弧线 当形状是椭圆形时 QPainterPath arcTo 的行为并不像我预期的那样 当它是一个圆圈时 它会按预期工作 下面的两张图片显示了结果 在第一种情况下 我创建了一个圆 然后将其转换为初始起始
  • Android 版 Qt 和 BoringSSL

    我正在开发一个基于 Qt 的 Android 应用程序 它使用 QSslSocket 下载数据 由于 Android 从 OpenSSL 转向 BoringSSL 因为依赖 OpenSSL 库的 Marshmallow Qt 程序在 And
  • 在 Windows 上从源代码构建 PhantomJS-2

    我正在尝试基于这些在 Windows 8 1 x64 上从源代码构建 PhantomJS 2 的开发版本指示 https github com ariya phantomjs wiki PhantomJS 2 但是我收到以下错误 mingw
  • 如何使用 qt 库中的调试符号为 qt 5.5 创建开发 shell

    我有一个开发外壳buildInputs条目包括qt55 qtbase 这很好用 今天 我在 qt 库中发生了段错误 我想要带有调试符号的 qt 库 我看了一下nixpkgs pkgs development libraries qt 5 5
  • 在 Windows 上以 QML 播放 RTSP 视频

    我正在尝试将 QML 中的 RTSP 流播放到视频标签中 如下所示 Repeater model 8 Video Layout fillWidth true Layout fillHeight true fillMode VideoOutp
  • 在信号/槽处理期间删除 QObject

    我知道从槽处理中删除 QObject 可能会使应用程序崩溃 因为它可能有其他排队的事件 因此 我将使用 obj gt deleteLater 而不是使用 delete obj 据我所知 obj 等待处理所有排队的事件 然后 删除 obj Q
  • QGraphicsSimpleTextItem“无效使用不完整类型”

    我的代码如下 指针部件 h QGraphicsSimpleTextItem text 指针控件 cpp void PointerWidget placeNumbers float spacing int currentTickNumber
  • MapItemView 在 dataChanged 信号后不会更新

    我正在使用 QMLMapItemView使用 C 的组件QAbstractListModel基于模型 这MapItemView当模型重置时 或者每当添加新项目或删除现有项目时 工作正常 但是 那MapItemView不反映对已添加项目的更改
  • 如何通过信号和槽传递参数?

    我的 GUI 包括LineEdit and a 按钮 当 的时候按钮单击后 插槽clicked 叫做 我想在之间建立信号槽关系clicked 作为信号和doSomething 作为插槽 问题是doSomething 无权访问 UI 并且do
  • 如何从键盘为 QTableWidget 创建信号?

    我有一张桌子 可以通过左 右 上 下按钮在里面移动 现在 当我停留在某个单元格并按空格键时 我需要创建一个信号 该信号还应该带来该单元格的坐标 我尝试使用 QTableWidget 的标准信号 但它不起作用 我该如何解决这个问题 创建一个单

随机推荐

  • 数学是成就卓越开发人员的必备技能

    本文转载至 http blog jobbole com 444 编者按 原文作者Alan Skorkin是一名软件开发人员 他在博客中分享对软件开发相关的心得 其中有很多优秀的文章 本文就是其中一篇 作者认为 成为优秀的开发人员 可以没有数
  • javaEE企业级框架ssm知识点整合【思维导图】

    ssm Spring SpringMVC Mybatis 框架是轻量级javaEE应用开发最受欢迎的一种组合框架之一 使用这种框架的项目使JavaEE架构具有高度可维护性和可扩展性 同时极大地提高了项目的开发效率 降低了开发和维护的成本 而
  • webkit和webkit2的区别

    转自 http blog csdn net shunzi 1984 article details 6196483 原文地址 https trac webkit org wiki WebKit2 webkit2为了在API层支持多进程改变了
  • Linux “/“ 分区扩容

    前言 扩容是一项很简单的工作 但是有时候因为长时间没有操作过扩容 指令会比较生疏 因此写一篇扩容的文档 方便在再次失忆的情况下能快速回忆起操作流程 逻辑卷扩容的流程 创建PV gt 扩容VG gt 扩容LV 以下是扩容的详细流程 1 查看当
  • 人工智能梯度下降的优化器SGD、Momentum、AdaGrad、Adam的数学原理以及无框架实现

    系列文章目录 人工智能 梯度下降的原理和手写实现 文章目录 系列文章目录 前言 一 梯度下降优化器是什么 二 SGD优化方法 1 SGD是什么 2 SGD的数学原理 3 SGD的实现 4 SGD的缺陷 三 Momentum优化方法 1 Mo
  • 为什么公司规定所有接口都必须加上分布式锁,你知道吗?

    上一篇文章我们聊了聊Redisson这个开源框架对Redis分布式锁的实现原理 如果有不了解的兄弟可以看一下 都2022年了 出去面试连分布式锁的源码你都不会画 今天就给大家聊一个有意思的话题 每秒上千订单场景下 如何对分布式锁的并发能力进
  • 如何通过代码获取framedebugger里面的drawcall信息

    最近想做个性能工具 用来分析当前drawcall里面的具体调用 不知道unity有没有获取数据的具体接口 不过framedebugger里面的确有相关数据 这是方案一 另外一个方案是hook 理论上应该参考下renderdoc的实现应该就可
  • 使用scrapy爬取数据

    安装scrapy 使用清华镜像 打开PyCharm 安装scrapy框架 pip install i https pypi tuna tsinghua edu cn simple scrapy 新建一个名为python scrapy的项目
  • 深入浅出图解CNN-卷积神经网络

    首先 介绍一下卷积的来源 它经常用在信号处理中 用于计算信号的延迟累积 假设一个信号发生器每个时刻t产生一个信号xt 其信息的衰减率为wk 即在k 1个时间步长后 信息为原来的wk 倍 假设w1 1 w2 1 2 w3 1 4 时刻t收到的
  • linux查找目录下的所有文件中是否含有某个字符串

    Linux查找文件内容的常用命令方法 从文件内容查找匹配指定字符串的行 grep 被查找的字符串 文件名 例子 在当前目录里第一级文件夹中寻找包含指定字符串的 in文件 grep thermcontact in 从文件内容查找与正则表达式匹
  • [论文阅读]《Database Maanagement Systems》-第六章

    第六章 QUERY BY EXAMPLE QBE 查询示例 QBE P201 P216 Example is always more efficacious than precept 身教胜于言教 榜样总是比教训更有效 precept 规则
  • openGL之API学习(一七三)glsl如何设置版本version和兼容性

    version 120 version 120 core version 120 compatibility version 300 es GLSL ES 提供了一个 version 指令来指定着色器使用的GLSL ES的版本 如果不指定G
  • c++ 日志输出库 spdlog 简介

    spdlog是一个开源的 快速的 仅有头文件的C 11 日志库 它提供了向流 标准输出 文件 系统日志 调试器等目标输出日志的能力 它支持的平台包括Windows Linux Mac Android iOS 官方参考 https githu
  • 后缀自动机(SAM)——黑盒使用方案

    首先讲下后缀自动机吧 会写一下部分必要的原理 其他的原理不做解释 代码未讲解的部分希望能当做黑盒来使用 既不了解具体原理但知道其性质以及如何使用 我实在是佩服发明出AC自动机 回文自动机 后缀自动机这人 前置知识 AC自动机中的Fail树
  • 如何使用Chrome浏览器模拟弱网情况

    点击谷歌浏览器图标 打开浏览器后 按下F12键 弹出开发者工具窗口 刷新网页 页面的加载速度为597ms 在开发者工具中 点击Online 在弹出的菜单中点击Slow 3G 慢速3G网络 重新加载网站 发现页面的加载速度变慢了 变成6 5s
  • openssl engine在tls中的应用

    openssl engine的实现和原理在上一篇文章 https blog csdn net liu942947766 article details 128837041 spm 1001 2014 3001 5502 openssl en
  • MATLAB随机生成m个三维坐标点,且各个坐标点之间的距离不小于n

    randi函数 randi max m n 生成均匀分布的随机整数 max生成的随机整数最大值 生成m行n列的矩阵 编写函数sampling function x y z sampling lowx upx lowy upy lowz up
  • 第五篇:进阶篇 发动机的噪声特性

    本专栏分享传统NVH知识点 从声学理论 材料声学 汽车噪声振动分析 车辆及其零部件甚至原材料的声学测试方法等多维度介绍汽车NVH 一些专用术语同时给出了中英文对照 欢迎新人 同行 爱好者一起交流 由于内容写的较为仓促 有误的地方欢迎大家批评
  • JS优化方法(使用最新的js方法)

    1 带有多个条件的if语句 将多个值放在一个数组中 然后调用数组的includes方法 longhand 直接的 if x abc x def x ghi x jkl logic 逻辑 shorthand 速记 if abc def ghi
  • 【FFmpeg】 音视频解码详细流程

    目录 一 视频解码流程 二 FFMPEG解码流程 三 FFmpeg解码函数 四 FFmpeg解码的数据结构 五 FFmpeg数据结构简介 六 FFmpeg数据结构分析 七 像素数据转换 八 FFMPEG解码 九 FFMPEG解码 视频播放