c实现mp4解封装

news2024/11/22 13:29:34

文章目录

    • 前序
    • MP4简介
      • MP4的定义
      • MP4的封装格式
    • Box类型详解
      • Box格式
      • ftyp box
      • mvhd box
      • tkhd box
      • hdlr box
      • mdat box
      • stbl box
      • stsd box
      • stco box
      • stsc box
      • stsz box
      • stts box
      • stss box
    • demuxer demo的实现(视频数据部分)
    • 总结:
    • 工具介绍
    • 源码
    • 参考

前序

最近为了更加深入了解音视频demux这块的功能,准备着手写个demuxer,提取视频流。

MP4简介

MP4的定义

MP4是一种常用的视音频流封装格式,按照指定的协议来存放媒体数据;因为mp4是基于苹果QuickTime文件格式,所以与mov有很多相同之处,在苹果开发者平台可以看到详细的有关封装文档(https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25615)

MP4的封装格式

  • MP4格式预览—mp4是由多个box嵌套组成的
    在这里插入图片描述

  • MP4主要的顶部box

    ftyp box:描述MP4锁遵循的规范和版本

    mdat box :存放媒体数据

    moov box:存放媒体参数(pps、sps等)相关信息和用于索引媒体数据存储位置的信息

  • MP4常用box
    在这里插入图片描述

Box类型详解

Box格式

在这里插入图片描述

  1. size字段为整个box的大小,包括box header和box body
  2. type为box的类型,通常为四字节的字符串,例如ftyp
  3. 当size == 0时,box的大小为large size
  4. 当box为full box时,存在version和flags字段,具体含义因box不同而不同
  5. 若box没有嵌套其他box,例如ftyp box,则box body部分根据具体规范解析相应字段;若box为container box,则box body部分嵌套其它box,还需一步步解套获取最终的数据

ftyp box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

  • major_brand:比如常见的 isom、mp41、mp42、avc1、qt等。它表示“最好”基于哪种格式来解析当前的文件。举例,major_brand 是 A,compatible_brands 是 A1,当解码器同时支持 A、A1 规范时,最好使用A规范来解码当前媒体文件,如果不支持A规范,但支持A1规范,那么,可以使用A1规范来解码;

  • minor_version:提供 major_brand 的说明信息,比如版本号,不得用来判断媒体文件是否符合某个标准/规范;

  • compatible_brands:文件兼容的brand列表。比如 mp41 的兼容 brand 为 isom。通过兼容列表里的 brand 规范,可以将文件 部分(或全部)解码出来;

mvhd box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • version :一字节用于指定mvhd的版本

    • flags:3字节,预留

    • Create time:媒体创建时间,与UTC时间不同的是,此时间是从1904年1月1日0:0:0开始计算的,而utc是从1970年1月1日0:0:0开始计算,故Create time须要减去时间差换算成utc时间

      // 注:66年时间差不是66*365*24*3600来计算
      creation_time_utc = creation_time - (66年时间差) = creation_time - 2082844800
      
    • Modification time:媒体最后被修改的时间,计算方式同Create time

    • Timescale:一秒包含的时间单位(整数)。举个例子,如果timescale等于1000,那么,一秒包含1000个时间单位(后面track等的时间,都要用这个来换算,比如track的duration为10,000,那么,track的实际时长为10,000/1000=10s);

    • Duration:影片时长(整数),根据文件中的track的信息推导出来,等于时间最长的track的duration;

    • Preferred rate:推荐的播放速率,32位整数,高16位、低16位分别代表整数部分、小数部分([16.16]),举例 0x0001 0000 代表1.0,正常播放速度;

    • Preferred volume:播放音量,16位整数,高8位、低8位分别代表整数部分、小数部分([8.8]),举例 0x01 00 表示 1.0,即最大音量;

    • Matrix struct:视频的转换矩阵,详情看

      Basic Data Types

    • Next_track_ID:32位整数,非0,一般可以忽略不计。当要添加一个新的track到这个影片时,可以使用的track id,必须比当前已经使用的track id要大。也就是说,添加新的track时,需要遍历所有track,确认可用的track id;

