| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- // QGMP4Box.m
- // Tencent is pleased to support the open source community by making vap available.
- //
- // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
- //
- // Licensed under the MIT License (the "License"); you may not use this file except in
- // compliance with the License. You may obtain a copy of the License at
- //
- // http://opensource.org/licenses/MIT
- //
- // Unless required by applicable law or agreed to in writing, software distributed under the License is
- // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- // either express or implied. See the License for the specific language governing permissions and
- // limitations under the License.
- #import "QGMP4Box.h"
- #import "QGMP4Parser.h"
- NSInteger const kQGBoxSizeLengthInBytes = 4;
- NSInteger const kQGBoxTypeLengthInBytes = 4;
- NSInteger const kQGBoxLargeSizeLengthInBytes = 8;
- NSInteger const kQGBoxLargeSizeFlagLengthInBytes = 1;
- #pragma mark - boxes
- #pragma mark -- base box
- @implementation QGMP4Box
- - (instancetype)initWithType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length {
-
- if (self = [super init]) {
- _type = type;
- _startIndexInBytes = startIndexInBytes;
- _length = length;
- }
- return self;
- }
- /**
- 前序遍历递归查找指定类型的子box,不包含自身。
- @param type box类型
- @return 符合条件的第一个子box
- */
- - (id)subBoxOfType:(QGMP4BoxType)type {
-
- if (self.subBoxes) {
- for (QGMP4Box *subBox in self.subBoxes) {
- if (subBox.type == type) {
- return subBox;
- }
- QGMP4Box *box = [subBox subBoxOfType:type];
- if (box) {
- return box;
- }
- }
- }
- return nil;
- }
- /**
- 向上查找指定类型的box,不包含自身
- @param type box类型
- @return h符合条件的第一个box
- */
- - (id)superBoxOfType:(QGMP4BoxType)type {
-
- if (self.superBox) {
- if (self.superBox.type == type) {
- return self.superBox;
- }
- QGMP4Box *box = [self.superBox superBoxOfType:type];
- if (box) {
- return box;
- }
- }
- return nil;
- }
- - (NSString *)description {
-
- return [self descriptionForRecursionLevel:0];
- }
- - (NSString *)descriptionForRecursionLevel:(NSInteger)level {
-
- __block NSString *des = [NSString stringWithFormat:@"Box:%@ offset:%@ size:%@ ",self.typeString,@(self.startIndexInBytes),@(self.length)];
- for (int i = 0; i < level; i++) {
- des = [NSString stringWithFormat:@"|--%@",des];
- }
- des = [NSString stringWithFormat:@"\n%@",des];
- [self.subBoxes enumerateObjectsUsingBlock:^(QGMP4Box *obj, NSUInteger idx, BOOL * _Nonnull stop) {
- des = [des stringByAppendingString:[obj descriptionForRecursionLevel:(level+1)]];
- }];
- return des;
- }
- - (NSString *)typeString {
-
- NSUInteger value = self.type;
- NSString *des = @"";
- while (value > 0) {
- NSUInteger hexValue = value&0xff;
- value = value>>8;
- des = [NSString stringWithFormat:@"%c%@",(int)hexValue,des];
- }
- return des;
- }
- @end
- #pragma mark -- hvcc box
- /**
- * QGMP4HvccBox
- */
- @implementation QGMP4HvccBox
- @end
- /**
- * QGCttsEntry 通过dts计算pts
- */
- @implementation QGCttsEntry
- @end
- /**
- * QGMP4CttsBox 通过dts计算pts
- */
- @implementation QGMP4CttsBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
- if (!_compositionOffsets) {
- _compositionOffsets = [NSMutableArray new];
- }
- NSData *cttsData = datablock(self);
- const char *bytes = cttsData.bytes;
- uint32_t entryCount = READ32BIT(&bytes[12]);
- for (int i = 0; i < entryCount; ++i) {
- uint32_t sampleCount = READ32BIT(&bytes[16+i*8]);
- uint32_t compositionOffset = READ32BIT(&bytes[16+i*8+4]);
- for (int j = 0; j < sampleCount; j++) {
- [_compositionOffsets addObject:@(compositionOffset)];
- }
- }
- }
- @end
- #pragma mark -- mdat box
- @implementation QGMP4MdatBox
- @end
- #pragma mark -- avcc box
- @implementation QGMP4AvccBox
- @end
- @implementation QGMP4MvhdBox
- @end
- /**
- 在video track中,stsd包含了sps&pps等编解码数据
-
- stsd是⼀个必不可少的atom,在它的 header和version字段后会有⼀个entry count字段,
- 根据entry的个数,每个entry会有type信息,根据type不同sample description会提供不
- 同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track
- 会有“AudioSampleEntry”类型信息。视频的关键信息如SPS和PPS,编码类型、宽⾼、
- ⻓度,⾳频的声道、采样等信息都会出现在这个box中。
- */
- @implementation QGMP4StsdBox
- @end
- /**
- There are two variants of the sample size box. The first variant has a fixed size 32-bit field for representing the sample sizes; it permits defining a constant size for all samples in a track. The second variant permits smaller size fields, to save space when the sizes are varying but small. One of these boxes must be present; the first version is preferred for maximum compatibility.
- 记录了每个sample的大小
- */
- @implementation QGMP4StszBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
-
- if (!_sampleSizes) {
- _sampleSizes = [NSMutableArray new];
- }
- NSData *stszData = datablock(self);
- const char *bytes = stszData.bytes;
- uint32_t sampleSize = READ32BIT(&bytes[12]);
- uint32_t sampleCount = READ32BIT(&bytes[16]);
- self.sampleCount = sampleCount;
- for (int i = 0; i < sampleCount; i ++) {
- if (sampleSize > 0) {
- [self.sampleSizes addObject:@(sampleSize)];
- } else {
- uint32_t entryValue = READ32BIT(&bytes[20+i*4]);
- [self.sampleSizes addObject:@(entryValue)];
- }
- }
- }
- @end
- /**
- Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the samples within a chunk can have different sizes. This table can be used to find the chunk that contains a sample, its position, and the associated sample description.
- stsc记录了每个chunk有多少个Sample https://img-blog.csdn.net/20140613154636296
- */
- @implementation QGStscEntry
- @end
- /*
- stsc,记录了每个chunk有多少个sample,通过这个表可以找到指定的sample
- */
- @implementation QGMP4StscBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
-
- if (!_entries) {
- _entries = [NSMutableArray new];
- }
- NSData *stscData = datablock(self);
- const char *bytes = stscData.bytes;
- uint32_t entry_count = READ32BIT(&bytes[12]);
- for (int i = 0; i < entry_count; ++i) {
- QGStscEntry *entry = [QGStscEntry new];
- entry.firstChunk = READ32BIT(&bytes[16+i*12]);
- entry.samplesPerChunk = READ32BIT(&bytes[16+i*12+4]);
- entry.sampleDescriptionIndex = READ32BIT(&bytes[16+i*12+8]);
- [_entries addObject:entry];
- }
- }
- @end
- /**
- stco 记录每个chunk位置信息,与stsc结合可以算出每个sample的位置和大小
-
- entry_count is an integer that gives the number of entries in the following table
- chunk_offset is a 32 or 64 bit integer that gives the offset of the start of a chunk into its containing
- media file.
- */
- @implementation QGMP4StcoBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
-
- if (!_chunkOffsets) {
- _chunkOffsets = [NSMutableArray new];
- }
- NSData *stcoData = datablock(self);
- const char *bytes = stcoData.bytes;
- uint32_t entry_count = READ32BIT(&bytes[12]);
- self.chunkCount = entry_count;
- for (int i = 0; i < entry_count; ++i) {
- [self.chunkOffsets addObject:@(READ32BIT(&bytes[16+i*4]))];
- }
- }
- @end
- @implementation QGMP4StssBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
- if (!_syncSamples) {
- _syncSamples = [NSMutableArray new];
- }
- NSData *stssData = datablock(self);
- const char *bytes = stssData.bytes;
- uint32_t sample_count = READ32BIT(&bytes[12]);
- for (int i = 0; i < sample_count; i++) {
- NSInteger index = READ32BIT(&bytes[16 + 4 * i]) - 1;
- [_syncSamples addObject:[NSNumber numberWithInteger:index]];
- }
- }
- @end
- /**
- Decoding Time to Sample Box
- 用来计算dts
- */
- @implementation QGSttsEntry
- @end
- /*
- stts记录了sample的时间信息,⾥⾯有多个entry,每个entry⾥⾯的的sample的时⻓都是
- 对应这个entry的delta值,通过这个atom可以得到⼀个时间和sample的映射表。
- */
- @implementation QGMP4SttsBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
-
- if (!_entries) {
- _entries = [NSMutableArray new];
- }
- NSData *sttsData = datablock(self);
- const char *bytes = sttsData.bytes;
- uint32_t entry_count = READ32BIT(&bytes[12]);
- for (int i = 0; i < entry_count; ++i) {
- QGSttsEntry *entry = [QGSttsEntry new];
- entry.sampleCount = READ32BIT(&bytes[16+i*8]);;
- entry.sampleDelta = READ32BIT(&bytes[16+i*8+4]);
- [_entries addObject:entry];
- }
- }
- @end
- @implementation QGMP4TrackBox
- @end
- @implementation QGMP4HdlrBox
- - (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock {
-
- NSData *hdlrData = datablock(self);
- const char *bytes = hdlrData.bytes;
- uint32_t trackType = READ32BIT(&bytes[16]);
- self.trackType = trackType;
- }
- @end
- @implementation QGMP4Sample
- @end
- @implementation QGChunkOffsetEntry
- @end
- @implementation QGMP4BoxFactory
- + (QGMP4Box *)createBoxForType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length {
-
- Class boxClass = [self boxClassForType:type] ?: [QGMP4Box class];
- QGMP4Box *box = [[boxClass alloc] initWithType:type startIndex:startIndexInBytes length:length];
- return box;
- }
- + (Class)boxClassForType:(QGMP4BoxType)type {
-
- switch (type) {
- case QGMP4BoxType_ftyp:
- case QGMP4BoxType_free:
- case QGMP4BoxType_moov:
- case QGMP4BoxType_mvhd:
- case QGMP4BoxType_trak:
- case QGMP4BoxType_tkhd:
- case QGMP4BoxType_edts:
- case QGMP4BoxType_elst:
- case QGMP4BoxType_mdia:
- case QGMP4BoxType_minf:
- case QGMP4BoxType_vmhd:
- case QGMP4BoxType_dinf:
- case QGMP4BoxType_dref:
- case QGMP4BoxType_url:
- case QGMP4BoxType_stbl:
- case QGMP4BoxType_avc1:
- case QGMP4BoxType_udta:
- case QGMP4BoxType_meta:
- case QGMP4BoxType_ilst:
- case QGMP4BoxType_data:
- case QGMP4BoxType_iods:
- case QGMP4BoxType_wide:
- case QGMP4BoxType_loci:
- case QGMP4BoxType_smhd:
- return [QGMP4Box class];
- case QGMP4BoxType_stss:
- return [QGMP4StssBox class];
- case QGMP4BoxType_mdat:
- return [QGMP4MdatBox class];
- case QGMP4BoxType_avcC:
- return [QGMP4AvccBox class];
- case QGMP4BoxType_mdhd:
- return [QGMP4MvhdBox class];
- case QGMP4BoxType_stsd:
- return [QGMP4StsdBox class];
- case QGMP4BoxType_stsz:
- return [QGMP4StszBox class];
- case QGMP4BoxType_hdlr:
- return [QGMP4HdlrBox class];
- case QGMP4BoxType_stsc:
- return [QGMP4StscBox class];
- case QGMP4BoxType_stts:
- return [QGMP4SttsBox class];
- case QGMP4BoxType_stco:
- return [QGMP4StcoBox class];
- case QGMP4BoxType_hvcC:
- return [QGMP4HvccBox class];
- case QGMP4BoxType_ctts:
- return [QGMP4CttsBox class];
- default:
- return nil;
- }
- }
- /**
- 根据boxClassForType:方法的返回判断是否为一个合法的box类型
- @param type box类型
- @return 除QGMP4BoxType定义的类型(不包含QGMP4BoxType_unknown)外全部为不合法类型
- */
- + (BOOL)isTypeValueValid:(QGMP4BoxType)type {
-
- Class class = [self boxClassForType:type];
- if (class) {
- return YES;
- }
- return NO;
- }
- @end
|