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

FFmpeg源码:read_packet_wrapper、fill_buffer函数分析

=================================================================

AVIOContext结构体和其相关的函数分析:

FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析

FFmpeg源码:read_packet_wrapper、fill_buffer函数分析

FFmpeg源码:avio_read函数分析

FFmpeg源码:avio_tell函数分析

=================================================================

一、read_packet_wrapper函数

(一)read_packet_wrapper函数的定义

read_packet_wrapper函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/aviobuf.c中:

static int read_packet_wrapper(AVIOContext *s, uint8_t *buf, int size)
{int ret;if (!s->read_packet)return AVERROR(EINVAL);ret = s->read_packet(s->opaque, buf, size);av_assert2(ret || s->max_packet_size);return ret;
}

该函数作用是:对本地媒体文件或网络流进行读取,将读上来的数据保存到形参buf指向的缓冲区中。简单来讲,就是通过文件描述符去读取本地媒体文件中的数据,或者通过socket接收网络流中的数据,保存到内存(buf指向的缓冲区)中。

形参s:输入型参数。指向一个AVIOContext(字节流上下文结构体)变量。关于AVIOContext结构体可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。

形参buf:输出型参数。保存读上来的数据的缓冲区。

形参size:输入型参数。要读取的字节数。

返回值:返回一个非负数表示成功,此时返回实际读取到的字节数;返回一个负数表示出错。

(二)read_packet_wrapper函数的内部实现分析

s->read_packet是函数指针,指向读取数据包的回调函数:

typedef struct AVIOContext {
//...int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
//...
}

read_packet_wrapper函数内部,首先会判断s->read_packet是否指向了回调函数。如果没有指向任何回调函数,read_packet_wrapper函数返回AVERROR(EINVAL)表示失败(无效的参数):

if (!s->read_packet)return AVERROR(EINVAL);

如果s->read_packet有指向回调函数,调用对应的回调函数。回调函数一般是ffurl_read2(关于该函数用法可以参考:《FFmpeg源码:retry_transfer_wrapper、ffurl_read2、ffurl_write2函数分析》),通过ffurl_read2函数对本地媒体文件或网络流进行读取:

ret = s->read_packet(s->opaque, buf, size);

备注:s->read_packet指向的回调函数一般是ffurl_read2函数,但也可能是其它:比如FFmpeg源码目录下doc/examples/avio_read_callback.c中的read_packet函数、libavformat/wtvdec.c中的wtvfile_read_packet函数或者tools/target_dem_fuzzer.c中的io_read函数。

二、fill_buffer函数

(一)fill_buffer函数的定义

fill_buffer函数定义在libavformat/aviobuf.c中:

static void fill_buffer(AVIOContext *s)
{FFIOContext *const ctx = (FFIOContext *)s;int max_buffer_size = s->max_packet_size ?s->max_packet_size : IO_BUFFER_SIZE;uint8_t *dst        = s->buf_end - s->buffer + max_buffer_size <= s->buffer_size ?s->buf_end : s->buffer;int len             = s->buffer_size - (dst - s->buffer);/* can't fill the buffer without read_packet, just set EOF if appropriate */if (!s->read_packet && s->buf_ptr >= s->buf_end)s->eof_reached = 1;/* no need to do anything if EOF already reached */if (s->eof_reached)return;if (s->update_checksum && dst == s->buffer) {if (s->buf_end > s->checksum_ptr)s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,s->buf_end - s->checksum_ptr);s->checksum_ptr = s->buffer;}/* make buffer smaller in case it ended up large after probing */if (s->read_packet && ctx->orig_buffer_size &&s->buffer_size > ctx->orig_buffer_size  && len >= ctx->orig_buffer_size) {if (dst == s->buffer && s->buf_ptr != dst) {int ret = set_buf_size(s, ctx->orig_buffer_size);if (ret < 0)av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");s->checksum_ptr = dst = s->buffer;}len = ctx->orig_buffer_size;}len = read_packet_wrapper(s, dst, len);if (len == AVERROR_EOF) {/* do not modify buffer if EOF reached so that a seek back canbe done without rereading data */s->eof_reached = 1;} else if (len < 0) {s->eof_reached = 1;s->error= len;} else {s->pos += len;s->buf_ptr = dst;s->buf_end = dst + len;ffiocontext(s)->bytes_read += len;s->bytes_read = ffiocontext(s)->bytes_read;}
}

该函数作用是:对本地媒体文件或网络流进行读取,将读上来的数据保存到AVIOContext输入缓冲区中。简单来讲,就是通过文件描述符去读取本地媒体文件中的数据,或者通过socket接收网络流中的数据,让读上来的数据填满整个AVIOContext输入缓冲区。该函数跟read_packet_wrapper函数的区别是:read_packet_wrapper函数是读取指定的字节数,而fill_buffer函数是读取不固定的字节数但会填满整个AVIOContext输入缓冲区。

形参s:既是输入型参数也是输出型参数。指向一个AVIOContext(字节流上下文结构体)变量。执行fill_buffer函数后,如果读取本地媒体文件或网络流成功:

s->pos的值会增加实际读取到的字节数大小;

s->buf_ptr会指向读上来的数据的开头,一般是原来的s->buf_end,也就是原来输入缓冲区有效数据的末尾;

s->buf_end会指向新的输入缓冲区中有效数据的末尾;

s->buf_end = s->buf_ptr + 实际读取到的字节数;

s->bytes_read的值会增加实际读取到的字节数大小。

返回值:无

(二)fill_buffer函数的内部实现分析

可以看到fill_buffer函数中,执行了read_packet_wrapper函数来对本地媒体文件或网络流进行读取:

static void fill_buffer(AVIOContext *s)
{//...len = read_packet_wrapper(s, dst, len);//...
}


http://www.mrgr.cn/news/7682.html

相关文章:

  • 闲鱼IP属地地址:去外地会自动变化吗?解析实时更新机制
  • 河南萌新联赛2024第(六)场:郑州大学
  • 鸿蒙Harmony实战开发知识:“UIAbility组件的3种启动模式”
  • 博弈论总结
  • C# --- 异常处理(Exception Handling)
  • leetcode135:分发糖果
  • Niushop商城第三方插件cps联盟_同城配送_上门预约上手教程配置方法适合单商户和多商户以及V6哈
  • 【安全靶场】-DC-8
  • 学习前端面试知识(13)
  • 探索Ruby的自然语言处理宝库:文本魔法的艺术
  • 稚晖君智元机器人远程机器人系列发布:引领具身智能新高度
  • 数据仓库中的表设计模式:全量表、增量表与拉链表
  • 自编码器(Autoencoder, AE):深入理解与应用
  • BEV学习---LSS-1:论文原理及代码串讲
  • 深入探讨视频美颜SDK:直播美颜工具的核心技术与实现
  • 网络设备net_device数据结构之ifindex
  • [数据集][目标检测]木材缺陷检测数据集VOC+YOLO格式2383张10类别
  • 《亿级流量系统架构设计与实战》第十二章 评论服务
  • 【赵渝强老师】Docker三剑客
  • Spring Boot 与 Spring Security 的集成及 OAuth2 实现