【多媒体封装格式详解】---MP4【4】

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 前面介绍过的几种格式flv、mkv、asf等。他们音视频的数据包一般都是按照文件的顺序交叉安放。你解析完头部信息后。剩下的一般就按照文件顺序一个数据包一个数据包的解析就行了。但是MP4完全不是这种概念。

前面介绍过的几种格式flv、mkv、asf等。他们音视频的数据包一般都是按照文件的顺序交叉安放。你解析完头部信息后。剩下的一般就按照文件顺序一个数据包一个数据包的解析就行了。但是MP4完全不是这种概念。他的媒体信息和数据是分开存放的。就是你想获得数据之前必须要解析出每个帧数据所有的位置。mp4存放这个帧信息的是放在stbl这个box里。而真实的数据放在mdat中。接下来就讲讲stbl与mdat的对应关系。

Sample Table Box(stbl)

来一张典型的stbl结构图:
常见子box

stts:    Decoding Time to Sample Box 时间戳和Sample映射表
stsd:   Sample Description Box
stsz, stz2: Sample Size Boxes 每个Sample大小的表。
stsc: Sample to chunk 的映射表。
‘stco’, ‘co64’: Chunk位置偏移表
stss:关键帧index。

1.解析stsd可获得coding类型视频宽高、音频samplesize、channelcount这些和解码器有关信息。

 

[cpp]  view plain copy
  1. aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type)  
  2. extends FullBox('stsd', 0, 0){  
  3. int i ;  
  4. unsigned int(32) entry_count;  
  5. for (i = 1 ; i u entry_count ; i++){  
  6. switch (handler_type){  
  7. case ‘soun’: // for audio tracks  
  8. AudioSampleEntry();  
  9. break;  
  10. case ‘vide’: // for video tracks  
  11. VisualSampleEntry();  
  12. break;  
  13. case ‘hint’: // Hint track  
  14. HintSampleEntry();  
  15. break;  
  16. }  
  17. }  
  18. }  

 

[cpp]  view plain copy
  1. aligned(8) abstract class SampleEntry (unsigned int(32) format)  
  2. extends Box(format){  
  3. const unsigned int(8)[6] reserved = 0;  
  4. unsigned int(16) data_reference_index;  
  5. }  
  6. class HintSampleEntry() extends SampleEntry (protocol) {  
  7. unsigned int(8) data [];  
  8. }  
  9. // Visual Sequences  
  10. class VisualSampleEntry(codingname) extends SampleEntry (codingname){  
  11. unsigned int(16) pre_defined = 0;  
  12. const unsigned int(16) reserved = 0;  
  13. unsigned int(32)[3] pre_defined = 0;  
  14. unsigned int(16) width;  
  15. unsigned int(16) height;  
  16. template unsigned int(32) horizresolution = 0x00480000; // 72 dpi  
  17. template unsigned int(32) vertresolution = 0x00480000; // 72 dpi  
  18. const unsigned int(32) reserved = 0;  
  19. template unsigned int(16) frame_count = 1;  
  20. string[32] compressorname;  
  21. template unsigned int(16) depth = 0x0018;  
  22. int(16) pre_defined = -1;  
  23. }  
  24. // Audio Sequences  
  25. class AudioSampleEntry(codingname) extends SampleEntry (codingname){  
  26. const unsigned int(32)[2] reserved = 0;  
  27. template unsigned int(16) channelcount = 2;  
  28. template unsigned int(16) samplesize = 16;  
  29. unsigned int(16) pre_defined = 0;  
  30. const unsigned int(16) reserved = 0 ;  
  31. template unsigned int(32) samplerate = {timescale of media}<<16;  
  32. }  

 

 

2.解析stsz box 可以获得一个sample size的表

 

[cpp]  view plain copy
  1. aligned(8) class SampleSizeBox extends FullBox(‘stsz’, version = 0, 0) {  
  2. unsigned int(32) sample_size;  
  3. unsigned int(32) sample_count;  
  4. if (sample_size==0) {  
  5. for (i=1; i u sample_count; i++) {  
  6. unsigned int(32) entry_size;  
  7. }  
  8. }  
  9. }  

