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);
    //...
}
相关推荐
PlumCarefree15 小时前
基于鸿蒙API10的RTSP播放器(五:拖动底部视频滑轨实现跳转)
华为·ffmpeg·音视频
LuckyInn15 小时前
从安装ffmpeg开始,把一个视频按照每秒30帧fps剪切为图片
ffmpeg·音视频
cuijiecheng201817 小时前
FFmpeg源码:skip_bits、skip_bits1、show_bits函数分析
ffmpeg
__Destiny__21 小时前
视频格式转为mp4(使用ffmpeg)
ffmpeg·视频编解码
<Sunny>21 小时前
SDL 2.0视频数据渲染到窗口上播放流程
ffmpeg·音视频
PlumCarefree1 天前
USB摄像头视频流转RTSP流
图像处理·ffmpeg·音视频·媒体·视频编解码
PlumCarefree1 天前
基于鸿蒙API10的RTSP播放器(八:音量和亮度调节功能的整合)
华为·ffmpeg·音视频·harmonyos
源之缘-OFD先行者1 天前
ffmpeg实现视频的合成与分割
ffmpeg·音视频
一尺丈量1 天前
ffmpeg硬件解码一般流程
c++·人工智能·ffmpeg·cuda·硬件解码
cuijiecheng20182 天前
音视频入门基础:AAC专题(5)——FFmpeg源码中,判断某文件是否为AAC裸流文件的实现
ffmpeg·音视频·aac