tkhd box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • version:tkhd box的版本;
    • flags:按位或操作获得,默认值是7(0x000001 | 0x000002 | 0x000004),表示这个track是启用的、用于播放的 且 用于预览的。
      • Track_enabled:值为0x000001,表示这个track是启用的,当值为0x000000,表示这个track没有启用;
      • Track_in_movie:值为0x000002,表示当前track在播放时会用到;
      • Track_in_preview:值为0x000004,表示当前track用于预览模式;
    • Creation time:当前track的创建时间;
    • Modification time:当前track的最近修改时间;
    • Track ID:当前track的唯一标识,不能为0,不能重复;
    • Duration:当前track的完整时长(需要除以timescale得到具体秒数);
    • Layer:视频轨道的叠加顺序,数字越小越靠近观看者,比如1比2靠上,0比1靠上;
    • Alternate_group:当前track的分组ID,alternate_group值相同的track在同一个分组里面。同个分组里的track,同一时间只能有一个track处于播放状态。当alternate_group为0时,表示当前track没有跟其他track处于同个分组。一个分组里面,也可以只有一个track;
    • Volume:audio track的音量,介于0.0~1.0之间;
    • Matrix structure:视频的变换矩阵;
    • Track width:视频的宽
    • Track height:视频的高

hdlr box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:hdlr box的版本
    • Flags:置0
    • Component type:四字节子串定义handler的类型;此字段只有两种值合法:'mhlr’(media handlers)和’dhlr’(data handlers)
    • Component subtype:针对Component type进行细分类型,例如’vide’定义为视频数据,'soun’定义为音频数据
    • Component manufacturer:保留,置0
    • Component flags:保留,置0
    • Component flags mask:保留,置0
    • Component name:子串指定Component 的名字,可能为空

mdat box

  • 数据结构分布图
    在这里插入图片描述

    注意:取到的frame前四个字节为frame数据的长度字节,须要偏移去掉

stbl box

主要存放了媒体参数(pps、sps、vps等)相关信息和用于解析mdat中视音频数据的关键信息

  • stsd:给出视音频的相关参数信息,有高宽、音量、位深度和每个sample多少个frame
  • stco:chunk在文件中的偏移
  • stsc:每个chunk中包含几个sample
  • stsz:每个sample的size(单位是字节)
  • stts:每个sample的时长
  • stss:哪些sample是关键帧

stsd box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stsd box的版本
    • Flags:置0
    • Number of entries:Sample description table的个数
    • Sample description table:以视频为例,此时Sample description table字段中为若干个视频编码相关的box,例如avc1 box
      • avc1 box

        • 字段分布图
          在这里插入图片描述

        • 字段解析

          这个在https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html的“Sample Description Atoms”一节可以研究下

      • avcC box(包含了视频关键参数,在ISO/IEC 14496-15中定义)

        • 字段分布图
          在这里插入图片描述

        • 字段解析

          • num_of_sps:sps的个数
          • sps_length:sps的长度
          • sps_nal_unit:长度为sps_length的sps
          • num_of_pps:pps的个数
          • pps_length:pps的长度
          • pps_nal_unit:长度为pps_length的pps

          其他字段可以自行在ISO/IEC 14496-15中查到

stco box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stco box的版本
    • Flags:置0
    • Number of entries:chunk的个数
    • Chunk offset table:每个chunk在整个视频文件的偏移值,每个值的长度为4字节

stsc box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stsc box的版本
    • Flags:置0
    • Number of entries:”Sample-to-chunk table”的条数
    • Sample-to-chunk table:
      • First chunk:chunk的索引
      • Samples per chunk:从’First chunk’开始,每个chunk中sample的个数
      • Sample description ID:stsd box中‘Sample description table’的下标
  • Sample-to-chunk table示意图
    在这里插入图片描述

    • chunk1-chunk2:每个chunk中有3个sample,并且Sample description ID为23
    • chunk3-chunk4:每个chunk中有1个sample,并且Sample description ID为23
    • chunk5:每个chunk中有3个sample,并且Sample description ID为24

stsz box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stsz box的版本
    • Flags:置0
    • Sample size:为0则表示所有sample的大小不一定一样,不为0则表示所有sample的大小一样
    • Number of entries:”Sample size table”的条数
    • Sample size table:每个sample的size,每个sample size的长度为4字节

