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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/75774.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins工具系列 —— 启动 Jenkins 服务报错

错误显示 apt-get 安装 Jenkins 后&#xff0c;自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log&#xff1a;Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明&#xff1a;这里提示的是8080端口号…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]

文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…

iOS数字转为图片

根据数字&#xff0c;转成对应的图片 - (void)viewDidLoad {[super viewDidLoad];[self testNum2String:10086]; }/// 根据数字&#xff0c;显示对应的图片 数字用特定的图片显示 - (void)testNum2String:(NSInteger)num {UIView *numContentView [[UIView alloc] initWithFr…

有利于提高xenomai /PREEMPT-RT 实时性的一些配置建议

版权声明:转自: https://www.cnblogs.com/wsg1100 一、前言 1. 什么是实时 “实时”一词在许多应用领域中使用,人们它有不同的解释,并不总是正确的。人们常说,如果控制系统能够对外部事件做出快速反应,那么它就是实时运行的。根据这种解释,如果系统速度快,则系统被认…

unity tolua热更新框架教程(1)

git GitHub - topameng/tolua: The fastest unity lua binding solution 拉取到本地 使用unity打开&#xff0c;此处使用环境 打开前几个弹窗(管线和api升级)都点确定 修改项目设置 切换到安卓平台尝试打包编译 设置包名 查看报错 打开 屏蔽接口导出 重新生成 编译通过 …

java编码规范 和 数据库规范

总体规约以《阿里巴巴Java开发手册》为主&#xff0c;请开发人员至少阅读一遍该手册。 一、java编码规范 1.1 java基础规范 多使用 jdk自带库和被验证的第三方库的类和函数&#xff0c;不要用野路子来的jar包 无论是包、类、方法、变量&#xff0c;见名知意 1.2 在线文档规…

Vue2.0基础

1、概述 Vue(读音/vju/&#xff0c;类似于view)是一套用于构建用户界面的渐进式框架&#xff0c;发布于2014年2月。与其它大型框架不同的是&#xff0c;Vue被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff08;也就是可以理解为HTMLCSSJS&#xff09;&#xff…

SQL Developer中的Active Data Guard

这篇文章 Display Data Guard configuration in SQL Developer 中&#xff0c;用SQL Developer展示了多种ADG的拓扑。 今天自己也试了一下&#xff0c;还蛮简单的&#xff0c;其实最麻烦的部分在于搭建一个ADG环境。 假设我已有一个ADG环境&#xff0c;即最典型的环境&#x…

实力肯定!Coremail入选中国信通院“铸基计划”2023全景图

近日&#xff0c;由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;主办的“2023数字生态发展大会”暨中国信通院“铸基计划”年中会议在京召开。 会上发布了《高质量数字化转型产品及服务全景图&#xff08;2023&#xff09;》&#xff0c;Coremail凭借着优…

数据泄露的平均成本创历史新高

IBM Security 发布了年度数据泄露成本报告&#xff0c;显示数据泄露的全球平均成本在 2023 年达到 445 万美元&#xff0c;创下该报告的历史新高&#xff0c;并且比过去 3 年增加了 15%。 检测和升级成本在同一时间段内跃升了 42%&#xff0c;占违规成本的最高部分&#xff0c…

LLaMA系列 | LLaMA和LLaMA-2精简总结

文章目录 1、LLaMA1.1、模型结构1.2、训练方式1.3、结论 2、LLaMA-22.1、相比LLaMA1的升级2.3、模型结构2.3.1、MHA, MQA, GQA区别与联系 2.4、训练方式 1、LLaMA &#x1f525; 纯基座语言模型 《LLaMA: Open and Efficient Foundation Language Models》&#xff1a;https:/…

【目标检测论文解读复现NO.33】改进YOLOv5的新能源电池集流盘缺陷检测方法

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

RabbitMQ输出日志配置

参考地址rabbitmq启用日志功能记录消息队列收发情况_rabbitmq开启日志_普通网友的博客-CSDN博客 启用日志插件命令 # 设置用户权限 rabbitmqctl set_user_tags mqtt-user administrator rabbitmqctl set_permissions -p / mqtt-user ".*" ".*" ".*&…

小白到运维工程师自学之路 第六十六集 (docker 网络模型)

一、概述 Docker网络模型是指Docker容器在网络中的通信方式和组织结构。Docker容器通过网络连接&#xff0c;使得容器之间可以相互通信&#xff0c;并与主机和外部网络进行交互。 在Docker中&#xff0c;有几种不同的网络模型可供选择&#xff1a; 1、主机模式&#xff08;H…

媒介易讲解体育冠军助力品牌解锁市场营销新玩法

在当今竞争激烈的市场中&#xff0c;品牌推广成为企业取得商业成功的重要一环。然而&#xff0c;随着传统市场推广方式的日益饱和&#xff0c;企业急需创新的市场营销策略来吸引消费者的关注和认可。在这样的背景下&#xff0c;体育冠军助力品牌成为了一种备受瞩目的市场营销新…

【大数据】LPG日志采集方案(官网入门案例)

文章目录 1. LPG简介2. 安装3. 测试日志方案的效果3.1. 测试1&#xff1a;Promtail监控/var/log目录的变化3.2. 测试2&#xff1a;Grafana可视化查看日志3.3. 测试3&#xff1a;可以预见部署Spring Boot程序的日志也可以被Grafana查看3.4. 踩坑记录 4. 官方入门案例介绍4.1. 获…

【设计模式——学习笔记】23种设计模式——访问者模式Visitor(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入要求传统方案 介绍基本介绍应用场景登场角色尚硅谷版本《图解设计模式》版本 案例实现案例一实现拓展 案例二(个人感觉这个案例较好)实现分析拓展一拓展二拓展三 总结额外知识双重分发 文章说明 案例引入 要求 测评系统需求&#xff1a;将观众分为男人和女人…

基于面向对象基础设计——里氏替换原则

在Java中&#xff0c;支持抽象和多态的关键机制之一是继承。正是使用了继承&#xff0c;我们才可以创建实现父类中抽象方法的子类。那么&#xff0c;是什么规则在支配着这种特殊的继承用法呢&#xff1f;最佳的继承层次的特征又是什么呢&#xff1f;在什么情况下会使我们创建的…

Mac端口扫描工具

端口扫描工具 Mac内置了一个网络工具 网络使用工具 按住 Command 空格 然后搜索 “网络实用工具” 或 “Network Utility” 即可 域名/ip转换Lookup ping功能 端口扫描 https://zhhll.icu/2022/Mac/端口扫描工具/ 本文由 mdnice 多平台发布

【深度学习_TensorFlow】梯度下降

写在前面 一直不太理解梯度下降算法是什么意思&#xff0c;今天我们就解开它神秘的面纱 写在中间 线性回归方程 如果要求出一条直线&#xff0c;我们只需知道直线上的两个不重合的点&#xff0c;就可以通过解方程组来求出直线 但是&#xff0c;如果我们选取的这两个点不在直…