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

723 lines
29 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.

// 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