FFmpeg5.0源码阅读—— avcodec_send_packetavcodec_receive_frame

  摘要:本文主要描述了FFmpeg中用于解码的接口的具体调用流程,详细描述了该接口被调用时所作的具体工作。
  关键字ffmpegavcodec_send_packetavcodec_receive_frame
  读者须知:读者需要了解FFmpeg的基本使用流程,以及一些FFmpeg的基本常识,了解FFmpegIO相关的内容,以及大致的解码流程。

  avcodec_send_packet接口将AVPacket数据发送给解码器进行解码,然后通过avcodec_receive_frame获取数据。

1 avcodec_send_packet

在这里插入图片描述

  avcodec_send_packet首先是检查解码器的合法性以及数据是否为空,如果输入数据和Context符合要求就会删除AVcodecContext->internal->buffer_pkt中缓存的一帧码流数据,将输入的Packet拷贝到该buffer上。av_bsf_send_packet只是拷贝增加输入的Packet引用计数到AVBSFInternal->buffer_pkt,最后如果缓存的buffer_frame是空的就会调用decode_receive_frame_internal解码帧,该过程根据配置项可谓同步也可为异步。

1.1 decode_receive_frame_internal

  decode_receive_frame_internal内就是真正的调用解码流程,如果解码器的receive_frame函数指针不为空就直接调用解码器的receive_frame进行解码该过程是同步的。否则就会调用decode_simple_receive_frame进行解码。解码完成后需要根据解码的数据和当前解码器Context的一些pts相关的值计算当前帧的具体pts和dts,另外如果有指定FrameDecodeData还会调用后处理流程fdd->post_process进行解码。

1.2 decode_simple_receive_frame

  decode_simple_receive_frame主要是调用decode_simple_internal进行解码。这里使用的Packet就是前面存储在AVBSFInternal中的buffer_pkt。然后就是实际调用解码的流程,如果没有配置解码线程就直接调用每个解码器对应的函数指针的avctx->codec->decode直接同步拿到帧。否则就会调用ff_thread_decode_frame进行多线程解码。
  FFmpeg中每种格式,解码器等都有自己的描述结构,比如下面是gif的解码器描述。

static const AVClass decoder_class = {.class_name = "gif decoder",.item_name  = av_default_item_name,.option     = options,.version    = LIBAVUTIL_VERSION_INT,.category   = AV_CLASS_CATEGORY_DECODER,
};const AVCodec ff_gif_decoder = {.name           = "gif",.long_name      = NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"),.type           = AVMEDIA_TYPE_VIDEO,.id             = AV_CODEC_ID_GIF,.priv_data_size = sizeof(GifState),.init           = gif_decode_init,.close          = gif_decode_close,.decode         = gif_decode_frame,.capabilities   = AV_CODEC_CAP_DR1,.caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |FF_CODEC_CAP_INIT_CLEANUP,.priv_class     = &decoder_class,
};

  ff_thread_decode_frame内都是通过锁和条件变量进行同步的。首先根据当前的状态获取一个解码线程的Context,然后将当前的Packet提交到该线程上,提交就是将一帧数据增加引用让解码Context的avpkt也占用输入帧的引用计数,提交完成就会发送信号通知在等待的解码线程启动。
  解码线程起始在avcodec_open2的时候就已经创建好了,在wait数据。具体的执行函数就是frame_worker_thread,该函数内就是调用codec->decode进行解码解码完成后就会发送通知到ff_thread_decode_frame中取解码完的帧。令条件if (!p->avctx->thread_safe_callbacks && ( p->avctx->get_format != avcodec_default_get_format || p->avctx->get_buffer2 != avcodec_default_get_buffer2))为A,如果A为true则当前线程是会被阻塞的,完全就是同步运行,否则就是多线程的。

if (!p->avctx->thread_safe_callbacks && (p->avctx->get_format != avcodec_default_get_format ||p->avctx->get_buffer2 != avcodec_default_get_buffer2)) {while (atomic_load(&p->state) != STATE_SETUP_FINISHED && atomic_load(&p->state) != STATE_INPUT_READY) {int call_done = 1;pthread_mutex_lock(&p->progress_mutex);while (atomic_load(&p->state) == STATE_SETTING_UP)pthread_cond_wait(&p->progress_cond, &p->progress_mutex);switch (atomic_load_explicit(&p->state, memory_order_acquire)) {case STATE_GET_BUFFER:p->result = ff_get_buffer(p->avctx, p->requested_frame, p->requested_flags);break;case STATE_GET_FORMAT:p->result_format = ff_get_format(p->avctx, p->available_formats);break;default:call_done = 0;break;}if (call_done) {atomic_store(&p->state, STATE_SETTING_UP);pthread_cond_signal(&p->progress_cond);}pthread_mutex_unlock(&p->progress_mutex);}}