stts box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stts box的版本
    • Flags:置0
    • Number of entries:“Time-to-sample table”的条数
    • Time-to-sample table:
      • Sample count:具有相同“Sample duration”的个数
      • Sample duration:sample的时长(以timescale为计量)
  • Time-to-sample table:示意图
    在这里插入图片描述

    sample1 - sample4的sample duration是4

stss box

  • 字段分布图
    在这里插入图片描述

  • 字段解析

    • Version:stts box的版本
    • Flags:置0
    • Number of entries:“Sync sample table”的条数
    • Sync sample table:关键帧对应的sample index

demuxer demo的实现(视频数据部分)

  1. 获取sps pps参数

    1. 解析stsd box,其中contain avc1 box和avcC box(此步骤详解见上文)

    2. 解析avcC box可以获取到sps和pps

      以下为ISO/IEC 14496-15中解析avcC的伪代码

    aligned(8) class AVCDecoderConfigurationRecord { 
    	 unsigned int(8) configurationVersion = 1; 
    	 unsigned int(8) AVCProfileIndication; 
    	 unsigned int(8) profile_compatibility; 
    	 unsigned int(8) AVCLevelIndication; 
    	 bit(6) reserved =111111’b; 
    	 unsigned int(2) lengthSizeMinusOne; 
    	 bit(3) reserved =111’b; 
    	 unsigned int(5) numOfSequenceParameterSets; 
    	 for (i=0; i< numOfSequenceParameterSets; i++) { 
    		 unsigned int(16) sequenceParameterSetLength ; 
    		 bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit; 
    	 } 
    	 unsigned int(8) numOfPictureParameterSets; 
    	 for (i=0; i< numOfPictureParameterSets; i++) { 
    		 unsigned int(16) pictureParameterSetLength; 
    		 bit(8*pictureParameterSetLength) pictureParameterSetNALUnit; 
     }
    
     if( profile_idc == 100 || profile_idc == 110 || 
    	 profile_idc == 122 || profile_idc == 144 ) 
    	 { 
    		 bit(6) reserved =111111’b; 
    		 unsigned int(2) chroma_format; 
    		 bit(5) reserved =11111’b; 
    		 unsigned int(3) bit_depth_luma_minus8; 
    		 bit(5) reserved =11111’b; 
    		 unsigned int(3) bit_depth_chroma_minus8; 
    		 unsigned int(8) numOfSequenceParameterSetExt; 
    		 for (i=0; i< numOfSequenceParameterSetExt; i++) { 
    			 unsigned int(16) sequenceParameterSetExtLength; 
    			 bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit; 
    		 } 
    	 } 
    }
    
  2. 获取关键帧位置

    解析stss box可以知道哪一个sample中包含关键帧

  3. 获取chunk位置

    解析stco box可以获取到每个chunk在视频文件中的索引

  4. 获取每个chunk中sample个数

    解析stsc box可以获取到每个chunk包含多少个sample

  5. 获取sample大小

    解析stsz box可以获取到每个sample的大小

  6. 获取frame位置(demo视频文件一个sample只包含一个frame,所以sample的位置和大小就是frame的位置和大小)

    1. 根据stsd解析到每个sample中有多少个frame
    2. 然后再根据trunk的位置和sample的大小来定位frame起始地址
    3. mdat中frame的数据格式为: | 4字节数据长度 | frame数据|,所以根据字节长度读取相应个数frame
  7. 获取到一帧数据后

    1. 判断当前frame为I帧,则添加写入(start_code+sps) + (start_code+pps) + (start_code + frame数据)到输出文件
    2. 判断当前frame不为I帧,则写入(start_code + frame数据)到输出文件
  8. 保存成h264文件,可使用ffplay和potplay播放

注意:有些非字串的字段为大端字节序,须要转换

总结:

  1. 解析非字符串的数据时,需要注意大小端的问题
  2. 解析对应的box获取到sps、vps、pps
  3. 解析对应的box拿到视频帧数据
  4. 将视频帧写入本地文件的时候要注意
    1. 视频帧前四个字节为视频帧数据长度
    2. 若为I帧则需要加上sps、vps、pps
    3. 视频帧注意加上start_code

