当前位置: 首页 > news >正文

FFmpeg解码详细流程

介绍

  1. FFmpeg的 libavcodec 模块完成音视频多媒体的编解码模块。
  2. 老版本的 FFmpeg 将avcodec_decode_video2()作为视频的解码函数 API,将avcodec_decode_audio4()作为音频的解码函数 API;从 3.4版本开始已经将二者标记为废弃过时 API(attribute_deprecated)。
  3. 新版本 FFmpeg 将 avcodec_send_packet()avcodec_receive_frame() 作为音视频的解码函数 API,但同时仍然保留了对老接口的兼容,通过avcodec_decode_video2()、avcodec_decode_audio4()调用 compat_decode()完成对新 API 的封装。
//具体可以参考 FFmpeg 中 doc/APIchanges 中的记录.2016-04-21 - 7fc329e - lavc 57.37.100 - avcodec.hAdd a new audio/video encoding and decoding API with decoupled inputand output -- avcodec_send_packet(), avcodec_receive_frame(),avcodec_send_frame() and avcodec_receive_packet().2017-09-26 - b1cf151c4d - lavc 57.106.102 - avcodec.hDeprecate AVCodecContext.refcounted_frames. This was useful for deprecatedAPI only (avcodec_decode_video2/avcodec_decode_audio4). The new decode APIs(avcodec_send_packet/avcodec_receive_frame) always work with referencecounted frames.

视频解码详细流程

  1. 以解码 H264标准为例,从 main 函数到最后的 MB 宏块解码。
    在这里插入图片描述

解码的核心 API 介绍

avcodec_send_packet()

  1. API 申明介绍
 * @param avctx codec context* @param[in] avpkt The input AVPacket. Usually, this will be a single video*                  frame, or several complete audio frames.*                  Ownership of the packet remains with the caller, and the*                  decoder will not write to the packet. The decoder may create*                  a reference to the packet data (or copy it if the packet is*                  not reference-counted).*                  Unlike with older APIs, the packet is always fully consumed,*                  and if it contains multiple frames (e.g. some audio codecs),*                  will require you to call avcodec_receive_frame() multiple*                  times afterwards before you can send a new packet.*                  It can be NULL (or an AVPacket with data set to NULL and*                  size set to 0); in this case, it is considered a flush*                  packet, which signals the end of the stream. Sending the*                  first flush packet will return success. Subsequent ones are*                  unnecessary and will return AVERROR_EOF. If the decoder*                  still has frames buffered, it will return them after sending*                  a flush packet.** @return 0 on success, otherwise negative error code:*      AVERROR(EAGAIN):   input is not accepted in the current state - user*                         must read output with avcodec_receive_frame() (once*                         all output is read, the packet should be resent, and*                         the call will not fail with EAGAIN).*      AVERROR_EOF:       the decoder has been flushed, and no new packets can*                         be sent to it (also returned if more than 1 flush*                         packet is sent)*      AVERROR(EINVAL):   codec not opened, it is an encoder, or requires flush*      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar*      other errors: legitimate decoding errors*/
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
  1. avcodec_send_packet()定义解析
    该 API 可以理解为 FFmpeg 完成音视频解码的核心函数,从函数的实现中可以看到,首先会调用av_bsf_send_packet()函数将输入的 AVPacket 拷贝到 buffer 中。最后当 buffer_frame为空的时候,就调用decode_receive_frame_internal()函数完成真正的解码。

    decode_receive_frame_internal()内部会调用decode_simple_receive_frame()完成解码。

    decode_simple_receive_frame()内部会调用decode_simple_internal()完成解码。

    而在decode_simple_internal()通过函数指针(*decode())指向不同的解码器完成解码不同标准的解码过程;如果涉及到多线程解码,还会涉及到ff_thread_decode_frame(),多线程解码不详细介绍。
    在这里插入图片描述

  2. (*decode())函数指针
    在 decode_simple_internal()通过ret = avctx->codec->decode(avctx, frame, &got_frame, pkt)实现对不同解码器的调用,完成具体的解码过程。

    以h264解码为例,h264_decode_frame()完成了具体的解码过程,其中核心函数是 decode_nal_units()进行 NAUL 解码;其中ff_H2645_packet_split()进行码流解析, ff_h264_execute_decode_slices()进行 Slice 级解码。

    在ff_h264_execute_decode_slices()核心函数是 decode_slice(),对每个 Slice 进行解码。

    decode_slice()中是视频数据的核心解码过程,主要分成四个模块:熵解码宏块解码环路滤波错误隐藏。其中 ff_h264_decode_mb_cabac()完成熵解码(如果熵解码是 cavlc,则会ff_h264_decode_mb_cavlac 完成熵解码);ff_h264_decode_mb()完成宏块 MB 解码;loop_filter()完成环路滤波;er_add_slice()完成错误隐藏处理。
    在这里插入图片描述