2 avcodec_receive_frame

  avcodec_receive_frame比较简单先检查buffer_frame有没有数据,有的话就直接返回,没有即调用decode_receive_frame_internal进行解码。

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

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

相关文章

Day48|198.打家劫舍、 213.打家劫舍II 、 337.打家劫舍III

198.打家劫舍 1.题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警…

UART串口收发数据

uart4.h ubuntuubuntu:05_uart$ cat include/uart4.h #ifndef __UART_H__ #define __UART_H__//初始化相关操作 void hal_uart4_init();//发送一个字符 void hal_put_char(const char str);//发送一个字符串 void hal_put_string(const char* string);//接收一个字符 char h…

如何在没有软件的情况下将 PDF 转换为 PPT(100% 免费)

演示文稿由文字、图片、音频、动画等元素组成,通常用于会议、课堂或演讲中,展示演讲者想要表达的主要内容。如果您遇到重要文档以 PDF 格式存储,但现在需要转换为 PPT 格式的情况,请不要担心。我们本指南的目标是帮助用户将 PDF 转…

一起学SF框架系列5.7-模块Beans-BeanDefinition解析

开发人员按元数据规则定义了应用bean,了解SF如何根据定义解析成BeanDefiniton有助于深入理解框架实现。解析过程如下: 资源加载 从资源文件加载bean的元数据配置,实际过程如下图: 实际从指定的XML文件加载bean定义是从XmlBeanD…

Spring系列2 -- Spring的创建和使用

Spring 就是⼀个包含了众多工具方法的 IOC容器。既然是容器那么它就具备两个最基本的功能: 将对象存储到容器(Spring)中;从容器中将对象取出来。 在Java中对象也叫做Bean,后续我们就把对象称之为Bean; 目录…

4.5 x64dbg 探索钩子劫持技术

钩子劫持技术是计算机编程中的一种技术,它们可以让开发者拦截系统函数或应用程序函数的调用,并在函数调用前或调用后执行自定义代码,钩子劫持技术通常用于病毒和恶意软件,也可以让开发者扩展或修改系统函数的功能,从而…

ARM64学习笔记---建立异常向量表(二)

源码: #include "mm.h" #include "sysregs.h".section .rodata .align 3 .globl el_string1 el_string1:.string "Booting at EL".section ".text.boot" .globl _start _start://读取mpidr_el1寄存器的值,该寄存器决定了…

高级运维开发工程师带你处理linux木马(挖矿病毒)实战例子

一、事态描述 centos7测试服务器,突然密码登不上去了,然后CPU占100%。已经猜到,登录密码过于简单,密码被破解挂马了。大概率是CPU挖矿病毒。 二、重置centos7登录root密码 步骤1 启动Linux Centos7系统,当出现如下画…

DevOps系列文章 之 Java使用jgit管理git仓库

最近设计基于gitops新的CICD方案,需要通过java读写git仓库,这里简单记录下。 在jgit中,存在最核心的三个组件:Git类,Repository类。Git类中包含了push commit之类的常见git操作,而Repository则实现了仓库的初始化和基…

使用黑盒测试在 Go 中重写 Bash 脚本

目录 前言: 开始 准备工作 描述行为:Bats 简介 行为描述:陷阱 描述行为:设计测试 重写:让我们开始用go吧! 重构和更新:实现胜利 结论 前言: 使用黑盒测试在Go中重写Bash脚本…

百度--搜索引擎是怎么实现的--如何制作一个搜索浏览器

1.搜索引擎是怎么实现的? 搜索引擎是通过以下步骤实现的: 网页抓取(Crawling):搜索引擎会使用网络爬虫(Web Crawler)自动地从互联网上抓取网页内容。爬虫按照一定的规则遍历网页并提取网页内容…

做题遇见的PHP函数汇总

mb_substr函数 mb_substr() 函数返回字符串的一部分,之前学过 substr() 函数,它只针对英文字符,如果要分割的中文文字则需要使用 mb_substr() 语法: mb_substr ( $str ,$start [, $length NULL [, $encoding mb_encoding() ]] …