工具介绍

  1. mp4info—可以看到相关box的字节信息,但发现对avcC的解析漏掉了几个字节
    在这里插入图片描述

  2. mp4 exploer—可以更加直观的看到视音频数据信息
    在这里插入图片描述

源码

https://github.com/TaoChou/demuxer-c

参考

  1. https://zhuanlan.zhihu.com/p/333765990
  2. https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691
  3. ISO/IEC 14496-15

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/53952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nginx源码分析--基数树

typedef struct {ngx_radix_node_t *root;ngx_pool_t *pool;ngx_radix_node_t *free;char *start;size_t size; } ngx_radix_tree_t;预备知识 1.基数树也是一种二叉查找树,目前官方模块中仅geo模块使用了基数树.2.ngx_radix_tree_t基数树要求…

微服务框架 SpringCloud微服务架构 8 Gateway 网关 8.7 网关的cors 跨域配置

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构8 Gateway 网关8.7 网关的cors 跨域配置8.7.1 跨域问题处理8.7.2 案例8.7.…

深入讲解Netty那些事儿之从内核角度看IO模型(下)

接上文深入讲解Netty那些事儿之从内核角度看IO模型&#xff08;上&#xff09; epoll 通过上边对select,poll核心原理的介绍&#xff0c;我们看到select,poll的性能瓶颈主要体现在下面三个地方&#xff1a; 因为内核不会保存我们要监听的socket集合&#xff0c;所以在每次调用…

最全面的Spring教程(六)——WebSocket

前言 本文为 【SpringMVC教程】WebSocket 相关知识介绍&#xff0c;具体将对WebSocket进行简介&#xff0c;并通过实战案例对WebSocket的使用进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &#x1f449;Java全栈学习路线可参考&#xff1a;【Java全栈学…

SpringBoot接口 - 如何优雅的写Controller并统一异常处理?

内容目录 为什么要优雅的处理异常 实现案例ControllerAdvice异常统一处理Controller接口运行测试 进一步理解ControllerAdvice还可以怎么用&#xff1f;ControllerAdvice是如何起作用的&#xff08;原理&#xff09;&#xff1f; 示例源码 更多内容 SpringBoot接口如何对异…

【Pygame实战】代码版《舞动青春*炫舞》能否引领音舞游戏再一次爆发?“你还记得最浪漫的舞蹈游戏炫舞吗?”

导语 Hello&#xff0c;大家好呀&#xff01;我是木木子吖&#xff5e; 一个集美貌幽默风趣善良可爱并努力码代码的程序媛一枚。 听说关注我的人会一夜暴富发大财哦~ &#xff08;哇哇哇 这真的爱&#x1f60d;&#x1f60d;&#xff09; 所有文章完整的素材源码都在&#…

GIS工具maptalks开发手册(二)01-11——渲染文字及参数注释

GIS工具maptalks开发手册(二)01-11——渲染文字及参数注释 效果 代码 index.html <!DOCTYPE html> <html> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1"> <title>…

E. Gardener and Tree(拓扑排序)

Problem - 1593E - Codeforces 树是一个无定向的连接图&#xff0c;其中没有循环。这个问题是关于无根的树。一棵树的叶子是一个顶点&#xff0c;它最多与一个顶点相连。 园丁维塔利用n个顶点种了一棵树。他决定对这棵树进行修剪。为了做到这一点&#xff0c;他进行了一些操作…

云原生应用的最小特权原则

IDC 预计&#xff0c;从现在到 2024 年初&#xff0c;将开发和部署 5 亿个新应用程序——超过过去 40 年的总和。 Gartner 预测&#xff0c;到 2025 年&#xff0c;75% 的企业将运行某种容器化应用程序。 现代应用程序需要现代安全性。 公共云供应商非常积极地提升平台安全性&…

JAVA培训之连接查询之子查询

子查询就是嵌套查询&#xff0c;即SELECT语句中包含SELECT语句&#xff0c;如果一条语句中存在两个&#xff0c;或两个以上SELECT&#xff0c;那么就是子查询语句了。 子查询出现的位置&#xff1a; Where子句中&#xff0c;作为条件存在&#xff1b;from后&#xff0c;作为表…