avcodec_receive_frame()

  1. API 申明介绍
/*** Return decoded output data from a decoder.//从解码器返回解码后的输出数据** @param avctx codec context* @param frame This will be set to a reference-counted video or audio*              frame (depending on the decoder type) allocated by the*              decoder. Note that the function will always call*              av_frame_unref(frame) before doing anything else.** @return*      0:                 success, a frame was returned*      AVERROR(EAGAIN):   output is not available in this state - user must try*                         to send new input*      AVERROR_EOF:       the decoder has been fully flushed, and there will be*                         no more output frames*      AVERROR(EINVAL):   codec not opened, or it is an encoder*      AVERROR_INPUT_CHANGED:   current decoded frame has changed parameters*                               with respect to first decoded frame. Applicable*                               when flag AV_CODEC_FLAG_DROPCHANGED is set.*      other negative values: legitimate decoding errors*/
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
  1. avcodec_receive_frame()定义解析
    从avcodec_receive_frame()函数内部实现可以看到,核心逻辑相对简单,先判断buffer_frame里是否有数据,如果有直接调用av_frame_move_ref()完成frame 拷贝过程,解码后数据从buffer_frame拷贝到 frame 中(即把数据从 AVCodecContext 中拷贝到 AVFrame 中);如果 buffer_frame中没有数据,则需要调用decode_receive_frame_internal()完成具体的解码,该步骤和 send packet 模块相同。
    在这里插入图片描述

官方解码实例

/** Copyright (c) 2001 Fabrice Bellard** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to deal* in the Software without restriction, including without limitation the rights* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell* copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN* THE SOFTWARE.*//*** @file* video decoding with libavcodec API example** @example decode_video.c*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavcodec/avcodec.h>#define INBUF_SIZE 4096static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,char *filename)
{FILE *f;int i;f = fopen(filename,"w");fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);for (i = 0; i < ysize; i++)fwrite(buf + i * wrap, 1, xsize, f);fclose(f);
}static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,const char *filename)
{char buf[1024];int ret;ret = avcodec_send_packet(dec_ctx, pkt);if (ret < 0) {fprintf(stderr, "Error sending a packet for decoding\n");exit(1);}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0) {fprintf(stderr, "Error during decoding\n");exit(1);}printf("saving frame %3d\n", dec_ctx->frame_number);fflush(stdout);/* the picture is allocated by the decoder. no need tofree it */snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);pgm_save(frame->data[0], frame->linesize[0],frame->width, frame->height, buf);}
}int main(int argc, char **argv)
{const char *filename, *outfilename;const AVCodec *codec;AVCodecParserContext *parser;AVCodecContext *c= NULL;FILE *f;AVFrame *frame;uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data;size_t   data_size;int ret;AVPacket *pkt;if (argc <= 2) {fprintf(stderr, "Usage: %s <input file> <output file>\n""And check your input file is encoded by mpeg1video please.\n", argv[0]);exit(0);}filename    = argv[1];outfilename = argv[2];pkt = av_packet_alloc();if (!pkt)exit(1);/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);/* find the MPEG-1 video decoder */codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}parser = av_parser_init(codec->id);if (!parser) {fprintf(stderr, "parser not found\n");exit(1);}c = avcodec_alloc_context3(codec);if (!c) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* For some codecs, such as msmpeg4 and mpeg4, width and heightMUST be initialized there because this information is notavailable in the bitstream. *//* open it */if (avcodec_open2(c, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}f = fopen(filename, "rb");if (!f) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}while (!feof(f)) {/* read raw data from the input file */data_size = fread(inbuf, 1, INBUF_SIZE, f);if (!data_size)break;/* use the parser to split the data into frames */data = inbuf;while (data_size > 0) {ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {fprintf(stderr, "Error while parsing\n");exit(1);}data      += ret;data_size -= ret;if (pkt->size)decode(c, frame, pkt, outfilename);}}/* flush the decoder */decode(c, frame, NULL, outfilename);fclose(f);av_parser_close(parser);avcodec_free_context(&c);av_frame_free(&frame);av_packet_free(&pkt);return 0;
}

