前面介绍过的几种格式flv、mkv、asf等。他们音视频的数据包一般都是按照文件的顺序交叉安放。你解析完头部信息后。剩下的一般就按照文件顺序一个数据包一个数据包的解析就行了。但是MP4完全不是这种概念。他的媒体信息和数据是分开存放的。就是你想获得数据之前必须要解析出每个帧数据所有的位置。mp4存放这个帧信息的是放在stbl这个box里。而真实的数据放在mdat中。接下来就讲讲stbl与mdat的对应关系。
Sample Table Box(stbl)
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这些和解码器有关信息。
aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox('stsd', 0, 0){ int i ; unsigned int(32) entry_count; for (i = 1 ; i u entry_count ; i++){ switch (handler_type){ case ‘soun’: // for audio tracks AudioSampleEntry(); break; case ‘vide’: // for video tracks VisualSampleEntry(); break; case ‘hint’: // Hint track HintSampleEntry(); break; } } }
aligned(8) abstract class SampleEntry (unsigned int(32) format) extends Box(format){ const unsigned int(8)[6] reserved = 0; unsigned int(16) data_reference_index; } class HintSampleEntry() extends SampleEntry (protocol) { unsigned int(8) data []; } // Visual Sequences class VisualSampleEntry(codingname) extends SampleEntry (codingname){ unsigned int(16) pre_defined = 0; const unsigned int(16) reserved = 0; unsigned int(32)[3] pre_defined = 0; unsigned int(16) width; unsigned int(16) height; template unsigned int(32) horizresolution = 0x00480000; // 72 dpi template unsigned int(32) vertresolution = 0x00480000; // 72 dpi const unsigned int(32) reserved = 0; template unsigned int(16) frame_count = 1; string[32] compressorname; template unsigned int(16) depth = 0x0018; int(16) pre_defined = -1; } // Audio Sequences class AudioSampleEntry(codingname) extends SampleEntry (codingname){ const unsigned int(32)[2] reserved = 0; template unsigned int(16) channelcount = 2; template unsigned int(16) samplesize = 16; unsigned int(16) pre_defined = 0; const unsigned int(16) reserved = 0 ; template unsigned int(32) samplerate = {timescale of media}<<16; }
aligned(8) class SampleSizeBox extends FullBox(‘stsz’, version = 0, 0) { unsigned int(32) sample_size; unsigned int(32) sample_count; if (sample_size==0) { for (i=1; i u sample_count; i++) { unsigned int(32) entry_size; } } }3.解析stts
aligned(8) class TimeToSampleBox extends FullBox(’stts’, version = 0, 0) { unsigned int(32) entry_count; int i; for (i=0; i < entry_count; i++) { unsigned int(32) sample_count; unsigned int(32) sample_delta; } }
4.解析stsc 还原Sample 与chunk的映射表
Sample 是存储的最基本单元,mp4把Sample 存在chunk中。chunk的长度、chunk的大小、chunk中Sample的数量及大小都是不定的。
通过解析这部分box来还原这个映射表。
aligned(8) class SampleToChunkBox extends FullBox(‘stsc’, version = 0, 0) { unsigned int(32) entry_count; for (i=1; i u entry_count; i++) { unsigned int(32) first_chunk; unsigned int(32) samples_per_chunk; unsigned int(32) sample_description_index; } }
每个entry 表示着一组数据,entry_count 表示这数量。这一组其实是相同类型的chunk。
first_chunk 表示 这一组相同类型的chunk中 的第一个chunk数。
这些chunk 中包含的Sample 数量,即samples_per_chunk 是一致的。
每个Sample 可以通过sample_description_index 去stsd box 找到描述信息。
看ffmpeg中mov_read_stsc() 它把这些数据放在一个结构体数组中备用。
static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; MOVStreamContext *sc; unsigned int i, entries; if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; sc = st->priv_data; avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ entries = avio_rb32(pb); av_dlog(c->fc, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); if (!entries) return 0; if (entries >= UINT_MAX / sizeof(*sc->stsc_data)) return AVERROR_INVALIDDATA; sc->stsc_data = av_malloc(entries * sizeof(*sc->stsc_data)); if (!sc->stsc_data) return AVERROR(ENOMEM); for (i = 0; i < entries && !pb->eof_reached; i++) { sc->stsc_data[i].first = avio_rb32(pb); sc->stsc_data[i].count = avio_rb32(pb); sc->stsc_data[i].id = avio_rb32(pb); } sc->stsc_count = i; if (pb->eof_reached) return AVERROR_EOF; return 0; }在获得完整的映射表,我们还需要chunk总个数信息。这些信息放在‘stco’, ‘co64’中。
5.解析‘stco’, ‘co64’
“stco”定义了每个thunk在媒体流中的位置。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。
32位
aligned(8) class ChunkOffsetBox extends FullBox(‘stco’, version = 0, 0) { unsigned int(32) entry_count; for (i=1; i u entry_count; i++) { unsigned int(32) chunk_offset; } }
64位
aligned(8) class ChunkLargeOffsetBox extends FullBox(‘co64’, version = 0, 0) { unsigned int(32) entry_count; for (i=1; i u entry_count; i++) { unsigned int(64) chunk_offset; } }从这个box我们就可以获得 chunk 的总数量,entry_count。
1.解析‘stco’, ‘co64’我们有了chunk 表,知道了chunk 的总数及每个chunk所在文件的位置。
2.解析stsc 配合着上面的chunk表,我们就能弄个Sample与chunk的关系表。我们也就能获得每个Sample的位置信息。
3.配合上面的stts 时间表和解码器信息等。搞出ES流已经不成问题了。
4.想获得关键帧的index,需要解析stss’
aligned(8) class SyncSampleBox extends FullBox(‘stss’, version = 0, 0) { unsigned int(32) entry_count; int i; for (i=0; i < entry_count; i++) { unsigned int(32) sample_number; } }
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
@autoreleasepool {
NSLog(@"请输入您的帐号");
int i;
scanf("%d",&i);
NSLog(@"您输入的是%d",i);
}
}
在项目中,我们一般使用list显示一个列表,如果这个列表还有子项的时候,有时候我们需要在上面加上子项的数目,这时候我们就可以使用jquerymobile提供的CSS类中的ui-li-count类实现这样的效果。一段例子代码如下:
<!DOCTYPE html> <html> <head> <title>Count Bubble Example</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/latest/jquery.mobile.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/latest/jquery.mobile.min.js"></script> </head> <body> <div data-role="page"> <div data-role="header"> <h1>My Header</h1> </div> <div data-role="content"> <ul data-role="listview" data-inset="true"> <li data-role="list-divider">Cookies Eaten</li> <li>A <span >9</span></li> <li>B <span >4</span></li> <li>C <span >13</span></li> <li>D <span >8</span></li> </ul> </div> <div data-role="footer"> <h4>My Footer</h4> </div> </div> </body> </html>
我们只要添加一个span并且给起添加ui-li-count类就可以实现了。效果如下:
如果哪里写错了,还请指出。