FFmpeg5.0源码阅读—— avcodec_send_frame avcodec_receive_packet

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

1 avcodec_send_frame

  avcodec_send_frame用于在编码时将一帧raw数据发送给编码器,其基本的调用流程比较简单,主要工作就是将输入的数据ref到Internal Frame上。
在这里插入图片描述

  avcodec_send_frame首先检查当前的codec是不是编码器且是否打开,并且检查codec中的buffer是否有数据没有,有的话就意味着上一帧的数据还没处理完需要等待这一帧处理完才能继续发送。

    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))return AVERROR(EINVAL);if (avci->draining)return AVERROR_EOF;if (avci->buffer_frame->data[0])return AVERROR(EAGAIN);

  然后是根据输入的frame是否为空来设置标志位,如果为空就表示是最后一帧数据后续的数据就无效了。能够看到在最后如果codec中的packet buffer是空的就会尝试获取一帧packet。

    if (!frame) {avci->draining = 1;} else {ret = encode_send_frame_internal(avctx, frame);if (ret < 0)return ret;}if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data) {ret = encode_receive_packet_internal(avctx, avci->buffer_pkt);if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)return ret;}

  encode_send_frame_internal比较简单,主要就是针对音频数据进行参数检查并对数据进行填充,最后调用av_frame_ref将输入的数据的引用计数+1、

2 avcodec_receive_packet

2.1 基本流程

  avcodec_receive_packet相对复杂一点点儿,下面是其调用流程图。
在这里插入图片描述

  首先是检查当前codec是否为编码器并且是否打开,如果是就继续。然后检查codec中的packet buffer是否有数据有的话就直接返回了,不然就会调用encode_receive_packet_internal

int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt){AVCodecInternal *avci = avctx->internal;int ret;av_packet_unref(avpkt);if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))return AVERROR(EINVAL);if (avci->buffer_pkt->data || avci->buffer_pkt->side_data) {av_packet_move_ref(avpkt, avci->buffer_pkt);} else {ret = encode_receive_packet_internal(avctx, avpkt);if (ret < 0)return ret;}return 0;
}

  encode_receive_packet_internal首先就是参数检查,然后根据codec的函数指针设置看调用哪个流程获取编码流。encode_simple_receive_packet就是个while循环调用encode_simple_internal直到获取编码数据或者出错为止。

    if (avctx->codec->receive_packet) {ret = avctx->codec->receive_packet(avctx, avpkt);if (ret < 0)av_packet_unref(avpkt);else// Encoders must always return ref-counted buffers.// Side-data only packets have no data and can be not ref-counted.av_assert0(!avpkt->data || avpkt->buf);} elseret = encode_simple_receive_packet(avctx, avpkt);

  encode_simple_internal除了前面一大坨参数检查,主要救赎下面这块儿,看是利用多线程编码还是利用codec的encode接口编码。

    if (CONFIG_FRAME_THREAD_ENCODER &&avci->frame_thread_encoder && (avctx->active_thread_type & FF_THREAD_FRAME))/* This might modify frame, but it doesn't matter, because* the frame properties used below are not used for video* (due to the delay inherent in frame threaded encoding, it makes*  no sense to use the properties of the current frame anyway). */ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet);else {ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);if (avctx->codec->type == AVMEDIA_TYPE_VIDEO && !ret && got_packet &&!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))avpkt->pts = avpkt->dts = frame->pts;}

2.2 多线程

  编码的线程和解码的线程一样都是在avcodec_open2时创建的,编码是调用ff_frame_thread_encoder_init创建的,其中主要就是调用pthread的接口创建线程和相关的参数,可以看到其工作的函数为static void * attribute_align_arg worker(void *v),编码过程中有多个线程每个线程都运行一个worker任务,通过信号量来进行消息的同步。该任务中最终会调用avctx->codec->encode2对数据进行编码。而所有的数据交互都是通过ThreadContext进行的,无论是输入数还是输出的数据还是消息同步都是通过该Context进行的。

