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中:

cpp 复制代码
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是函数指针,指向读取数据包的回调函数:

cpp 复制代码
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)表示失败(无效的参数):

cpp 复制代码
if (!s->read_packet)
        return AVERROR(EINVAL);

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

cpp 复制代码
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中:

cpp 复制代码
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 can
           be 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函数来对本地媒体文件或网络流进行读取:

cpp 复制代码
static void fill_buffer(AVIOContext *s)
{
    //...
        len = read_packet_wrapper(s, dst, len);
    //...
}
相关推荐
mortimer8 小时前
一键实现人声伴奏分离:基于 `uv`, `FFmpeg` 和 `audio-separator` 的高效解决方案
python·ffmpeg·音视频开发
筏.k2 天前
WebRTC 项目中捕获 FFmpeg 底层源码日志(av_log)的完整方案
ffmpeg·webrtc
学习_学习_再学习2 天前
ffmpeg学习记录
学习·ffmpeg
我科绝伦(Huanhuan Zhou)3 天前
Oracle AWR管理与快照操作完整指南
数据库·oracle·ffmpeg
梵尔纳多3 天前
ffmpeg 使用滤镜实现播放倍速
c++·qt·ffmpeg
无敌最俊朗@4 天前
音视频播放的核心处理流程
ffmpeg
mortimer5 天前
搞懂FFmpeg中2个桀骜不驯的参数:CRF 与 Preset
ffmpeg·音视频开发·视频编码
2401_841495646 天前
Windows 系统中ffmpeg安装问题的彻底解决
windows·python·ffmpeg·bug·语音识别·下载·安装步骤
八月的雨季 最後的冰吻6 天前
FFmpeg --15-视频解码: AVIO内存输入模式分析
ffmpeg·音视频
aqi007 天前
FFmpeg开发笔记(八十八)基于Compose的国产电视直播开源框架MyTV
android·ffmpeg·音视频·直播·流媒体