=================================================================
AVIOContext结构体和其相关的函数分析:
FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析
=================================================================
一、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)的偏移字节数。