#include "config.h"
#include
#include
#include
#include
#include
#include
#include "input/demuxer.h"
#ifdef _MSC_VER
#define ftello _ftelli64
#endif
/*
IVF是一个非常简单的视频容器。用于封装VP8/VP9的数据。
下面多字节的数据是采用小端编码(little-endian)。IVF文件头由32字节组成:
bytes 0-3 signature: 'DKIF' 标志位。
bytes 4-5 version (should be 0) 版本号
bytes 6-7 length of header in bytes IVF头长度
bytes 8-11 codec FourCC (e.g., 'VP80') //子标志,这儿用于AV1,使用标志为AV01
bytes 12-13 width in pixels 视频宽
bytes 14-15 height in pixels 视频高
bytes 16-19 frame rate 帧率
bytes 20-23 time scale 与帧率配合的时间比例,
bytes 24-27 number of frames in file 文件中帧个数。
bytes 28-31 unused
typedef struct DemuxerPriv {
FILE *f;
} IvfInputContext;
/*字节数组转32位无符号整数
**/
static unsigned rl32(const uint8_t *const p) {
return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0];
}
/*字节数组转64位整数
**/
static int64_t rl64(const uint8_t *const p) {
return (((uint64_t) rl32(&p[4])) << 32) | rl32(p);
}
/**
file 输入文件名
fps 输出帧率
num_frames 输出总帧数。
**/
static int ivf_open(IvfInputContext *const c, const char *const file,
unsigned fps[2], unsigned *const num_frames)
{
int res;
uint8_t hdr[32];
memset(c, 0, sizeof(*c));
if (!(c->f = fopen(file, "rb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
} else if ((res = fread(hdr, 32, 1, c->f)) != 1) {/* 读取IVF头 **/
fprintf(stderr, "Failed to read stream header: %s\n", strerror(errno));
fclose(c->f);
return -1;
} else if (memcmp(hdr, "DKIF", 4)) {
fprintf(stderr, "%s is not an IVF file [tag=%.4s|0x%02x%02x%02x%02x]\n",
file, hdr, hdr[0], hdr[1], hdr[2], hdr[3]);
fclose(c->f);
return -1;
} else if (memcmp(&hdr[8], "AV01", 4)) {
fprintf(stderr, "%s is not an AV1 file [tag=%.4s|0x%02x%02x%02x%02x]\n",
file, &hdr[8], hdr[8], hdr[9], hdr[10], hdr[11]);
fclose(c->f);
return -1;
}
fps[0] = rl32(&hdr[16]);
fps[1] = rl32(&hdr[20]);
const unsigned duration = rl32(&hdr[24]);
uint8_t data[4];
for (*num_frames = 0;; (*num_frames)++) {
if ((res = fread(data, 4, 1, c->f)) != 1)
break; // EOF
fseek(c->f, rl32(data) + 8, SEEK_CUR);/**帧个数没有使用文件标注,采用实际读取计数的方式**/
}
fps[0] *= *num_frames;
fps[1] *= duration;
fseek(c->f, 32, SEEK_SET);//读取完成后文件指针返回到帧起始处。即IVF文件头尾部
return 0;
}
static int ivf_read(IvfInputContext *const c, Dav1dData *const buf) {
uint8_t data[8];
uint8_t *ptr;
int res;
const int64_t off = ftello(c->f);
if ((res = fread(data, 4, 1, c->f)) != 1)/**4字节帧长度**/
return -1; // EOF
const ptrdiff_t sz = rl32(data);
if ((res = fread(data, 8, 1, c->f)) != 1)/**8字节时间戳 **/
return -1; // EOF
ptr = dav1d_data_create(buf, sz);
if (!ptr) return -1;
buf->m.offset = off;
buf->m.timestamp = rl64(data);
if ((res = fread(ptr, sz, 1, c->f)) != 1) {/**帧数据 **/
fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
dav1d_data_unref(buf);
return -1;
}
return 0;
}
static void ivf_close(IvfInputContext *const c) {
fclose(c->f);
}
const Demuxer ivf_demuxer = {
.priv_data_size = sizeof(IvfInputContext),
.name = "ivf",
.extension = "ivf",//文件后缀
.open = ivf_open,
.read = ivf_read,
.close = ivf_close,
};