typedef struct{AVCodecContext *parent_avctx;pthread_mutex_t buffer_mutex;pthread_mutex_t task_fifo_mutex; /* Used to guard (next_)task_index */pthread_cond_t task_fifo_cond;unsigned max_tasks;Task tasks[BUFFER_SIZE];pthread_mutex_t finished_task_mutex; /* Guards tasks[i].finished */pthread_cond_t finished_task_cond;unsigned next_task_index;unsigned task_index;unsigned finished_task_index;pthread_t worker[MAX_THREADS];atomic_int exit;
} ThreadContext;

  当数据到达时主线程会先拷贝数据然后发送信号量signal给任务线程,任务线程拿到消息后编码完成后给主线程发信号finish,主线程取走数据。

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

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

相关文章

如何理解自动化

目录 1.如何定义自动化 2.自动化给人类带来的福利 3.如何学习自动化 4.自动化潜在的危害 1.如何定义自动化 自动化是指利用计算机、机械、电子技术和控制系统等现代科学技术手段&#xff0c;对各种工业、商业、农业和日常生活中的操作和过程进行自动控制和执行的过程。它旨在…

如何使用Java开发iOS应用?

使用Java开发iOS应用并不直接支持&#xff0c;因为iOS平台采用的是Objective-C和Swift作为主要的开发语言。然而&#xff0c;有一些跨平台工具和框架可以让你使用Java来开发跨平台的移动应用&#xff0c;包括iOS。 以下是一些可以考虑的选项&#xff1a; React Native&#xf…

Vc - Qt - 自定义ComboBox

示例代码创建了一个名为ComboBoxWidget的自定义QWidget类&#xff0c;并在initUI方法中创建了一个垂直布局。然后将一个只读的QLineEdit和一个QPushButton添加到布局中。当按钮被点击时&#xff0c;会调用showMenu方法&#xff0c;该方法创建一个QMenu并添加选项。每个选项连接…

CodeForces:Madoka and Underground Competitions

经过观察&#xff0c;发现只要延小区域 右上-左下 的对角线填满X即可&#xff0c;那么就是可以总结为满足(i j) % k (r c) % k #include <bits/stdc.h> using namespace std; int t; void solve(){int n, k, r, c;cin >> n >> k >> r >> c…

什么是搜索引擎?2023 年搜索引擎如何运作?

目录 什么是搜索引擎&#xff1f;搜索引擎的原理什么是搜索引擎爬取&#xff1f;什么是搜索引擎索引&#xff1f;什么是搜索引擎检索?什么是搜索引擎排序&#xff1f; 搜索引擎的目的是什么&#xff1f;搜索引擎如何赚钱&#xff1f;搜索引擎如何建立索引?网页抓取文本处理建…