参考

  1. http://ffmpeg.org/

相关文章:

FFmpeg解码详细流程

介绍 FFmpeg的 libavcodec 模块完成音视频多媒体的编解码模块。老版本的 FFmpeg 将avcodec_decode_video2()作为视频的解码函数 API&#xff0c;将avcodec_decode_audio4()作为音频的解码函数 API&#xff1b;从 3.4版本开始已经将二者标记为废弃过时 API&#xff08;attribut…...

人工智能的缺陷

首先从应用层面理解什么是人工智能&#xff0c;目前人工智能主流应用面包括&#xff1a;自然语言处理领域&#xff0c;代表为chatgpt&#xff0c;我们能用其进行日常交流&#xff0c;问题答疑&#xff0c;论文书写等。计算机视觉领域&#xff0c;代表为人脸识别&#xff0c;现在…...

基于ASP.NET MVC开发的、开源的个人博客系统

推荐一个功能丰富、易于使用和扩展的开源博客&#xff0c;可以轻松地创建和管理自己的博客。 项目简介 基于.Net Framework 4.5开发的、开源博客系统&#xff0c;具有丰富的功能&#xff0c;包括文章发布、分类、标签、评论、订阅、统计等功能&#xff0c;同时也可以根据需要…...

【LeetCode】对称二叉树 平衡二叉树

对称二叉树 即先判断根节点的左右子树相不相同&#xff0c;相同时&#xff0c;再判断左孩子的左子树和右孩子的右子树比较&#xff0c;左孩子的右子树和右孩子的左子树&#xff08;当两个都相同时才是对称的&#xff09;.....依次递推&#xff0c;过程中并设置一些不满足相同的…...

区块链和WEB3.0有哪些基础知识呢

区块链基础知识 常用区块链基础知识包括&#xff1a; &#xff08;1&#xff09;区块链概念&#xff1a;区块链是一种去中心化的分布式账本技术&#xff0c;它通过加密算法和共识机制保证了数据的安全性和不可篡改性。区块链中的每一个区块都包含了前一个区块的哈希值&#x…...

七、封装(1)

本章概要 包的概念 代码组织创建独一无二的包名冲突定制工具库使用 import 改变行为使用包的忠告 访问控制&#xff08;Access control&#xff09;&#xff08;或者_隐藏实现&#xff08;implementation hiding&#xff09;_&#xff09;与“最初的实现不恰当”有关。 所有优…...

问题解决和批判性思维是软件工程的重要核心

软件工程的重心在于问题解决和批判性思维&#xff08;合理设计和架构降低复杂度&#xff09;&#xff0c;而非仅局限于编程。 许多人误以为软件工程就只是编程&#xff0c;即用编程语言编写指令&#xff0c;让计算机按照这些指令行事。但实际上&#xff0c;软件工程的内涵远超…...

【EI/SCOPUS征稿】2023年通信网络与机器学习国际学术会议(CNML 2023)

