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);
    //...
}
相关推荐
嘟嘟实验室7 小时前
微信小程序xr-frame透明视频实现
微信小程序·ffmpeg·音视频·xr
泰勒朗斯12 小时前
如何编译Opencv +ffmpeg linux 明明安装了ffmpeg但是opencv就是找不到
linux·opencv·ffmpeg
-Mr_X-18 小时前
windows下srs流媒体服务器使用ffmpeg推流
ffmpeg
dvlinker19 小时前
C++开源项目 VLC 源代码的交叉编译以及库的裁剪方法详解
ffmpeg·mingw-w64·msys2·cygwin·开源vlc·vlc编译·vlc裁剪
因我你好久不见1 天前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
cuijiecheng20182 天前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频
cuijiecheng20182 天前
音视频入门基础:AAC专题(13)——FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现
ffmpeg·音视频·aac
流氓也是种气质 _Cookie2 天前
uniapp blob格式转换为video .mp4文件使用ffmpeg工具
ffmpeg·uni-app
网络安全queen2 天前
网络安全-企业环境渗透2-wordpress任意文件读&&FFmpeg任意文件读
安全·web安全·ffmpeg
yerennuo2 天前
FFmpeg库之ffmpeg
qt·ffmpeg