Files
DKWechatHelper/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.m
T
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

723 lines
29 KiB
Objective-C

// UIView+VAP.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 <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "UIView+VAP.h"
#import "QGAnimatedImageDecodeManager.h"
#import "QGMP4HWDFileInfo.h"
#import "QGMP4FrameHWDecoder.h"
#import "QGBaseAnimatedImageFrame+Displaying.h"
#import "QGHWDMP4OpenGLView.h"
#import "QGVAPWeakProxy.h"
#import "NSNotificationCenter+VAPThreadSafe.h"
#import "QGHWDMP4OpenGLView.h"
#import "QGMP4FrameHWDecoder.h"
#import "QGMP4AnimatedImageFrame.h"
#import "QGMP4FrameHWDecoder.h"
#import "QGHWDMetalView.h"
#import "QGVAPMetalView.h"
#import "QGBaseAnimatedImageFrame+Displaying.h"
#import "QGVAPConfigManager.h"
#import "QGHWDMetalRenderer.h"
#import "UIGestureRecognizer+VAPUtil.h"
NSInteger const kQGHWDMP4DefaultFPS = 20;
NSInteger const kQGHWDMP4MinFPS = 1;
NSInteger const QGHWDMP4MaxFPS = 60;
NSInteger const VapMaxCompatibleVersion = 2;
@interface UIView () <QGAnimatedImageDecoderDelegate,QGHWDMP4OpenGLViewDelegate, QGHWDMetelViewDelegate, QGVAPMetalViewDelegate, QGVAPConfigDelegate>
@property (nonatomic, assign) QGHWDTextureBlendMode hwd_blendMode; //alpha通道混合模式
@property (nonatomic, strong) QGMP4AnimatedImageFrame *hwd_currentFrameInstance; //store the frame value
@property (nonatomic, strong) QGMP4HWDFileInfo *hwd_fileInfo; //MP4文件信息
@property (nonatomic, strong) QGAnimatedImageDecodeManager *hwd_decodeManager; //解码逻辑
@property (nonatomic, strong) QGAnimatedImageDecodeConfig *hwd_decodeConfig; //线程数与buffer数
@property (nonatomic, strong) NSOperationQueue *hwd_callbackQueue; //回调执行队列
@property (nonatomic, assign) BOOL hwd_onPause; //标记是否暂停中
@property (nonatomic, strong) QGHWDMP4OpenGLView *hwd_openGLView; //opengl绘制图层
@property (nonatomic, strong) QGHWDMetalView *hwd_metalView; //metal绘制图层
@property (nonatomic, strong) QGVAPMetalView *vap_metalView; //vap格式mp4渲染图层
@property (nonatomic, assign) BOOL hwd_isFinish; //标记是否结束
@property (nonatomic, assign) NSInteger hwd_repeatCount; //播放次数;-1 表示无限循环
@property (nonatomic, strong) QGVAPConfigManager *hwd_configManager; //额外的配置信息
@property (nonatomic, strong) dispatch_queue_t vap_renderQueue; //播放队列
@end
@implementation UIView (VAP)
#pragma mark - private methods
- (void)hwd_registerNotification {
[[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveEnterBackgroundNotification:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] hwd_addSafeObserver:self selector:@selector(hwd_didReceiveWillEnterForegroundNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)hwd_didReceiveEnterBackgroundNotification:(NSNotification *)notification {
[self hwd_stopHWDMP4];
}
//结束播放
- (void)hwd_stopHWDMP4 {
VAP_Info(kQGVAPModuleCommon, @"hwd stop playing");
self.hwd_repeatCount = 0;
if (self.hwd_isFinish) {
VAP_Info(kQGVAPModuleCommon, @"isFinish already set");
return ;
}
self.hwd_isFinish = YES;
self.hwd_onPause = YES;
if (self.hwd_openGLView) {
self.hwd_openGLView.pause = YES;
if ([EAGLContext currentContext] != self.hwd_openGLView.glContext) {
[EAGLContext setCurrentContext:self.hwd_openGLView.glContext];
}
[self.hwd_openGLView dispose];
glFinish();
}
if (self.hwd_metalView) {
[self.hwd_metalView dispose];
}
if (self.vap_metalView) {
[self.vap_metalView dispose];
}
[self.hwd_callbackQueue addOperationWithBlock:^{
//此处必须延迟释放,避免野指针
if ([self.hwd_Delegate respondsToSelector:@selector(viewDidStopPlayMP4:view:)]) {
[self.hwd_Delegate viewDidStopPlayMP4:self.hwd_currentFrame.frameIndex view:self];
}
}];
self.hwd_decodeManager = nil;
self.hwd_decodeConfig = nil;
self.hwd_currentFrameInstance = nil;
self.hwd_fileInfo = nil;
[EAGLContext setCurrentContext:nil];
}
//播放完成
- (void)hwd_didFinishDisplay {
VAP_Info(kQGVAPModuleCommon, @"hwd didFinishDisplay");
[self.hwd_callbackQueue addOperationWithBlock:^{
//此处必须延迟释放,避免野指针
if ([self.hwd_Delegate respondsToSelector:@selector(viewDidFinishPlayMP4:view:)]) {
[self.hwd_Delegate viewDidFinishPlayMP4:self.hwd_currentFrame.frameIndex+1 view:self];
}
}];
NSInteger currentCount = self.hwd_repeatCount;
if (currentCount == -1 || currentCount-- > 0) {
//continuing
VAP_Info(kQGVAPModuleCommon, @"continue to display. currentCount:%@", @(currentCount));
[self p_playHWDMP4:self.hwd_fileInfo.filePath
fps:self.hwd_fps
blendMode:self.hwd_blendMode
repeatCount:currentCount
delegate:self.hwd_Delegate];
return ;
}
[self hwd_stopHWDMP4];
}
- (void)hwd_didReceiveWillEnterForegroundNotification:(NSNotification *)notification {
[self resumeHWDMP4];
}
- (void)hwd_loadMetalViewIfNeed:(QGHWDTextureBlendMode)mode {
if (self.hwd_renderByOpenGL) {
return ;
}
//use vap metal
if (self.useVapMetalView) {
if (self.vap_metalView) {
self.vap_metalView.commonInfo = self.hwd_configManager.model.info;
return ;
}
QGVAPMetalView *vapMetalView = [[QGVAPMetalView alloc] initWithFrame:self.bounds];
vapMetalView.commonInfo = self.hwd_configManager.model.info;
vapMetalView.maskInfo = self.vap_maskInfo;
vapMetalView.delegate = self;
[self addSubview:vapMetalView];
vapMetalView.translatesAutoresizingMaskIntoConstraints = false;
NSDictionary *views = @{@"vapMetalView": vapMetalView};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[vapMetalView]|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[vapMetalView]|" options:0 metrics:nil views:views]];
self.vap_metalView = vapMetalView;
[self hwd_registerNotification];
return ;
}
//use hwd metal
if (self.hwd_metalView) {
self.hwd_metalView.blendMode = mode;
return ;
}
QGHWDMetalView *metalView = [[QGHWDMetalView alloc] initWithFrame:self.bounds blendMode:mode];
if (!metalView) {
VAP_Event(kQGVAPModuleCommon, @"metal view is nil!");
return ;
}
metalView.blendMode = mode;
metalView.delegate = self;
[self addSubview:metalView];
metalView.translatesAutoresizingMaskIntoConstraints = false;
NSDictionary *views = @{@"metalView": metalView};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[metalView]|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[metalView]|" options:0 metrics:nil views:views]];
self.hwd_metalView = metalView;
[self hwd_registerNotification];
}
- (void)hwd_loadMetalDataIfNeed {
[self.hwd_configManager loadMTLTextures:kQGHWDMetalRendererDevice];//加载所需的纹理数据
[self.hwd_configManager loadMTLBuffers:kQGHWDMetalRendererDevice];//加载所需的buffer
}
- (void)hwd_loadOpenglViewIfNeed:(QGHWDTextureBlendMode)mode {
if (!self.hwd_renderByOpenGL) {
return ;
}
if (self.hwd_openGLView) {
self.hwd_openGLView.blendMode = mode;
self.hwd_openGLView.pause = NO;
VAP_Info(kQGVAPModuleCommon, @"quit loading openglView for already loaded.");
return ;
}
QGHWDMP4OpenGLView *openGLView = [[QGHWDMP4OpenGLView alloc] initWithFrame:self.bounds];
openGLView.displayDelegate = self;
openGLView.blendMode = mode;
[self addSubview:openGLView];
openGLView.userInteractionEnabled = NO;
[openGLView setupGL];
self.hwd_openGLView = openGLView;
NSDictionary *views = @{@"openGLView": openGLView};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[openGLView]|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[openGLView]|" options:0 metrics:nil views:views]];
[self hwd_registerNotification];
}
//fps策略:优先使用调用者指定的fps;若不合法则使用mp4中的数据;若还是不合法则使用默认18
- (NSTimeInterval)hwd_appropriateDurationForFrame:(QGMP4AnimatedImageFrame *)frame {
NSInteger fps = self.hwd_fps;
if (fps < kQGHWDMP4MinFPS || fps > QGHWDMP4MaxFPS) {
if (frame.defaultFps >= kQGHWDMP4MinFPS && frame.defaultFps <= QGHWDMP4MaxFPS) {
fps = frame.defaultFps;
}else {
fps = kQGHWDMP4DefaultFPS;
}
}
return 1000/(double)fps;
}
#pragma mark - main
/**
见playHWDMP4:blendMode:repeatCount:delegate:
播放一遍,alpha数据在左边,不需要回调
*/
- (void)playHWDMp4:(NSString *)filePath {
[self playHWDMP4:filePath delegate:nil];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
播放一遍,alpha数据在左边,设置回调
*/
- (void)playHWDMP4:(NSString *)filePath delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:0 blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:0 delegate:delegate];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
alpha数据在左边
*/
- (void)playHWDMP4:(NSString *)filePath repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:0 blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:repeatCount delegate:delegate];
}
- (void)p_playHWDMP4:(NSString *)filePath
fps:(NSInteger)fps
blendMode:(QGHWDTextureBlendMode)mode
repeatCount:(NSInteger)repeatCount
delegate:(id<HWDMP4PlayDelegate>)delegate {
VAP_Info(kQGVAPModuleCommon, @"try to display mp4:%@ blendMode:%@ fps:%@ repeatCount:%@", filePath, @(mode), @(fps), @(repeatCount));
NSAssert([NSThread isMainThread], @"HWDMP4 needs to be accessed on the main thread.");
//filePath check
if (!filePath || filePath.length == 0) {
VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! has no filePath!");
return ;
}
NSFileManager *fileMgr = [NSFileManager defaultManager];
if (![fileMgr fileExistsAtPath:filePath]) {
VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! fileNotExistsAtPath filePath:%#", filePath);
return ;
}
self.hwd_isFinish = NO;
self.hwd_blendMode = mode;
self.hwd_fps = fps;
self.hwd_repeatCount = repeatCount;
self.hwd_Delegate = delegate;
if (self.hwd_Delegate && !self.hwd_callbackQueue) {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
self.hwd_callbackQueue = queue;
}
//mp4 info
QGMP4HWDFileInfo *fileInfo = [[QGMP4HWDFileInfo alloc] init];
fileInfo.filePath = filePath;
fileInfo.mp4Parser = [[QGMP4ParserProxy alloc] initWithFilePath:fileInfo.filePath];
[fileInfo.mp4Parser parse];
self.hwd_fileInfo = fileInfo;
//config manager
QGVAPConfigManager *configManager = [[QGVAPConfigManager alloc] initWith:fileInfo];
configManager.delegate = self;
self.hwd_configManager = configManager;
if (configManager.model.info.version > VapMaxCompatibleVersion) {
VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! not compatible vap version:%@!", @(configManager.model.info.version));
[self stopHWDMP4];
return ;
}
//reset
self.hwd_currentFrameInstance = nil;
self.hwd_decodeManager = nil;
self.hwd_onPause = NO;
if (!self.hwd_decodeConfig) {
self.hwd_decodeConfig = [QGAnimatedImageDecodeConfig defaultConfig];
}
//OpenGLView
[self hwd_loadOpenglViewIfNeed:mode];
//metalView
[self hwd_loadMetalViewIfNeed:mode];
if ([[UIDevice currentDevice] hwd_isSimulator]) {
VAP_Error(kQGVAPModuleCommon, @"playHWDMP4 error! not allowed in Simulator!");
[self stopHWDMP4];
return ;
}
if (!self.vap_renderQueue) {
self.vap_renderQueue = dispatch_queue_create("com.qgame.vap.render", DISPATCH_QUEUE_SERIAL);
}
self.hwd_decodeManager = [[QGAnimatedImageDecodeManager alloc] initWith:self.hwd_fileInfo config:self.hwd_decodeConfig delegate:self];
[self.hwd_configManager loadConfigResources]; //必须按先加载必要资源才能播放 - onVAPConfigResourcesLoaded
}
#pragma mark - play run
- (void)hwd_renderVideoRun {
static NSTimeInterval durationForWaitingFrame = 16/1000.0;
static NSTimeInterval minimumDurationForLoop = 1/1000.0;
__block NSTimeInterval lastRenderingInterval = 0;
__block NSTimeInterval lastRenderingDuration = 0;
dispatch_async(self.vap_renderQueue, ^{
if (self.hwd_onPause || self.hwd_isFinish) {
return ;
}
//不能将self.hwd_onPause判断加到while语句中!会导致releasepool不断上涨
while (YES) {
@autoreleasepool {
if (self.hwd_onPause || self.hwd_isFinish) {
break ;
}
__block QGMP4AnimatedImageFrame *nextFrame = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
nextFrame = [self hwd_displayNext];
});
NSTimeInterval duration = nextFrame.duration/1000.0;
if (duration == 0) {
duration = durationForWaitingFrame;
}
NSTimeInterval currentTimeInterval = NSDate.timeIntervalSinceReferenceDate;
if (nextFrame && nextFrame.frameIndex != 0) {
duration -= ((currentTimeInterval-lastRenderingInterval) - lastRenderingDuration); //追回时间
}
duration = MAX(minimumDurationForLoop, duration);
lastRenderingInterval = currentTimeInterval;
lastRenderingDuration = duration;
[NSThread sleepForTimeInterval:duration];
}
}
});
}
- (QGMP4AnimatedImageFrame *)hwd_displayNext {
if (self.hwd_onPause || self.hwd_isFinish) {
return nil;
}
NSInteger nextIndex = self.hwd_currentFrame.frameIndex + 1;
if (!self.hwd_currentFrame) {
nextIndex = 0;
}
QGMP4AnimatedImageFrame *nextFrame = (QGMP4AnimatedImageFrame *)[self.hwd_decodeManager consumeDecodedFrame:nextIndex];
//没取到预期的帧
if (!nextFrame || nextFrame.frameIndex != nextIndex || ![nextFrame isKindOfClass:[QGMP4AnimatedImageFrame class]]) {
return nil;
}
//音频播放
if (nextIndex == 0) {
[self.hwd_decodeManager tryToStartAudioPlay];
}
nextFrame.duration = [self hwd_appropriateDurationForFrame:nextFrame];
//VAP_Debug(kQGVAPModuleCommon, @"display frame:%@, has frameBuffer:%@",@(nextIndex),@(nextFrame.pixelBuffer != nil));
if (self.hwd_renderByOpenGL) {
[self.hwd_openGLView displayPixelBuffer:nextFrame.pixelBuffer];
} else if (self.useVapMetalView) {
NSArray<QGVAPMergedInfo *> *mergeInfos = self.hwd_configManager.model.mergedConfig[@(nextFrame.frameIndex)];
[self.vap_metalView display:nextFrame.pixelBuffer mergeInfos:mergeInfos];
} else {
[self.hwd_metalView display:nextFrame.pixelBuffer];
}
self.hwd_currentFrameInstance = nextFrame;
[self.hwd_callbackQueue addOperationWithBlock:^{
if (nextIndex == 0 && [self.hwd_Delegate respondsToSelector:@selector(viewDidStartPlayMP4:)]) {
[self.hwd_Delegate viewDidStartPlayMP4:self];
}
//此处必须延迟释放,避免野指针
if ([self.hwd_Delegate respondsToSelector:@selector(viewDidPlayMP4AtFrame:view:)]) {
[self.hwd_Delegate viewDidPlayMP4AtFrame:self.hwd_currentFrame view:self];
}
}];
return nextFrame;
}
//结束播放
- (void)stopHWDMP4 {
[self hwd_stopHWDMP4];
}
- (void)pauseHWDMP4 {
VAP_Info(kQGVAPModuleCommon, @"pauseHWDMP4");
self.hwd_onPause = YES;
[self.hwd_callbackQueue addOperationWithBlock:^{
//此处必须延迟释放,避免野指针
if ([self.hwd_Delegate respondsToSelector:@selector(viewDidStopPlayMP4:view:)]) {
[self.hwd_Delegate viewDidStopPlayMP4:self.hwd_currentFrame.frameIndex view:self];
}
}];
}
- (void)resumeHWDMP4 {
VAP_Info(kQGVAPModuleCommon, @"resumeHWDMP4");
self.hwd_onPause = NO;
self.hwd_openGLView.pause = NO;
}
+ (void)registerHWDLog:(QGVAPLoggerFunc)logger {
[QGVAPLogger registerExternalLog:logger];
}
#pragma mark - delegate
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
//decoder
- (Class)decoderClassForManager:(QGAnimatedImageDecodeManager *)manager {
return [QGMP4FrameHWDecoder class];
}
- (void)decoderDidFinishDecode:(QGBaseDecoder *)decoder {
VAP_Info(kQGVAPModuleCommon, @"decoderDidFinishDecode.");
[self hwd_didFinishDisplay];
}
- (void)decoderDidFailDecode:(QGBaseDecoder *)decoder error:(NSError *)error{
VAP_Error(kQGVAPModuleCommon, @"decoderDidFailDecode:%@", error);
[self hwd_stopHWDMP4];
[self.hwd_callbackQueue addOperationWithBlock:^{
//此处必须延迟释放,避免野指针
if ([self.hwd_Delegate respondsToSelector:@selector(viewDidFailPlayMP4:)]) {
[self.hwd_Delegate viewDidFailPlayMP4:error];
}
}];
}
//opengl
- (void)onViewUnavailableStatus {
VAP_Error(kQGVAPModuleCommon, @"onViewUnavailableStatus");
[self hwd_stopHWDMP4];
}
//metal
- (void)onMetalViewUnavailable {
VAP_Error(kQGVAPModuleCommon, @"onMetalViewUnavailable");
[self stopHWDMP4];
}
//config resources loaded
- (void)onVAPConfigResourcesLoaded:(QGVAPConfigModel *)config error:(NSError *)error {
[self hwd_loadMetalDataIfNeed];
if ([self.hwd_Delegate respondsToSelector:@selector(shouldStartPlayMP4:config:)]) {
BOOL shouldStart = [self.hwd_Delegate shouldStartPlayMP4:self config:self.hwd_configManager.model];
if (!shouldStart) {
VAP_Event(kQGVAPModuleCommon, @"shouldStartPlayMP4 return no!");
[self hwd_stopHWDMP4];
return ;
}
}
[self hwd_renderVideoRun];
}
- (NSString *)vap_contentForTag:(NSString *)tag resource:(QGVAPSourceInfo *)info {
if ([self.hwd_Delegate respondsToSelector:@selector(contentForVapTag:resource:)]) {
return [self.hwd_Delegate contentForVapTag:tag resource:info];
}
return nil;
}
- (void)vap_loadImageWithURL:(NSString *)urlStr context:(NSDictionary *)context completion:(VAPImageCompletionBlock)completionBlock {
if ([self.hwd_Delegate respondsToSelector:@selector(loadVapImageWithURL:context:completion:)]) {
[self.hwd_Delegate loadVapImageWithURL:urlStr context:context completion:completionBlock];
} else if (completionBlock) {
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{@"msg" : @"hwd_Delegate doesn't responds to selector loadVapImageWithURL:context:completion:"}];
completionBlock(nil, error, nil);
}
}
#pragma clang diagnostic pop
#pragma mark - setters&getters
- (BOOL)useVapMetalView {
return self.hwd_configManager.hasValidConfig;
}
- (QGMP4AnimatedImageFrame *)hwd_currentFrame {
return self.hwd_currentFrameInstance;
}
- (id<HWDMP4PlayDelegate>)hwd_Delegate {
return objc_getAssociatedObject(self, @"MP4PlayDelegate");
}
- (void)setHwd_Delegate:(id<HWDMP4PlayDelegate>)MP4PlayDelegate {
//解决循环播放问题,本身已经是一个weakproxy对象,就不再处理
id weakDelegate = MP4PlayDelegate;
if (![MP4PlayDelegate isKindOfClass:[QGVAPWeakProxy class]]) {
weakDelegate = [QGVAPWeakProxy proxyWithTarget:MP4PlayDelegate];
}
return objc_setAssociatedObject(self, @"MP4PlayDelegate", weakDelegate, OBJC_ASSOCIATION_RETAIN);
}
//category methods
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_onPause, setHwd_onPause, BOOL)
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_renderByOpenGL, setHwd_renderByOpenGL, BOOL)
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_isFinish, setHwd_isFinish, BOOL)
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_fps, setHwd_fps, NSInteger)
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_blendMode, setHwd_blendMode, NSInteger)
HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(hwd_repeatCount, setHwd_repeatCount, NSInteger)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_currentFrameInstance, setHwd_currentFrameInstance, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_MP4FilePath, setHwd_MP4FilePath, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_decodeManager, setHwd_decodeManager, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_fileInfo, setHwd_fileInfo, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_decodeConfig, setHwd_decodeConfig, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_callbackQueue, setHwd_callbackQueue, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_openGLView, setHwd_openGLView, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_metalView, setHwd_metalView, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(vap_metalView, setVap_metalView, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_attachmentsModel, setHwd_attachmentsModel, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(hwd_configManager, setHwd_configManager, OBJC_ASSOCIATION_RETAIN)
HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(vap_renderQueue, setVap_renderQueue, OBJC_ASSOCIATION_RETAIN)
@end
/// vap 增加手势识别的能力
@implementation UIView (VAPGesture)
/// 手势识别通用接口
/// @param gestureRecognizer 需要的手势识别器
/// @param handler 手势识别事件回调,按照gestureRecognizer回调时机回调
/// @note 例:[mp4View addVapGesture:[UILongPressGestureRecognizer new] callback:^(UIGestureRecognizer *gestureRecognizer, BOOL insideSource,QGVAPSourceDisplayItem *source) { NSLog(@"long press"); }];
- (void)addVapGesture:(UIGestureRecognizer *)gestureRecognizer callback:(VAPGestureEventBlock)handler {
if (!gestureRecognizer) {
VAP_Event(kQGVAPModuleCommon, @"addVapTapGesture with empty gestureRecognizer!");
return ;
}
if (!handler) {
VAP_Event(kQGVAPModuleCommon, @"addVapTapGesture with empty handler!");
return ;
}
__weak __typeof(self) weakSelf = self;
[gestureRecognizer addVapActionBlock:^(UITapGestureRecognizer *sender) {
QGVAPSourceDisplayItem *diplaySource = [weakSelf displayingSourceAt:[sender locationInView:weakSelf]];
if (diplaySource) {
handler(sender, YES, diplaySource);
} else {
handler(sender, NO, nil);
}
}];
[self addGestureRecognizer:gestureRecognizer];
}
/// 增加点击的手势识别
/// @param handler 点击事件回调
- (void)addVapTapGesture:(VAPGestureEventBlock)handler {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
[self addVapGesture:tapGesture callback:handler];
}
/// 获取当前视图中point位置最近的一个source,没有的话返回nil
/// @param point 当前view坐标系下的某一个位置
- (QGVAPSourceDisplayItem *)displayingSourceAt:(CGPoint)point {
NSArray<QGVAPMergedInfo *> *mergeInfos = self.hwd_configManager.model.mergedConfig[@(self.hwd_currentFrame.frameIndex)];
mergeInfos = [mergeInfos sortedArrayUsingComparator:^NSComparisonResult(QGVAPMergedInfo *obj1, QGVAPMergedInfo *obj2) {
return [@(obj2.renderIndex) compare:@(obj1.renderIndex)];
}];
CGSize renderingPixelSize = self.hwd_configManager.model.info.size;
if (renderingPixelSize.width <= 0 || renderingPixelSize.height <= 0) {
return nil;
}
__block QGVAPMergedInfo *targetMergeInfo = nil;
__block CGRect targetSourceFrame = CGRectZero;
CGSize viewSize = self.frame.size;
CGFloat xRatio = viewSize.width / renderingPixelSize.width;
CGFloat yRatio = viewSize.height / renderingPixelSize.height;
[mergeInfos enumerateObjectsUsingBlock:^(QGVAPMergedInfo * mergeInfo, NSUInteger idx, BOOL * _Nonnull stop) {
CGRect sourceRenderingRect = mergeInfo.renderRect;
CGRect sourceRenderingFrame = CGRectMake(CGRectGetMinX(sourceRenderingRect) * xRatio, CGRectGetMinY(sourceRenderingRect) * yRatio, CGRectGetWidth(sourceRenderingRect) * xRatio, CGRectGetHeight(sourceRenderingRect) * yRatio);
BOOL inside = CGRectContainsPoint(sourceRenderingFrame, point);
if (inside) {
targetMergeInfo = mergeInfo;
targetSourceFrame = sourceRenderingFrame;
*stop = YES;
}
}];
if (!targetMergeInfo) {
return nil;
}
QGVAPSourceDisplayItem *diplayItem = [QGVAPSourceDisplayItem new];
diplayItem.sourceInfo = targetMergeInfo.source;
diplayItem.frame = targetSourceFrame;
return diplayItem;
}
@end
@implementation UIView (VAPMask)
- (void)setVap_maskInfo:(QGVAPMaskInfo *)vap_maskInfo {
objc_setAssociatedObject(self, @"VAPMaskInfo", vap_maskInfo, OBJC_ASSOCIATION_RETAIN);
[self.vap_metalView setMaskInfo:vap_maskInfo];
}
- (QGVAPMaskInfo *)vap_maskInfo {
return objc_getAssociatedObject(self, @"VAPMaskInfo");
}
@end
@implementation UIView (MP4HWDDeprecated)
/**
见playHWDMP4:blendMode:repeatCount:delegate:
@param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
播放一遍,alpha数据在左边,设置回调
*/
- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:fps blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:0 delegate:delegate];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
@param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
alpha数据在左边
*/
- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:fps blendMode:QGHWDTextureBlendMode_AlphaLeft repeatCount:repeatCount delegate:delegate];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
播放一遍
*/
- (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:0 blendMode:mode repeatCount:0 delegate:delegate];
}
/**
利用GPU解码并播放mp4-h.264素材,在模拟器中会直接失败无法播放。
@param filePath mp4s素材本地地址
@param mode 确定素材中alpha通道数据位置,默认QGHWDTextureBlendMode_AlphaLeft
@param repeatCount 重复播放次数,若repeatCount==n, 则播放n+1次;若repeatCount==-1,则循环播放.
@param delegate 播放回调,⚠️注意:不在主线程回调
@note 素材文件需要按照规范生成
*/
- (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:0 blendMode:mode repeatCount:repeatCount delegate:delegate];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
@param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
播放一遍
*/
- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:fps blendMode:mode repeatCount:0 delegate:delegate];
}
/**
见playHWDMP4:blendMode:repeatCount:delegate:
@param fps 每一帧播放时优先使用这个值确定帧展示时长;若不合法则使用mp4中的数据;若还是不合法则使用默认18
*/
- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id<HWDMP4PlayDelegate>)delegate {
[self p_playHWDMP4:filePath fps:fps blendMode:mode repeatCount:repeatCount delegate:delegate];
}
@end