#include "config.h"
#include
#include
#include
#include
#include
#include
#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,
};