FFmpeg源码:ffurl_seek2、ffurl_seek、avio_size函数分析

一、ffurl_seek2函数

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

cpp 复制代码
int64_t ffurl_seek2(void *urlcontext, int64_t pos, int whence)
{
    URLContext *h = urlcontext;
    int64_t ret;

    if (!h->prot->url_seek)
        return AVERROR(ENOSYS);
    ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
    return ret;
}

该函数的作用是:改变形参urlcontext访问的资源的下一个读/写操作将使用的位置。

ffurl_seek2函数内部,h->prot->url_seek是函数指针:

cpp 复制代码
typedef struct URLProtocol {
//...
    int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
//...
}

h->prot->url_seek不为空时,会调用h->prot->url_seek指向的回调函数:

cpp 复制代码
ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);

而函数指针h->prot->url_seek一般指向libavformat/file.c中的file_seek函数。这时,执行ffurl_seek2函数等价于执行file_seek函数。关于file_seek函数用法可以参考:《FFmpeg源码:file_read、file_write、file_seek函数分析》。

所以当h->prot->url_seek指向file_seek函数时,ffurl_seek2函数的作用是:

ffurl_seek2(h,0,AVSEEK_SIZE):不改变读写位置,返回文件描述符为h->priv_data->fd的文件的大小;

ffurl_seek2(h,0,SEEK_SET):将读写位置移到文件描述符为h->priv_data->fd的文件的开头,此时返回0;

ffurl_seek2(h,0,SEEK_END):将读写位置移到文件尾,此时返回文件描述符为h->priv_data->fd的文件的大小;

ffurl_seek2(h,0,SEEK_CUR):取得目前文件位置。此时返回当前读写位置距离文件开头多少个字节;

二、ffurl_seek函数

ffurl_seek函数定义在libavformat/url.h中:

cpp 复制代码
/**
 * Change the position that will be used by the next read/write
 * operation on the resource accessed by h.
 *
 * @param pos specifies the new position to set
 * @param whence specifies how pos should be interpreted, it must be
 * one of SEEK_SET (seek from the beginning), SEEK_CUR (seek from the
 * current position), SEEK_END (seek from the end), or AVSEEK_SIZE
 * (return the filesize of the requested resource, pos is ignored).
 * @return a negative value corresponding to an AVERROR code in case
 * of failure, or the resulting file position, measured in bytes from
 * the beginning of the file. You can use this feature together with
 * SEEK_CUR to read the current file position.
 */
static inline int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
{
    return ffurl_seek2(h, pos, whence);
}

可以看到该函数内部仅调用了ffurl_seek2函数,所以ffurl_seek函数和ffurl_seek2函数用法完全等价。不同点仅仅在于ffurl_seek函数用static和inline关键字修饰,内联展开节省了每次调用函数的开销,能提高程序性能,具体可以参考:《C语言static和inline》。

三、avio_size函数

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

cpp 复制代码
/**
 * Get the filesize.
 * @return filesize or AVERROR
 */
int64_t avio_size(AVIOContext *s)
{
    FFIOContext *const ctx = ffiocontext(s);
    int64_t size;

    if (!s)
        return AVERROR(EINVAL);

    if (ctx->written_output_size)
        return ctx->written_output_size;

    if (!s->seek)
        return AVERROR(ENOSYS);
    size = s->seek(s->opaque, 0, AVSEEK_SIZE);
    if (size < 0) {
        if ((size = s->seek(s->opaque, -1, SEEK_END)) < 0)
            return size;
        size++;
        s->seek(s->opaque, s->pos, SEEK_SET);
    }
    return size;
}

该函数的作用是:返回文件描述符为s->opaque->priv_data->fd的文件的大小,单位为字节。该函数内部首先会尝试通过fstat函数获取文件大小,如果获取失败,会继续尝试通过lseek函数获取文件大小。

可以看到avio_size函数内部调用了函数指针s->seek指向的回调函数,而s->seek一般指向ffurl_seek2函数,所以下面语句相当于执行了ffurl_seek2(s->opaque,0,AVSEEK_SIZE),从而能获取到文件大小:

cpp 复制代码
size = s->seek(s->opaque, 0, AVSEEK_SIZE);

从 《FFmpeg源码:file_read、file_write、file_seek函数分析》中我们可以知道,ffurl_seek2(s->opaque,0,AVSEEK_SIZE)实际相当于执行了:

cpp 复制代码
/* XXX: use llseek */
static int64_t file_seek(URLContext *h, int64_t pos, int whence)
{
//...
    struct stat st;
    ret = fstat(c->fd, &st);
    return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size);
   //...
}

即通过fstat函数得到文件大小。

avio_size函数中执行语句"size = s->seek(s->opaque, 0, AVSEEK_SIZE)"后,如果得到的size值小于0,会继续执行:

cpp 复制代码
    if (size < 0) {
        if ((size = s->seek(s->opaque, -1, SEEK_END)) < 0)
            return size;
        size++;
        s->seek(s->opaque, s->pos, SEEK_SET);
    }

意思就是如果通过fstat函数获取文件大小失败,继续尝试通过lseek函数获取文件大小:

cpp 复制代码
/* XXX: use llseek */
static int64_t file_seek(URLContext *h, int64_t pos, int whence)
{
//...
    ret = lseek(c->fd, pos, whence);
 
    return ret < 0 ? AVERROR(errno) : ret;
}
相关推荐
Java程序员 拥抱ai41 分钟前
SpringBoot + FFmpeg + Redis:视频转码、截图、水印异步处理平台搭建
spring boot·redis·ffmpeg
给算法爸爸上香14 小时前
yolo tensorrt视频流检测软解码和硬解码
yolo·ffmpeg·视频编解码·tensorrt·nvcodec
dwp114717060720 小时前
在Windows上基于MSYS2 UCRT64工具链编译ffmpeg源码
windows·ffmpeg
专业开发者1 天前
开发者可从蓝牙 ® 低功耗音频(Bluetooth® LE Audio)中获得的 4 大功能
物联网·ffmpeg·音视频
dwp11471706071 天前
在Windows上基于MSYS2 MSVC工具链编译ffmpeg源码
windows·ffmpeg
筏.k2 天前
FFmpeg 视频解码入门:H264 软解码器简单示例
ffmpeg·音视频
hk11242 天前
【音视频/边缘计算】2025年度H.265/HEVC高并发解码与画质修复(Super-Resolution)基准测试报告(含沙丘/失控玩家核心样本)
ffmpeg·边缘计算·音视频开发·h.265·测试数据集
筏.k2 天前
FFmpeg 视频解码进阶:H264_CUVID 硬解码器简单示例
ffmpeg·音视频
auspicious航3 天前
数据库同步技术演进:从备份转储到实时CDC的DBA实战指南
数据库·ffmpeg·dba
小馬佩德罗4 天前
如何将x264 x265的动态库编译入Linux系统中的FFmpeg源码 - x264库编译
linux·ffmpeg·x264