FFmpeg源码:avio_tell函数分析

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

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

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

FFmpeg源码:avio_tell函数分析

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

一、avio_tell函数的定义

avio_tell函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavformat/avio.h中:

cpp 复制代码
/**
 * ftell() equivalent for AVIOContext.
 * @return position or AVERROR.
 */
static av_always_inline int64_t avio_tell(AVIOContext *s)
{
    return avio_seek(s, 0, SEEK_CUR);
}

该函数作用是:得到文件位置指针当前位置(s->buf_ptr)相对于文件首(s->buffer)的偏移字节数。

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

返回值:返回一个非负数表示文件位置指针当前位置相对于文件首的偏移字节数,单位为byte。返回一个负数表示失败。

二、avio_tell函数的内部实现原理

avio_tell函数内部调用了语句:avio_seek(s, 0, SEEK_CUR)。SEEK_CUR是宏,定义在/usr/include/stdio.h中,可以看到宏定义SEEK_CUR相当于1:

cpp 复制代码
/* The possibilities for the third argument to `fseek'.
   These values should not be changed.  */
#define SEEK_SET	0	/* Seek from beginning of file.  */
#define SEEK_CUR	1	/* Seek from current position.  */
#define SEEK_END	2	/* Seek from end of file.  */
#ifdef __USE_GNU
# define SEEK_DATA	3	/* Seek to next data.  */
# define SEEK_HOLE	4	/* Seek to next hole.  */
#endif

avio_seek函数定义在源文件libavformat/aviobuf.c中:

cpp 复制代码
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
{
    FFIOContext *const ctx = ffiocontext(s);
    int64_t offset1;
    int64_t pos;
    int force = whence & AVSEEK_FORCE;
    int buffer_size;
    int short_seek;
    whence &= ~AVSEEK_FORCE;

    if(!s)
        return AVERROR(EINVAL);

    if ((whence & AVSEEK_SIZE))
        return s->seek ? s->seek(s->opaque, offset, AVSEEK_SIZE) : AVERROR(ENOSYS);

    buffer_size = s->buf_end - s->buffer;
    // pos is the absolute position that the beginning of s->buffer corresponds to in the file
    pos = s->pos - (s->write_flag ? 0 : buffer_size);

    if (whence != SEEK_CUR && whence != SEEK_SET)
        return AVERROR(EINVAL);

    if (whence == SEEK_CUR) {
        offset1 = pos + (s->buf_ptr - s->buffer);
        if (offset == 0)
            return offset1;
        if (offset > INT64_MAX - offset1)
            return AVERROR(EINVAL);
        offset += offset1;
    }
    if (offset < 0)
        return AVERROR(EINVAL);

    short_seek = ctx->short_seek_threshold;
    if (ctx->short_seek_get) {
        int tmp = ctx->short_seek_get(s->opaque);
        short_seek = FFMAX(tmp, short_seek);
    }

    offset1 = offset - pos; // "offset1" is the relative offset from the beginning of s->buffer
    s->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);
    if ((!s->direct || !s->seek) &&
        offset1 >= 0 && offset1 <= (s->write_flag ? s->buf_ptr_max - s->buffer : buffer_size)) {
        /* can do the seek inside the buffer */
        s->buf_ptr = s->buffer + offset1;
    } else if ((!(s->seekable & AVIO_SEEKABLE_NORMAL) ||
               offset1 <= buffer_size + short_seek) &&
               !s->write_flag && offset1 >= 0 &&
               (!s->direct || !s->seek) &&
              (whence != SEEK_END || force)) {
        while(s->pos < offset && !s->eof_reached)
            fill_buffer(s);
        if (s->eof_reached)
            return AVERROR_EOF;
        s->buf_ptr = s->buf_end - (s->pos - offset);
    } else if(!s->write_flag && offset1 < 0 && -offset1 < buffer_size>>1 && s->seek && offset > 0) {
        int64_t res;

        pos -= FFMIN(buffer_size>>1, pos);
        if ((res = s->seek(s->opaque, pos, SEEK_SET)) < 0)
            return res;
        s->buf_end =
        s->buf_ptr = s->buffer;
        s->pos = pos;
        s->eof_reached = 0;
        fill_buffer(s);
        return avio_seek(s, offset, SEEK_SET | force);
    } else {
        int64_t res;
        if (s->write_flag) {
            flush_buffer(s);
        }
        if (!s->seek)
            return AVERROR(EPIPE);
        if ((res = s->seek(s->opaque, offset, SEEK_SET)) < 0)
            return res;
        ctx->seek_count++;
        if (!s->write_flag)
            s->buf_end = s->buffer;
        s->buf_ptr = s->buf_ptr_max = s->buffer;
        s->pos = offset;
    }
    s->eof_reached = 0;
    return offset;
}

语句avio_seek(s, 0, SEEK_CUR) 等价于 avio_seek(s, 0, 1)。这时,avio_seek函数可以化简为:

cpp 复制代码
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
{
    int64_t offset1;
    if(!s)
        return AVERROR(EINVAL);
    buffer_size = s->buf_end - s->buffer;
    pos = s->pos - (s->write_flag ? 0 : buffer_size);
    if (whence == SEEK_CUR) {
        offset1 = pos + (s->buf_ptr - s->buffer);
        if (offset == 0)
            return offset1;
    }
}

pos的值为0的情况下,avio_seek(s, 0, 1)就是:

cpp 复制代码
​
int64_t avio_seek(AVIOContext *s, 0, 1)
{
    int64_t offset1;
    if(!s)
        return AVERROR(EINVAL);

    offset1 = s->buf_ptr - s->buffer;
    return offset1;
}

​

所以avio_tell函数,也就是avio_seek(s, 0, 1)相当于执行了:s->buf_ptr - s->buffer。从而能得到文件位置指针当前位置(s->buf_ptr)相对于文件首(s->buffer)的偏移字节数。

相关推荐
都在酒里16 小时前
【极致低延时】香橙派部署 MediaMTX 实现 WebRTC 推流,延时仅 500-800ms,比局域网 ffmpeg 拉流快近 10 倍!(附踩坑全记录)
linux·arm开发·ffmpeg·webrtc·orangepi·嵌入式软件
Empty-Filled16 小时前
用 Kap + FFmpeg 把录屏转成小体积 GIF:产品操作演示图制作实践
ffmpeg·kap
矜辰所致1 天前
嵌入式语音开发应用基础说明
ffmpeg·ai 语音·嵌入式语音·语音播放·语音采样
luoyayun3612 天前
Qt + FFmpeg 实战:音频静音段检测
qt·ffmpeg·音视频·静音段检测
小鹿研究点东西3 天前
直播带货长视频AI自动剪辑开播:一场直播如何反复利用?
ffmpeg·自动化·音视频·语音识别
luoyayun3614 天前
Qt + FFmpeg 实战:获取音视频文件基础属性、流信息和元数据
qt·ffmpeg·音视频·元数据·获取音视频文件属性
Rudon滨海渔村4 天前
ffmpeg裁剪视频黑屏、不准时等处理方式 - ffmpeg基本操作
ffmpeg·音视频
The Sheep 20234 天前
ffmpeg速成
ffmpeg
街灯L4 天前
【Ubuntu】使用ffmpeg解析m3u8网页视频
ubuntu·ffmpeg·音视频
烟雨江南7855 天前
特高压输电线路带电作业直升机吊篮与强电磁感应放电:基于“灵声智库”空间自适应滤波与声纹授权的离线语音控制指令方案
人工智能·ffmpeg·webrtc·语音识别·ai质检