QGMP4Parser.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // QGMP4Parser.m
  2. // Tencent is pleased to support the open source community by making vap available.
  3. //
  4. // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  5. //
  6. // Licensed under the MIT License (the "License"); you may not use this file except in
  7. // compliance with the License. You may obtain a copy of the License at
  8. //
  9. // http://opensource.org/licenses/MIT
  10. //
  11. // Unless required by applicable law or agreed to in writing, software distributed under the License is
  12. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. // either express or implied. See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #import "QGMP4Parser.h"
  16. #import "QGVAPLogger.h"
  17. #pragma mark - mp4 parser
  18. @interface QGMP4Parser() {
  19. QGMp4BoxDataFetcher _boxDataFetcher;
  20. }
  21. @property (nonatomic, strong) NSString *filePath;
  22. @end
  23. @implementation QGMP4Parser
  24. #pragma mark -- life cycle
  25. - (instancetype)initWithFilePath:(NSString *)filePath {
  26. if (self = [super init]) {
  27. _filePath = filePath;
  28. _fileHandle = [NSFileHandle fileHandleForReadingAtPath:_filePath];
  29. __weak __typeof(self) weakSelf = self;
  30. _boxDataFetcher = ^NSData *(QGMP4Box *box) {return [weakSelf readDataForBox:box];};
  31. }
  32. return self;
  33. }
  34. - (void)dealloc {
  35. [_fileHandle closeFile];
  36. }
  37. #pragma mark -- methods
  38. - (void)parse {
  39. if (!_filePath || !_fileHandle) {
  40. return ;
  41. }
  42. unsigned long long fileSize = [_fileHandle seekToEndOfFile];
  43. [_fileHandle seekToFileOffset:0];
  44. _rootBox = [QGMP4BoxFactory createBoxForType:QGMP4BoxType_unknown startIndex:0 length:fileSize];
  45. NSMutableArray *BFSQueue = [NSMutableArray new];
  46. [BFSQueue addObject:_rootBox];
  47. QGMP4Box *calBox = _rootBox;
  48. //长度包含包含类型码长度+本身长度
  49. while ((calBox = [BFSQueue firstObject])) {
  50. [BFSQueue removeObjectAtIndex:0];
  51. if (calBox.length <= 2*(kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes)) {
  52. //长度限制
  53. continue ;
  54. }
  55. unsigned long long offset = 0;
  56. unsigned long long length = 0;
  57. QGMP4BoxType type = QGMP4BoxType_unknown;
  58. //第一个子box
  59. offset = calBox.superBox ? (calBox.startIndexInBytes + kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes) : 0;
  60. //特殊处理
  61. if ([self shouldResetOffset:calBox.type]) {
  62. [self calibrateOffset:&offset boxType:calBox.type];
  63. }
  64. //解析子box
  65. do {
  66. //判断是否会越界
  67. if ((offset+kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes)>(calBox.startIndexInBytes+calBox.length)) {
  68. break ;
  69. }
  70. if (![self readBoxTypeAndLength:offset type:&type length:&length]) {
  71. break;
  72. }
  73. if ((offset+length)>(calBox.startIndexInBytes+calBox.length)) {
  74. //reach to super box end or not a box
  75. break ;
  76. }
  77. if (![QGMP4BoxFactory isTypeValueValid:type] && (offset == (calBox.startIndexInBytes + kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes))) {
  78. //目前的策略是
  79. break ;
  80. }
  81. QGMP4Box *subBox = [QGMP4BoxFactory createBoxForType:type startIndex:offset length:length];
  82. subBox.superBox = calBox;
  83. if (!calBox.subBoxes) {
  84. calBox.subBoxes = [NSMutableArray new];
  85. }
  86. //加入box节点
  87. [calBox.subBoxes addObject:subBox];
  88. //进入广度优先遍历队列
  89. [BFSQueue addObject:subBox];
  90. [self didParseBox:subBox];
  91. //继续兄弟box
  92. offset += length;
  93. } while(1);
  94. }
  95. [self didFinisheParseFile];
  96. }
  97. - (BOOL)readBoxTypeAndLength:(uint64_t)offset type:(QGMP4BoxType *)type length:(uint64_t*)length {
  98. [_fileHandle seekToFileOffset:offset];
  99. NSData *data = [_fileHandle readDataOfLength:(kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes)];
  100. if (data.length < kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes) {
  101. VAP_Error(kQGVAPModuleCommon, @"read box length and type error");
  102. return NO;
  103. }
  104. const char *bytes = data.bytes;
  105. *length = [self readValue:bytes length:kQGBoxSizeLengthInBytes];
  106. *type = [self readValue:&bytes[kQGBoxSizeLengthInBytes] length:kQGBoxTypeLengthInBytes];
  107. if (*length == kQGBoxLargeSizeFlagLengthInBytes) {
  108. offset += kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes;
  109. [_fileHandle seekToFileOffset:offset];
  110. data = [_fileHandle readDataOfLength:kQGBoxLargeSizeLengthInBytes];
  111. if (data.length < kQGBoxLargeSizeLengthInBytes) {
  112. VAP_Error(kQGVAPModuleCommon, @"read box length and type error");
  113. return NO;
  114. }
  115. bytes = data.bytes;
  116. *length = [self readValue:bytes length:kQGBoxLargeSizeLengthInBytes];
  117. if (*length == 0) {
  118. VAP_Error(kQGVAPModuleCommon, @"read box length is 0");
  119. return NO;
  120. }
  121. }
  122. return YES;
  123. }
  124. - (BOOL)shouldResetOffset:(QGMP4BoxType)type {
  125. return type == QGMP4BoxType_stsd ||
  126. type == QGMP4BoxType_avc1 ||
  127. type == QGMP4BoxType_hvc1;
  128. }
  129. - (void)calibrateOffset:(uint64_t*)offset boxType:(QGMP4BoxType)type {
  130. switch (type) {
  131. case QGMP4BoxType_stsd:
  132. *offset += 8;
  133. break;
  134. case QGMP4BoxType_avc1:
  135. case QGMP4BoxType_hvc1:
  136. *offset += (24 + 2 + 2 + 14 + 32 + 4);
  137. break;
  138. default:
  139. break;
  140. }
  141. }
  142. - (NSData *)readDataForBox:(QGMP4Box *)box {
  143. if (!box) {
  144. return nil;
  145. }
  146. [_fileHandle seekToFileOffset:box.startIndexInBytes];
  147. return [_fileHandle readDataOfLength:(NSUInteger)box.length];
  148. }
  149. - (NSInteger)readValue:(const char*)bytes length:(NSInteger)length {
  150. NSInteger value = 0;
  151. for (int i = 0; i < length; i++) {
  152. value += (bytes[i]&0xff)<<((length-i-1)*8);
  153. }
  154. VAP_Debug(kQGVAPModuleCommon, @"readValue length:%lld value:%lld", length, value);
  155. return value;
  156. }
  157. #pragma mark -- private methods
  158. - (void)didParseBox:(QGMP4Box *)box {
  159. if ([box respondsToSelector:@selector(boxDidParsed:)]) {
  160. [box boxDidParsed:_boxDataFetcher];
  161. }
  162. if ([self.delegate respondsToSelector:@selector(didParseMP4Box:parser:)]) {
  163. [self.delegate didParseMP4Box:box parser:self];
  164. }
  165. }
  166. - (void)didFinisheParseFile {
  167. if ([self.delegate respondsToSelector:@selector(MP4FileDidFinishParse:)]) {
  168. [self.delegate MP4FileDidFinishParse:self];
  169. }
  170. }
  171. @end
  172. #pragma mark - parser proxy
  173. @interface QGMP4ParserProxy() <QGMP4ParserDelegate> {
  174. QGMP4Parser *_parser;
  175. }
  176. @end
  177. @implementation QGMP4ParserProxy
  178. - (instancetype)initWithFilePath:(NSString *)filePath {
  179. if (self = [super init]) {
  180. _parser = [[QGMP4Parser alloc] initWithFilePath:filePath];
  181. _parser.delegate = self;
  182. }
  183. return self;
  184. }
  185. - (NSInteger)picWidth {
  186. if (_picWidth == 0) {
  187. _picWidth = [self readPicWidth];
  188. }
  189. return _picWidth;
  190. }
  191. - (NSInteger)picHeight {
  192. if (_picHeight == 0) {
  193. _picHeight = [self readPicHeight];
  194. }
  195. return _picHeight;
  196. }
  197. - (NSInteger)fps {
  198. if (_fps == 0) {
  199. if (self.videoSamples.count == 0) {
  200. return 0;
  201. }
  202. _fps = lround(self.videoSamples.count/self.duration);
  203. }
  204. return _fps;
  205. }
  206. - (double)duration {
  207. if (_duration == 0) {
  208. _duration = [self readDuration];
  209. }
  210. return _duration;
  211. }
  212. - (NSArray *)videoSamples {
  213. if (_videoSamples) {
  214. return _videoSamples;
  215. }
  216. NSMutableArray *videoSamples = [NSMutableArray new];
  217. uint64_t tmp = 0;
  218. QGMP4SttsBox *sttsBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stts];
  219. QGMP4StszBox *stszBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stsz];
  220. QGMP4StscBox *stscBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stsc];
  221. QGMP4StcoBox *stcoBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stco];
  222. QGMP4CttsBox *cttsBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_ctts];
  223. uint32_t stscEntryIndex = 0;
  224. uint32_t stscEntrySampleIndex = 0;
  225. uint32_t stscEntrySampleOffset = 0;
  226. uint32_t sttsEntryIndex = 0;
  227. uint32_t sttsEntrySampleIndex = 0;
  228. uint32_t stcoChunkLogicIndex = 0;
  229. for (int i = 0; i < stszBox.sampleCount; ++i) {
  230. if (stscEntryIndex >= stscBox.entries.count ||
  231. sttsEntryIndex >= sttsBox.entries.count ||
  232. stcoChunkLogicIndex >= stcoBox.chunkOffsets.count) {
  233. break;
  234. }
  235. QGStscEntry *stscEntry = stscBox.entries[stscEntryIndex];
  236. QGSttsEntry *sttsEntry = sttsBox.entries[sttsEntryIndex];
  237. uint32_t sampleOffset = [stcoBox.chunkOffsets[stcoChunkLogicIndex] unsignedIntValue] + stscEntrySampleOffset;
  238. uint32_t ctts = 0;
  239. if (i < cttsBox.compositionOffsets.count) {
  240. ctts = [cttsBox.compositionOffsets[i] unsignedIntValue];
  241. }
  242. QGMP4Sample *sample = [QGMP4Sample new];
  243. sample.codecType = QGMP4CodecTypeVideo;
  244. sample.sampleIndex = i;
  245. sample.chunkIndex = stcoChunkLogicIndex;
  246. sample.sampleDelta = sttsEntry.sampleDelta;
  247. sample.sampleSize = [stszBox.sampleSizes[i] unsignedIntValue];
  248. sample.pts = tmp + ctts;
  249. sample.streamOffset = sampleOffset;
  250. [videoSamples addObject:sample];
  251. stscEntrySampleOffset += sample.sampleSize;
  252. tmp += sample.sampleDelta;
  253. stscEntrySampleIndex++;
  254. if (stscEntrySampleIndex >= stscEntry.samplesPerChunk) {
  255. if (stcoChunkLogicIndex + 1 < stcoBox.chunkOffsets.count) {
  256. stcoChunkLogicIndex++;
  257. }
  258. stscEntrySampleIndex = 0;
  259. stscEntrySampleOffset = 0;
  260. }
  261. sttsEntrySampleIndex++;
  262. if (sttsEntrySampleIndex >= sttsEntry.sampleCount) {
  263. sttsEntrySampleIndex = 0;
  264. if (sttsEntryIndex + 1 < sttsBox.entries.count) {
  265. sttsEntryIndex++;
  266. }
  267. }
  268. if (stscEntryIndex + 1 < stscBox.entries.count) {
  269. if (stcoChunkLogicIndex >= stscBox.entries[stscEntryIndex + 1].firstChunk - 1) {
  270. stscEntryIndex++;
  271. }
  272. }
  273. }
  274. _videoSamples = videoSamples;
  275. return _videoSamples;
  276. }
  277. - (NSArray *)videoSyncSampleIndexes {
  278. QGMP4StssBox *stssBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stss];
  279. return stssBox.syncSamples;
  280. }
  281. /**
  282. 调用该方法才会解析mp4文件并得到必要信息。
  283. */
  284. - (void)parse {
  285. [_parser parse];
  286. _rootBox = _parser.rootBox;
  287. // 解析视频解码配置信息
  288. [self parseVideoDecoderConfigRecord];
  289. }
  290. #pragma mark - Private
  291. - (void)parseVideoDecoderConfigRecord {
  292. if (self.videoCodecID == QGMP4VideoStreamCodecIDH264) {
  293. [self parseAvccDecoderConfigRecord];
  294. } else if (self.videoCodecID == QGMP4VideoStreamCodecIDH265) {
  295. [self parseHvccDecoderConfigRecord];
  296. }
  297. }
  298. - (void)parseAvccDecoderConfigRecord {
  299. self.spsData = [self parseAvccSPSData];
  300. self.ppsData = [self parseAvccPPSData];
  301. }
  302. - (void)parseHvccDecoderConfigRecord {
  303. NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_hvcC]];
  304. if (extraData.length <= 8) {
  305. return;
  306. }
  307. const char *bytes = extraData.bytes;
  308. int index = 30; // 21 + 4 + 4
  309. //int lengthSize = ((bytes[index++] & 0xff) & 0x03) + 1;
  310. int arrayNum = bytes[index++] & 0xff;
  311. // sps pps vps 种类数量
  312. for (int i = 0; i < arrayNum; i++) {
  313. int value = bytes[index++] & 0xff;
  314. int naluType = value & 0x3F;
  315. // sps pps vps 各自的数量
  316. int naluNum = ((bytes[index] & 0xff) << 8) + (bytes[index + 1] & 0xff);
  317. index += 2;
  318. for (int j = 0; j < naluNum; j++) {
  319. int naluLength = ((bytes[index] & 0xff) << 8) + (bytes[index + 1] & 0xff);
  320. index += 2;
  321. NSData *paramData = [NSData dataWithBytes:&bytes[index] length:naluLength];
  322. if (naluType == 32) {
  323. // vps
  324. self.vpsData = paramData;
  325. } else if (naluType == 33) {
  326. // sps
  327. self.spsData = paramData;
  328. } else if (naluType == 34) {
  329. // pps
  330. self.ppsData = paramData;
  331. }
  332. index += naluLength;
  333. }
  334. }
  335. }
  336. - (NSData *)parseAvccSPSData {
  337. //boxsize(32)+boxtype(32)+prefix(40)+预留(3)+spsCount(5)+spssize(16)+...+ppscount(8)+ppssize(16)+...
  338. NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_avcC]];
  339. if (extraData.length <= 8) {
  340. return nil;
  341. }
  342. const char *bytes = extraData.bytes;
  343. //sps数量 默认一个暂无使用
  344. //NSInteger spsCount = bytes[13]&0x1f;
  345. NSInteger spsLength = ((bytes[14]&0xff)<<8) + (bytes[15]&0xff);
  346. NSInteger naluType = (uint8_t)bytes[16]&0x1F;
  347. if (spsLength + 16 > extraData.length || naluType != 7) {
  348. return nil;
  349. }
  350. NSData *spsData = [NSData dataWithBytes:&bytes[16] length:spsLength];
  351. return spsData;
  352. }
  353. - (NSData *)parseAvccPPSData {
  354. NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_avcC]];
  355. if (extraData.length <= 8) {
  356. return nil;
  357. }
  358. const char *bytes = extraData.bytes;
  359. NSInteger spsCount = bytes[13]&0x1f;
  360. NSInteger spsLength = ((bytes[14]&0xff)<<8) + (bytes[15]&0xff);
  361. NSInteger prefixLength = 16 + spsLength;
  362. while (--spsCount > 0) {
  363. if (prefixLength+2 >= extraData.length) {
  364. return nil;
  365. }
  366. NSInteger nextSpsLength = ((bytes[prefixLength]&0xff)<<8)+bytes[prefixLength+1]&0xff;
  367. prefixLength += nextSpsLength;
  368. }
  369. //默认1个
  370. // NSInteger ppsCount = bytes[prefixLength]&0xff;
  371. if (prefixLength+3 >= extraData.length) {
  372. return nil;
  373. }
  374. NSInteger ppsLength = ((bytes[prefixLength+1]&0xff)<<8)+(bytes[prefixLength+2]&0xff);
  375. NSInteger naluType = (uint8_t)bytes[prefixLength+3]&0x1F;
  376. if (naluType != 8 || (ppsLength+prefixLength+3) > extraData.length) {
  377. return nil;
  378. }
  379. NSData *ppsData = [NSData dataWithBytes:&bytes[prefixLength+3] length:ppsLength];
  380. return ppsData;
  381. }
  382. - (NSInteger)readPicWidth {
  383. if (self.videoCodecID == QGMP4VideoStreamCodecIDUnknown) {
  384. return 0;
  385. }
  386. QGMP4BoxType boxType = self.videoCodecID == QGMP4VideoStreamCodecIDH264 ? QGMP4BoxType_avc1 : QGMP4BoxType_hvc1;
  387. NSInteger sizeIndex = 32;
  388. NSUInteger readLength = 2;
  389. QGMP4Box *avc1 = [self.videoTrackBox subBoxOfType:boxType];
  390. [_parser.fileHandle seekToFileOffset:avc1.startIndexInBytes+sizeIndex];
  391. NSData *widthData = [_parser.fileHandle readDataOfLength:readLength];
  392. if (widthData.length < readLength) {
  393. return 0;
  394. }
  395. const char *bytes = widthData.bytes;
  396. NSInteger width = ((bytes[0]&0xff)<<8)+(bytes[1]&0xff);
  397. return width;
  398. }
  399. - (NSInteger)readPicHeight {
  400. if (self.videoCodecID == QGMP4VideoStreamCodecIDUnknown) {
  401. return 0;
  402. }
  403. QGMP4BoxType boxType = self.videoCodecID == QGMP4VideoStreamCodecIDH264 ? QGMP4BoxType_avc1 : QGMP4BoxType_hvc1;
  404. NSInteger sizeIndex = 34;
  405. NSUInteger readLength = 2;
  406. QGMP4Box *avc1 = [self.videoTrackBox subBoxOfType:boxType];
  407. [_parser.fileHandle seekToFileOffset:avc1.startIndexInBytes+sizeIndex];
  408. NSData *heightData = [_parser.fileHandle readDataOfLength:readLength];
  409. if (heightData.length < readLength) {
  410. return 0;
  411. }
  412. const char *bytes = heightData.bytes;
  413. NSInteger height = ((bytes[0]&0xff)<<8)+(bytes[1]&0xff);
  414. return height;
  415. }
  416. - (double)readDuration {
  417. QGMP4MvhdBox *mdhdBox = [self.rootBox subBoxOfType:QGMP4BoxType_mvhd];
  418. NSData *mvhdData = [_parser readDataForBox:mdhdBox];
  419. const char *bytes = mvhdData.bytes;
  420. NSInteger version = READ32BIT(&bytes[8]);
  421. NSInteger timescaleIndex = 20;
  422. NSInteger timescaleLength = 4;
  423. NSInteger durationIndex = 24;
  424. NSInteger durationLength = 4;
  425. if (version == 1) {
  426. timescaleIndex = 28;
  427. durationIndex = 32;
  428. durationLength = 8;
  429. }
  430. NSInteger scale = [_parser readValue:&bytes[timescaleIndex] length:timescaleLength];
  431. NSInteger duration = [_parser readValue:&bytes[durationIndex] length:durationLength];
  432. if (scale == 0) {
  433. return 0;
  434. }
  435. double result = duration/(double)scale;
  436. return result;
  437. }
  438. - (NSData *)readPacketOfSample:(NSInteger)sampleIndex {
  439. if (sampleIndex >= self.videoSamples.count) {
  440. VAP_Error(kQGVAPModuleCommon, @"readPacketOfSample beyond bounds!:%@ > %@", @(sampleIndex), @(self.videoSamples.count-1));
  441. return nil;
  442. }
  443. QGMP4Sample *videoSample = self.videoSamples[sampleIndex];
  444. NSInteger currentSampleSize = videoSample.sampleSize;
  445. [_parser.fileHandle seekToFileOffset:videoSample.streamOffset];
  446. // 当视频文件有问题时,sampleIndex还没有到最后,sampleIndex < self.videoSamples.count(总帧数)时,readDataOfLength长度可能为0Bytes
  447. NSData *packetData = [_parser.fileHandle readDataOfLength:currentSampleSize];
  448. return packetData;
  449. }
  450. - (NSData *)readDataOfBox:(QGMP4Box *)box length:(NSInteger)length offset:(NSInteger)offset {
  451. if (length <= 0 || offset + length > box.length) {
  452. return nil;
  453. }
  454. [_parser.fileHandle seekToFileOffset:box.startIndexInBytes+offset];
  455. NSData *data = [_parser.fileHandle readDataOfLength:length];
  456. return data;
  457. }
  458. #pragma mark -- delegate
  459. - (void)MP4FileDidFinishParse:(QGMP4Parser *)parser {
  460. }
  461. - (void)didParseMP4Box:(QGMP4Box *)box parser:(QGMP4Parser *)parser {
  462. switch (box.type) {
  463. case QGMP4BoxType_hdlr: {
  464. QGMP4TrackType trackType = ((QGMP4HdlrBox*)box).trackType;
  465. QGMP4TrackBox *trackBox = (QGMP4TrackBox*)[box superBoxOfType:QGMP4BoxType_trak];
  466. switch (trackType) {
  467. case QGMP4TrackType_Video:
  468. self.videoTrackBox = trackBox;
  469. break;
  470. case QGMP4TrackType_Audio:
  471. self.audioTrackBox = trackBox;
  472. break;
  473. default:
  474. break;
  475. }
  476. } break;
  477. case QGMP4BoxType_avc1: {
  478. self.videoCodecID = QGMP4VideoStreamCodecIDH264;
  479. } break;
  480. case QGMP4BoxType_hvc1: {
  481. self.videoCodecID = QGMP4VideoStreamCodecIDH265;
  482. } break;
  483. default:
  484. break;
  485. }
  486. }
  487. @end