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

AV1解码器DAV1D 源码分析:annexb.c相关推荐

  1. Netty源码分析第6章(解码器)----第4节: 分隔符解码器

    Netty源码分析第6章(解码器)---->第4节: 分隔符解码器 Netty源码分析第六章: 解码器 第四节: 分隔符解码器 基于分隔符解码器DelimiterBasedFrameDecode ...

  2. Netty源码分析系列之常用解码器(下)——LengthFieldBasedFrameDecoder

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 前言 在上一篇文章中分析了三个比较简单的解码器,今天接着分析最后一个常用的解码器:Leng ...

  3. SparkRPC源码分析之RPC管道与消息类型

    SparkRPC源码分析之RPC管道与消息类型 我们前面看过了netty基础知识扫盲,那我们应该明白,ChannelHandler这个组件内为channel的各种事件提供了处理逻辑,也就是主要业务逻辑 ...

  4. Netty源码分析第7章(编码器和写数据)----第2节: MessageToByteEncoder

    Netty源码分析第7章(编码器和写数据)---->第2节: MessageToByteEncoder Netty源码分析第七章: Netty源码分析 第二节: MessageToByteEnc ...

  5. LIVE555再学习 -- OpenRTSP 源码分析

    看了很多东西,感觉有点杂.源码分析部分也看了,讲的也就那样.现在有点不知道从哪讲起了. 参看:nkmnkm的专栏-流媒体 参看:smilestone322的专栏-live555 一.源码组成 包括上述 ...

  6. FFMPEG源码分析(二)

    ffmpeg源码分析之数据流 本文主要介绍ffmpeg的数据流,在ffmpeg中主要分有三个主要用途用于媒体流的解码播放,媒体流的转换(解码之后再编码)和媒体流录制. 媒体流的解码播放 在ffmpeg ...

  7. live555 源码分析:子会话 SDP 行生成

    如我们在前文 live555 源码分析:ServerMediaSession 中看到的,H264VideoFileServerMediaSubsession 的继承层次体系如下图: 在这个继承层次体系 ...

  8. mplayer安装记录 源码分析

    mplayer源码下载地址: http://www.mplayerhq.hu/MPlayer/releases/ 下载最新的MPlayer-1.0rc4 #mkdir /usr/local/mplay ...

  9. FFMPEG 源码分析

    FFMPEG基本概念: ffmpeg是一个开源的编解码框架,它提供了一个音视频录制,解码和编码库.FFMPEG是在linux下开发的,但也有windows下的编译版本. ffmpeg项目由以下几部分组 ...

  10. Dubbo源码分析笔记-一(工程目录介绍)

    Dubbo 是阿里开发的分布式服务调用框架,提供了它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现. 工程目录 模块介绍 dubbo-common   Dubb ...

最新文章

  1. redis教程(五)之redis数据类型
  2. [C++再学习系列] 引用和指针
  3. 数据库集群和高可用解决方案
  4. 安装包镜像_创建 macOS Catalina cdr格式镜像安装包
  5. commons cli_从Commons CLI迁移到picocli
  6. gulp学习笔记,基本使用流程,基本函数,使用监听、插件
  7. HTMLParser使用举例
  8. 中事件源previous_PM2.5传感器在扬尘监测系统中的应用
  9. DevOps on DevCloud|代码检查服务如何降低“Billion Dollar Mistake” NPE风险
  10. C语言-Ubuntu下GDB与GCC的安装与使用
  11. 在CentOS上安装Git(转)
  12. C++基础教程之数组
  13. mysql数据库中实现内连接、左连接、右连接
  14. php在线文件编辑管理器 在线文本查看PHP插件
  15. git --amend用法
  16. python图片截取斜四边形_python shapely.geometry.polygon任意两个四边形的IOU计算实例...
  17. MySQL的基本查询语句
  18. IDEA类文件图标变成“J”
  19. 谈谈最近管理情绪和时间的心得:真的是破心中贼难
  20. JAVA用一维数组生成福彩双色球中奖号码

热门文章

  1. Mysql导入sql脚本时 脚本太长 报 Mysql server has gone away时解决方法
  2. 使用.NET开发AutoCAD——设计师不做画图匠(一)
  3. 【通信原理】复习笔记——模拟调制系统
  4. linux卸载php,很干净
  5. 使用MyBatis框架遇到There is no getter for property named 'param' in 'class com.vo.ParamVO'
  6. 机器人总动员拟人_机器人总动员在线观看高清-免费未删减-动画片-好恐怖
  7. 云端时代桌面云架构介绍(CTVI)
  8. 关于自定义tabbar项的问题
  9. 文件系统XFS与EXT4的区别
  10. Windows10关闭端口