2023年通信网络与机器学习国际学术会议&#xff08;CNML 2023&#xff09; 2023 International Conference on Communication Networks and Machine Learning 随着数据流量的显著增长&#xff0c;新的通信应用程序不断出现&#xff0c;并产生更多的数据流量&#xff0c;这些数…...

算法-岛屿数量

给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该网格的四条边…...

Crescent QuickPak Crack

Crescent QuickPak Crack Crescent QuickPak是一个32位ActiveX组件的综合集合&#xff0c;用于使用Visual Basic开发应用程序&#xff0c;这将减少开发时间并提高生产力。Crescent QuickPak包含Internet功能&#xff0c;用于打开、读取和解析IIS日志文件&#xff0c;将日志文件…...

六、ESP32数码管显示数字

1. 本节课的成功 2. 数码管 为什么会亮呢? 答:里面就是LED灯...

【Kubernetes】当K8s出现问题时,从哪些方面可以排查

前言 kubernetes&#xff0c;简称K8s&#xff0c;是用8代替名字中间的8个字符“ubernete”而成的缩写。是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效&#xff08;powerful&#xff09;,Kub…...

[ MySQL ] — 库和表的操作

目录 库的操作 创建数据库 语法&#xff1a; 使用&#xff1a; 字符集和校验规则 查看系统默认字符集以及校验规则 查看数据库支持的字符集 查看数据库支持的字符集校验规则 校验规则对数据库的影响 操纵数据库 查看数据库 显示创建语句 修改数据库 删除数据库 备…...

Hive常见面试题

Hive的基本概念 什么是Hive&#xff1f;它的主要作用是什么&#xff1f; Hive是一个基于Hadoop生态系统的数据仓库和数据处理工具。 它提供了类似于SQL的查询语言&#xff08;HiveQL&#xff09;&#xff0c;使用户能够使用SQL语句来查询和分析 大规模存储在Hadoop集群上的数…...

【单片机】晨启科技,酷黑版,密码锁

密码锁 任务要求&#xff1a; 当输入密码&#xff08;至少6位密码&#xff09;时&#xff0c;OLED显示屏显示输入的数字&#xff08;或者字符&#xff09;&#xff0c;当密码位数输入完毕按下确认键时&#xff0c;对输入的密码与设定的密码进行比较&#xff08;可使用外设键盘&…...

常见监控网络链路和网络设备的方法

网络监控主要包括网络链路监控和网络设备监控&#xff0c;通常系统运维人员会比较关注。 一、网络链路监控 网络链路监控主要包含三个部分&#xff0c;网络连通性、网络质量、网络流量。 连通性和质量的监控手段非常简单&#xff0c;就是在链路一侧部署探针&#xff0c;去探…...

C#控制台程序+Window增加右键菜单

有时候我们可能会想定制一些自己的右键菜单功能&#xff0c;帮我们减少重复的操作。那么使用控制台程序加自定义右键菜单&#xff0c;就可以很好地满足我们的需求。 1 编写控制台程序 因为我只用到了在文件夹中空白处的右键菜单&#xff0c;所以这里提供了一个对应的模板&…...

【Docker】Docker+Zipkin+Elasticsearch+Kibana部署分布式链路追踪

文章目录 1. 组件介绍2. 服务整合2.1. 前提&#xff1a;安装好Elaticsearch和Kibana2.2. 再整合Zipkin 点击跳转&#xff1a;Docker安装MySQL、Redis、RabbitMQ、Elasticsearch、Nacos等常见服务全套&#xff08;质量有保证&#xff0c;内容详情&#xff09; 本文主要讨论在Ela…...

【小沐学C++】C++ 基于CMake构建工程项目(Windows、Linux)

文章目录 1、简介2、下载cmake3、安装cmake4、测试cmake4.1 单个源文件4.2 同一目录下多个源文件4.3 不同目录下多个源文件4.4 标准组织结构4.5 动态库和静态库的编译4.6 对库进行链接4.7 添加编译选项4.8 添加控制选项 5、构建最小项目5.1 新建代码文件5.2 新建CMakeLists.txt…...