【C++基础(五)】类和对象(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 类和对象-上 1. 前言2. 类的引入3. 类的定义4. 类的…

【数据归一化与标准化】

数据归一化与标准化 数据预处理是机器学习和数据分析中非常重要的一环&#xff0c;它能够有效地提高模型的性能和准确度。在数据预处理的过程中&#xff0c;数据归一化和标准化是两个常用的技术&#xff0c;它们可以将数据转换成一定的标准形式&#xff0c;有助于模型更好地学…

【人工智能】深度优先搜索、代价一致搜索、深度有限搜索、迭代深度优先搜索、图搜索

【人工智能】无信息搜索—BFS 、代价一致、DFS、深度受限、迭代深入深度优先、图搜索 什么是搜索 搜索问题是指既不能通过数学建模解决,又没有其他算法可以套用或者非遍历所有情况才能得出正确结果。这时就需要采用搜索算法来解决问题。搜索就是一种通过穷举所有解的状态,来…

iClient3D for CesiumWebGL入门之使用vscode以服务方式运行调试

作者&#xff1a;超图研究院技术支持中心-于丁 iClient3D for Cesium&WebGL入门之使用vscode以服务方式运行调试 相信大家第一次使用SuperMap iClient3D for Cesium或SuperMap iClient3D for WebGL的时候&#xff0c;都遇到过和我一样的事情&#xff1a; 在文件夹中直接打…

django递归菜单

实例代码 from django.shortcuts import render from .models import Menudef menu_list(request):menus Menu.objects.filter(parentNone)def get_children(menu):return Menu.objects.filter(parentmenu)def get_menu_tree(menu):children get_children(menu)if children:r…

【图论】最小生成树

1、Kruskal算法 步骤与基本思路 &#xff08;1&#xff09;初始化所有点&#xff0c;每个点单独在一个点集。把所有边按权重排序 &#xff08;2&#xff09;按边权重从小到大遍历每一条边&#xff0c;如果这条边的两个顶点不在同一个点集&#xff0c;就将它们加到同一点集&a…

Docker网络模式

Docker网络模式 一、Docker网络实现原理二、Docker的网络模式1、host模式1.1 host模式原理1.2 host模式实操 2、Container模式2.1 模式原理 3、none模式4、bridger模式4.1 bridge模式的原理4.2 bridge实操 5、overlay模式6、自定义网络模式6.1 为什么需要自定义网络模式&#x…

[每日一氵] C++ 类 “xxx“ 不存在默认构造函数

80年不写C, 今天遇到 类 "xxx" 不存在默认构造函数 我都忘了怎么解决… customer.h 文件&#xff1a; #ifndef CUSTOM_H #define CUSTOM_H#include<string>class Customer{public:Customer(std::string name, int age);// virtual ~Customer();void printInf…

Debezium日常分享系列之:Debezium Operator

Debezium日常分享系列之&#xff1a;Debezium Operator 一、安装步骤二、快速入门示例三、DebeziumServerSpec 参考 Debezium Operator 提供了一种在 Kubernetes 或 Openshift 上运行 Debezium Server 的简单方法。 一、安装步骤 debezium operator 当前支持按名称空间安装。…

ctemplate,安装以及出现报错处理方式

简介 Google CTemplate是一个开源的C模板引擎&#xff0c;可以生成html文件&#xff0c;今天安装出现一些问题&#xff0c;在此将遇到的问题以及解决方式记录下来# 简介 安装 git clone https://github.com/OlafvdSpek/ctemplate.gitcd ctemplate./autogen.sh./configure ma…

mysql主主架构搭建,删库恢复

mysql主主架构搭建&#xff0c;删库恢复 搭建mysql主主架构环境信息安装msql服务mysql1mysql2设置mysql2同步mysql1设置mysql1同步mysql2授权测试用账户 安装配置keepalivedmysql1检查脚本mysql2检查脚本 备份策略mysqldump全量备份mysqldump增量备份数据库目录全量备份 删除my…

03. 自定义镜像 Dockerfile

目录 1、前言 2、构建镜像的方式 2.1、docker commit 2.1.1、先查看下当前的容器 2.1.2、生成该容器镜像 2.1.3、查看镜像列表 2.2、Dockerfile 2.2.1、创建Dockerfile文件 2.2.2、编写Dockerfile文件 2.2.3、构建镜像 2.2.4、使用该镜像生成容器 3、Dockerfile 3…

FTP与HTTP: 哪种协议更适合大文件传输?

随着互联网技术的发展&#xff0c;网络传输已成为了现代社会中不可或缺的一部分。无论是文本、图像、音频、视频等各种类型的数据&#xff0c;相应的传输协议也在不断地发展和更新。FTP&#xff08;File Transfer Protocol&#xff09;和HTTP&#xff08;Hyper Text Transfer P…

【密码学】二、古典密码

古典密码 1.置换密码1.1列置换密码1.2周期置换密码 2.代换密码2.1单表代换密码2.1.1凯撒密码2.1.2仿射密码 2.2多表代换密码2.2.1维吉尼亚密码2.2.2普莱费尔密码 3.轮转密码3.1恩尼格玛密码机Enigma3.1.1Enigma加密3.1.1Enigma解密 4.古典密码的分类5.古典密码的统计分析5.1单表…

【云原生】Prometheus之图形化界面grafana与服务发现部署

前言 上一篇文章中我们介绍了Prometheus的组件&#xff0c;监控作用&#xff0c;部署方式&#xff0c;以及如何通过在客户机安装exporter再添加监控项的操作。但是不免会发现原生的Prometheus的图像化界面对于监控数据并不能其他很好的展示效果。所以本次我们将介绍一款开源非常…