parseChunk学习
2016-06-26 22:57
197 查看
任意一个MP4文件,它都是由一个个相邻的box组成的,播放MP4文件前需要解析这些box获得视频的数据,主要结构如下:
fytp-----------moov(视频数据结构)---------------------------------mdat(视频数据)
| |
|-----mvhd |----chunk1
|-----trak | |
|-----trak | |--sample1
| | |--sample2
|----tkhd | |--samplen
|----mdia |
| |
|--mdhd |
|--minf |----chunkn
| |
|---stbl |--samplem
|
|---stts
|---stsd
|---stsc
|---stco
|---....
1:解析FOURCC('s', 't', 'b', 'l')和FOURCC('t', 'r', 'a', 'k')这种容器型的box之后会跟着解析器内部的子box,两者的代码是放在一起的
2:parseChunk主要是解析moov以及其内部的子box,上面这段代码放在判断moov之前,就是为了在确保遇到moov函数返回前,会进行moov内部子box的深入解析。
case FOURCC('m', 'o', 'o', 'v'):
case FOURCC('t', 'r', 'a', 'k'):
case FOURCC('m', 'd', 'i', 'a'):
case FOURCC('m', 'i', 'n', 'f'):
case FOURCC('d', 'i', 'n', 'f'):
case FOURCC('s', 't', 'b', 'l'):
case FOURCC('m', 'v', 'e', 'x'):
case FOURCC('m', 'o', 'o', 'f'):
case FOURCC('t', 'r', 'a', 'f'):
case FOURCC('m', 'f', 'r', 'a'):
case FOURCC('u', 'd', 't', 'a'):
case FOURCC('i', 'l', 's', 't'):
{
if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
bool isTrack = false;
if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
isTrack = true;
Track *track = new Track;
track->next = NULL;
if (mLastTrack) {
mLastTrack->next = track;
} else {
mFirstTrack = track;
}
}
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset;
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
4000
}
if (*offset != stop_offset) {
return ERROR_MALFORMED;
}
if (isTrack) {
if (mLastTrack->skipTrack) {
Track *cur = mFirstTrack;
if (cur == mLastTrack) {
delete cur;
mFirstTrack = mLastTrack = NULL;
}
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mInitCheck = OK;
if (!mIsDrm) {
return UNKNOWN_ERROR; // Return a dummy error.
} else {
return OK;
}
}
break;
}
3:解析trak的第一个子box,tkhd头box
case FOURCC('t', 'k', 'h', 'd'):
{
status_t err;
if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
return err;
}
*offset += chunk_size;
break;
}
4:解析mdia box中的第一个子box,头box:mdhd,并分别从里面获取版本,timescale,track的时常等信息
case FOURCC('m', 'd', 'h', 'd'):
{
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
uint8_t version;
if (mDataSource->readAt(
data_offset, &version, sizeof(version))
< (ssize_t)sizeof(version)) {
return ERROR_IO;
}
mLastTrack->timescale = ntohl(timescale);
int64_t duration;
mLastTrack->meta->setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
mLastTrack->meta->setCString(
kKeyMediaLanguage, lang_code);
*offset += chunk_size;
break;
}
5:音频相关的box的解析,获取音频相关的信息
case FOURCC('m', 'p', '4', 'a'):
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
{
uint8_t buffer[8 + 20];
if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t num_channels = U16_AT(&buffer[16]);
uint16_t sample_size = U16_AT(&buffer[18]);
uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB,
FourCC2MIME(chunk_type))) {
// AMR NB audio is always mono, 8kHz
num_channels = 1;
sample_rate = 8000;
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB,
FourCC2MIME(chunk_type))) {
// AMR WB audio is always mono, 16kHz
num_channels = 1;
sample_rate = 16000;
}
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
if (*offset != stop_offset) {
return ERROR_MALFORMED;
}
break;
}
6:视频box解析,获取视频相关的信息
case FOURCC('m', 'p', '4', 'v'):
case FOURCC('s', '2', '6', '3'):
case FOURCC('H', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
case FOURCC('a', 'v', 'c', '1'):
{
mHasVideo = true;
uint8_t buffer[78];
if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t width = U16_AT(&buffer[6 + 18]);
uint16_t height = U16_AT(&buffer[6 + 20]);
// The video sample is not stand-compliant if it has invalid dimension.
// Use some default width and height value, and
// let the decoder figure out the actual width and height (and thus
// be prepared for INFO_FOMRAT_CHANGED event).
if (width == 0) width = 352;
if (height == 0) height = 288;
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyWidth, width);
mLastTrack->meta->setInt32(kKeyHeight, height);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
break;
}
7:所有stbl box以及其子box相关的解析,解析stbl的时候new了一个SampleTable,随后调用其各个方法完成设置,大部分函数的参数为data_offset,和chunk_data_size。
if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
case FOURCC('s', 't', 's', 'd'):
{
break;
}
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'c'):
{
status_t err =
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
size_t max_size;
err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
// Assume that a given buffer only contains at most 10 fragments,
// each fragment originally prefixed with a 2 byte length will
// have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
// and thus will grow by 2 bytes per fragment.
mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
*offset += chunk_size;
// Calculate average frame rate.
const char *mime;
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
if (!strncasecmp("video/", mime, 6)) {
size_t nSamples = mLastTrack->sampleTable->countSamples();
int64_t durationUs;
if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > 0) {
int32_t frameRate = (nSamples * 1000000LL +
(durationUs >> 1)) / durationUs;
mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
}
}
}
break;
}
case FOURCC('s', 't', 't', 's'):
{
status_t err =
mLastTrack->sampleTable->setTimeToSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('c', 't', 't', 's'):
{
status_t err =
mLastTrack->sampleTable->setCompositionTimeToSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 's'):
{
status_t err =
mLastTrack->sampleTable->setSyncSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
fytp-----------moov(视频数据结构)---------------------------------mdat(视频数据)
| |
|-----mvhd |----chunk1
|-----trak | |
|-----trak | |--sample1
| | |--sample2
|----tkhd | |--samplen
|----mdia |
| |
|--mdhd |
|--minf |----chunkn
| |
|---stbl |--samplem
|
|---stts
|---stsd
|---stsc
|---stco
|---....
1:解析FOURCC('s', 't', 'b', 'l')和FOURCC('t', 'r', 'a', 'k')这种容器型的box之后会跟着解析器内部的子box,两者的代码是放在一起的
2:parseChunk主要是解析moov以及其内部的子box,上面这段代码放在判断moov之前,就是为了在确保遇到moov函数返回前,会进行moov内部子box的深入解析。
case FOURCC('m', 'o', 'o', 'v'):
case FOURCC('t', 'r', 'a', 'k'):
case FOURCC('m', 'd', 'i', 'a'):
case FOURCC('m', 'i', 'n', 'f'):
case FOURCC('d', 'i', 'n', 'f'):
case FOURCC('s', 't', 'b', 'l'):
case FOURCC('m', 'v', 'e', 'x'):
case FOURCC('m', 'o', 'o', 'f'):
case FOURCC('t', 'r', 'a', 'f'):
case FOURCC('m', 'f', 'r', 'a'):
case FOURCC('u', 'd', 't', 'a'):
case FOURCC('i', 'l', 's', 't'):
{
if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
bool isTrack = false;
if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
isTrack = true;
Track *track = new Track;
track->next = NULL;
if (mLastTrack) {
mLastTrack->next = track;
} else {
mFirstTrack = track;
}
}
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset;
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
4000
}
if (*offset != stop_offset) {
return ERROR_MALFORMED;
}
if (isTrack) {
if (mLastTrack->skipTrack) {
Track *cur = mFirstTrack;
if (cur == mLastTrack) {
delete cur;
mFirstTrack = mLastTrack = NULL;
}
} else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
mInitCheck = OK;
if (!mIsDrm) {
return UNKNOWN_ERROR; // Return a dummy error.
} else {
return OK;
}
}
break;
}
3:解析trak的第一个子box,tkhd头box
case FOURCC('t', 'k', 'h', 'd'):
{
status_t err;
if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
return err;
}
*offset += chunk_size;
break;
}
4:解析mdia box中的第一个子box,头box:mdhd,并分别从里面获取版本,timescale,track的时常等信息
case FOURCC('m', 'd', 'h', 'd'):
{
if (chunk_data_size < 4) {
return ERROR_MALFORMED;
}
uint8_t version;
if (mDataSource->readAt(
data_offset, &version, sizeof(version))
< (ssize_t)sizeof(version)) {
return ERROR_IO;
}
mLastTrack->timescale = ntohl(timescale);
int64_t duration;
mLastTrack->meta->setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
mLastTrack->meta->setCString(
kKeyMediaLanguage, lang_code);
*offset += chunk_size;
break;
}
5:音频相关的box的解析,获取音频相关的信息
case FOURCC('m', 'p', '4', 'a'):
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
{
uint8_t buffer[8 + 20];
if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t num_channels = U16_AT(&buffer[16]);
uint16_t sample_size = U16_AT(&buffer[18]);
uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB,
FourCC2MIME(chunk_type))) {
// AMR NB audio is always mono, 8kHz
num_channels = 1;
sample_rate = 8000;
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB,
FourCC2MIME(chunk_type))) {
// AMR WB audio is always mono, 16kHz
num_channels = 1;
sample_rate = 16000;
}
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
if (*offset != stop_offset) {
return ERROR_MALFORMED;
}
break;
}
6:视频box解析,获取视频相关的信息
case FOURCC('m', 'p', '4', 'v'):
case FOURCC('s', '2', '6', '3'):
case FOURCC('H', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
case FOURCC('a', 'v', 'c', '1'):
{
mHasVideo = true;
uint8_t buffer[78];
if (mDataSource->readAt(
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
return ERROR_IO;
}
uint16_t data_ref_index = U16_AT(&buffer[6]);
uint16_t width = U16_AT(&buffer[6 + 18]);
uint16_t height = U16_AT(&buffer[6 + 20]);
// The video sample is not stand-compliant if it has invalid dimension.
// Use some default width and height value, and
// let the decoder figure out the actual width and height (and thus
// be prepared for INFO_FOMRAT_CHANGED event).
if (width == 0) width = 352;
if (height == 0) height = 288;
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
mLastTrack->meta->setInt32(kKeyWidth, width);
mLastTrack->meta->setInt32(kKeyHeight, height);
off64_t stop_offset = *offset + chunk_size;
*offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
return err;
}
}
break;
}
7:所有stbl box以及其子box相关的解析,解析stbl的时候new了一个SampleTable,随后调用其各个方法完成设置,大部分函数的参数为data_offset,和chunk_data_size。
if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
case FOURCC('s', 't', 's', 'd'):
{
break;
}
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'c'):
{
status_t err =
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
size_t max_size;
err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
// Assume that a given buffer only contains at most 10 fragments,
// each fragment originally prefixed with a 2 byte length will
// have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
// and thus will grow by 2 bytes per fragment.
mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
*offset += chunk_size;
// Calculate average frame rate.
const char *mime;
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
if (!strncasecmp("video/", mime, 6)) {
size_t nSamples = mLastTrack->sampleTable->countSamples();
int64_t durationUs;
if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) {
if (durationUs > 0) {
int32_t frameRate = (nSamples * 1000000LL +
(durationUs >> 1)) / durationUs;
mLastTrack->meta->setInt32(kKeyFrameRate, frameRate);
}
}
}
break;
}
case FOURCC('s', 't', 't', 's'):
{
status_t err =
mLastTrack->sampleTable->setTimeToSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('c', 't', 't', 's'):
{
status_t err =
mLastTrack->sampleTable->setCompositionTimeToSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
case FOURCC('s', 't', 's', 's'):
{
status_t err =
mLastTrack->sampleTable->setSyncSampleParams(
data_offset, chunk_data_size);
*offset += chunk_size;
break;
}
相关文章推荐
- Intel处理器 天梯图
- Intel处理器 天梯图
- 中间件和微服务,Docker以及原生云架构的关系
- Spring MVC 中的 forward 和 redirect
- 单片机--1.开发环境配置以及第一个单片机程序
- android开发脚本之awk
- 个人信息填写css
- Service组件
- Spark RDD详解
- Redhat7/Centos7 设置默认启动内核
- C++ 观察者模式
- Shiro & CAS 实现单点登录
- 《软件工程》课程总结(补)
- 【优酷视频破解】最新可用视频解析接口
- 能否通过内嵌汇编修改C++中const常量的值
- 【DRP】数据库连接—读取XML文件
- HDU 2553 N皇后问题 (回溯DFS)
- 简单实用的js模板引擎
- 输出合并后的键值对
- C++类的继承与派生