计算机视觉与图形学-神经渲染专题-ConsistentNeRF

摘要 Neural Radiance Fields (NeRF) 已通过密集视图图像展示了卓越的 3D 重建能力。然而&#xff0c;在稀疏视图设置下&#xff0c;其性能显着恶化。我们观察到&#xff0c;在这种情况下&#xff0c;学习不同视图之间像素的 3D 一致性对于提高重建质量至关重要。在本文中&…...

初级算法-其他

文章目录 位1的个数题意&#xff1a;解&#xff1a;代码&#xff1a; 汉明距离题意&#xff1a;解&#xff1a;代码&#xff1a; 颠倒二进制位题意&#xff1a;解&#xff1a;代码&#xff1a; 杨辉三角题意&#xff1a;解&#xff1a;代码&#xff1a; 有效的括号题意&#xf…...

Containerd的两种安装方式

1. 轻量级容器管理工具 Containerd 2. Containerd的两种安装方式 3. Containerd容器镜像管理 4. Containerd数据持久化和网络管理 操作系统环境为centos7u6 1. YUM方式安装 1.1 获取YUM源 获取阿里云YUM源 # wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun…...

Android学习之路(1) 文本设置

Android学习之路(1) 文本 一、设置文本内容 设置文本内容的两种方式&#xff1a; 一种是在XML文件中通过属性android:text设置文本代码如下 <TextViewandroid:id"id/tv_hello"android:layout_width"wrap_content"android:layout_height"wrap_c…...

Docker相关命令与入门

1. Docker 命令 # centos 7 systemctl start docker # 启动服务 systemctl stop docker systemctl restart docker # 重启服务 systemctl status docker systemctl enable docker # 开机自启动1.1 镜像相关的命令 # 查看镜像 docker images docker images -q # 查看…...

如何配置一个永久固定的公网TCP地址来SSH远程树莓派?

文章目录 如何配置一个永久固定的公网TCP地址来SSH远程树莓派&#xff1f;前置条件命令行使用举例&#xff1a;修改cpolar配置文件 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 …...

Kubernetes架构和工作流程

目录 一、kubernetes简介 1.k8s的由来 2.为什么用 k8s &#xff1f; 3.k8s主要功能 二、k8s集群架构与组件 1.Master 组件 1.1Kube-apiserver 1.2Kube-controller-manager 1.3Kube-scheduler 2.Node组件 2.1Kubelet 2.2Kube-Proxy 2.3docker 或 rocket 3.配置存储中…...

C语言赋值号的运算顺序

从右到左。 int & f(int & a) { printf("参数 %d\n", a); return a; } int main(void) {int a 9;int b 3;f(a) f(b);// 运行到此处&#xff0c;a 3&#xff0c;b 3return 0; } 输出...

fishing之第四篇使用案例一模拟登陆口

文章目录 一、访问钓鱼平台二、Sending Profiles&#xff08;发件人邮箱配置&#xff09;三、User&Groups&#xff08;接收人邮件列表&#xff09;四、Landing Pags&#xff08;钓鱼页面配置&#xff09;五、Email Templates&#xff08;邮件内容配置&#xff09;六、Campa…...

CS 144 Lab Six -- building an IP router

CS 144 Lab Six -- building an IP router 引言路由器的实现测试 对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Six 对应的PDF: Lab Checkpoint 5: building an IP router 引言 在本实验中&#xff0c;你将在现有的NetworkInterface基础上实现一个IP路由器&#xf…...

edge://settings/defaultbrowser default ie

Microsoft Edge 中的 Internet Explorer 模式 有些网站专为与 Internet Explorer 一起使用&#xff0c;它们具有 Microsoft Edge 等新式浏览器不支持的功能。 如果你需要查看其中的某个网站&#xff0c;可使用 Microsoft Edge 中的 Internet Explorer 模式。 大多数网站在新…...