AV1解码器DAV1D 源码分析:ivf.c

#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

文件后跟随多帧的数据,每帧数据由一个12字节的帧头(4字节帧长+8字节时间戳)+视频数据组成。
**/

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,
};
 

你可能感兴趣的:(vlc)