3.解析stts 

 

 

[cpp]  view plain copy
  1. aligned(8) class TimeToSampleBox  
  2. extends FullBox(’stts’, version = 0, 0) {  
  3. unsigned int(32) entry_count;  
  4. int i;  
  5. for (i=0; i < entry_count; i++) {  
  6. unsigned int(32) sample_count;  
  7. unsigned int(32) sample_delta;  
  8. }  
  9. }  

 

4.解析stsc 还原Sample 与chunk的映射表 

 


Sample 是存储的最基本单元,mp4把Sample 存在chunk中。chunk的长度、chunk的大小、chunk中Sample的数量及大小都是不定的。

通过解析这部分box来还原这个映射表。

 

[cpp]  view plain copy
  1. aligned(8) class SampleToChunkBox  
  2. extends FullBox(‘stsc’, version = 0, 0) {  
  3. unsigned int(32) entry_count;  
  4. for (i=1; i u entry_count; i++) {  
  5. unsigned int(32) first_chunk;  
  6. unsigned int(32) samples_per_chunk;  
  7. unsigned int(32) sample_description_index;  
  8. }  
  9. }  


每个entry 表示着一组数据,entry_count 表示这数量。这一组其实是相同类型的chunk。

 

first_chunk 表示 这一组相同类型的chunk中 的第一个chunk数。

这些chunk 中包含的Sample 数量,即samples_per_chunk 是一致的。

每个Sample 可以通过sample_description_index 去stsd box 找到描述信息。

看ffmpeg中mov_read_stsc() 它把这些数据放在一个结构体数组中备用。

 

[cpp]  view plain copy
  1. static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)  
  2. {  
  3.     AVStream *st;  
  4.     MOVStreamContext *sc;  
  5.     unsigned int i, entries;  
  6.   
  7.     if (c->fc->nb_streams < 1)  
  8.         return 0;  
  9.     st = c->fc->streams[c->fc->nb_streams-1];  
  10.     sc = st->priv_data;  
  11.   
  12.     avio_r8(pb); /* version */  
  13.     avio_rb24(pb); /* flags */  
  14.   
  15.     entries = avio_rb32(pb);  
  16.   
  17.     av_dlog(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries);  
  18.   
  19.     if (!entries)  
  20.         return 0;  
  21.     if (entries >= UINT_MAX / sizeof(*sc->stsc_data))  
  22.         return AVERROR_INVALIDDATA;  
  23.     sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data));  
  24.     if (!sc->stsc_data)  
  25.         return AVERROR(ENOMEM);  
  26.   
  27.     for (i = 0; i < entries && !pb->eof_reached; i++) {  
  28.         sc->stsc_data[i].first = avio_rb32(pb);  
  29.         sc->stsc_data[i].count = avio_rb32(pb);  
  30.         sc->stsc_data[i].id = avio_rb32(pb);  
  31.     }  
  32.   
  33.     sc->stsc_count = i;  
  34.   
  35.     if (pb->eof_reached)  
  36.         return AVERROR_EOF;  
  37.   
  38.     return 0;  
  39. }  

在获得完整的映射表,我们还需要chunk总个数信息。这些信息放在‘stco’, ‘co64’中。

 

5.解析‘stco’, ‘co64’

“stco”定义了每个thunk在媒体流中的位置。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。

32位

 

[cpp]  view plain copy
  1. aligned(8) class ChunkOffsetBox  
  2. extends FullBox(‘stco’, version = 0, 0) {  
  3. unsigned int(32) entry_count;  
  4. for (i=1; i u entry_count; i++) {  
  5. unsigned int(32) chunk_offset;  
  6. }  
  7. }  


64位

 

 

[cpp]  view plain copy
  1. aligned(8) class ChunkLargeOffsetBox  
  2. extends FullBox(‘co64’, version = 0, 0) {  
  3. unsigned int(32) entry_count;  
  4. for (i=1; i u entry_count; i++) {  
  5. unsigned int(64) chunk_offset;  
  6. }  
  7. }  

