DKWechatHelper/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.m
DKJone 888af8954e [v1.0.7](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.7) / 2021-01-29
what's new
* 动态启动图
* 动态聊天背景
* 支持8.0.1
* 更新越狱包8.0.1
* 更新已注入助手的8.0.1未签名包
* 更新越狱源安装包
2021-01-29 16:35:34 +08:00

397 lines
11 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;
#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
/**
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_stss:
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_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