android 多媒体文件之mp4分析---base on jellybean(六)
来源:互联网 发布:剑灵画面设置优化 编辑:程序博客网 时间:2024/06/06 19:06
我们讲多媒体,涉及到的最多的就是MP4文件和MP3文件了,但是我们对这两个文件的格式了解多少呢,它的由有哪些部分部分组成呢?它的核心部件是哪些?它哪些部分是供解码器去解析的呢?带着这些疑问,我们首先来探索下MP4文件。
我们首先用MP4Info这个工具来看下MP4的大貌:
从上图我们可以看到MP4文件中的所有数据都装在box中,也就是说MP4文件由若干个box组成,每个box有类型和长度,可以将box理解为一个数据对象块。box中可以包含另一个box,这种box称为container box。一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;一个moov可以由多个tracks组成。每个track就是一个随时间变化的媒体序列,例如,视频帧序列。track里的每个时间单位是一个sample,它可以是一帧视频,或者音频。sample按照时间顺序排列。注意,一帧音频可以分解成多个音频sample,所以音频一般用sample作为单位,而不用帧。MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。“free”类型的box,就是一些自由的信息,可以写,也可以不写。
box中的字节序为网络字节序,也就是大端字节序(Big-Endian),简单的说,就是一个32位的4字节整数存储方式为高位字节在内存的低端。Box由header和body组成,其中header统一指明box的大小和类型,body根据类型有不同的意义和格式。
BOX
标准的box开头的4个字节(32位)为box size,该大小包括box header和box body整个box的大小,这样我们就可以在文件中定位各个box。如果size为1,则表示这个box的大小为large size,真正的size值要在largesize域上得到。(实际上只有“mdat”类型的box才有可能用到large size。)如果size为0,表示该box为文件的最后一个box,文件结尾即为该box结尾。(同样只存在于“mdat”类型的box中。)
size后面紧跟的32位为box type,一般是4个字符,如“ftyp”、“moov”等,这些box type都是已经预定义好的,分别表示固定的意义。如果是“uuid”,表示该box为用户扩展类型。如果box type是未定义的,应该将其忽略。
对应的代码片段为:framework/av/media/libstagefright/MPEG4Extrator.cpp
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
ALOGV("entering parseChunk %lld/%d", *offset, depth);
uint32_t hdr[2];
static const char* mQTMajorBrand = "qt ";
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);---box size
uint32_t chunk_type = ntohl(hdr[1]);---box type
off64_t data_offset = *offset + 8;
if (chunk_size == 1) {
if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {---读取box size的大小
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size); ---将64位的网络字节转换为主机字节
data_offset += 8;
……….
char chunk[5];
MakeFourCCString(chunk_type, chunk); ----FOURCC全称Four-Character Codes,是在编程
中非常常用的东西,一般用作标示符。它是一个32位的标示符,其实就是typedef unsigned long FOURCC
}
…………
}
static void MakeFourCCString(uint32_t x, char *s) {
s[0] = x >> 24;
s[1] = (x >> 16) & 0xff;
s[2] = (x >> 8) & 0xff;
s[3] = x & 0xff;
s[4] = '\0';
}
File Type Box(ftyp)
File Type Box(ftyp):该box有且只有1个,并且只能被包含在文件层,而不能被其他box包含。该box应该被放在文件的最开始,指示该MP4文件应用的相关信息。 “ftyp” body依次包括1个32位的major brand(4个字符),1个32位的minor version(整数)和1个以32位(4个字符)为单位元素的数组compatible brands。这些都是用来指示文件应用级别的信息。该box的字节实例如下:
对应的的代码如下:
framework/av/media/libstagefright/MPEG4Extrator.cpp
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
switch(chunk_type) {
case FOURCC('f', 't', 'y', 'p'):
{
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
uint32_t ftype;
if (mDataSource->readAt(data_offset, &ftype, 4) < 4) {
return ERROR_IO;
}
MakeFourCCString(ntohl(ftype), mMajorBrand); -----major brand
*offset += chunk_size;
break;
}
}
Movie Box(moov)
该box包含了文件媒体的metadata信息,“moov”是一个container box,具体内容信息由子box诠释。同File Type Box一样,该box有且只有一个,且只被包含在文件层。一般情况下,
“moov”会紧随“ftyp”出现。一般情况下, “moov”中会包含1个“mvhd”和若干个“trak”。其中“mvhd”为header box,一般作为“moov”的第一个子box出现(对于其他container box来说,header box都应作为首个子box出现)。“trak”包含了一个track的相关信息,是一个container box。结构如下图:
Movie Header Box(mvhd)
字段
字节数
意义
box size
4
box大小
box type
4
box类型
version
1
box版本,0或1,一般为0。(以下字节数均按version=0)
flags
3
creation time
4
创建时间(相对于UTC时间1904-01-01零点的秒数)
modification time
4
修改时间
time scale
4
文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
duration
4
该track的时间长度,用duration和time scale值可以计算track时长,比如audio track的time scale = 8000, duration = 560128,时长为70.016,video track的time scale = 600, duration = 42000,时长为70
rate
4
推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16]格式,该值为1.0(0x00010000)表示正常前向播放
volume
2
与rate类似,[8.8]格式,1.0(0x0100)表示最大音量
reserved
10
保留位
matrix
36
视频变换矩阵
pre-defined
24
next track id
4
下一个track使用的id号
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
ALOGV("entering parseChunk %lld/%d", *offset, depth);
uint32_t hdr[2];
static const char* mQTMajorBrand = "qt ";
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);---box size
uint32_t chunk_type = ntohl(hdr[1]);---box type
………………….
case FOURCC('m', 'v', 'h', 'd'):
{
if (chunk_data_size < 12) { //increase to 16?---
return ERROR_MALFORMED;
}
uint8_t header[16];
if (mDataSource->readAt(
data_offset, header, sizeof(header))
< (ssize_t)sizeof(header)) {
return ERROR_IO;
}
int64_t creationTime;
if (header[0] == 1) {
creationTime = U64_AT(&header[4]);
mFileMetaData->setInt64(kKeyEditOffset, 0 );
} else if (header[0] != 0) {
return ERROR_MALFORMED;
} else {
creationTime = U32_AT(&header[4]);-------创建时间,4个字节
int32_t mvTimeScale = U32_AT(&header[12]);---时间刻度,4个字节
mFileMetaData->setInt32(kKeyEditOffset, mvTimeScale );
}
String8 s;
convertTimeToDate(creationTime, &s);
mFileMetaData->setCString(kKeyDate, s.string());
*offset += chunk_size;
break;
}
Track Box(trak)
“trak”也是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外)。一个MP4文件中的媒体可以包含多个track,且至少有一个track,这些track之间彼此独立,有自己的时间和空间信息。“trak”必须包含一个“tkhd”和一个“mdia”,此外还有很多可选的box其中“tkhd”为track header box,“mdia”为media box,该box是一个包含一些track媒体数据信息box的container box。
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
ALOGV("entering parseChunk %lld/%d", *offset, depth);
uint32_t hdr[2];
static const char* mQTMajorBrand = "qt ";
if (mDataSource->readAt(*offset, hdr, 8) < 8) {
return ERROR_IO;
}
uint64_t chunk_size = ntohl(hdr[0]);---box size
uint32_t chunk_type = ntohl(hdr[1]);---box type
……………………..
if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
isTrack = true;
Track *track = new Track; --- 如果是Track,new 个track
track->next = NULL;
if (mLastTrack) {
mLastTrack->next = track;
} else {
mFirstTrack = track;
}
mLastTrack = track;
track->meta = new MetaData;
track->includes_expensive_metadata = false;
track->skipTrack = false;
track->timescale = 0;
track->meta->setCString(kKeyMIMEType, "application/octet-stream");
}
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset;
while (*offset < stop_offset) {
if (stop_offset - *offset >= 8) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
if(chunk_type == FOURCC('u', 'd', 't', 'a')){
ALOGW("error in udta atom, ignoring %llu bytes",stop_offset - *offset);
*offset = stop_offset;
} else {
return err;
}
}
}
………….
}
- android 多媒体文件之mp4分析---base on jellybean(六)
- android 多媒体文件之mp4分析---base on jellybean(六)
- android 多媒体文件之mp4分析---base on jellybean(六)
- android 多媒体文件之mp4分析---base on jellybean(六)
- android 多媒体文件之mp4分析---base on jellybean(五)
- android 多媒体文件之mp4分析(续)---base on jellybean(六)
- android 多媒体文件之mp4分析(续)---base on jellybean(七)
- android 多媒体文件之mp4分析(续)---base on jellybean(七)
- android 多媒体文件之mp4分析(续)---base on jellybean(七)
- android 多媒体文件之mp4分析(续)---base on jellybean(七)
- android多媒体框架之流媒体----base on jellybean(八)
- android多媒体框架之流媒体框架----base on jellybean(九)
- android多媒体框架之流媒体----base on jellybean(七)
- android多媒体框架之流媒体框架----base on jellybean(八)
- android多媒体框架之流媒体----base on jellybean(八)
- android多媒体框架之流媒体框架----base on jellybean(九)
- android多媒体框架之流媒体----base on jellybean(八)
- android多媒体框架之流媒体框架----base on jellybean(九)
- Dr.Sum EA是简单、高速、自助式的、易于推广的BI工具
- 扪心第五想 Accept the act 面带微笑,昂首前行
- VC发布release版本出现问题的解决方案!
- 平面图网络流(poj 2822 & hdu 4280 )
- python操作数据库
- android 多媒体文件之mp4分析---base on jellybean(六)
- python学习笔记5
- 超过30个超棒的漂亮iphone 4s墙纸
- Win7下取得c盘受保护文件夹权限
- 标准库string
- HDU 2057
- Android高手进阶必备 (一)
- adaboost学习(一)
- 阿里巴巴的阿里云操作系统希望做“中国的Android”