UIView+VAP.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. // UIView+VAP.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 <UIKit/UIKit.h>
  16. #import <objc/runtime.h>
  17. #import "UIView+VAP.h"
  18. #import "QGAnimatedImageDecodeManager.h"
  19. #import "QGMP4HWDFileInfo.h"
  20. #import "QGMP4FrameHWDecoder.h"
  21. #import "QGBaseAnimatedImageFrame+Displaying.h"
  22. #import "QGHWDMP4OpenGLView.h"
  23. #import "QGVAPWeakProxy.h"
  24. #import "NSNotificationCenter+VAPThreadSafe.h"
  25. #import "QGHWDMP4OpenGLView.h"
  26. #import "QGMP4FrameHWDecoder.h"
  27. #import "QGMP4AnimatedImageFrame.h"
  28. #import "QGMP4FrameHWDecoder.h"
  29. #import "QGHWDMetalView.h"
  30. #import "QGVAPMetalView.h"
  31. #import "QGBaseAnimatedImageFrame+Displaying.h"
  32. #import "QGVAPConfigManager.h"
  33. #import "QGHWDMetalRenderer.h"
  34. #import "UIGestureRecognizer+VAPUtil.h"
  35. NSInteger const kQGHWDMP4DefaultFPS = 20;
  36. NSInteger const kQGHWDMP4MinFPS = 1;
  37. NSInteger const QGHWDMP4MaxFPS = 60;
  38. NSInteger const VapMaxCompatibleVersion = 2;
  39. @interface UIView () <QGAnimatedImageDecoderDelegate,QGHWDMP4OpenGLViewDelegate, QGHWDMetelViewDelegate, QGVAPMetalViewDelegate, QGVAPConfigDelegate>
  40. @property (nonatomic, assign) QGHWDTextureBlendMode hwd_blendMode; //alpha通道混合模式
  41. @property (nonatomic, strong) QGMP4AnimatedImageFrame *hwd_currentFrameInstance; //store the frame value
  42. @property (nonatomic, strong) QGMP4HWDFileInfo *hwd_fileInfo; //MP4文件信息
  43. @property (nonatomic, strong) QGAnimatedImageDecodeManager *hwd_decodeManager; //解码逻辑
  44. @property (nonatomic, strong) QGAnimatedImageDecodeConfig *hwd_decodeConfig; //线程数与buffer数
  45. @property (nonatomic, strong) NSOperationQueue *hwd_callbackQueue; //回调执行队列
  46. @property (nonatomic, assign) BOOL hwd_onPause; //标记是否暂停中
  47. @property (nonatomic, assign) BOOL hwd_onSeek; //正在seek当中,此时继续播放会导致时序混乱
  48. @property (nonatomic, strong) QGHWDMP4OpenGLView *hwd_openGLView; //opengl绘制图层
  49. @property (nonatomic, strong) QGHWDMetalView *hwd_metalView; //metal绘制图层
  50. @property (nonatomic, strong) QGVAPMetalView *vap_metalView; //vap格式mp4渲染图层
  51. @property (nonatomic, assign) BOOL hwd_isFinish; //标记是否结束
  52. @property (nonatomic, assign) NSInteger hwd_repeatCount; //播放次数;-1 表示无限循环
  53. @property (nonatomic, strong) QGVAPConfigManager *hwd_configManager; //额外的配置信息
  54. @property (nonatomic, strong) dispatch_queue_t vap_renderQueue; //播放队列
  55. @property (nonatomic, assign) BOOL vap_enableOldVersion; //标记是否兼容不含vapc box的素材播放
  56. @property (nonatomic, assign) BOOL vap_isMute; //标记是否禁止音频播放
  57. @end
  58. @implementation UIView (VAP)
  59. #pragma mark - private methods
  60. - (void)hwd_registerNotification {
  61. [[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
  62. [[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
  63. [[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveSeekStartNotification:) name:kQGVAPDecoderSeekStart object:nil];
  64. [[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveSeekFinishNotification:) name:kQGVAPDecoderSeekFinish object:nil];
  65. }
  66. - (void)hwd_didReceiveEnterBackgroundNotification:(NSNotification *)notification {
  67. switch (self.hwd_enterBackgroundOP) {
  68. case HWDMP4EBOperationTypePauseAndResume:
  69. [self pauseHWDMP4];
  70. break;
  71. case HWDMP4EBOperationTypeDoNothing:
  72. break;
  73. default:
  74. [self stopHWDMP4];
  75. }
  76. }
  77. - (void)hwd_didReceiveWillEnterForegroundNotification:(NSNotification *)notification {
  78. switch (self.hwd_enterBackgroundOP) {
  79. case HWDMP4EBOperationTypePauseAndResume:
  80. [self resumeHWDMP4];
  81. break;
  82. default:
  83. break;
  84. }
  85. }
  86. - (void)hwd_didReceiveSeekStartNotification:(NSNotification *)notification {
  87. if ([self.hwd_decodeManager containsThisDeocder:notification.object]) {
  88. self.hwd_onSeek = YES;
  89. }
  90. }
  91. - (void)hwd_didReceiveSeekFinishNotification:(NSNotification *)notification {
  92. if ([self.hwd_decodeManager containsThisDeocder:notification.object]) {
  93. self.hwd_onSeek = NO;
  94. }
  95. }
  96. //结束播放
  97. - (void)hwd_stopHWDMP4 {
  98. VAP_Info(kQGVAPModuleCommon, @"hwd stop playing");
  99. self.hwd_repeatCount = 0;
  100. if (self.hwd_isFinish) {
  101. VAP_Info(kQGVAPModuleCommon, @"isFinish already set");
  102. return ;
  103. }
  104. self.hwd_isFinish = YES;
  105. self.hwd_onPause = YES;
  106. if (self.hwd_openGLView) {
  107. self.hwd_openGLView.pause = YES;
  108. if ([EAGLContext currentContext] != self.hwd_openGLView.glContext) {
  109. [EAGLContext setCurrentContext:self.hwd_openGLView.glContext];
  110. }
  111. [self.hwd_openGLView dispose];
  112. glFinish();
  113. }
  114. if (self.hwd_metalView) {
  115. [self.hwd_metalView dispose];
  116. }
  117. if (self.vap_metalView) {
  118. [self.vap_metalView dispose];
  119. }
  120. [self.hwd_decodeManager tryToStopAudioPlay];
  121. [self.hwd_callbackQueue addOperationWithBlock:^{
  122. //此处必须延迟释放,避免野指针
  123. if ([self.hwd_Delegate respondsToSelector:@selector(viewDidStopPlayMP4:view:)]) {
  124. [self.hwd_Delegate viewDidStopPlayMP4:self.hwd_currentFrame.frameIndex view:self];
  125. }
  126. }];
  127. self.hwd_decodeManager = nil;
  128. self.hwd_decodeConfig = nil;
  129. self.hwd_currentFrameInstance = nil;
  130. self.hwd_fileInfo = nil;
  131. [EAGLContext setCurrentContext:nil];
  132. }
  133. //播放完成
  134. - (void)hwd_didFinishDisplay {
  135. VAP_Info(kQGVAPModuleCommon, @"hwd didFinishDisplay");
  136. [self.hwd_callbackQueue addOperationWithBlock:^{
  137. //此处必须延迟释放,避免野指针
  138. if ([self.hwd_Delegate respondsToSelector:@selector(viewDidFinishPlayMP4:view:)]) {
  139. [self.hwd_Delegate viewDidFinishPlayMP4:self.hwd_currentFrame.frameIndex+1 view:self];
  140. }
  141. }];
  142. NSInteger currentCount = self.hwd_repeatCount;
  143. if (currentCount == -1 || currentCount-- > 0) {
  144. //continuing
  145. VAP_Info(kQGVAPModuleCommon, @"continue to display. currentCount:%@", @(currentCount));
  146. [self p_playHWDMP4:self.hwd_fileInfo.filePath
  147. fps:self.hwd_fps
  148. blendMode:self.hwd_blendMode
  149. repeatCount:currentCount
  150. delegate:self.hwd_Delegate];
  151. return ;
  152. }
  153. [self hwd_stopHWDMP4];
  154. }
  155. - (void)hwd_loadMetalViewIfNeed:(QGHWDTextureBlendMode)mode {
  156. if (self.hwd_renderByOpenGL) {
  157. return ;
  158. }
  159. //use vap metal
  160. if (self.useVapMetalView) {
  161. if (self.vap_metalView) {
  162. self.vap_metalView.commonInfo = self.hwd_configManager.model.info;
  163. return ;
  164. }
  165. QGVAPMetalView *vapMetalView = [[QGVAPMetalView alloc] initWithFrame:self.bounds];
  166. vapMetalView.commonInfo = self.hwd_configManager.model.info;
  167. vapMetalView.maskInfo = self.vap_maskInfo;
  168. vapMetalView.delegate = self;
  169. [self addSubview:vapMetalView];
  170. vapMetalView.translatesAutoresizingMaskIntoConstraints = false;
  171. NSDictionary *views = @{@"vapMetalView": vapMetalView};
  172. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[vapMetalView]|" options:0 metrics:nil views:views]];
  173. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[vapMetalView]|" options:0 metrics:nil views:views]];
  174. self.vap_metalView = vapMetalView;
  175. [self hwd_registerNotification];
  176. return ;
  177. }
  178. //use hwd metal
  179. if (self.hwd_metalView) {
  180. self.hwd_metalView.blendMode = mode;
  181. return ;
  182. }
  183. QGHWDMetalView *metalView = [[QGHWDMetalView alloc] initWithFrame:self.bounds blendMode:mode];
  184. if (!metalView) {
  185. VAP_Event(kQGVAPModuleCommon, @"metal view is nil!");
  186. return ;
  187. }
  188. metalView.blendMode = mode;
  189. metalView.delegate = self;
  190. [self addSubview:metalView];
  191. metalView.translatesAutoresizingMaskIntoConstraints = false;
  192. NSDictionary *views = @{@"metalView": metalView};
  193. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[metalView]|" options:0 metrics:nil views:views]];
  194. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[metalView]|" options:0 metrics:nil views:views]];
  195. self.hwd_metalView = metalView;
  196. [self hwd_registerNotification];
  197. }
  198. - (void)hwd_loadMetalDataIfNeed {
  199. [self.hwd_configManager loadMTLTextures:kQGHWDMetalRendererDevice];//加载所需的纹理数据
  200. [self.hwd_configManager loadMTLBuffers:kQGHWDMetalRendererDevice];//加载所需的buffer
  201. }
  202. - (void)hwd_loadOpenglViewIfNeed:(QGHWDTextureBlendMode)mode {
  203. if (!self.hwd_renderByOpenGL) {
  204. return ;
  205. }
  206. if (self.hwd_openGLView) {
  207. self.hwd_openGLView.blendMode = mode;
  208. self.hwd_openGLView.pause = NO;
  209. VAP_Info(kQGVAPModuleCommon, @"quit loading openglView for already loaded.");
  210. return ;
  211. }
  212. QGHWDMP4OpenGLView *openGLView = [[QGHWDMP4OpenGLView alloc] initWithFrame:self.bounds];
  213. openGLView.displayDelegate = self;
  214. openGLView.blendMode = mode;
  215. [self addSubview:openGLView];
  216. openGLView.userInteractionEnabled = NO;
  217. [openGLView setupGL];
  218. self.hwd_openGLView = openGLView;
  219. NSDictionary *views = @{@"openGLView": openGLView};
  220. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[openGLView]|" options:0 metrics:nil views:views]];
  221. [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[openGLView]|" options:0 metrics:nil views:views]];
  222. [self hwd_registerNotification];
  223. }
  224. //fps策略:优先使用调用者指定的fps;若不合法则使用mp4中的数据;若还是不合法则使用默认18
  225. - (NSTimeInterval)hwd_appropriateDurationForFrame:(QGMP4AnimatedImageFrame *)frame {
  226. NSInteger fps = self.hwd_fps;
  227. if (fps < kQGHWDMP4MinFPS || fps > QGHWDMP4MaxFPS) {
  228. if (frame.defaultFps >= kQGHWDMP4MinFPS && frame.defaultFps <= QGHWDMP4MaxFPS) {
  229. fps = frame.defaultFps;
  230. }else {
  231. fps = kQGHWDMP4DefaultFPS;
  232. }
  233. }
  234. return 1000/(double)fps;
  235. }
  236. #pragma mark - main
  237. /**
  238. 见playHWDMP4:blendMode:repeatCount:delegate:
  239. 播放一遍,alpha数据在左边,不需要回调
  240. */
  241. - (void)playHWDMp4:(NSString *)filePath {
  242. [self playHWDMP4:filePath delegate:nil];
  243. }
  244. /**
  245. 见playHWDMP4:blendMode:repeatCount:delegate:
  246. 播放一遍,alpha数据在左边,设置回调
  247. */
  248. - (void)playHWDMP4:(NSString *)filePath delegate:(id<HWDMP4PlayDelegate>)delegate {
  249. [self p_playHWDMP4:filePath fps:0 blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:0 delegate:delegate];
  250. }
  251. /**
  252. 见playHWDMP4:blendMode:repeatCount:delegate:
  253. alpha数据在左边
  254. */
  255. - (void)playHWDMP4:(NSString *)filePath repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
  256. [self p_playHWDMP4:filePath fps:0 blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:repeatCount delegate:delegate];
  257. }
  258. - (void)p_playHWDMP4:(NSString *)filePath
  259. fps:(NSInteger)fps
  260. blendMode:(QGHWDTextureBlendMode)mode
  261. repeatCount:(NSInteger)repeatCount
  262. delegate:(id<HWDMP4PlayDelegate>)delegate {
  263. VAP_Info(kQGVAPModuleCommon, @"try to display mp4:%@ blendMode:%@ fps:%@ repeatCount:%@", filePath, @(mode), @(fps), @(repeatCount));
  264. NSAssert([NSThread isMainThread], @"HWDMP4 needs to be accessed on the main thread.");
  265. //filePath check
  266. if (!filePath || filePath.length == 0) {
  267. VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! has no filePath!");
  268. return ;
  269. }
  270. NSFileManager *fileMgr = [NSFileManager defaultManager];
  271. if (![fileMgr fileExistsAtPath:filePath]) {
  272. VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! fileNotExistsAtPath filePath:%#", filePath);
  273. return ;
  274. }
  275. self.hwd_isFinish = NO;
  276. self.hwd_blendMode = mode;
  277. self.hwd_fps = fps;
  278. self.hwd_repeatCount = repeatCount;
  279. self.hwd_Delegate = delegate;
  280. if (self.hwd_Delegate && !self.hwd_callbackQueue) {
  281. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  282. queue.maxConcurrentOperationCount = 1;
  283. self.hwd_callbackQueue = queue;
  284. }
  285. //mp4 info
  286. QGMP4HWDFileInfo *fileInfo = [[QGMP4HWDFileInfo alloc] init];
  287. fileInfo.filePath = filePath;
  288. fileInfo.mp4Parser = [[QGMP4ParserProxy alloc] initWithFilePath:fileInfo.filePath];
  289. [fileInfo.mp4Parser parse];
  290. self.hwd_fileInfo = fileInfo;
  291. //config manager
  292. QGVAPConfigManager *configManager = [[QGVAPConfigManager alloc] initWith:fileInfo];
  293. configManager.delegate = self;
  294. self.hwd_configManager = configManager;
  295. if (configManager.model.info.version > VapMaxCompatibleVersion) {
  296. VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! not compatible vap version:%@!", @(configManager.model.info.version));
  297. [self stopHWDMP4];
  298. return ;
  299. }
  300. if (!configManager.hasValidConfig && !self.vap_enableOldVersion) {
  301. VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! don't has vapc box and enableOldVersion is false!");
  302. [self stopHWDMP4];
  303. return ;
  304. }
  305. //reset
  306. self.hwd_currentFrameInstance = nil;
  307. self.hwd_decodeManager = nil;
  308. self.hwd_onPause = NO;
  309. if (!self.hwd_decodeConfig) {
  310. self.hwd_decodeConfig = [QGAnimatedImageDecodeConfig defaultConfig];
  311. }
  312. //OpenGLView
  313. [self hwd_loadOpenglViewIfNeed:mode];
  314. //metalView
  315. [self hwd_loadMetalViewIfNeed:mode];
  316. if ([[UIDevice currentDevice] hwd_isSimulator]) {
  317. VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! not allowed in Simulator!");
  318. [self stopHWDMP4];
  319. return ;
  320. }
  321. if (!self.vap_renderQueue) {
  322. self.vap_renderQueue = dispatch_queue_create("com.qgame.vap.render", DISPATCH_QUEUE_SERIAL);
  323. }
  324. self.hwd_decodeManager = [[QGAnimatedImageDecodeManager alloc] initWith:self.hwd_fileInfo config:self.hwd_decodeConfig delegate:self];
  325. [self.hwd_configManager loadConfigResources]; //必须按先加载必要资源才能播放 - onVAPConfigResourcesLoaded
  326. }
  327. #pragma mark - play run
  328. - (void)hwd_renderVideoRun {
  329. static NSTimeInterval durationForWaitingFrame = 16/1000.0;
  330. static NSTimeInterval minimumDurationForLoop = 1/1000.0;
  331. __block NSTimeInterval lastRenderingInterval = 0;
  332. __block NSTimeInterval lastRenderingDuration = 0;
  333. dispatch_async(self.vap_renderQueue, ^{
  334. if (self.hwd_onPause || self.hwd_isFinish) {
  335. return ;
  336. }
  337. //不能将self.hwd_onPause判断加到while语句中!会导致releasepool不断上涨
  338. while (YES) {
  339. @autoreleasepool {
  340. if (self.hwd_isFinish) {
  341. break ;
  342. }
  343. if (self.hwd_onPause || self.hwd_onSeek) {
  344. lastRenderingInterval = NSDate.timeIntervalSinceReferenceDate;
  345. [NSThread sleepForTimeInterval:durationForWaitingFrame];
  346. continue;
  347. }
  348. __block QGMP4AnimatedImageFrame *nextFrame = nil;
  349. dispatch_sync(dispatch_get_main_queue(), ^{
  350. nextFrame = [self hwd_displayNext];
  351. });
  352. NSTimeInterval duration = nextFrame.duration/1000.0;
  353. if (duration == 0) {
  354. duration = durationForWaitingFrame;
  355. }
  356. NSTimeInterval currentTimeInterval = NSDate.timeIntervalSinceReferenceDate;
  357. if (nextFrame && nextFrame.frameIndex != 0) {
  358. duration -= ((currentTimeInterval-lastRenderingInterval) - lastRenderingDuration); //追回时间
  359. }
  360. duration = MAX(minimumDurationForLoop, duration);
  361. lastRenderingInterval = currentTimeInterval;
  362. lastRenderingDuration = duration;
  363. [NSThread sleepForTimeInterval:duration];
  364. }
  365. }
  366. });
  367. }
  368. - (QGMP4AnimatedImageFrame *)hwd_displayNext {
  369. if (self.hwd_onPause || self.hwd_isFinish) {
  370. return nil;
  371. }
  372. NSInteger nextIndex = self.hwd_currentFrame.frameIndex + 1;
  373. if (!self.hwd_currentFrame) {
  374. nextIndex = 0;
  375. }
  376. QGMP4AnimatedImageFrame *nextFrame = (QGMP4AnimatedImageFrame *)[self.hwd_decodeManager consumeDecodedFrame:nextIndex];
  377. //没取到预期的帧
  378. if (!nextFrame || nextFrame.frameIndex != nextIndex || ![nextFrame isKindOfClass:[QGMP4AnimatedImageFrame class]]) {
  379. return nil;
  380. }
  381. //音频播放
  382. if (nextIndex == 0) {
  383. [self.hwd_decodeManager tryToStartAudioPlay];
  384. }
  385. nextFrame.duration = [self hwd_appropriateDurationForFrame:nextFrame];
  386. //VAP_Debug(kQGVAPModuleCommon, @"display frame:%@, has frameBuffer:%@",@(nextIndex),@(nextFrame.pixelBuffer != nil));
  387. if (self.hwd_renderByOpenGL) {
  388. [self.hwd_openGLView displayPixelBuffer:nextFrame.pixelBuffer];
  389. } else if (self.useVapMetalView) {
  390. NSArray<QGVAPMergedInfo *> *mergeInfos = self.hwd_configManager.model.mergedConfig[@(nextFrame.frameIndex)];
  391. [self.vap_metalView display:nextFrame.pixelBuffer mergeInfos:mergeInfos];
  392. } else {
  393. [self.hwd_metalView display:nextFrame.pixelBuffer];
  394. }
  395. self.hwd_currentFrameInstance = nextFrame;
  396. [self.hwd_callbackQueue addOperationWithBlock:^{
  397. if (nextIndex == 0 && [self.hwd_Delegate respondsToSelector:@selector(viewDidStartPlayMP4:)]) {
  398. [self.hwd_Delegate viewDidStartPlayMP4:self];
  399. }
  400. //此处必须延迟释放,避免野指针
  401. if ([self.hwd_Delegate respondsToSelector:@selector(viewDidPlayMP4AtFrame:view:)]) {
  402. [self.hwd_Delegate viewDidPlayMP4AtFrame:self.hwd_currentFrame view:self];
  403. }
  404. }];
  405. return nextFrame;
  406. }
  407. //结束播放
  408. - (void)stopHWDMP4 {
  409. [self hwd_stopHWDMP4];
  410. }
  411. - (void)pauseHWDMP4 {
  412. VAP_Info(kQGVAPModuleCommon, @"pauseHWDMP4");
  413. self.hwd_onPause = YES;
  414. [self.hwd_decodeManager tryToPauseAudioPlay];
  415. // pause回调stop会导致一般使用场景将view移除,无法resume,因此暂时去掉该回调触发
  416. // [self.hwd_callbackQueue addOperationWithBlock:^{
  417. // //此处必须延迟释放,避免野指针
  418. // if ([self.hwd_Delegate respondsToSelector:@selector(viewDidStopPlayMP4:view:)]) {
  419. // [self.hwd_Delegate viewDidStopPlayMP4:self.hwd_currentFrame.frameIndex view:self];
  420. // }
  421. // }];
  422. }
  423. - (void)resumeHWDMP4 {
  424. VAP_Info(kQGVAPModuleCommon, @"resumeHWDMP4");
  425. self.hwd_onPause = NO;
  426. self.hwd_openGLView.pause = NO;
  427. // 目前音频和视频没有同步逻辑,多次暂停恢复会使音视频差距越来越大
  428. [self.hwd_decodeManager tryToResumeAudioPlay];
  429. }
  430. + (void)registerHWDLog:(QGVAPLoggerFunc)logger {
  431. [QGVAPLogger registerExternalLog:logger];
  432. }
  433. - (void)enableOldVersion:(BOOL)enable {
  434. self.vap_enableOldVersion = enable;
  435. }
  436. - (void)setMute:(BOOL)isMute {
  437. self.vap_isMute = isMute;
  438. }
  439. #pragma mark - delegate
  440. #pragma clang diagnostic push
  441. #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
  442. //decoder
  443. - (Class)decoderClassForManager:(QGAnimatedImageDecodeManager *)manager {
  444. return [QGMP4FrameHWDecoder class];
  445. }
  446. - (BOOL)shouldSetupAudioPlayer {
  447. return !self.vap_isMute;
  448. }
  449. - (void)decoderDidFinishDecode:(QGBaseDecoder *)decoder {
  450. VAP_Info(kQGVAPModuleCommon, @"decoderDidFinishDecode.");
  451. [self hwd_didFinishDisplay];
  452. }
  453. - (void)decoderDidFailDecode:(QGBaseDecoder *)decoder error:(NSError *)error{
  454. VAP_Error(kQGVAPModuleCommon, @"decoderDidFailDecode:%@", error);
  455. [self hwd_stopHWDMP4];
  456. [self.hwd_callbackQueue addOperationWithBlock:^{
  457. //此处必须延迟释放,避免野指针
  458. if ([self.hwd_Delegate respondsToSelector:@selector(viewDidFailPlayMP4:)]) {
  459. [self.hwd_Delegate viewDidFailPlayMP4:error];
  460. }
  461. }];
  462. }
  463. //opengl
  464. - (void)onViewUnavailableStatus {
  465. VAP_Error(kQGVAPModuleCommon, @"onViewUnavailableStatus");
  466. [self hwd_stopHWDMP4];
  467. }
  468. //metal
  469. - (void)onMetalViewUnavailable {
  470. VAP_Error(kQGVAPModuleCommon, @"onMetalViewUnavailable");
  471. [self stopHWDMP4];
  472. }
  473. //config resources loaded
  474. - (void)onVAPConfigResourcesLoaded:(QGVAPConfigModel *)config error:(NSError *)error {
  475. [self hwd_loadMetalDataIfNeed];
  476. if ([self.hwd_Delegate respondsToSelector:@selector(shouldStartPlayMP4:config:)]) {
  477. BOOL shouldStart = [self.hwd_Delegate shouldStartPlayMP4:self config:self.hwd_configManager.model];
  478. if (!shouldStart) {
  479. VAP_Event(kQGVAPModuleCommon, @"shouldStartPlayMP4 return no!");
  480. [self hwd_stopHWDMP4];
  481. return ;
  482. }
  483. }
  484. [self hwd_renderVideoRun];
  485. }
  486. - (NSString *)vap_contentForTag:(NSString *)tag resource:(QGVAPSourceInfo *)info {
  487. if ([self.hwd_Delegate respondsToSelector:@selector(contentForVapTag:resource:)]) {
  488. return [self.hwd_Delegate contentForVapTag:tag resource:info];
  489. }
  490. return nil;
  491. }
  492. - (void)vap_loadImageWithURL:(NSString *)urlStr context:(NSDictionary *)context completion:(VAPImageCompletionBlock)completionBlock {
  493. if ([self.hwd_Delegate respondsToSelector:@selector(loadVapImageWithURL:context:completion:)]) {
  494. [self.hwd_Delegate loadVapImageWithURL:urlStr context:context completion:completionBlock];
  495. } else if (completionBlock) {
  496. NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{@"msg" : @"hwd_Delegate doesn't responds to selector loadVapImageWithURL:context:completion:"}];
  497. completionBlock(nil, error, nil);
  498. }
  499. }
  500. #pragma clang diagnostic pop
  501. #pragma mark - setters&getters
  502. - (BOOL)useVapMetalView {
  503. return self.hwd_configManager.hasValidConfig;
  504. }
  505. - (QGMP4AnimatedImageFrame *)hwd_currentFrame {
  506. return self.hwd_currentFrameInstance;
  507. }
  508. - (id<HWDMP4PlayDelegate>)hwd_Delegate {
  509. return objc_getAssociatedObject(self, @"MP4PlayDelegate");
  510. }
  511. - (void)setHwd_Delegate:(id<HWDMP4PlayDelegate>)MP4PlayDelegate {
  512. //解决循环播放问题,本身已经是一个weakproxy对象,就不再处理
  513. id weakDelegate = MP4PlayDelegate;
  514. if (![MP4PlayDelegate isKindOfClass:[QGVAPWeakProxy class]]) {
  515. weakDelegate = [QGVAPWeakProxy proxyWithTarget:MP4PlayDelegate];
  516. }
  517. return objc_setAssociatedObject(self, @"MP4PlayDelegate", weakDelegate, OBJC_ASSOCIATION_RETAIN);
  518. }
  519. //category methods
  520. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_onPause, setHwd_onPause, BOOL)
  521. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_onSeek, setHwd_onSeek, BOOL)
  522. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_enterBackgroundOP, setHwd_enterBackgroundOP, HWDMP4EBOperationType)
  523. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_renderByOpenGL, setHwd_renderByOpenGL, BOOL)
  524. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_isFinish, setHwd_isFinish, BOOL)
  525. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_fps, setHwd_fps, NSInteger)
  526. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_blendMode, setHwd_blendMode, NSInteger)
  527. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_repeatCount, setHwd_repeatCount, NSInteger)
  528. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_currentFrameInstance, setHwd_currentFrameInstance, OBJC_ASSOCIATION_RETAIN)
  529. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_MP4FilePath, setHwd_MP4FilePath, OBJC_ASSOCIATION_RETAIN)
  530. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_decodeManager, setHwd_decodeManager, OBJC_ASSOCIATION_RETAIN)
  531. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_fileInfo, setHwd_fileInfo, OBJC_ASSOCIATION_RETAIN)
  532. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_decodeConfig, setHwd_decodeConfig, OBJC_ASSOCIATION_RETAIN)
  533. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_callbackQueue, setHwd_callbackQueue, OBJC_ASSOCIATION_RETAIN)
  534. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_openGLView, setHwd_openGLView, OBJC_ASSOCIATION_RETAIN)
  535. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_metalView, setHwd_metalView, OBJC_ASSOCIATION_RETAIN)
  536. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(vap_metalView, setVap_metalView, OBJC_ASSOCIATION_RETAIN)
  537. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_attachmentsModel, setHwd_attachmentsModel, OBJC_ASSOCIATION_RETAIN)
  538. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_configManager, setHwd_configManager, OBJC_ASSOCIATION_RETAIN)
  539. HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(vap_renderQueue, setVap_renderQueue, OBJC_ASSOCIATION_RETAIN)
  540. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(vap_enableOldVersion, setVap_enableOldVersion, BOOL)
  541. HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(vap_isMute, setVap_isMute, BOOL)
  542. @end
  543. /// vap 增加手势识别的能力
  544. @implementation UIView (VAPGesture)
  545. /// 手势识别通用接口
  546. /// @param gestureRecognizer 需要的手势识别器
  547. /// @param handler 手势识别事件回调,按照gestureRecognizer回调时机回调
  548. /// @note 例:[mp4View addVapGesture:[UILongPressGestureRecognizer new] callback:^(UIGestureRecognizer *gestureRecognizer, BOOL insideSource,QGVAPSourceDisplayItem *source) { NSLog(@"long press"); }];
  549. - (void)addVapGesture:(UIGestureRecognizer *)gestureRecognizer callback:(VAPGestureEventBlock)handler {
  550. if (!gestureRecognizer) {
  551. VAP_Event(kQGVAPModuleCommon, @"addVapTapGesture with empty gestureRecognizer!");
  552. return ;
  553. }
  554. if (!handler) {
  555. VAP_Event(kQGVAPModuleCommon, @"addVapTapGesture with empty handler!");
  556. return ;
  557. }
  558. __weak __typeof(self) weakSelf = self;
  559. [gestureRecognizer addVapActionBlock:^(UITapGestureRecognizer *sender) {
  560. QGVAPSourceDisplayItem *diplaySource = [weakSelf displayingSourceAt:[sender locationInView:weakSelf]];
  561. if (diplaySource) {
  562. handler(sender, YES, diplaySource);
  563. } else {
  564. handler(sender, NO, nil);
  565. }
  566. }];
  567. [self addGestureRecognizer:gestureRecognizer];
  568. }
  569. /// 增加点击的手势识别
  570. /// @param handler 点击事件回调
  571. - (void)addVapTapGesture:(VAPGestureEventBlock)handler {
  572. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
  573. [self addVapGesture:tapGesture callback:handler];
  574. }
  575. /// 获取当前视图中point位置最近的一个source,没有的话返回nil
  576. /// @param point 当前view坐标系下的某一个位置
  577. - (QGVAPSourceDisplayItem *)displayingSourceAt:(CGPoint)point {
  578. NSArray<QGVAPMergedInfo *> *mergeInfos = self.hwd_configManager.model.mergedConfig[@(self.hwd_currentFrame.frameIndex)];
  579. mergeInfos = [mergeInfos sortedArrayUsingComparator:^NSComparisonResult(QGVAPMergedInfo *obj1, QGVAPMergedInfo *obj2) {
  580. return [@(obj2.renderIndex) compare:@(obj1.renderIndex)];
  581. }];
  582. CGSize renderingPixelSize = self.hwd_configManager.model.info.size;
  583. if (renderingPixelSize.width <= 0 || renderingPixelSize.height <= 0) {
  584. return nil;
  585. }
  586. __block QGVAPMergedInfo *targetMergeInfo = nil;
  587. __block CGRect targetSourceFrame = CGRectZero;
  588. CGSize viewSize = self.frame.size;
  589. CGFloat xRatio = viewSize.width / renderingPixelSize.width;
  590. CGFloat yRatio = viewSize.height / renderingPixelSize.height;
  591. [mergeInfos enumerateObjectsUsingBlock:^(QGVAPMergedInfo * mergeInfo, NSUInteger idx, BOOL * _Nonnull stop) {
  592. CGRect sourceRenderingRect = mergeInfo.renderRect;
  593. CGRect sourceRenderingFrame = CGRectMake(CGRectGetMinX(sourceRenderingRect) * xRatio, CGRectGetMinY(sourceRenderingRect) * yRatio, CGRectGetWidth(sourceRenderingRect) * xRatio, CGRectGetHeight(sourceRenderingRect) * yRatio);
  594. BOOL inside = CGRectContainsPoint(sourceRenderingFrame, point);
  595. if (inside) {
  596. targetMergeInfo = mergeInfo;
  597. targetSourceFrame = sourceRenderingFrame;
  598. *stop = YES;
  599. }
  600. }];
  601. if (!targetMergeInfo) {
  602. return nil;
  603. }
  604. QGVAPSourceDisplayItem *diplayItem = [QGVAPSourceDisplayItem new];
  605. diplayItem.sourceInfo = targetMergeInfo.source;
  606. diplayItem.frame = targetSourceFrame;
  607. return diplayItem;
  608. }
  609. @end
  610. @implementation UIView (VAPMask)
  611. - (void)setVap_maskInfo:(QGVAPMaskInfo *)vap_maskInfo {
  612. objc_setAssociatedObject(self, @"VAPMaskInfo", vap_maskInfo, OBJC_ASSOCIATION_RETAIN);
  613. [self.vap_metalView setMaskInfo:vap_maskInfo];
  614. }
  615. - (QGVAPMaskInfo *)vap_maskInfo {
  616. return objc_getAssociatedObject(self, @"VAPMaskInfo");
  617. }
  618. @end
  619. @implementation UIView (MP4HWDDeprecated)
  620. /**
  621. 见playHWDMP4:blendMode:repeatCount:delegate:
  622. @param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
  623. 播放一遍,alpha数据在左边,设置回调
  624. */
  625. - (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps delegate:(id<HWDMP4PlayDelegate>)delegate {
  626. [self p_playHWDMP4:filePath fps:fps blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:0 delegate:delegate];
  627. }
  628. /**
  629. 见playHWDMP4:blendMode:repeatCount:delegate:
  630. @param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
  631. alpha数据在左边
  632. */
  633. - (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
  634. [self p_playHWDMP4:filePath fps:fps blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:repeatCount delegate:delegate];
  635. }
  636. /**
  637. 见playHWDMP4:blendMode:repeatCount:delegate:
  638. 播放一遍
  639. */
  640. - (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode delegate:(id<HWDMP4PlayDelegate>)delegate {
  641. [self p_playHWDMP4:filePath fps:0 blendMode:mode repeatCount:0 delegate:delegate];
  642. }
  643. /**
  644. 利用GPU解码并播放mp4-h.264素材,在模拟器中会直接失败无法播放。
  645. @param filePath mp4s素材本地地址
  646. @param mode 确定素材中alpha通道数据位置,默认QGHWDTextureBlendMode_AlphaLeft
  647. @param repeatCount 重复播放次数,若repeatCount==n, 则播放n+1次;若repeatCount==-1,则循环播放.
  648. @param delegate 播放回调,⚠️注意:不在主线程回调
  649. @note 素材文件需要按照规范生成
  650. */
  651. - (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
  652. [self p_playHWDMP4:filePath fps:0 blendMode:mode repeatCount:repeatCount delegate:delegate];
  653. }
  654. /**
  655. 见playHWDMP4:blendMode:repeatCount:delegate:
  656. @param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
  657. 播放一遍
  658. */
  659. - (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode delegate:(id<HWDMP4PlayDelegate>)delegate {
  660. [self p_playHWDMP4:filePath fps:fps blendMode:mode repeatCount:0 delegate:delegate];
  661. }
  662. /**
  663. 见playHWDMP4:blendMode:repeatCount:delegate:
  664. @param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
  665. */
  666. - (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
  667. [self p_playHWDMP4:filePath fps:fps blendMode:mode repeatCount:repeatCount delegate:delegate];
  668. }
  669. @end