从这个box我们就可以获得 chunk 的总数量,entry_count

 

 from:http://blog.csdn.net/tx3344/article/details/8506131

1.解析‘stco’, ‘co64’我们有了chunk 表,知道了chunk 的总数及每个chunk所在文件的位置。

2.解析stsc 配合着上面的chunk表,我们就能弄个Sample与chunk的关系表。我们也就能获得每个Sample的位置信息。

3.配合上面的stts 时间表和解码器信息等。搞出ES流已经不成问题了。

4.想获得关键帧的index,需要解析stss’

 

[cpp]  view plain copy
    1. aligned(8) class SyncSampleBox  
    2. extends FullBox(‘stss’, version = 0, 0) {  
    3. unsigned int(32) entry_count;  
    4. int i;  
    5. for (i=0; i < entry_count; i++) {  
    6. unsigned int(32) sample_number;  
    7. }  
    8. }  
相关实践学习
【AI破次元壁合照】少年白马醉春风,函数计算一键部署AI绘画平台
本次实验基于阿里云函数计算产品能力开发AI绘画平台,可让您实现“破次元壁”与角色合照,为角色换背景效果,用AI绘图技术绘出属于自己的少年江湖。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
目录
相关文章
halcon的灰度变换(图像增强)
halcon的灰度变换(图像增强)
916 1
|
2月前
|
JSON 安全 API
通用图片搜索-搜狗源免费API接口使用指南
本文介绍接口盒子(apihz.cn)提供的搜狗图片搜索免费API,支持通过关键词获取图片搜索结果,适用于图片采集、内容分析等场景。提供请求地址、参数说明、返回示例及调用注意事项。
|
5月前
|
人工智能 IDE API
10行代码,实现你的专属阿里云OpenAPI MCP Server
本文介绍如何用10行Python代码创建专属阿里云OpenAPI MCP Server。针对传统MCP Server工具固化、开发复杂等问题,提出借助alibaba-cloud-ops-mcp-server实现灵活拓展的方案。通过配置服务与API名称,运行简短代码即可生成支持SSE连接的MCP Server。用户无需深入了解阿里云OpenAPI细节,大幅降低开发门槛。未来将探索通用工具设计,实现固定工具调用任意API,进一步提升灵活性与效率。
|
7月前
|
机器学习/深度学习 自动驾驶 计算机视觉
RT-DETR改进策略【模型轻量化】| 替换骨干网络为 GhostNet V1 基于 Ghost Module 和 Ghost Bottlenecks的轻量化网络结构
RT-DETR改进策略【模型轻量化】| 替换骨干网络为 GhostNet V1 基于 Ghost Module 和 Ghost Bottlenecks的轻量化网络结构
311 61
RT-DETR改进策略【模型轻量化】| 替换骨干网络为 GhostNet V1 基于 Ghost Module 和 Ghost Bottlenecks的轻量化网络结构
|
11月前
|
机器学习/深度学习 自然语言处理
掩码语言模型(MLM)
【10月更文挑战第6天】掩码语言模型(MLM)
|
算法 数据挖掘 开发者
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
|
XML Android开发 数据格式
37. 【Android教程】基于监听的事件处理机制
37. 【Android教程】基于监听的事件处理机制
204 2
|
存储 算法 C++
C++ unordered_map和unordered_set的使用
C++ unordered_map和unordered_set的使用
215 0
|
移动开发 JavaScript 前端开发
【实战篇】微信公众号JS-SDK获取当前经纬度及地址信息
【实战篇】微信公众号JS-SDK获取当前经纬度及地址信息
782 0
【实战篇】微信公众号JS-SDK获取当前经纬度及地址信息
|
数据挖掘 Linux 程序员
手把手教你---猿如意之八大高效利器使用(一)
手把手教你---猿如意之八大高效利器使用
754 0
手把手教你---猿如意之八大高效利器使用(一)