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

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "input/demuxer.h"
/**AnnexB格式  H264的通用编码格式。

H264编码的两种格式:
1,AnnexB格式 = NALU(网络提取层) + startCode(00000001 or 000001)
0x000001场景:一个完整帧被编为多个slice时,包含这些slice的NALU使用。其余场景使用4字节的。
为防止NAL编码与起始码干扰,采用 EBSP编码方式
即在NAL中连续出现两个00时,编码在后插入一个03.解码器检测到000003时就将此03删除。
2,MP4格式, H264的另一个格式。
没有NAL概念,起始码000001进行帧分界。
第四个字节也很有用:
B0,表示视频对象序列的开始。
B6表示一个VOP(视频对象平面Video Object Plane)的开始。B6后的两字节00表示I 帧,01表示P帧。
VOP:Video Object Plane,视频对象平面。VOP 是一个物件单位,
MPEG-4 可以将画面上的每个物体(物件)切割出来,个别压缩, 由许多个 VOP 组成一个画面。
一个VOP是一个VO在特定时刻的快照,反映了该时刻VO的形状、纹理和运动参数,
一般来说,一个VOP是一个任意形状的图像
**/
typedef struct DemuxerPriv {
    FILE *f;
    size_t temporal_unit_size;
    size_t frame_unit_size;
} AnnexbInputContext;
/**leb128
LEB128(little endian base 128)是一种变长的整数压缩编码形式。
由于32位整数占用固定的4个字节,
可能大多数整数并不需要4个字节,最高几个字节可能为0(正数)或者为1(负数),
该编码就是不保存最高位的这些字节
每个字节中的最高bit是标识信息,1表示还有后续字节,0表示结束,后面7bits是有效数据。
将多个字节的该7bits从低到高组合起来就是所表示的整数

将无符号整数写成二进制形式,从低位到高位7个bits为一个整体组合成一个字节,在该字节最高位填入上述所说的标识信息。
以10000为例,编码过程:
二进制形式为    10 0111 0001 0000
以7bits为整体    1001110 0010000
添加标识组合成新的字节(从后往前,即低bits到高bits)    01001110(0x4E) 10010000(0x90) (最高位标识设置为0,表示没有后续字节)
LEB128 则为    0x90 0x4F (小端存放)

有符号数 正数和负数,
在计算机的存储中都是以补码存储,正数和上述无符号数一样处理,
负数的处理会有些区别,以-10000为例说明,编码过程:
二进制补码    11111111 11111111 11111100 00011000(可以看出最高两字节都是符号扩展的1)
以7bits为整体    1111 1111111 1111111 1111000 0011000
添加标识信息组合新的字节(从后往前,即低bits到高bits)    01111000 10011000
(此处结束条件不像上面那么明显,若前面和该7bits的最高位都为1时停止)
LEB128则为    0x98 0x78

原文:https://blog.csdn.net/liwugang43210/article/details/50475928

len 为解编码后的数据,返回值表示编码字节的个数,-1表示失败。
**/
static int leb128(AnnexbInputContext *const c, size_t *const len) {
    unsigned more, i = 0;
    uint8_t byte;
    *len = 0;
    do {
        if (fread(&byte, 1, 1, c->f) < 1)
            return -1;
        more = byte & 0x80;
        unsigned bits = byte & 0x7f;
        if (i <= 3 || (i == 4 && bits < (1 << 4)))
            *len |= bits << (i * 7);
        else if (bits) return -1;
        if (++i == 8 && more) return -1;
    } while (more);
    return i;
}
/**
file 输入文件名
fps 输出帧率
num_frames 输出总帧数。
**/
static int annexb_open(AnnexbInputContext *const c, const char *const file,
                       unsigned fps[2], unsigned *const num_frames)
{
    int res;
    size_t len;

    if (!(c->f = fopen(file, "rb"))) {
        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
        return -1;
    }

    // TODO: Parse sequence header and read timing info if any.
    fps[0] = 25;
    fps[1] = 1;
    for (*num_frames = 0;; (*num_frames)++) {
        res = leb128(c, &len);/** 读取并计算帧长度。 **/
        if (res < 0)
            break;
        fseek(c->f, len, SEEK_CUR);/** 跳过当前帧,进入下一帧 **/
    }
    fseek(c->f, 0, SEEK_SET);/**恢复定位到文件开始 **/

    return 0;
}

static int annexb_read(AnnexbInputContext *const c, Dav1dData *const data) {
    size_t len;
    int res;

    if (!c->temporal_unit_size) {
        res = leb128(c, &c->temporal_unit_size);/**读取并计算出临时单元长度 这个长度不含自身长度字节 **/
        if (res < 0) return -1;
    }
    if (!c->frame_unit_size) {
        res = leb128(c, &c->frame_unit_size);/**读取并计算出帧单元长度。**/
        if (res < 0 || (c->frame_unit_size + res) > c->temporal_unit_size) return -1;
        c->temporal_unit_size -= res;//临时单元长度 - 帧单元长度字节
    }
    res = leb128(c, &len);//这个长度才是帧长度, 前两个长度是每帧都有呢还是一个文件只有一个?
    if (res < 0 || (len + res) > c->frame_unit_size) return -1;
    uint8_t *ptr = dav1d_data_create(data, len);//依帧长度申请内存
    if (!ptr) return -1;
    c->temporal_unit_size -= len + res;//临时单元长度 - 帧长度字节 - 帧数据长度
    c->frame_unit_size -= len + res;//帧单元长度 - 帧长度字节 - 帧数据长度
    if ((res = fread(ptr, len, 1, c->f)) != 1) {//读取一帧
        fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
        dav1d_data_unref(data);
        return -1;
    }

    return 0;
}

static void annexb_close(AnnexbInputContext *const c) {
    fclose(c->f);
}

const Demuxer annexb_demuxer = {
    .priv_data_size = sizeof(AnnexbInputContext),
    .name = "annexb",
    .extension = "obu",//文件后缀。  开放位流单元?Open Bitstream Units
    .open = annexb_open,
    .read = annexb_read,
    .close = annexb_close,
};
 

猜你喜欢

转载自blog.csdn.net/DANFBAORE/article/details/88060225
今日推荐