Bootstrap学习(十一)

模态框使用&#xff1a; tab标签页组件 模态框使用&#xff1a; 有属性、方法、事件 fade显示时的渐变动画可加可不加&#xff0c;role是屏幕辅助设备用的 aria-lable屏幕辅助设备用的 静态的模态框是不展示的&#xff0c;需要调用展示方法才能展示 在中心内容放一个表单&…

Transformer Encoder-Decoer 结构回顾

有关于Transformer、BERT及其各种变体的详细介绍请参照笔者另一篇博客&#xff1a;最火的几个全网络预训练模型梳理整合&#xff08;BERT、ALBERT、XLNet详解&#xff09;。 本文基于对T5一文的理解&#xff0c;再重新回顾一下有关于auto-encoder、auto-regressive等常见概念&…

Elasticsearch 安装及启动【Windows】

一、下载 Elasticsearch 官网下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases#elasticsearch 选择自己所需版本进行下载&#xff0c;这里以Elasticsearch 8.2.2 为例 点击 Download&#xff0c;选择 Windows 版本 二、使用步骤 1.安装 Elasticse…

大数据培训课程WordCount案例实操

WordCount案例实操 1&#xff0e;需求 在给定的文本文件中统计输出每一个单词出现的总次数 &#xff08;1&#xff09;输入数据 &#xff08;2&#xff09;期望输出数据 atguigu 2 banzhang 1 cls 2 hadoop 1 jiao 1 ss 2 xue 1 2&#xff0e;需求分析 …

如何看待越来越多人报名参加软考?

可以肯定的告诉你软考证书是有用的。 但是软考证书如果对于自己今后的职业生涯规划也有帮助&#xff0c;和你的职业发展和需求相匹配&#xff0c;那才能发挥软考证书最大的优势。 软考证书的用处体现在哪里&#xff1f; 1、证书认可度高 软考是一种简称&#xff0c;全称是计…

变分推断(Variational Inference)解析

一、什么是变分推断 假设在一个贝叶斯模型中&#xff0c;xxx为一组观测变量&#xff0c;zzz为一组隐变量&#xff08;参数也看做随机变量&#xff0c;包含在zzz中&#xff09;&#xff0c;则推断问题为计算后验概率密度P(z∣x)P(z|x)P(z∣x)。根据贝叶斯公式&#xff0c;有&am…

如何使用向导创建Openflow 流表-网络测试仪实操

使用向导创建Openflow中的FlowTable&#xff0c;按照下面的步骤&#xff1a; 1、打开Renix软件&#xff0c;连接机框并预约测试端口&#xff1b; 2、配置一个IPv4接口 3、配置一个OpenFlowController绑定步骤二中的IPv4接口 4、创建一条RAW流&#xff08;这条流中包含FlowTabl…

虹科QA | SWCF2022 11月29日演讲笔记:卫星传输链路中的关键技术分享

虹科2022年度SWCF卫星通信与仿真测试研讨会正在进行中。昨日精彩演讲&#xff1a;卫星传输链路中的关键技术分享&#xff0c;感谢大家的观看与支持&#xff01; 昨晚的直播间收到一些粉丝的技术问题&#xff0c;我们汇总了热点问题并请讲师详细解答&#xff0c;在此整理分享给…

2022年十一届认证杯(小美赛)C题思路新鲜出炉

对人类活动进行分类 人类行为理解的一个重要方面是对日常活动的识别和监控。一个可穿戴的活动识别 系统可以提高许多关键领域的生活质量&#xff0c;如动态监测、家庭康复和跌倒检测。基于 惯性传感器的活动识别系统用于监测和观察老年人远程个人报警系统[1]&#xff0c;检测和…

结合RocketMQ 源码,带你了解并发编程的三大神器

摘要&#xff1a;本文结合 RocketMQ 源码&#xff0c;分享并发编程三大神器的相关知识点。本文分享自华为云社区《读 RocketMQ 源码&#xff0c;学习并发编程三大神器》&#xff0c;作者&#xff1a;勇哥java实战分享。 这篇文章&#xff0c;笔者结合 RocketMQ 源码&#xff0…