diff --git a/CHANGELOG.md b/CHANGELOG.md index efde262..5f9520d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # DKWechatHelper ChangeLog +[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未签名包 +* 更新越狱源安装包 + [v1.0.6](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.6) / 2020-10-16 what's new * 朋友圈转发 @@ -7,7 +16,6 @@ what's new * 更新已注入助手的7.0.17未签名包 * 更新越狱源安装包 -# DKWechatHelper ChangeLog [v1.0.5](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.5) / 2020-09-29 what's new * 新增好友关系检测 diff --git a/README.md b/README.md index 280b64b..6953ab5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # DKWechatHelper -不止于抢红包,功能丰富的微信插件。本软件已成功运行在(iphone7-iphone11、iPad) +不止于抢红包,功能丰富的微信插件。本软件已成功运行在(iphone7-iphone12、iPad) 给个⭐️不迷路↗️ -[v1.0.6](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.5)适用微信7.0.17 +[v1.0.7](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.7)适用微信8.0.1 +[v1.0.6](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.6)适用微信7.0.17 [v1.0.5](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.5)适用微信7.0.15 [v1.0.4](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.4)适用微信7.0.14 [v1.0.3](https://github.com/DKWechatHelper/DKWechatHelper/releases/tag/1.0.3)适用微信7.0.13 @@ -12,7 +13,7 @@ 更多更新内容请查看[CHANGELOG](./CHANGELOG.md) 图片加载失败可以到[简书](https://www.jianshu.com/p/8f3eae328a20)查看,本文将在[简书](https://www.jianshu.com/p/8f3eae328a20)同步更新。 ## 特色 -1.完全融入微信7.0UI风格,沉浸式体验 +1.完全融入微信8.0UI风格,沉浸式体验 2.完全免费的全功能插件 3.非越狱可后台抢红包 4.安全高效源码公开 @@ -30,6 +31,8 @@ * [x] 集赞助手 * [x] 好友关系检测 * [x] 朋友圈转发 +* [x] 动态聊天背景 +* [x] 动态启动图 安装后在微信设置中点击“微信小助手” 更多请查看下图: @@ -38,33 +41,36 @@ ![IMG_0223](./IMG_0031.JPG) ## 使用: +⚠️⚠️⚠️下载默认密码:`dkjone` 1.源码学习 - 下载本项目,下载砸壳后的微信(7.0以上)(我给你准备了个 -* [7.0.0](https://pan.baidu.com/s/15pVma66Ea822YVGrBa2GHw) -* [7.0.2](https://pan.baidu.com/s/1SHZHfu94Z_jhCkaaFDx8pA) -* [7.0.8-*提取码:h54d*](https://pan.baidu.com/s/11VoUXPC4vb5zg8HzP3kC0Q) -* [7.0.11-*提取码: 2igg*](https://pan.baidu.com/s/1mU_mezsWhqL2-AY0PB-vVg) -* [7.0.13-*提取码: nx4m*](https://pan.baidu.com/s/1rqB0pV4zMEB6Z3VJTsTa8Q) -* [7.0.14](https://n802.com/file/24576815-452231690) -* [7.0.15](https://n802.com/file/24576815-463029595) + 下载本项目,下载砸壳后的微信(7.0以上)(我给你准备了个) +* [8.0.1](https://n802.com/f/24576815-480279421-6c84b9) * [7.0.17](https://n802.com/file/24576815-467161527) - +* [7.0.15](https://n802.com/file/24576815-463029595) +* [7.0.14](https://n802.com/file/24576815-452231690) +* [7.0.13-*提取码: nx4m*](https://pan.baidu.com/s/1rqB0pV4zMEB6Z3VJTsTa8Q) +* [7.0.11-*提取码: 2igg*](https://pan.baidu.com/s/1mU_mezsWhqL2-AY0PB-vVg) +* [7.0.8-*提取码:h54d*](https://pan.baidu.com/s/11VoUXPC4vb5zg8HzP3kC0Q) +* [7.0.2](https://pan.baidu.com/s/1SHZHfu94Z_jhCkaaFDx8pA) +* [7.0.0](https://pan.baidu.com/s/15pVma66Ea822YVGrBa2GHw) 将下载的微信放到'/dkhelper/dkhelper/TargetApp/'目录下该目录下有一个空白文件’put ipa or app here‘,和它放在同一文件夹下即可,修改项目BundleId及签名信息,选择真机设备运行即可 2.开发者: 下载以下ipa后重签名应用: +* [未签名版8.0.1](https://n802.com/f/24576815-480300249-d79448) +* [未签名版7.0.17](https://n802.com/file/24576815-467161662) +* [未签名版7.0.15](https://n802.com/file/24576815-463034104) +* [未签名版7.0.14](https://n802.com/file/24576815-452232682) +* [未签名版7.0.13-*提取码: fhes*](https://pan.baidu.com/s/1DgSl5u0Gip3cNdqZmRFEWw) +* [未签名版7.0.11-*提取码: fe9f*](https://pan.baidu.com/s/1sOPCqnCPxSdIKq7TKuHK9g) * [未签名版7.0.0](https://pan.baidu.com/s/1-zEUQRGn3H4bZVqHpyffzQ) -* [未签名版7.0.11-*提取码: fe9f*](https://pan.baidu.com/s/1sOPCqnCPxSdIKq7TKuHK9g) -* [未签名版7.0.13-*提取码: fhes*](https://pan.baidu.com/s/1DgSl5u0Gip3cNdqZmRFEWw) -* [未签名版7.0.14](https://n802.com/file/24576815-452232682) -* [未签名版7.0.15](https://n802.com/file/24576815-463034104) -* [未签名版7.0.17](https://n802.com/file/24576815-467161662) - 注:推荐使用[ios-app-signer](https://github.com/DanTheMan827/ios-app-signer)签名,使用方法自行百度 + + 注:推荐使用[ios-app-signer](https://github.com/DanTheMan827/ios-app-signer)签名,使用方法自行百度 3.普通用户: 前往以下地址下载:上方未签名版 - 下载后使用 [Cydia Impactor](http://www.cydiaimpactor.com/)安装 + 下载后使用 [Cydia Impactor](http://www.cydiaimpactor.com/)或者`爱思助手`或者[AltStore](https://github.com/rileytestut/AltStore)安装 4.越狱用户: 添加源`https://dkjone.github.io/`下载 diff --git a/dkhelper/dkhelper.xcodeproj/project.pbxproj b/dkhelper/dkhelper.xcodeproj/project.pbxproj index ab80041..e5c86f6 100644 --- a/dkhelper/dkhelper.xcodeproj/project.pbxproj +++ b/dkhelper/dkhelper.xcodeproj/project.pbxproj @@ -51,6 +51,116 @@ 114CCC9D21F831EB00A4646E /* DKHelperSettingController.h in Headers */ = {isa = PBXBuildFile; fileRef = 114CCC9B21F831EA00A4646E /* DKHelperSettingController.h */; }; 114CCCA021F8323E00A4646E /* DKGroupFilterController.h in Headers */ = {isa = PBXBuildFile; fileRef = 114CCC9E21F8323D00A4646E /* DKGroupFilterController.h */; }; 114CCCA121F8323E00A4646E /* DKGroupFilterController.m in Sources */ = {isa = PBXBuildFile; fileRef = 114CCC9F21F8323D00A4646E /* DKGroupFilterController.m */; }; + 116B4CF925BE91990079C6FE /* DKLaunchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4CF525BE91980079C6FE /* DKLaunchViewController.m */; }; + 116B4CFA25BE91990079C6FE /* DKLaunchHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4CF625BE91980079C6FE /* DKLaunchHelper.m */; }; + 116B4CFB25BE91990079C6FE /* DKLaunchHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4CF725BE91980079C6FE /* DKLaunchHelper.h */; }; + 116B4CFC25BE91990079C6FE /* DKLaunchViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4CF825BE91990079C6FE /* DKLaunchViewController.h */; }; + 116B4D7825BE92470079C6FE /* QGHWDShaderTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D1D25BE92460079C6FE /* QGHWDShaderTypes.h */; }; + 116B4D7925BE92470079C6FE /* QGHWDShaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D1E25BE92460079C6FE /* QGHWDShaders.metal */; }; + 116B4D7A25BE92470079C6FE /* QGHWDMetalShaderSourceDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D1F25BE92460079C6FE /* QGHWDMetalShaderSourceDefine.h */; }; + 116B4D7B25BE92470079C6FE /* QGMP4Box.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2225BE92460079C6FE /* QGMP4Box.m */; }; + 116B4D7C25BE92470079C6FE /* QGMP4Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2325BE92460079C6FE /* QGMP4Parser.h */; }; + 116B4D7D25BE92470079C6FE /* QGMP4Parser.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2425BE92460079C6FE /* QGMP4Parser.m */; }; + 116B4D7E25BE92470079C6FE /* QGMP4Box.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2525BE92460079C6FE /* QGMP4Box.h */; }; + 116B4D7F25BE92470079C6FE /* UIView+VAP.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2625BE92460079C6FE /* UIView+VAP.m */; }; + 116B4D8025BE92470079C6FE /* QGVAPMetalShaderFunctionLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2825BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.m */; }; + 116B4D8125BE92470079C6FE /* QGVAPMetalUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2925BE92460079C6FE /* QGVAPMetalUtil.h */; }; + 116B4D8225BE92470079C6FE /* QGVAPSafeMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2A25BE92460079C6FE /* QGVAPSafeMutableDictionary.h */; }; + 116B4D8325BE92470079C6FE /* QGVAPSafeMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2B25BE92460079C6FE /* QGVAPSafeMutableArray.h */; }; + 116B4D8425BE92470079C6FE /* QGVAPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2D25BE92460079C6FE /* QGVAPLogger.m */; }; + 116B4D8525BE92470079C6FE /* QGVAPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D2E25BE92460079C6FE /* QGVAPLogger.h */; }; + 116B4D8625BE92470079C6FE /* QGVAPWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D2F25BE92460079C6FE /* QGVAPWeakProxy.m */; }; + 116B4D8725BE92470079C6FE /* QGVAPMetalUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3025BE92460079C6FE /* QGVAPMetalUtil.m */; }; + 116B4D8825BE92470079C6FE /* QGVAPMetalShaderFunctionLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3125BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.h */; }; + 116B4D8925BE92470079C6FE /* QGVAPSafeMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3225BE92460079C6FE /* QGVAPSafeMutableArray.m */; }; + 116B4D8A25BE92470079C6FE /* QGVAPSafeMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3325BE92460079C6FE /* QGVAPSafeMutableDictionary.m */; }; + 116B4D8B25BE92470079C6FE /* QGVAPWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3425BE92460079C6FE /* QGVAPWeakProxy.h */; }; + 116B4D8C25BE92470079C6FE /* NSDictionary+VAPUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3625BE92470079C6FE /* NSDictionary+VAPUtil.h */; }; + 116B4D8D25BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3725BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.m */; }; + 116B4D8E25BE92470079C6FE /* NSArray+VAPUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3825BE92470079C6FE /* NSArray+VAPUtil.m */; }; + 116B4D8F25BE92470079C6FE /* UIDevice+VAPUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3925BE92470079C6FE /* UIDevice+VAPUtil.h */; }; + 116B4D9025BE92470079C6FE /* UIColor+VAPUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3A25BE92470079C6FE /* UIColor+VAPUtil.h */; }; + 116B4D9125BE92470079C6FE /* UIView+MP4HWDecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3B25BE92470079C6FE /* UIView+MP4HWDecode.h */; }; + 116B4D9225BE92470079C6FE /* UIGestureRecognizer+VAPUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3C25BE92470079C6FE /* UIGestureRecognizer+VAPUtil.h */; }; + 116B4D9325BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D3D25BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.h */; }; + 116B4D9425BE92470079C6FE /* NSDictionary+VAPUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3E25BE92470079C6FE /* NSDictionary+VAPUtil.m */; }; + 116B4D9525BE92470079C6FE /* UIDevice+VAPUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D3F25BE92470079C6FE /* UIDevice+VAPUtil.m */; }; + 116B4D9625BE92470079C6FE /* NSArray+VAPUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4025BE92470079C6FE /* NSArray+VAPUtil.h */; }; + 116B4D9725BE92470079C6FE /* UIColor+VAPUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4125BE92470079C6FE /* UIColor+VAPUtil.m */; }; + 116B4D9825BE92470079C6FE /* UIView+MP4HWDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4225BE92470079C6FE /* UIView+MP4HWDecode.m */; }; + 116B4D9925BE92470079C6FE /* UIGestureRecognizer+VAPUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4325BE92470079C6FE /* UIGestureRecognizer+VAPUtil.m */; }; + 116B4D9A25BE92470079C6FE /* QGMP4AnimatedImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4525BE92470079C6FE /* QGMP4AnimatedImageFrame.m */; }; + 116B4D9B25BE92470079C6FE /* QGBaseDFileInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4625BE92470079C6FE /* QGBaseDFileInfo.h */; }; + 116B4D9C25BE92470079C6FE /* QGVAPMaskInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4725BE92470079C6FE /* QGVAPMaskInfo.m */; }; + 116B4D9D25BE92470079C6FE /* QGBaseAnimatedImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4825BE92470079C6FE /* QGBaseAnimatedImageFrame.h */; }; + 116B4D9E25BE92470079C6FE /* QGVAPTextureLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4925BE92470079C6FE /* QGVAPTextureLoader.m */; }; + 116B4D9F25BE92470079C6FE /* QGMP4HWDFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4A25BE92470079C6FE /* QGMP4HWDFileInfo.m */; }; + 116B4DA025BE92470079C6FE /* QGVAPConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4B25BE92470079C6FE /* QGVAPConfigModel.h */; }; + 116B4DA125BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4C25BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.h */; }; + 116B4DA225BE92470079C6FE /* QGBaseDFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D4D25BE92470079C6FE /* QGBaseDFileInfo.m */; }; + 116B4DA325BE92470079C6FE /* QGMP4AnimatedImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4E25BE92470079C6FE /* QGMP4AnimatedImageFrame.h */; }; + 116B4DA425BE92470079C6FE /* QGVAPMaskInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D4F25BE92470079C6FE /* QGVAPMaskInfo.h */; }; + 116B4DA525BE92470079C6FE /* QGVAPConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D5025BE92470079C6FE /* QGVAPConfigModel.m */; }; + 116B4DA625BE92470079C6FE /* QGMP4HWDFileInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5125BE92470079C6FE /* QGMP4HWDFileInfo.h */; }; + 116B4DA725BE92470079C6FE /* QGVAPTextureLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5225BE92470079C6FE /* QGVAPTextureLoader.h */; }; + 116B4DA825BE92470079C6FE /* QGBaseAnimatedImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D5325BE92470079C6FE /* QGBaseAnimatedImageFrame.m */; }; + 116B4DA925BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D5425BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.m */; }; + 116B4DAA25BE92470079C6FE /* VAPMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5525BE92470079C6FE /* VAPMacros.h */; }; + 116B4DAB25BE92470079C6FE /* UIView+VAP.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5625BE92470079C6FE /* UIView+VAP.h */; }; + 116B4DAC25BE92470079C6FE /* QGVAPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5725BE92470079C6FE /* QGVAPlayer.h */; }; + 116B4DAD25BE92470079C6FE /* QGAnimatedImageDecodeConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D5925BE92470079C6FE /* QGAnimatedImageDecodeConfig.m */; }; + 116B4DAE25BE92470079C6FE /* QGVAPConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D5A25BE92470079C6FE /* QGVAPConfigManager.m */; }; + 116B4DAF25BE92470079C6FE /* QGAnimatedImageDecodeThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5B25BE92470079C6FE /* QGAnimatedImageDecodeThread.h */; }; + 116B4DB025BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5C25BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.h */; }; + 116B4DB125BE92470079C6FE /* QGAnimatedImageBufferManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5D25BE92470079C6FE /* QGAnimatedImageBufferManager.h */; }; + 116B4DB225BE92470079C6FE /* QGAnimatedImageDecodeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5E25BE92470079C6FE /* QGAnimatedImageDecodeManager.h */; }; + 116B4DB325BE92470079C6FE /* QGAnimatedImageDecodeConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D5F25BE92470079C6FE /* QGAnimatedImageDecodeConfig.h */; }; + 116B4DB425BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6025BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.m */; }; + 116B4DB525BE92470079C6FE /* QGBaseDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6225BE92470079C6FE /* QGBaseDecoder.m */; }; + 116B4DB625BE92470079C6FE /* QGMP4FrameHWDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6325BE92470079C6FE /* QGMP4FrameHWDecoder.m */; }; + 116B4DB725BE92470079C6FE /* QGBaseDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D6425BE92470079C6FE /* QGBaseDecoder.h */; }; + 116B4DB825BE92470079C6FE /* QGMP4FrameHWDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D6525BE92470079C6FE /* QGMP4FrameHWDecoder.h */; }; + 116B4DB925BE92470079C6FE /* QGAnimatedImageDecodeThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6625BE92470079C6FE /* QGAnimatedImageDecodeThread.m */; }; + 116B4DBA25BE92470079C6FE /* QGVAPConfigManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D6725BE92470079C6FE /* QGVAPConfigManager.h */; }; + 116B4DBB25BE92470079C6FE /* QGAnimatedImageBufferManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6825BE92470079C6FE /* QGAnimatedImageBufferManager.m */; }; + 116B4DBC25BE92470079C6FE /* QGAnimatedImageDecodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6925BE92470079C6FE /* QGAnimatedImageDecodeManager.m */; }; + 116B4DBD25BE92470079C6FE /* QGHWDMetalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6C25BE92470079C6FE /* QGHWDMetalView.m */; }; + 116B4DBE25BE92470079C6FE /* QGVAPMetalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6E25BE92470079C6FE /* QGVAPMetalView.m */; }; + 116B4DBF25BE92470079C6FE /* QGVAPMetalRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D6F25BE92470079C6FE /* QGVAPMetalRenderer.m */; }; + 116B4DC025BE92470079C6FE /* QGVAPMetalView.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D7025BE92470079C6FE /* QGVAPMetalView.h */; }; + 116B4DC125BE92470079C6FE /* QGVAPMetalRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D7125BE92470079C6FE /* QGVAPMetalRenderer.h */; }; + 116B4DC225BE92470079C6FE /* QGHWDMetalRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D7225BE92470079C6FE /* QGHWDMetalRenderer.m */; }; + 116B4DC325BE92470079C6FE /* QGHWDMetalView.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D7325BE92470079C6FE /* QGHWDMetalView.h */; }; + 116B4DC425BE92470079C6FE /* QGHWDMetalRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D7425BE92470079C6FE /* QGHWDMetalRenderer.h */; }; + 116B4DC525BE92470079C6FE /* QGHWDMP4OpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 116B4D7625BE92470079C6FE /* QGHWDMP4OpenGLView.m */; }; + 116B4DC625BE92470079C6FE /* QGHWDMP4OpenGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = 116B4D7725BE92470079C6FE /* QGHWDMP4OpenGLView.h */; }; + 116B4DE425BE98030079C6FE /* uranus.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DC925BE98030079C6FE /* uranus.jpg */; }; + 116B4DE525BE98030079C6FE /* venus.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCA25BE98030079C6FE /* venus.jpg */; }; + 116B4DE625BE98030079C6FE /* nepture.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCB25BE98030079C6FE /* nepture.jpg */; }; + 116B4DE725BE98030079C6FE /* venusVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCC25BE98030079C6FE /* venusVap.mp4 */; }; + 116B4DE825BE98030079C6FE /* mars.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCD25BE98030079C6FE /* mars.jpg */; }; + 116B4DE925BE98030079C6FE /* sature.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCE25BE98030079C6FE /* sature.jpg */; }; + 116B4DEA25BE98030079C6FE /* launchBgm.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DCF25BE98030079C6FE /* launchBgm.mp3 */; }; + 116B4DEB25BE98030079C6FE /* marsVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD025BE98030079C6FE /* marsVap.mp4 */; }; + 116B4DEC25BE98030079C6FE /* blackholeVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD125BE98030079C6FE /* blackholeVap.mp4 */; }; + 116B4DED25BE98030079C6FE /* moonVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD225BE98030079C6FE /* moonVap.mp4 */; }; + 116B4DEE25BE98030079C6FE /* earth.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD325BE98030079C6FE /* earth.jpg */; }; + 116B4DEF25BE98030079C6FE /* blackhole.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD425BE98030079C6FE /* blackhole.jpg */; }; + 116B4DF025BE98030079C6FE /* earthVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD525BE98030079C6FE /* earthVap.mp4 */; }; + 116B4DF125BE98030079C6FE /* moon.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD625BE98030079C6FE /* moon.jpg */; }; + 116B4DF225BE98030079C6FE /* uranusVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD725BE98030079C6FE /* uranusVap.mp4 */; }; + 116B4DF325BE98030079C6FE /* satureVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD825BE98030079C6FE /* satureVap.mp4 */; }; + 116B4DF425BE98030079C6FE /* neptureVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DD925BE98030079C6FE /* neptureVap.mp4 */; }; + 116B4DF525BE98030079C6FE /* pluto.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDA25BE98030079C6FE /* pluto.jpg */; }; + 116B4DF625BE98030079C6FE /* sedna.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDB25BE98030079C6FE /* sedna.jpg */; }; + 116B4DF725BE98030079C6FE /* sun.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDC25BE98030079C6FE /* sun.jpg */; }; + 116B4DF825BE98030079C6FE /* jupitreVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDD25BE98030079C6FE /* jupitreVap.mp4 */; }; + 116B4DF925BE98030079C6FE /* mercuryVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDE25BE98030079C6FE /* mercuryVap.mp4 */; }; + 116B4DFA25BE98030079C6FE /* sednaVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DDF25BE98030079C6FE /* sednaVap.mp4 */; }; + 116B4DFB25BE98030079C6FE /* mercury.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DE025BE98030079C6FE /* mercury.jpg */; }; + 116B4DFC25BE98030079C6FE /* sunVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DE125BE98030079C6FE /* sunVap.mp4 */; }; + 116B4DFD25BE98030079C6FE /* plutoVap.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DE225BE98030079C6FE /* plutoVap.mp4 */; }; + 116B4DFE25BE98030079C6FE /* jupitre.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 116B4DE325BE98030079C6FE /* jupitre.jpg */; }; 11D27C9E25208FC800AE6337 /* NSArray+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 11D27C9C25208FC800AE6337 /* NSArray+Utils.h */; }; 11D27C9F25208FC800AE6337 /* NSArray+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 11D27C9D25208FC800AE6337 /* NSArray+Utils.m */; }; /* End PBXBuildFile section */ @@ -130,6 +240,116 @@ 114CCC9B21F831EA00A4646E /* DKHelperSettingController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DKHelperSettingController.h; sourceTree = ""; }; 114CCC9E21F8323D00A4646E /* DKGroupFilterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DKGroupFilterController.h; sourceTree = ""; }; 114CCC9F21F8323D00A4646E /* DKGroupFilterController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DKGroupFilterController.m; sourceTree = ""; }; + 116B4CF525BE91980079C6FE /* DKLaunchViewController.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = DKLaunchViewController.m; sourceTree = ""; }; + 116B4CF625BE91980079C6FE /* DKLaunchHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DKLaunchHelper.m; sourceTree = ""; }; + 116B4CF725BE91980079C6FE /* DKLaunchHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DKLaunchHelper.h; sourceTree = ""; }; + 116B4CF825BE91990079C6FE /* DKLaunchViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DKLaunchViewController.h; sourceTree = ""; }; + 116B4D1D25BE92460079C6FE /* QGHWDShaderTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGHWDShaderTypes.h; sourceTree = ""; }; + 116B4D1E25BE92460079C6FE /* QGHWDShaders.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = QGHWDShaders.metal; sourceTree = ""; }; + 116B4D1F25BE92460079C6FE /* QGHWDMetalShaderSourceDefine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGHWDMetalShaderSourceDefine.h; sourceTree = ""; }; + 116B4D2225BE92460079C6FE /* QGMP4Box.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGMP4Box.m; sourceTree = ""; }; + 116B4D2325BE92460079C6FE /* QGMP4Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGMP4Parser.h; sourceTree = ""; }; + 116B4D2425BE92460079C6FE /* QGMP4Parser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGMP4Parser.m; sourceTree = ""; }; + 116B4D2525BE92460079C6FE /* QGMP4Box.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGMP4Box.h; sourceTree = ""; }; + 116B4D2625BE92460079C6FE /* UIView+VAP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+VAP.m"; sourceTree = ""; }; + 116B4D2825BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPMetalShaderFunctionLoader.m; sourceTree = ""; }; + 116B4D2925BE92460079C6FE /* QGVAPMetalUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPMetalUtil.h; sourceTree = ""; }; + 116B4D2A25BE92460079C6FE /* QGVAPSafeMutableDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPSafeMutableDictionary.h; sourceTree = ""; }; + 116B4D2B25BE92460079C6FE /* QGVAPSafeMutableArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPSafeMutableArray.h; sourceTree = ""; }; + 116B4D2D25BE92460079C6FE /* QGVAPLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPLogger.m; sourceTree = ""; }; + 116B4D2E25BE92460079C6FE /* QGVAPLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPLogger.h; sourceTree = ""; }; + 116B4D2F25BE92460079C6FE /* QGVAPWeakProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPWeakProxy.m; sourceTree = ""; }; + 116B4D3025BE92460079C6FE /* QGVAPMetalUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPMetalUtil.m; sourceTree = ""; }; + 116B4D3125BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPMetalShaderFunctionLoader.h; sourceTree = ""; }; + 116B4D3225BE92460079C6FE /* QGVAPSafeMutableArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPSafeMutableArray.m; sourceTree = ""; }; + 116B4D3325BE92460079C6FE /* QGVAPSafeMutableDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPSafeMutableDictionary.m; sourceTree = ""; }; + 116B4D3425BE92460079C6FE /* QGVAPWeakProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPWeakProxy.h; sourceTree = ""; }; + 116B4D3625BE92470079C6FE /* NSDictionary+VAPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+VAPUtil.h"; sourceTree = ""; }; + 116B4D3725BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+VAPThreadSafe.m"; sourceTree = ""; }; + 116B4D3825BE92470079C6FE /* NSArray+VAPUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+VAPUtil.m"; sourceTree = ""; }; + 116B4D3925BE92470079C6FE /* UIDevice+VAPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+VAPUtil.h"; sourceTree = ""; }; + 116B4D3A25BE92470079C6FE /* UIColor+VAPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+VAPUtil.h"; sourceTree = ""; }; + 116B4D3B25BE92470079C6FE /* UIView+MP4HWDecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+MP4HWDecode.h"; sourceTree = ""; }; + 116B4D3C25BE92470079C6FE /* UIGestureRecognizer+VAPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIGestureRecognizer+VAPUtil.h"; sourceTree = ""; }; + 116B4D3D25BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+VAPThreadSafe.h"; sourceTree = ""; }; + 116B4D3E25BE92470079C6FE /* NSDictionary+VAPUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+VAPUtil.m"; sourceTree = ""; }; + 116B4D3F25BE92470079C6FE /* UIDevice+VAPUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+VAPUtil.m"; sourceTree = ""; }; + 116B4D4025BE92470079C6FE /* NSArray+VAPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+VAPUtil.h"; sourceTree = ""; }; + 116B4D4125BE92470079C6FE /* UIColor+VAPUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+VAPUtil.m"; sourceTree = ""; }; + 116B4D4225BE92470079C6FE /* UIView+MP4HWDecode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+MP4HWDecode.m"; sourceTree = ""; }; + 116B4D4325BE92470079C6FE /* UIGestureRecognizer+VAPUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+VAPUtil.m"; sourceTree = ""; }; + 116B4D4525BE92470079C6FE /* QGMP4AnimatedImageFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGMP4AnimatedImageFrame.m; sourceTree = ""; }; + 116B4D4625BE92470079C6FE /* QGBaseDFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGBaseDFileInfo.h; sourceTree = ""; }; + 116B4D4725BE92470079C6FE /* QGVAPMaskInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPMaskInfo.m; sourceTree = ""; }; + 116B4D4825BE92470079C6FE /* QGBaseAnimatedImageFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGBaseAnimatedImageFrame.h; sourceTree = ""; }; + 116B4D4925BE92470079C6FE /* QGVAPTextureLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPTextureLoader.m; sourceTree = ""; }; + 116B4D4A25BE92470079C6FE /* QGMP4HWDFileInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGMP4HWDFileInfo.m; sourceTree = ""; }; + 116B4D4B25BE92470079C6FE /* QGVAPConfigModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPConfigModel.h; sourceTree = ""; }; + 116B4D4C25BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "QGBaseAnimatedImageFrame+Displaying.h"; sourceTree = ""; }; + 116B4D4D25BE92470079C6FE /* QGBaseDFileInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGBaseDFileInfo.m; sourceTree = ""; }; + 116B4D4E25BE92470079C6FE /* QGMP4AnimatedImageFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGMP4AnimatedImageFrame.h; sourceTree = ""; }; + 116B4D4F25BE92470079C6FE /* QGVAPMaskInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPMaskInfo.h; sourceTree = ""; }; + 116B4D5025BE92470079C6FE /* QGVAPConfigModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPConfigModel.m; sourceTree = ""; }; + 116B4D5125BE92470079C6FE /* QGMP4HWDFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGMP4HWDFileInfo.h; sourceTree = ""; }; + 116B4D5225BE92470079C6FE /* QGVAPTextureLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPTextureLoader.h; sourceTree = ""; }; + 116B4D5325BE92470079C6FE /* QGBaseAnimatedImageFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGBaseAnimatedImageFrame.m; sourceTree = ""; }; + 116B4D5425BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "QGBaseAnimatedImageFrame+Displaying.m"; sourceTree = ""; }; + 116B4D5525BE92470079C6FE /* VAPMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VAPMacros.h; sourceTree = ""; }; + 116B4D5625BE92470079C6FE /* UIView+VAP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+VAP.h"; sourceTree = ""; }; + 116B4D5725BE92470079C6FE /* QGVAPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPlayer.h; sourceTree = ""; }; + 116B4D5925BE92470079C6FE /* QGAnimatedImageDecodeConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGAnimatedImageDecodeConfig.m; sourceTree = ""; }; + 116B4D5A25BE92470079C6FE /* QGVAPConfigManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPConfigManager.m; sourceTree = ""; }; + 116B4D5B25BE92470079C6FE /* QGAnimatedImageDecodeThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGAnimatedImageDecodeThread.h; sourceTree = ""; }; + 116B4D5C25BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGAnimatedImageDecodeThreadPool.h; sourceTree = ""; }; + 116B4D5D25BE92470079C6FE /* QGAnimatedImageBufferManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGAnimatedImageBufferManager.h; sourceTree = ""; }; + 116B4D5E25BE92470079C6FE /* QGAnimatedImageDecodeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGAnimatedImageDecodeManager.h; sourceTree = ""; }; + 116B4D5F25BE92470079C6FE /* QGAnimatedImageDecodeConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGAnimatedImageDecodeConfig.h; sourceTree = ""; }; + 116B4D6025BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGAnimatedImageDecodeThreadPool.m; sourceTree = ""; }; + 116B4D6225BE92470079C6FE /* QGBaseDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGBaseDecoder.m; sourceTree = ""; }; + 116B4D6325BE92470079C6FE /* QGMP4FrameHWDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGMP4FrameHWDecoder.m; sourceTree = ""; }; + 116B4D6425BE92470079C6FE /* QGBaseDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGBaseDecoder.h; sourceTree = ""; }; + 116B4D6525BE92470079C6FE /* QGMP4FrameHWDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGMP4FrameHWDecoder.h; sourceTree = ""; }; + 116B4D6625BE92470079C6FE /* QGAnimatedImageDecodeThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGAnimatedImageDecodeThread.m; sourceTree = ""; }; + 116B4D6725BE92470079C6FE /* QGVAPConfigManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPConfigManager.h; sourceTree = ""; }; + 116B4D6825BE92470079C6FE /* QGAnimatedImageBufferManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGAnimatedImageBufferManager.m; sourceTree = ""; }; + 116B4D6925BE92470079C6FE /* QGAnimatedImageDecodeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGAnimatedImageDecodeManager.m; sourceTree = ""; }; + 116B4D6C25BE92470079C6FE /* QGHWDMetalView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGHWDMetalView.m; sourceTree = ""; }; + 116B4D6E25BE92470079C6FE /* QGVAPMetalView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPMetalView.m; sourceTree = ""; }; + 116B4D6F25BE92470079C6FE /* QGVAPMetalRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGVAPMetalRenderer.m; sourceTree = ""; }; + 116B4D7025BE92470079C6FE /* QGVAPMetalView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPMetalView.h; sourceTree = ""; }; + 116B4D7125BE92470079C6FE /* QGVAPMetalRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGVAPMetalRenderer.h; sourceTree = ""; }; + 116B4D7225BE92470079C6FE /* QGHWDMetalRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGHWDMetalRenderer.m; sourceTree = ""; }; + 116B4D7325BE92470079C6FE /* QGHWDMetalView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGHWDMetalView.h; sourceTree = ""; }; + 116B4D7425BE92470079C6FE /* QGHWDMetalRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGHWDMetalRenderer.h; sourceTree = ""; }; + 116B4D7625BE92470079C6FE /* QGHWDMP4OpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QGHWDMP4OpenGLView.m; sourceTree = ""; }; + 116B4D7725BE92470079C6FE /* QGHWDMP4OpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QGHWDMP4OpenGLView.h; sourceTree = ""; }; + 116B4DC925BE98030079C6FE /* uranus.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = uranus.jpg; sourceTree = ""; }; + 116B4DCA25BE98030079C6FE /* venus.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = venus.jpg; sourceTree = ""; }; + 116B4DCB25BE98030079C6FE /* nepture.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = nepture.jpg; sourceTree = ""; }; + 116B4DCC25BE98030079C6FE /* venusVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = venusVap.mp4; sourceTree = ""; }; + 116B4DCD25BE98030079C6FE /* mars.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = mars.jpg; sourceTree = ""; }; + 116B4DCE25BE98030079C6FE /* sature.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sature.jpg; sourceTree = ""; }; + 116B4DCF25BE98030079C6FE /* launchBgm.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = launchBgm.mp3; sourceTree = ""; }; + 116B4DD025BE98030079C6FE /* marsVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = marsVap.mp4; sourceTree = ""; }; + 116B4DD125BE98030079C6FE /* blackholeVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = blackholeVap.mp4; sourceTree = ""; }; + 116B4DD225BE98030079C6FE /* moonVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = moonVap.mp4; sourceTree = ""; }; + 116B4DD325BE98030079C6FE /* earth.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = earth.jpg; sourceTree = ""; }; + 116B4DD425BE98030079C6FE /* blackhole.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = blackhole.jpg; sourceTree = ""; }; + 116B4DD525BE98030079C6FE /* earthVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = earthVap.mp4; sourceTree = ""; }; + 116B4DD625BE98030079C6FE /* moon.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = moon.jpg; sourceTree = ""; }; + 116B4DD725BE98030079C6FE /* uranusVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = uranusVap.mp4; sourceTree = ""; }; + 116B4DD825BE98030079C6FE /* satureVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = satureVap.mp4; sourceTree = ""; }; + 116B4DD925BE98030079C6FE /* neptureVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = neptureVap.mp4; sourceTree = ""; }; + 116B4DDA25BE98030079C6FE /* pluto.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = pluto.jpg; sourceTree = ""; }; + 116B4DDB25BE98030079C6FE /* sedna.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sedna.jpg; sourceTree = ""; }; + 116B4DDC25BE98030079C6FE /* sun.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sun.jpg; sourceTree = ""; }; + 116B4DDD25BE98030079C6FE /* jupitreVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = jupitreVap.mp4; sourceTree = ""; }; + 116B4DDE25BE98030079C6FE /* mercuryVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = mercuryVap.mp4; sourceTree = ""; }; + 116B4DDF25BE98030079C6FE /* sednaVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = sednaVap.mp4; sourceTree = ""; }; + 116B4DE025BE98030079C6FE /* mercury.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = mercury.jpg; sourceTree = ""; }; + 116B4DE125BE98030079C6FE /* sunVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = sunVap.mp4; sourceTree = ""; }; + 116B4DE225BE98030079C6FE /* plutoVap.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = plutoVap.mp4; sourceTree = ""; }; + 116B4DE325BE98030079C6FE /* jupitre.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = jupitre.jpg; sourceTree = ""; }; 11D27C9C25208FC800AE6337 /* NSArray+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSArray+Utils.h"; sourceTree = ""; }; 11D27C9D25208FC800AE6337 /* NSArray+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Utils.m"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -222,10 +442,14 @@ 114CCC4321F817D500A4646E /* dkhelperDylib */ = { isa = PBXGroup; children = ( + 116B4DC825BE98030079C6FE /* vapFiles */, + 116B4D1B25BE92460079C6FE /* QGVAPlayer */, 114CCC8921F8307500A4646E /* wechatHeaders */, 114CCC8821F8306500A4646E /* MyUtils */, 114CCC7C21F817D500A4646E /* dkhelperDylib.h */, 114CCC7E21F817D500A4646E /* dkhelperDylib.m */, + 116B4CF825BE91990079C6FE /* DKLaunchViewController.h */, + 116B4CF525BE91980079C6FE /* DKLaunchViewController.m */, 114CCC9B21F831EA00A4646E /* DKHelperSettingController.h */, 114CCC9A21F831EA00A4646E /* DKHelperSettingController.m */, 114CCC9E21F8323D00A4646E /* DKGroupFilterController.h */, @@ -321,6 +545,8 @@ 114CCC8821F8306500A4646E /* MyUtils */ = { isa = PBXGroup; children = ( + 116B4CF725BE91980079C6FE /* DKLaunchHelper.h */, + 116B4CF625BE91980079C6FE /* DKLaunchHelper.m */, 114CCC9621F831D400A4646E /* DKHelperConfig.h */, 114CCC9721F831D400A4646E /* DKHelperConfig.m */, 114CCC9221F831C200A4646E /* DKHelper.h */, @@ -342,6 +568,231 @@ path = wechatHeaders; sourceTree = ""; }; + 116B4D1B25BE92460079C6FE /* QGVAPlayer */ = { + isa = PBXGroup; + children = ( + 116B4D1C25BE92460079C6FE /* Shaders */, + 116B4D2025BE92460079C6FE /* class */, + ); + path = QGVAPlayer; + sourceTree = ""; + }; + 116B4D1C25BE92460079C6FE /* Shaders */ = { + isa = PBXGroup; + children = ( + 116B4D1D25BE92460079C6FE /* QGHWDShaderTypes.h */, + 116B4D1E25BE92460079C6FE /* QGHWDShaders.metal */, + 116B4D1F25BE92460079C6FE /* QGHWDMetalShaderSourceDefine.h */, + ); + path = Shaders; + sourceTree = ""; + }; + 116B4D2025BE92460079C6FE /* class */ = { + isa = PBXGroup; + children = ( + 116B4D2125BE92460079C6FE /* MP4Parser */, + 116B4D2625BE92460079C6FE /* UIView+VAP.m */, + 116B4D2725BE92460079C6FE /* Utils */, + 116B4D4425BE92470079C6FE /* Models */, + 116B4D5525BE92470079C6FE /* VAPMacros.h */, + 116B4D5625BE92470079C6FE /* UIView+VAP.h */, + 116B4D5725BE92470079C6FE /* QGVAPlayer.h */, + 116B4D5825BE92470079C6FE /* Controllers */, + 116B4D6A25BE92470079C6FE /* Views */, + ); + path = class; + sourceTree = ""; + }; + 116B4D2125BE92460079C6FE /* MP4Parser */ = { + isa = PBXGroup; + children = ( + 116B4D2225BE92460079C6FE /* QGMP4Box.m */, + 116B4D2325BE92460079C6FE /* QGMP4Parser.h */, + 116B4D2425BE92460079C6FE /* QGMP4Parser.m */, + 116B4D2525BE92460079C6FE /* QGMP4Box.h */, + ); + path = MP4Parser; + sourceTree = ""; + }; + 116B4D2725BE92460079C6FE /* Utils */ = { + isa = PBXGroup; + children = ( + 116B4D2825BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.m */, + 116B4D2925BE92460079C6FE /* QGVAPMetalUtil.h */, + 116B4D2A25BE92460079C6FE /* QGVAPSafeMutableDictionary.h */, + 116B4D2B25BE92460079C6FE /* QGVAPSafeMutableArray.h */, + 116B4D2C25BE92460079C6FE /* Logger */, + 116B4D2F25BE92460079C6FE /* QGVAPWeakProxy.m */, + 116B4D3025BE92460079C6FE /* QGVAPMetalUtil.m */, + 116B4D3125BE92460079C6FE /* QGVAPMetalShaderFunctionLoader.h */, + 116B4D3225BE92460079C6FE /* QGVAPSafeMutableArray.m */, + 116B4D3325BE92460079C6FE /* QGVAPSafeMutableDictionary.m */, + 116B4D3425BE92460079C6FE /* QGVAPWeakProxy.h */, + 116B4D3525BE92470079C6FE /* Categorys */, + ); + path = Utils; + sourceTree = ""; + }; + 116B4D2C25BE92460079C6FE /* Logger */ = { + isa = PBXGroup; + children = ( + 116B4D2D25BE92460079C6FE /* QGVAPLogger.m */, + 116B4D2E25BE92460079C6FE /* QGVAPLogger.h */, + ); + path = Logger; + sourceTree = ""; + }; + 116B4D3525BE92470079C6FE /* Categorys */ = { + isa = PBXGroup; + children = ( + 116B4D3625BE92470079C6FE /* NSDictionary+VAPUtil.h */, + 116B4D3725BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.m */, + 116B4D3825BE92470079C6FE /* NSArray+VAPUtil.m */, + 116B4D3925BE92470079C6FE /* UIDevice+VAPUtil.h */, + 116B4D3A25BE92470079C6FE /* UIColor+VAPUtil.h */, + 116B4D3B25BE92470079C6FE /* UIView+MP4HWDecode.h */, + 116B4D3C25BE92470079C6FE /* UIGestureRecognizer+VAPUtil.h */, + 116B4D3D25BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.h */, + 116B4D3E25BE92470079C6FE /* NSDictionary+VAPUtil.m */, + 116B4D3F25BE92470079C6FE /* UIDevice+VAPUtil.m */, + 116B4D4025BE92470079C6FE /* NSArray+VAPUtil.h */, + 116B4D4125BE92470079C6FE /* UIColor+VAPUtil.m */, + 116B4D4225BE92470079C6FE /* UIView+MP4HWDecode.m */, + 116B4D4325BE92470079C6FE /* UIGestureRecognizer+VAPUtil.m */, + ); + path = Categorys; + sourceTree = ""; + }; + 116B4D4425BE92470079C6FE /* Models */ = { + isa = PBXGroup; + children = ( + 116B4D4525BE92470079C6FE /* QGMP4AnimatedImageFrame.m */, + 116B4D4625BE92470079C6FE /* QGBaseDFileInfo.h */, + 116B4D4725BE92470079C6FE /* QGVAPMaskInfo.m */, + 116B4D4825BE92470079C6FE /* QGBaseAnimatedImageFrame.h */, + 116B4D4925BE92470079C6FE /* QGVAPTextureLoader.m */, + 116B4D4A25BE92470079C6FE /* QGMP4HWDFileInfo.m */, + 116B4D4B25BE92470079C6FE /* QGVAPConfigModel.h */, + 116B4D4C25BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.h */, + 116B4D4D25BE92470079C6FE /* QGBaseDFileInfo.m */, + 116B4D4E25BE92470079C6FE /* QGMP4AnimatedImageFrame.h */, + 116B4D4F25BE92470079C6FE /* QGVAPMaskInfo.h */, + 116B4D5025BE92470079C6FE /* QGVAPConfigModel.m */, + 116B4D5125BE92470079C6FE /* QGMP4HWDFileInfo.h */, + 116B4D5225BE92470079C6FE /* QGVAPTextureLoader.h */, + 116B4D5325BE92470079C6FE /* QGBaseAnimatedImageFrame.m */, + 116B4D5425BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.m */, + ); + path = Models; + sourceTree = ""; + }; + 116B4D5825BE92470079C6FE /* Controllers */ = { + isa = PBXGroup; + children = ( + 116B4D5925BE92470079C6FE /* QGAnimatedImageDecodeConfig.m */, + 116B4D5A25BE92470079C6FE /* QGVAPConfigManager.m */, + 116B4D5B25BE92470079C6FE /* QGAnimatedImageDecodeThread.h */, + 116B4D5C25BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.h */, + 116B4D5D25BE92470079C6FE /* QGAnimatedImageBufferManager.h */, + 116B4D5E25BE92470079C6FE /* QGAnimatedImageDecodeManager.h */, + 116B4D5F25BE92470079C6FE /* QGAnimatedImageDecodeConfig.h */, + 116B4D6025BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.m */, + 116B4D6125BE92470079C6FE /* Decoders */, + 116B4D6625BE92470079C6FE /* QGAnimatedImageDecodeThread.m */, + 116B4D6725BE92470079C6FE /* QGVAPConfigManager.h */, + 116B4D6825BE92470079C6FE /* QGAnimatedImageBufferManager.m */, + 116B4D6925BE92470079C6FE /* QGAnimatedImageDecodeManager.m */, + ); + path = Controllers; + sourceTree = ""; + }; + 116B4D6125BE92470079C6FE /* Decoders */ = { + isa = PBXGroup; + children = ( + 116B4D6225BE92470079C6FE /* QGBaseDecoder.m */, + 116B4D6325BE92470079C6FE /* QGMP4FrameHWDecoder.m */, + 116B4D6425BE92470079C6FE /* QGBaseDecoder.h */, + 116B4D6525BE92470079C6FE /* QGMP4FrameHWDecoder.h */, + ); + path = Decoders; + sourceTree = ""; + }; + 116B4D6A25BE92470079C6FE /* Views */ = { + isa = PBXGroup; + children = ( + 116B4D6B25BE92470079C6FE /* Metal */, + 116B4D7525BE92470079C6FE /* OpenGL */, + ); + path = Views; + sourceTree = ""; + }; + 116B4D6B25BE92470079C6FE /* Metal */ = { + isa = PBXGroup; + children = ( + 116B4D6C25BE92470079C6FE /* QGHWDMetalView.m */, + 116B4D6D25BE92470079C6FE /* Vapx */, + 116B4D7225BE92470079C6FE /* QGHWDMetalRenderer.m */, + 116B4D7325BE92470079C6FE /* QGHWDMetalView.h */, + 116B4D7425BE92470079C6FE /* QGHWDMetalRenderer.h */, + ); + path = Metal; + sourceTree = ""; + }; + 116B4D6D25BE92470079C6FE /* Vapx */ = { + isa = PBXGroup; + children = ( + 116B4D6E25BE92470079C6FE /* QGVAPMetalView.m */, + 116B4D6F25BE92470079C6FE /* QGVAPMetalRenderer.m */, + 116B4D7025BE92470079C6FE /* QGVAPMetalView.h */, + 116B4D7125BE92470079C6FE /* QGVAPMetalRenderer.h */, + ); + path = Vapx; + sourceTree = ""; + }; + 116B4D7525BE92470079C6FE /* OpenGL */ = { + isa = PBXGroup; + children = ( + 116B4D7625BE92470079C6FE /* QGHWDMP4OpenGLView.m */, + 116B4D7725BE92470079C6FE /* QGHWDMP4OpenGLView.h */, + ); + path = OpenGL; + sourceTree = ""; + }; + 116B4DC825BE98030079C6FE /* vapFiles */ = { + isa = PBXGroup; + children = ( + 116B4DC925BE98030079C6FE /* uranus.jpg */, + 116B4DCA25BE98030079C6FE /* venus.jpg */, + 116B4DCB25BE98030079C6FE /* nepture.jpg */, + 116B4DCC25BE98030079C6FE /* venusVap.mp4 */, + 116B4DCD25BE98030079C6FE /* mars.jpg */, + 116B4DCE25BE98030079C6FE /* sature.jpg */, + 116B4DCF25BE98030079C6FE /* launchBgm.mp3 */, + 116B4DD025BE98030079C6FE /* marsVap.mp4 */, + 116B4DD125BE98030079C6FE /* blackholeVap.mp4 */, + 116B4DD225BE98030079C6FE /* moonVap.mp4 */, + 116B4DD325BE98030079C6FE /* earth.jpg */, + 116B4DD425BE98030079C6FE /* blackhole.jpg */, + 116B4DD525BE98030079C6FE /* earthVap.mp4 */, + 116B4DD625BE98030079C6FE /* moon.jpg */, + 116B4DD725BE98030079C6FE /* uranusVap.mp4 */, + 116B4DD825BE98030079C6FE /* satureVap.mp4 */, + 116B4DD925BE98030079C6FE /* neptureVap.mp4 */, + 116B4DDA25BE98030079C6FE /* pluto.jpg */, + 116B4DDB25BE98030079C6FE /* sedna.jpg */, + 116B4DDC25BE98030079C6FE /* sun.jpg */, + 116B4DDD25BE98030079C6FE /* jupitreVap.mp4 */, + 116B4DDE25BE98030079C6FE /* mercuryVap.mp4 */, + 116B4DDF25BE98030079C6FE /* sednaVap.mp4 */, + 116B4DE025BE98030079C6FE /* mercury.jpg */, + 116B4DE125BE98030079C6FE /* sunVap.mp4 */, + 116B4DE225BE98030079C6FE /* plutoVap.mp4 */, + 116B4DE325BE98030079C6FE /* jupitre.jpg */, + ); + name = vapFiles; + path = ../../../../learn/launchDemo/launchDemo/vapFiles; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -349,24 +800,67 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 116B4D8125BE92470079C6FE /* QGVAPMetalUtil.h in Headers */, + 116B4D8325BE92470079C6FE /* QGVAPSafeMutableArray.h in Headers */, + 116B4DC025BE92470079C6FE /* QGVAPMetalView.h in Headers */, + 116B4DBA25BE92470079C6FE /* QGVAPConfigManager.h in Headers */, + 116B4D9125BE92470079C6FE /* UIView+MP4HWDecode.h in Headers */, + 116B4DAB25BE92470079C6FE /* UIView+VAP.h in Headers */, 114CCC7D21F817D500A4646E /* dkhelperDylib.h in Headers */, + 116B4DAF25BE92470079C6FE /* QGAnimatedImageDecodeThread.h in Headers */, 114CCC4E21F817D500A4646E /* MDCycriptManager.h in Headers */, + 116B4D8C25BE92470079C6FE /* NSDictionary+VAPUtil.h in Headers */, + 116B4D7C25BE92470079C6FE /* QGMP4Parser.h in Headers */, + 116B4D8F25BE92470079C6FE /* UIDevice+VAPUtil.h in Headers */, 114CCC5221F817D500A4646E /* MDMethodTrace.h in Headers */, 114CCC9821F831D400A4646E /* DKHelperConfig.h in Headers */, 114CCC8B21F830C800A4646E /* MMUICommonUtil.h in Headers */, + 116B4DB825BE92470079C6FE /* QGMP4FrameHWDecoder.h in Headers */, + 116B4D7A25BE92470079C6FE /* QGHWDMetalShaderSourceDefine.h in Headers */, + 116B4D9025BE92470079C6FE /* UIColor+VAPUtil.h in Headers */, + 116B4DAA25BE92470079C6FE /* VAPMacros.h in Headers */, 114CCCA021F8323E00A4646E /* DKGroupFilterController.h in Headers */, + 116B4DC625BE92470079C6FE /* QGHWDMP4OpenGLView.h in Headers */, + 116B4DC325BE92470079C6FE /* QGHWDMetalView.h in Headers */, + 116B4DB225BE92470079C6FE /* QGAnimatedImageDecodeManager.h in Headers */, + 116B4DB725BE92470079C6FE /* QGBaseDecoder.h in Headers */, 114CCC9421F831C200A4646E /* DKHelper.h in Headers */, + 116B4DC125BE92470079C6FE /* QGVAPMetalRenderer.h in Headers */, + 116B4DA125BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.h in Headers */, 114CCC9021F830D200A4646E /* WechatHeaders.h in Headers */, + 116B4D7E25BE92470079C6FE /* QGMP4Box.h in Headers */, 114CCC9D21F831EB00A4646E /* DKHelperSettingController.h in Headers */, 11D27C9E25208FC800AE6337 /* NSArray+Utils.h in Headers */, + 116B4CFB25BE91990079C6FE /* DKLaunchHelper.h in Headers */, + 116B4DA425BE92470079C6FE /* QGVAPMaskInfo.h in Headers */, 114CCC8F21F830D200A4646E /* WCUIAlertView.h in Headers */, + 116B4D8225BE92470079C6FE /* QGVAPSafeMutableDictionary.h in Headers */, + 116B4DA625BE92470079C6FE /* QGMP4HWDFileInfo.h in Headers */, + 116B4DA325BE92470079C6FE /* QGMP4AnimatedImageFrame.h in Headers */, + 116B4DA025BE92470079C6FE /* QGVAPConfigModel.h in Headers */, 114CCC7B21F817D500A4646E /* fishhook.h in Headers */, + 116B4DAC25BE92470079C6FE /* QGVAPlayer.h in Headers */, 114CCC6F21F817D500A4646E /* LLDBTools.h in Headers */, + 116B4D8825BE92470079C6FE /* QGVAPMetalShaderFunctionLoader.h in Headers */, 114CCC4A21F817D500A4646E /* MDConfigManager.h in Headers */, 114CCC6921F817D500A4646E /* OCSelectorTrampolines.h in Headers */, + 116B4DC425BE92470079C6FE /* QGHWDMetalRenderer.h in Headers */, + 116B4D8B25BE92470079C6FE /* QGVAPWeakProxy.h in Headers */, + 116B4D8525BE92470079C6FE /* QGVAPLogger.h in Headers */, + 116B4D9D25BE92470079C6FE /* QGBaseAnimatedImageFrame.h in Headers */, + 116B4DB025BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.h in Headers */, 114CCC9121F830D200A4646E /* UiUtil.h in Headers */, + 116B4D9B25BE92470079C6FE /* QGBaseDFileInfo.h in Headers */, + 116B4D9225BE92470079C6FE /* UIGestureRecognizer+VAPUtil.h in Headers */, 1138B1C7251D9049008A6629 /* DKCleanFriendsController.h in Headers */, + 116B4D9625BE92470079C6FE /* NSArray+VAPUtil.h in Headers */, + 116B4DB125BE92470079C6FE /* QGAnimatedImageBufferManager.h in Headers */, + 116B4D9325BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.h in Headers */, + 116B4DB325BE92470079C6FE /* QGAnimatedImageDecodeConfig.h in Headers */, 114CCC6521F817D500A4646E /* OCMethodTrace.h in Headers */, + 116B4DA725BE92470079C6FE /* QGVAPTextureLoader.h in Headers */, + 116B4D7825BE92470079C6FE /* QGHWDShaderTypes.h in Headers */, + 116B4CFC25BE91990079C6FE /* DKLaunchViewController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -453,6 +947,33 @@ buildActionMask = 2147483647; files = ( 114CCC2F21F817D500A4646E /* MDConfig.plist in Resources */, + 116B4DF825BE98030079C6FE /* jupitreVap.mp4 in Resources */, + 116B4DF225BE98030079C6FE /* uranusVap.mp4 in Resources */, + 116B4DF125BE98030079C6FE /* moon.jpg in Resources */, + 116B4DEC25BE98030079C6FE /* blackholeVap.mp4 in Resources */, + 116B4DFE25BE98030079C6FE /* jupitre.jpg in Resources */, + 116B4DFA25BE98030079C6FE /* sednaVap.mp4 in Resources */, + 116B4DFB25BE98030079C6FE /* mercury.jpg in Resources */, + 116B4DF325BE98030079C6FE /* satureVap.mp4 in Resources */, + 116B4DF025BE98030079C6FE /* earthVap.mp4 in Resources */, + 116B4DE725BE98030079C6FE /* venusVap.mp4 in Resources */, + 116B4DFD25BE98030079C6FE /* plutoVap.mp4 in Resources */, + 116B4DF425BE98030079C6FE /* neptureVap.mp4 in Resources */, + 116B4DE925BE98030079C6FE /* sature.jpg in Resources */, + 116B4DE625BE98030079C6FE /* nepture.jpg in Resources */, + 116B4DEA25BE98030079C6FE /* launchBgm.mp3 in Resources */, + 116B4DFC25BE98030079C6FE /* sunVap.mp4 in Resources */, + 116B4DF925BE98030079C6FE /* mercuryVap.mp4 in Resources */, + 116B4DE525BE98030079C6FE /* venus.jpg in Resources */, + 116B4DED25BE98030079C6FE /* moonVap.mp4 in Resources */, + 116B4DF525BE98030079C6FE /* pluto.jpg in Resources */, + 116B4DEE25BE98030079C6FE /* earth.jpg in Resources */, + 116B4DF725BE98030079C6FE /* sun.jpg in Resources */, + 116B4DF625BE98030079C6FE /* sedna.jpg in Resources */, + 116B4DE825BE98030079C6FE /* mars.jpg in Resources */, + 116B4DEB25BE98030079C6FE /* marsVap.mp4 in Resources */, + 116B4DE425BE98030079C6FE /* uranus.jpg in Resources */, + 116B4DEF25BE98030079C6FE /* blackhole.jpg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -524,28 +1045,68 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 116B4D7925BE92470079C6FE /* QGHWDShaders.metal in Sources */, + 116B4D9725BE92470079C6FE /* UIColor+VAPUtil.m in Sources */, + 116B4DC225BE92470079C6FE /* QGHWDMetalRenderer.m in Sources */, + 116B4DB925BE92470079C6FE /* QGAnimatedImageDecodeThread.m in Sources */, 114CCC5021F817D500A4646E /* MDCycriptManager.m in Sources */, + 116B4DA525BE92470079C6FE /* QGVAPConfigModel.m in Sources */, + 116B4DA225BE92470079C6FE /* QGBaseDFileInfo.m in Sources */, 114CCC5921F817D500A4646E /* a1a2-selectortramps-arm64.s in Sources */, 114CCC5721F817D500A4646E /* a1a2-selectortramps-arm.s in Sources */, + 116B4DBF25BE92470079C6FE /* QGVAPMetalRenderer.m in Sources */, + 116B4D9C25BE92470079C6FE /* QGVAPMaskInfo.m in Sources */, 114CCC5D21F817D500A4646E /* a1a2-selectortramps-x86_64.s in Sources */, + 116B4D9825BE92470079C6FE /* UIView+MP4HWDecode.m in Sources */, + 116B4DB425BE92470079C6FE /* QGAnimatedImageDecodeThreadPool.m in Sources */, 11D27C9F25208FC800AE6337 /* NSArray+Utils.m in Sources */, 114CCC6721F817D500A4646E /* OCMethodTrace.m in Sources */, + 116B4D9525BE92470079C6FE /* UIDevice+VAPUtil.m in Sources */, + 116B4D9A25BE92470079C6FE /* QGMP4AnimatedImageFrame.m in Sources */, + 116B4D8A25BE92470079C6FE /* QGVAPSafeMutableDictionary.m in Sources */, 114CCC9921F831D400A4646E /* DKHelperConfig.m in Sources */, + 116B4D8D25BE92470079C6FE /* NSNotificationCenter+VAPThreadSafe.m in Sources */, + 116B4DBD25BE92470079C6FE /* QGHWDMetalView.m in Sources */, + 116B4D9925BE92470079C6FE /* UIGestureRecognizer+VAPUtil.m in Sources */, 114CCC4C21F817D500A4646E /* MDConfigManager.m in Sources */, 114CCC9C21F831EB00A4646E /* DKHelperSettingController.m in Sources */, 1138B1C8251D9049008A6629 /* DKCleanFriendsController.m in Sources */, + 116B4DBE25BE92470079C6FE /* QGVAPMetalView.m in Sources */, 114CCC7921F817D500A4646E /* fishhook.c in Sources */, + 116B4D8725BE92470079C6FE /* QGVAPMetalUtil.m in Sources */, + 116B4DC525BE92470079C6FE /* QGHWDMP4OpenGLView.m in Sources */, 114CCC7621F817D500A4646E /* AntiAntiDebug.m in Sources */, + 116B4DBC25BE92470079C6FE /* QGAnimatedImageDecodeManager.m in Sources */, + 116B4D9E25BE92470079C6FE /* QGVAPTextureLoader.m in Sources */, + 116B4DA925BE92470079C6FE /* QGBaseAnimatedImageFrame+Displaying.m in Sources */, + 116B4DB525BE92470079C6FE /* QGBaseDecoder.m in Sources */, + 116B4DB625BE92470079C6FE /* QGMP4FrameHWDecoder.m in Sources */, + 116B4D8E25BE92470079C6FE /* NSArray+VAPUtil.m in Sources */, 114CCC6B21F817D500A4646E /* OCSelectorTrampolines.mm in Sources */, + 116B4D8025BE92470079C6FE /* QGVAPMetalShaderFunctionLoader.m in Sources */, + 116B4DAD25BE92470079C6FE /* QGAnimatedImageDecodeConfig.m in Sources */, 114CCC5B21F817D500A4646E /* a1a2-selectortramps-i386.s in Sources */, + 116B4D7F25BE92470079C6FE /* UIView+VAP.m in Sources */, 114CCC6121F817D500A4646E /* a2a3-selectortramps-i386.s in Sources */, + 116B4CFA25BE91990079C6FE /* DKLaunchHelper.m in Sources */, + 116B4D7B25BE92470079C6FE /* QGMP4Box.m in Sources */, + 116B4D8925BE92470079C6FE /* QGVAPSafeMutableArray.m in Sources */, + 116B4D8625BE92470079C6FE /* QGVAPWeakProxy.m in Sources */, 114CCC5F21F817D500A4646E /* a2a3-selectortramps-arm.s in Sources */, 114CCCA121F8323E00A4646E /* DKGroupFilterController.m in Sources */, + 116B4DBB25BE92470079C6FE /* QGAnimatedImageBufferManager.m in Sources */, + 116B4D9425BE92470079C6FE /* NSDictionary+VAPUtil.m in Sources */, 114CCC7F21F817D500A4646E /* dkhelperDylib.m in Sources */, + 116B4D9F25BE92470079C6FE /* QGMP4HWDFileInfo.m in Sources */, 114CCC4721F817D500A4646E /* dkhelperDylib.mm in Sources */, 114CCC9521F831C200A4646E /* DKHelper.m in Sources */, + 116B4CF925BE91990079C6FE /* DKLaunchViewController.m in Sources */, 114CCC7121F817D500A4646E /* LLDBTools.mm in Sources */, 114CCC6321F817D500A4646E /* a2a3-selectortramps-x86_64.s in Sources */, + 116B4DAE25BE92470079C6FE /* QGVAPConfigManager.m in Sources */, + 116B4D8425BE92470079C6FE /* QGVAPLogger.m in Sources */, + 116B4D7D25BE92470079C6FE /* QGMP4Parser.m in Sources */, + 116B4DA825BE92470079C6FE /* QGBaseAnimatedImageFrame.m in Sources */, 114CCC5421F817D500A4646E /* MDMethodTrace.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -703,6 +1264,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/dkhelper/Info.plist"; INSTALL_PATH = "@executable_path/Frameworks/libdkhelperDylib.dylib"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -730,8 +1292,10 @@ "-framework", RevealServer, ); + PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.dkhelper2; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = NO; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = NO; }; @@ -767,6 +1331,7 @@ ); INFOPLIST_FILE = "$(SRCROOT)/dkhelper/Info.plist"; INSTALL_PATH = "@executable_path/Frameworks/libdkhelperDylib.dylib"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -791,8 +1356,10 @@ "-weak_library", "$(MonkeyDevPath)/Frameworks/libsubstrate.dylib", ); + PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.dkhelper2; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTS_MACCATALYST = NO; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -801,9 +1368,11 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NL54V6A3P5; ENABLE_BITCODE = NO; INFOPLIST_FILE = dkhelper/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 7.0.17; MONKEYDEV_ADD_SUBSTRATE = YES; MONKEYDEV_CLASS_DUMP = NO; @@ -813,7 +1382,7 @@ MONKEYDEV_TARGET_APP = Optional; PODS_CONFIGURATION_BUILD_DIR = "$BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; PODS_ROOT = "${SRCROOT}/Pods"; - PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.test11; + PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.dkhelper1; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -823,9 +1392,11 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NL54V6A3P5; ENABLE_BITCODE = NO; INFOPLIST_FILE = dkhelper/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 7.0.17; MONKEYDEV_ADD_SUBSTRATE = YES; MONKEYDEV_CLASS_DUMP = NO; @@ -835,7 +1406,7 @@ MONKEYDEV_TARGET_APP = Optional; PODS_CONFIGURATION_BUILD_DIR = "$BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; PODS_ROOT = "${SRCROOT}/Pods"; - PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.test11; + PRODUCT_BUNDLE_IDENTIFIER = com.dkjone.dkhelper1; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/UserInterfaceState.xcuserstate b/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/UserInterfaceState.xcuserstate index 877bc26..47eea15 100644 Binary files a/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/UserInterfaceState.xcuserstate and b/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Expressions.xcexplist b/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Expressions.xcexplist index 991e630..ce15ea6 100644 --- a/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Expressions.xcexplist +++ b/dkhelper/dkhelper.xcodeproj/project.xcworkspace/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Expressions.xcexplist @@ -16,6 +16,14 @@ + + + + + + diff --git a/dkhelper/dkhelper.xcodeproj/xcshareddata/xcschemes/dkhelperDylib.xcscheme b/dkhelper/dkhelper.xcodeproj/xcshareddata/xcschemes/dkhelperDylib.xcscheme new file mode 100644 index 0000000..b0fb259 --- /dev/null +++ b/dkhelper/dkhelper.xcodeproj/xcshareddata/xcschemes/dkhelperDylib.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 9eafe89..866f43d 100644 --- a/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,4 +3,70 @@ uuid = "942D21E8-F47E-4ED7-A8EE-F67111152412" type = "1" version = "2.0"> + + + + + + + + + + + + + + + + + + diff --git a/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcschemes/xcschememanagement.plist b/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcschemes/xcschememanagement.plist index 3a830c3..c590f92 100644 --- a/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/dkhelper/dkhelper.xcodeproj/xcuserdata/zhudekun.xcuserdatad/xcschemes/xcschememanagement.plist @@ -22,6 +22,11 @@ primary + 114CCC3721F817D500A4646E + + primary + + diff --git a/dkhelper/dkhelper/Config/MDConfig.plist b/dkhelper/dkhelper/Config/MDConfig.plist index df39fdd..da06327 100644 --- a/dkhelper/dkhelper/Config/MDConfig.plist +++ b/dkhelper/dkhelper/Config/MDConfig.plist @@ -144,7 +144,7 @@ TraceFlag 1 - CMessageMgr1 + CMessageMgr TraceMode 1 diff --git a/dkhelper/dkhelperDylib/DKHelperSettingController.m b/dkhelper/dkhelperDylib/DKHelperSettingController.m index 9f3ec23..2f80681 100644 --- a/dkhelper/dkhelperDylib/DKHelperSettingController.m +++ b/dkhelper/dkhelperDylib/DKHelperSettingController.m @@ -58,7 +58,7 @@ [manager addTableViewToSuperView:self.view]; manager.tableView.frame = tableFrame; self.view.backgroundColor = [DKHelper backgroundColor]; - [m_MMLoadingView setLabelText:@"正在检测..."]; + [m_MMLoadingView setText:@"正在检测..."]; [self.view addSubview:m_MMLoadingView]; [self reloadTableData]; self.navigationItem.leftBarButtonItem = [DKHelper leftNavigationItem]; @@ -123,6 +123,11 @@ //消息防撤回 WCTableViewCellManager *revokeInterceptCell = [DKHelper switchCellWithSel:@selector(revokeIntercept:) target:self title:@"消息防撤回" switchOn:[DKHelperConfig preventRevoke]]; [toBeNO1Section addCell:revokeInterceptCell]; + //动态聊天背景和启动图 + WCTableViewCellManager *setLaunchCell = [DKHelper switchCellWithSel:@selector(setLaunch:) target:self title:@"动态启动图" switchOn:[DKHelperConfig dkLaunchEnable]]; + [toBeNO1Section addCell:setLaunchCell]; + WCTableViewCellManager *setChatBgCell = [DKHelper switchCellWithSel:@selector(setChatBg:) target:self title:@"动态聊天背景" switchOn:[DKHelperConfig dkChatBgEnable]]; + [toBeNO1Section addCell:setChatBgCell]; //步数修改 WCTableViewCellManager *changeStepsCell = [DKHelper switchCellWithSel:@selector(changedSteps:) target:self title:@"修改微信步数" switchOn:[DKHelperConfig changeSteps]]; [toBeNO1Section addCell:changeStepsCell]; @@ -262,6 +267,27 @@ DKHelperConfig.preventRevoke = sender.isOn; } +- (void)setLaunch:(UISwitch *)sender{ + DKHelperConfig.dkLaunchEnable = sender.isOn; + if(sender.isOn){ + DKLaunchViewController *launchVC = [[DKLaunchViewController alloc] init]; + launchVC.setType = 1; + launchVC.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:launchVC animated:true completion:nil]; + } +} +- (void)setChatBg:(UISwitch *)sender{ + DKHelperConfig.dkChatBgEnable = sender.isOn; + if(sender.isOn){ + [DKHelper showAlertWithTitle:@"提示" message:@"设置动态背景时,需要确保不使用默认聊天背景才能生效。请在设置->通用->聊天背景 里修改,可修改为任意非默认聊天背景" btnTitle:@"你在教我做事?" handler:^(UIButton *sender) { + DKLaunchViewController *launchVC = [[DKLaunchViewController alloc] init]; + launchVC.setType = 2; + launchVC.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:launchVC animated:true completion:nil]; + }]; + } +} + - (void)forwardTimeline:(UISwitch *)sender{ DKHelperConfig.timeLineForwardEnable = sender.isOn; } diff --git a/dkhelper/dkhelperDylib/DKLaunchViewController.h b/dkhelper/dkhelperDylib/DKLaunchViewController.h new file mode 100644 index 0000000..df49e39 --- /dev/null +++ b/dkhelper/dkhelperDylib/DKLaunchViewController.h @@ -0,0 +1,22 @@ +// ViewController.h +// +// Created by ___ORGANIZATIONNAME___ on 2021/1/18 +// +// +// +// ██████╗ ██╗ ██╗ ██╗ ██████╗ ███╗ ██╗███████╗ +// ██╔══██╗██║ ██╔╝ ██║██╔═══██╗████╗ ██║██╔════╝ +// ██║ ██║█████╔╝ ██║██║ ██║██╔██╗ ██║█████╗ +// ██║ ██║██╔═██╗ ██ ██║██║ ██║██║╚██╗██║██╔══╝ +// ██████╔╝██║ ██╗╚█████╔╝╚██████╔╝██║ ╚████║███████╗ +// ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ +// +// + +#import +#import "DKLaunchHelper.h" +@interface DKLaunchViewController : UIViewController +/// 设置类型 0 :启动页,1:设置启动页,2:设置聊天背景 +@property (nonatomic,assign)int setType; +@end + diff --git a/dkhelper/dkhelperDylib/DKLaunchViewController.m b/dkhelper/dkhelperDylib/DKLaunchViewController.m new file mode 100644 index 0000000..b3d7a1c --- /dev/null +++ b/dkhelper/dkhelperDylib/DKLaunchViewController.m @@ -0,0 +1,183 @@ +// ViewController.m +// +// Created by ___ORGANIZATIONNAME___ on 2021/1/18 +// +// +// +// ██████╗ ██╗ ██╗ ██╗ ██████╗ ███╗ ██╗███████╗ +// ██╔══██╗██║ ██╔╝ ██║██╔═══██╗████╗ ██║██╔════╝ +// ██║ ██║█████╔╝ ██║██║ ██║██╔██╗ ██║█████╗ +// ██║ ██║██╔═██╗ ██ ██║██║ ██║██║╚██╗██║██╔══╝ +// ██████╔╝██║ ██╗╚█████╔╝╚██████╔╝██║ ╚████║███████╗ +// ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ +// +// + +#import "DKLaunchViewController.h" +#import "DKHelper.h" +#import +@interface DKLaunchViewController () +@property (nonatomic,strong)AVAudioPlayer * player; +@property (nonatomic,strong)UIView *controlView; +@property (nonatomic,strong)UIView *animaView; +@property (nonatomic,assign)int animaIndex ; +@property (nonatomic,assign)BOOL hasNext ; +@property (nonatomic,strong)UILabel *textLabel; +@end + +@implementation DKLaunchViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.animaView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.width *16/9)]; + self.animaView.center = self.view.center; + [self.view addSubview:self.animaView]; + self.view.backgroundColor = UIColor.blackColor; + [self.navigationController.navigationBar setHidden:true]; + + if (self.setType !=0 ){ + NSString* path = [NSString stringWithFormat:@"/Library/Application Support/dkhelper/vapFiles/launchBgm.mp3"]; + if (![NSFileManager.defaultManager fileExistsAtPath:path]){ + path = [NSBundle.mainBundle pathForResource:@"launchBgm" ofType:@"mp3"]; + } + self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:nil]; + [self.player play]; + [self.player setNumberOfLoops:100]; + + self.controlView = [[UIView alloc] initWithFrame:self.view.frame]; + self.controlView.backgroundColor = UIColor.clearColor; + [self.view addSubview:self.controlView]; + + UIButton *exitBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + exitBtn.titleLabel.font = [UIFont systemFontOfSize:14]; + [exitBtn setTitle:@"退出" forState:UIControlStateNormal]; + exitBtn.frame = CGRectMake(15, 80, 80, 40); + [exitBtn addTarget:self action:@selector(exitVC) forControlEvents:UIControlEventTouchUpInside]; + [self.controlView addSubview:exitBtn]; + + UIButton *nextBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + [nextBtn setTitle:@"右滑下一个" forState:UIControlStateNormal]; + nextBtn.titleLabel.font = [UIFont systemFontOfSize:14]; + nextBtn.frame = CGRectMake( UIScreen.mainScreen.bounds.size.width - 100, 80, 80, 40); + [nextBtn addTarget:self action:@selector(nextAnima:) forControlEvents:UIControlEventTouchUpInside]; + [self.controlView addSubview:nextBtn]; + + UIButton *setDoneBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + [setDoneBtn setTitle:(self.setType==1 ? @"设为启动图" : @"设为聊天背景") forState:UIControlStateNormal]; + setDoneBtn.titleLabel.font = [UIFont systemFontOfSize:14]; + setDoneBtn.frame = CGRectMake(UIScreen.mainScreen.bounds.size.width - 120, UIScreen.mainScreen.bounds.size.height - 100, 100, 40); + [setDoneBtn addTarget:self action:@selector(setLaunch) forControlEvents:UIControlEventTouchUpInside]; + [self.controlView addSubview:setDoneBtn]; + UIPanGestureRecognizer *swip = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(nextAnima:)]; + [self.view addGestureRecognizer:swip]; + + UILabel * label = [[UILabel alloc] init]; + self.textLabel = label; + label.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height/3*2, UIScreen.mainScreen.bounds.size.width, 70); + [self.view addSubview:label]; + label.font = [UIFont boldSystemFontOfSize:20]; + label.numberOfLines = 0; + label.textAlignment = NSTextAlignmentCenter; + } + [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showControl)]]; + self.animaIndex = self.setType == 2 ? (DKHelperConfig.dkChatBGIndex.intValue - 1) : (DKHelperConfig.dkLaunchIndex.intValue - 1); + [self nextAnima:nil]; + +} + +- (void)exitVC{ + [self.navigationController popViewControllerAnimated:true]; + [self dismissViewControllerAnimated:true completion:nil]; +} + + +- (void)showControl{ + if (self.setType == 0){[self goNextVC];} + [UIView animateWithDuration:0.25 animations:^{ + int alpha = ceil(self.controlView.alpha); + self.controlView.alpha = (alpha + 1) % 2; + }]; +} + +- (void)nextAnima:(NSObject *) gest{ + self.hasNext = false; + + BOOL isNext = true; + if ([gest isKindOfClass:UIPanGestureRecognizer.class]){ + UIPanGestureRecognizer *recog = (UIPanGestureRecognizer *)gest; + if (recog.state != UIGestureRecognizerStateEnded){return;} + CGPoint trans = [recog translationInView:recog.view]; + if (trans.x > 0 ){ + isNext = false; + }else { + isNext = true; + } + + } + self.animaIndex += (isNext? 1 : -1); + self.animaIndex = self.animaIndex > 0 ? self.animaIndex : 0; + self.animaIndex = self.animaIndex % 13; + NSString *animaName = DKLaunchHelper.animaNames[self.animaIndex][@"name"]; + NSString *text = DKLaunchHelper.animaNames[self.animaIndex][@"desc"]; + self.textLabel.text = [NSString stringWithFormat:@"%@\n%@",text,animaName]; + self.textLabel.textColor = DKLaunchHelper.animaNames[self.animaIndex][@"color"]; + + NSString* path = [NSString stringWithFormat:@"/Library/Application Support/dkhelper/vapFiles/%@",[NSString stringWithFormat:@"%@Vap.mp4", animaName]]; + if (![NSFileManager.defaultManager fileExistsAtPath:path]){ + path = [NSBundle.mainBundle pathForResource:[NSString stringWithFormat:@"%@Vap", animaName] ofType:@"mp4"]; + } + [self.animaView playHWDMP4:path repeatCount:0 delegate:self]; + self.hasNext = true; + +} + +- (void)setLaunch{ + NSString *text = DKLaunchHelper.animaNames[self.animaIndex][@"desc"]; + if (self.setType == 2){ + //设为聊天背景 + DKHelperConfig.dkChatBGIndex = [NSNumber numberWithInt:self.animaIndex]; + [DKHelper showAlertWithTitle:@"已设置聊天背景为" message:text btnTitle:@"确定" handler:^(UIButton *sender) {}]; + return; + } + NSString *animaName = DKLaunchHelper.animaNames[self.animaIndex][@"name"]; + UIImageView *imgv = [[UIImageView alloc] initWithFrame:self.animaView.frame]; + + NSString* path = [NSString stringWithFormat:@"/Library/Application Support/dkhelper/vapFiles/%@",[NSString stringWithFormat:@"%@.jpg", animaName]]; + if (![NSFileManager.defaultManager fileExistsAtPath:path]){ + path = [NSBundle.mainBundle pathForResource:animaName ofType:@"jpg"]; + } + imgv.image = [[UIImage alloc] initWithContentsOfFile:path]; + UIView *bgView = [[UIView alloc] initWithFrame:self.view.bounds]; + bgView.backgroundColor = UIColor.blackColor; + bgView.clipsToBounds = true; + [bgView addSubview:imgv]; + [LaunchImageHelper changeAllLaunchImageToPortrait:bgView.dkScreenShoot]; + DKHelperConfig.dkLaunchIndex = [NSNumber numberWithInt:self.animaIndex]; + [NSUserDefaults.standardUserDefaults synchronize]; + [DKHelper showAlertWithTitle:@"已设置启动图为" message:text btnTitle:@"确定" handler:^(UIButton *sender) {}]; +} + +-(void)viewDidStopPlayMP4:(NSInteger)lastFrameIndex view:(VAPView *)container{ + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (self.setType == 0){ + [self goNextVC]; + return; + } + if (self.hasNext){ + [self nextAnima:nil]; + } + }); +} + +-(void)goNextVC{ + UIWindow *window = UIApplication.sharedApplication.keyWindow; + window.rootViewController = nil; + [UIApplication.sharedApplication.keyWindow setHidden:true]; + [UIApplication.sharedApplication.delegate.window makeKeyAndVisible]; +} + +@end + + diff --git a/dkhelper/dkhelperDylib/Logos/dkhelperDylib.mm b/dkhelper/dkhelperDylib/Logos/dkhelperDylib.mm index fb16dd4..898e636 100644 --- a/dkhelper/dkhelperDylib/Logos/dkhelperDylib.mm +++ b/dkhelper/dkhelperDylib/Logos/dkhelperDylib.mm @@ -35,9 +35,9 @@ #define _LOGOS_RETURN_RETAINED #endif -@class WCRedEnvelopesLogicMgr; @class MMServiceCenter; @class WCTableViewNormalCellManager; @class CMessageWrap; @class WCBizUtil; @class WCDeviceStepObject; @class CGroupMgr; @class MMContext; @class VoipCXMgr; @class MicroMessengerAppDelegate; @class WCTimelineMgr; @class UIViewController; @class MMTipsViewController; @class CMessageMgr; @class WCOperateFloatView; @class CContactMgr; @class NewSettingViewController; -static id _logos_meta_method$_ungrouped$MMServiceCenter$defaultCenter(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$)(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void _logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void (*_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$)(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void _logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void (*_logos_orig$_ungrouped$NewSettingViewController$reloadTableData)(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$NewSettingViewController$reloadTableData(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$NewSettingViewController$setting(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void (*_logos_orig$_ungrouped$CMessageMgr$onRevokeMsg$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$onRevokeMsg$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, CMessageWrap *); static void (*_logos_orig$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static NSInteger (*_logos_orig$_ungrouped$WCDeviceStepObject$m7StepCount)(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger _logos_method$_ungrouped$WCDeviceStepObject$m7StepCount(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger (*_logos_orig$_ungrouped$WCDeviceStepObject$hkStepCount)(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger _logos_method$_ungrouped$WCDeviceStepObject$hkStepCount(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$)(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL, HongBaoRes *, HongBaoReq *); static void _logos_method$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL, HongBaoRes *, HongBaoReq *); static unsigned int _logos_method$_ungrouped$WCRedEnvelopesLogicMgr$calculateDelaySeconds(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL); static BOOL (*_logos_meta_orig$_ungrouped$VoipCXMgr$isCallkitAvailable)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL _logos_meta_method$_ungrouped$VoipCXMgr$isCallkitAvailable(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL (*_logos_meta_orig$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL _logos_meta_method$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$UIViewController$viewWillAppear$)(_LOGOS_SELF_TYPE_NORMAL UIViewController* _LOGOS_SELF_CONST, SEL, BOOL); static void _logos_method$_ungrouped$UIViewController$viewWillAppear$(_LOGOS_SELF_TYPE_NORMAL UIViewController* _LOGOS_SELF_CONST, SEL, BOOL); static NSString * _logos_method$_ungrouped$MMTipsViewController$text(_LOGOS_SELF_TYPE_NORMAL MMTipsViewController* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCTimelineMgr$modifyDataItem$notify$)(_LOGOS_SELF_TYPE_NORMAL WCTimelineMgr* _LOGOS_SELF_CONST, SEL, WCDataItem *, BOOL); static void _logos_method$_ungrouped$WCTimelineMgr$modifyDataItem$notify$(_LOGOS_SELF_TYPE_NORMAL WCTimelineMgr* _LOGOS_SELF_CONST, SEL, WCDataItem *, BOOL); static void (*_logos_orig$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$)(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void _logos_method$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void (*_logos_orig$_ungrouped$CGroupMgr$addCreateMsg$ContactList$)(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void _logos_method$_ungrouped$CGroupMgr$addCreateMsg$ContactList$(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static UIButton * _logos_method$_ungrouped$WCOperateFloatView$m_shareBtn(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL); static UIImageView * _logos_method$_ungrouped$WCOperateFloatView$m_lineView2(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$)(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id, struct CGPoint); static void _logos_method$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id, struct CGPoint); static void _logos_method$_ungrouped$WCOperateFloatView$forwordTimeLine$(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id); -static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$WCBizUtil(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("WCBizUtil"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$CMessageWrap(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("CMessageWrap"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$MMContext(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("MMContext"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$WCTableViewNormalCellManager(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("WCTableViewNormalCellManager"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$CContactMgr(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("CContactMgr"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$MMServiceCenter(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("MMServiceCenter"); } return _klass; } +@class CMessageMgr; @class WCTimelineMgr; @class WCRedEnvelopesLogicMgr; @class WCDeviceStepObject; @class NewSettingViewController; @class MicroMessengerAppDelegate; @class MMTipsViewController; @class CGroupMgr; @class MMContext; @class MMServiceCenter; @class WCTableViewNormalCellManager; @class WCOperateFloatView; @class UIViewController; @class WCBizUtil; @class BaseMsgContentViewController; @class VoipCXMgr; @class CMessageWrap; @class CContactMgr; +static id _logos_meta_method$_ungrouped$MMServiceCenter$defaultCenter(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$)(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void _logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void (*_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$)(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static void _logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, UIApplication *); static _Bool (*_logos_orig$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$)(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, id, id); static _Bool _logos_method$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST, SEL, id, id); static void (*_logos_orig$_ungrouped$NewSettingViewController$reloadTableData)(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$NewSettingViewController$reloadTableData(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$NewSettingViewController$setting(_LOGOS_SELF_TYPE_NORMAL NewSettingViewController* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void (*_logos_orig$_ungrouped$CMessageMgr$onRevokeMsg$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$onRevokeMsg$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, CMessageWrap *); static void (*_logos_orig$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$)(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static void _logos_method$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$(_LOGOS_SELF_TYPE_NORMAL CMessageMgr* _LOGOS_SELF_CONST, SEL, NSString *, CMessageWrap *); static NSInteger (*_logos_orig$_ungrouped$WCDeviceStepObject$m7StepCount)(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger _logos_method$_ungrouped$WCDeviceStepObject$m7StepCount(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger (*_logos_orig$_ungrouped$WCDeviceStepObject$hkStepCount)(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static NSInteger _logos_method$_ungrouped$WCDeviceStepObject$hkStepCount(_LOGOS_SELF_TYPE_NORMAL WCDeviceStepObject* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$)(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL, HongBaoRes *, HongBaoReq *); static void _logos_method$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL, HongBaoRes *, HongBaoReq *); static unsigned int _logos_method$_ungrouped$WCRedEnvelopesLogicMgr$calculateDelaySeconds(_LOGOS_SELF_TYPE_NORMAL WCRedEnvelopesLogicMgr* _LOGOS_SELF_CONST, SEL); static BOOL (*_logos_meta_orig$_ungrouped$VoipCXMgr$isCallkitAvailable)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL _logos_meta_method$_ungrouped$VoipCXMgr$isCallkitAvailable(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL (*_logos_meta_orig$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable)(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static BOOL _logos_meta_method$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable(_LOGOS_SELF_TYPE_NORMAL Class _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$UIViewController$viewWillAppear$)(_LOGOS_SELF_TYPE_NORMAL UIViewController* _LOGOS_SELF_CONST, SEL, BOOL); static void _logos_method$_ungrouped$UIViewController$viewWillAppear$(_LOGOS_SELF_TYPE_NORMAL UIViewController* _LOGOS_SELF_CONST, SEL, BOOL); static NSString * _logos_method$_ungrouped$MMTipsViewController$text(_LOGOS_SELF_TYPE_NORMAL MMTipsViewController* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCTimelineMgr$modifyDataItem$notify$)(_LOGOS_SELF_TYPE_NORMAL WCTimelineMgr* _LOGOS_SELF_CONST, SEL, WCDataItem *, BOOL); static void _logos_method$_ungrouped$WCTimelineMgr$modifyDataItem$notify$(_LOGOS_SELF_TYPE_NORMAL WCTimelineMgr* _LOGOS_SELF_CONST, SEL, WCDataItem *, BOOL); static void (*_logos_orig$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$)(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void _logos_method$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void (*_logos_orig$_ungrouped$CGroupMgr$addCreateMsg$ContactList$)(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static void _logos_method$_ungrouped$CGroupMgr$addCreateMsg$ContactList$(_LOGOS_SELF_TYPE_NORMAL CGroupMgr* _LOGOS_SELF_CONST, SEL, id, id); static UIButton * _logos_method$_ungrouped$WCOperateFloatView$m_shareBtn(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL); static UIImageView * _logos_method$_ungrouped$WCOperateFloatView$m_lineView2(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$)(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id, struct CGPoint); static void _logos_method$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id, struct CGPoint); static void _logos_method$_ungrouped$WCOperateFloatView$forwordTimeLine$(_LOGOS_SELF_TYPE_NORMAL WCOperateFloatView* _LOGOS_SELF_CONST, SEL, id); static void (*_logos_orig$_ungrouped$BaseMsgContentViewController$viewDidLoad)(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$BaseMsgContentViewController$viewDidLoad(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST, SEL); static void (*_logos_orig$_ungrouped$BaseMsgContentViewController$viewWillAppear$)(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST, SEL, BOOL); static void _logos_method$_ungrouped$BaseMsgContentViewController$viewWillAppear$(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST, SEL, BOOL); +static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$CContactMgr(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("CContactMgr"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$WCTableViewNormalCellManager(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("WCTableViewNormalCellManager"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$CMessageWrap(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("CMessageWrap"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$MMServiceCenter(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("MMServiceCenter"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$WCBizUtil(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("WCBizUtil"); } return _klass; }static __inline__ __attribute__((always_inline)) __attribute__((unused)) Class _logos_static_class_lookup$MMContext(void) { static Class _klass; if(!_klass) { _klass = objc_getClass("MMContext"); } return _klass; } #line 16 "/Users/zhudekun/mycode/github/DKWechatHelper/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm" @@ -506,10 +506,62 @@ static void _logos_method$_ungrouped$WCOperateFloatView$forwordTimeLine$(_LOGOS_ +@interface BaseMsgContentViewController:UIViewController +- (id)getMsgTableView; +- (id)getParentTableView; +@end + + +static void _logos_method$_ungrouped$BaseMsgContentViewController$viewDidLoad(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd){ + _logos_orig$_ungrouped$BaseMsgContentViewController$viewDidLoad(self, _cmd); +} + +static void _logos_method$_ungrouped$BaseMsgContentViewController$viewWillAppear$(_LOGOS_SELF_TYPE_NORMAL BaseMsgContentViewController* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, BOOL animated){ + _logos_orig$_ungrouped$BaseMsgContentViewController$viewWillAppear$(self, _cmd, animated); + UIView * annimView = [self.view viewWithTag:66666]; + if (!DKHelperConfig.dkChatBgEnable){ + [annimView removeFromSuperview]; + [annimView stopHWDMP4]; + return; + } + if (annimView == nil){ + annimView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.width *16/9)]; + annimView.tag = 66666; + annimView.backgroundColor = UIColor.clearColor; + [self.view insertSubview:annimView belowSubview:[self getMsgTableView]]; + } + + annimView.center = self.view.center; + NSString *animaName = DKLaunchHelper.animaNames[DKHelperConfig.dkChatBGIndex.intValue][@"name"]; + NSString* path = [NSBundle.mainBundle pathForResource:[NSString stringWithFormat:@"%@Vap", animaName] ofType:@"mp4"]; + [annimView playHWDMP4:path repeatCount:-1 delegate:nil]; + +} +@interface MicroMessengerAppDelegate ++ (id)GlobalInstance; +@property(retain, nonatomic) UIWindow *window; +@property (nonatomic, retain) UIWindow *launchWindow; +@end + + +__attribute__((used)) static UIWindow * _logos_method$_ungrouped$MicroMessengerAppDelegate$launchWindow(MicroMessengerAppDelegate * __unused self, SEL __unused _cmd) { return (UIWindow *)objc_getAssociatedObject(self, (void *)_logos_method$_ungrouped$MicroMessengerAppDelegate$launchWindow); }; __attribute__((used)) static void _logos_method$_ungrouped$MicroMessengerAppDelegate$setLaunchWindow(MicroMessengerAppDelegate * __unused self, SEL __unused _cmd, UIWindow * rawValue) { objc_setAssociatedObject(self, (void *)_logos_method$_ungrouped$MicroMessengerAppDelegate$launchWindow, rawValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } + +static _Bool _logos_method$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$(_LOGOS_SELF_TYPE_NORMAL MicroMessengerAppDelegate* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd, id arg1, id arg2){ + if (!DKHelperConfig.dkLaunchEnable){return _logos_orig$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$(self, _cmd, arg1, arg2); } + BOOL end = _logos_orig$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$(self, _cmd, arg1, arg2); + DKLaunchViewController * launchVC = [[DKLaunchViewController alloc] init]; + UIWindow *launchWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; + self.launchWindow = launchWindow; + launchWindow.windowLevel = UIWindowLevelAlert + 1; + launchWindow.rootViewController = launchVC; + [launchWindow makeKeyAndVisible]; + return end; + +} static __attribute__((constructor)) void _logosLocalInit() { -{Class _logos_class$_ungrouped$MMServiceCenter = objc_getClass("MMServiceCenter"); Class _logos_metaclass$_ungrouped$MMServiceCenter = object_getClass(_logos_class$_ungrouped$MMServiceCenter); { char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_metaclass$_ungrouped$MMServiceCenter, @selector(defaultCenter), (IMP)&_logos_meta_method$_ungrouped$MMServiceCenter$defaultCenter, _typeEncoding); }Class _logos_class$_ungrouped$MicroMessengerAppDelegate = objc_getClass("MicroMessengerAppDelegate"); MSHookMessageEx(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(applicationDidEnterBackground:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$, (IMP*)&_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$);MSHookMessageEx(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(applicationDidBecomeActive:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$, (IMP*)&_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$);Class _logos_class$_ungrouped$NewSettingViewController = objc_getClass("NewSettingViewController"); MSHookMessageEx(_logos_class$_ungrouped$NewSettingViewController, @selector(reloadTableData), (IMP)&_logos_method$_ungrouped$NewSettingViewController$reloadTableData, (IMP*)&_logos_orig$_ungrouped$NewSettingViewController$reloadTableData);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'v'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$NewSettingViewController, @selector(setting), (IMP)&_logos_method$_ungrouped$NewSettingViewController$setting, _typeEncoding); }Class _logos_class$_ungrouped$CMessageMgr = objc_getClass("CMessageMgr"); MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(AddEmoticonMsg:MsgWrap:), (IMP)&_logos_method$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$);MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(onRevokeMsg:), (IMP)&_logos_method$_ungrouped$CMessageMgr$onRevokeMsg$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$onRevokeMsg$);MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(AsyncOnAddMsg:MsgWrap:), (IMP)&_logos_method$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$);Class _logos_class$_ungrouped$WCDeviceStepObject = objc_getClass("WCDeviceStepObject"); MSHookMessageEx(_logos_class$_ungrouped$WCDeviceStepObject, @selector(m7StepCount), (IMP)&_logos_method$_ungrouped$WCDeviceStepObject$m7StepCount, (IMP*)&_logos_orig$_ungrouped$WCDeviceStepObject$m7StepCount);MSHookMessageEx(_logos_class$_ungrouped$WCDeviceStepObject, @selector(hkStepCount), (IMP)&_logos_method$_ungrouped$WCDeviceStepObject$hkStepCount, (IMP*)&_logos_orig$_ungrouped$WCDeviceStepObject$hkStepCount);Class _logos_class$_ungrouped$WCRedEnvelopesLogicMgr = objc_getClass("WCRedEnvelopesLogicMgr"); MSHookMessageEx(_logos_class$_ungrouped$WCRedEnvelopesLogicMgr, @selector(OnWCToHongbaoCommonResponse:Request:), (IMP)&_logos_method$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$, (IMP*)&_logos_orig$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'I'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCRedEnvelopesLogicMgr, @selector(calculateDelaySeconds), (IMP)&_logos_method$_ungrouped$WCRedEnvelopesLogicMgr$calculateDelaySeconds, _typeEncoding); }Class _logos_class$_ungrouped$VoipCXMgr = objc_getClass("VoipCXMgr"); Class _logos_metaclass$_ungrouped$VoipCXMgr = object_getClass(_logos_class$_ungrouped$VoipCXMgr); MSHookMessageEx(_logos_metaclass$_ungrouped$VoipCXMgr, @selector(isCallkitAvailable), (IMP)&_logos_meta_method$_ungrouped$VoipCXMgr$isCallkitAvailable, (IMP*)&_logos_meta_orig$_ungrouped$VoipCXMgr$isCallkitAvailable);MSHookMessageEx(_logos_metaclass$_ungrouped$VoipCXMgr, @selector(isDeviceCallkitAvailable), (IMP)&_logos_meta_method$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable, (IMP*)&_logos_meta_orig$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable);Class _logos_class$_ungrouped$UIViewController = objc_getClass("UIViewController"); MSHookMessageEx(_logos_class$_ungrouped$UIViewController, @selector(viewWillAppear:), (IMP)&_logos_method$_ungrouped$UIViewController$viewWillAppear$, (IMP*)&_logos_orig$_ungrouped$UIViewController$viewWillAppear$);Class _logos_class$_ungrouped$MMTipsViewController = objc_getClass("MMTipsViewController"); { char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(NSString *), strlen(@encode(NSString *))); i += strlen(@encode(NSString *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$MMTipsViewController, @selector(text), (IMP)&_logos_method$_ungrouped$MMTipsViewController$text, _typeEncoding); }Class _logos_class$_ungrouped$WCTimelineMgr = objc_getClass("WCTimelineMgr"); MSHookMessageEx(_logos_class$_ungrouped$WCTimelineMgr, @selector(modifyDataItem:notify:), (IMP)&_logos_method$_ungrouped$WCTimelineMgr$modifyDataItem$notify$, (IMP*)&_logos_orig$_ungrouped$WCTimelineMgr$modifyDataItem$notify$);Class _logos_class$_ungrouped$CGroupMgr = objc_getClass("CGroupMgr"); MSHookMessageEx(_logos_class$_ungrouped$CGroupMgr, @selector(addChatMemberNeedVerifyMsg:ContactList:), (IMP)&_logos_method$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$, (IMP*)&_logos_orig$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$);MSHookMessageEx(_logos_class$_ungrouped$CGroupMgr, @selector(addCreateMsg:ContactList:), (IMP)&_logos_method$_ungrouped$CGroupMgr$addCreateMsg$ContactList$, (IMP*)&_logos_orig$_ungrouped$CGroupMgr$addCreateMsg$ContactList$);Class _logos_class$_ungrouped$WCOperateFloatView = objc_getClass("WCOperateFloatView"); { char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(UIButton *), strlen(@encode(UIButton *))); i += strlen(@encode(UIButton *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(m_shareBtn), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$m_shareBtn, _typeEncoding); }{ char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(UIImageView *), strlen(@encode(UIImageView *))); i += strlen(@encode(UIImageView *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(m_lineView2), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$m_lineView2, _typeEncoding); }MSHookMessageEx(_logos_class$_ungrouped$WCOperateFloatView, @selector(showWithItemData:tipPoint:), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$, (IMP*)&_logos_orig$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'v'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(forwordTimeLine:), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$forwordTimeLine$, _typeEncoding); }} } -#line 487 "/Users/zhudekun/mycode/github/DKWechatHelper/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm" +{Class _logos_class$_ungrouped$MMServiceCenter = objc_getClass("MMServiceCenter"); Class _logos_metaclass$_ungrouped$MMServiceCenter = object_getClass(_logos_class$_ungrouped$MMServiceCenter); { char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_metaclass$_ungrouped$MMServiceCenter, @selector(defaultCenter), (IMP)&_logos_meta_method$_ungrouped$MMServiceCenter$defaultCenter, _typeEncoding); }Class _logos_class$_ungrouped$MicroMessengerAppDelegate = objc_getClass("MicroMessengerAppDelegate"); MSHookMessageEx(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(applicationDidEnterBackground:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$, (IMP*)&_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidEnterBackground$);MSHookMessageEx(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(applicationDidBecomeActive:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$, (IMP*)&_logos_orig$_ungrouped$MicroMessengerAppDelegate$applicationDidBecomeActive$);MSHookMessageEx(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(application:didFinishLaunchingWithOptions:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$, (IMP*)&_logos_orig$_ungrouped$MicroMessengerAppDelegate$application$didFinishLaunchingWithOptions$);{ char _typeEncoding[1024]; sprintf(_typeEncoding, "%s@:", @encode(UIWindow *)); class_addMethod(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(launchWindow), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$launchWindow, _typeEncoding); sprintf(_typeEncoding, "v@:%s", @encode(UIWindow *)); class_addMethod(_logos_class$_ungrouped$MicroMessengerAppDelegate, @selector(setLaunchWindow:), (IMP)&_logos_method$_ungrouped$MicroMessengerAppDelegate$setLaunchWindow, _typeEncoding); } Class _logos_class$_ungrouped$NewSettingViewController = objc_getClass("NewSettingViewController"); MSHookMessageEx(_logos_class$_ungrouped$NewSettingViewController, @selector(reloadTableData), (IMP)&_logos_method$_ungrouped$NewSettingViewController$reloadTableData, (IMP*)&_logos_orig$_ungrouped$NewSettingViewController$reloadTableData);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'v'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$NewSettingViewController, @selector(setting), (IMP)&_logos_method$_ungrouped$NewSettingViewController$setting, _typeEncoding); }Class _logos_class$_ungrouped$CMessageMgr = objc_getClass("CMessageMgr"); MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(AddEmoticonMsg:MsgWrap:), (IMP)&_logos_method$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$AddEmoticonMsg$MsgWrap$);MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(onRevokeMsg:), (IMP)&_logos_method$_ungrouped$CMessageMgr$onRevokeMsg$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$onRevokeMsg$);MSHookMessageEx(_logos_class$_ungrouped$CMessageMgr, @selector(AsyncOnAddMsg:MsgWrap:), (IMP)&_logos_method$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$, (IMP*)&_logos_orig$_ungrouped$CMessageMgr$AsyncOnAddMsg$MsgWrap$);Class _logos_class$_ungrouped$WCDeviceStepObject = objc_getClass("WCDeviceStepObject"); MSHookMessageEx(_logos_class$_ungrouped$WCDeviceStepObject, @selector(m7StepCount), (IMP)&_logos_method$_ungrouped$WCDeviceStepObject$m7StepCount, (IMP*)&_logos_orig$_ungrouped$WCDeviceStepObject$m7StepCount);MSHookMessageEx(_logos_class$_ungrouped$WCDeviceStepObject, @selector(hkStepCount), (IMP)&_logos_method$_ungrouped$WCDeviceStepObject$hkStepCount, (IMP*)&_logos_orig$_ungrouped$WCDeviceStepObject$hkStepCount);Class _logos_class$_ungrouped$WCRedEnvelopesLogicMgr = objc_getClass("WCRedEnvelopesLogicMgr"); MSHookMessageEx(_logos_class$_ungrouped$WCRedEnvelopesLogicMgr, @selector(OnWCToHongbaoCommonResponse:Request:), (IMP)&_logos_method$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$, (IMP*)&_logos_orig$_ungrouped$WCRedEnvelopesLogicMgr$OnWCToHongbaoCommonResponse$Request$);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'I'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCRedEnvelopesLogicMgr, @selector(calculateDelaySeconds), (IMP)&_logos_method$_ungrouped$WCRedEnvelopesLogicMgr$calculateDelaySeconds, _typeEncoding); }Class _logos_class$_ungrouped$VoipCXMgr = objc_getClass("VoipCXMgr"); Class _logos_metaclass$_ungrouped$VoipCXMgr = object_getClass(_logos_class$_ungrouped$VoipCXMgr); MSHookMessageEx(_logos_metaclass$_ungrouped$VoipCXMgr, @selector(isCallkitAvailable), (IMP)&_logos_meta_method$_ungrouped$VoipCXMgr$isCallkitAvailable, (IMP*)&_logos_meta_orig$_ungrouped$VoipCXMgr$isCallkitAvailable);MSHookMessageEx(_logos_metaclass$_ungrouped$VoipCXMgr, @selector(isDeviceCallkitAvailable), (IMP)&_logos_meta_method$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable, (IMP*)&_logos_meta_orig$_ungrouped$VoipCXMgr$isDeviceCallkitAvailable);Class _logos_class$_ungrouped$UIViewController = objc_getClass("UIViewController"); MSHookMessageEx(_logos_class$_ungrouped$UIViewController, @selector(viewWillAppear:), (IMP)&_logos_method$_ungrouped$UIViewController$viewWillAppear$, (IMP*)&_logos_orig$_ungrouped$UIViewController$viewWillAppear$);Class _logos_class$_ungrouped$MMTipsViewController = objc_getClass("MMTipsViewController"); { char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(NSString *), strlen(@encode(NSString *))); i += strlen(@encode(NSString *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$MMTipsViewController, @selector(text), (IMP)&_logos_method$_ungrouped$MMTipsViewController$text, _typeEncoding); }Class _logos_class$_ungrouped$WCTimelineMgr = objc_getClass("WCTimelineMgr"); MSHookMessageEx(_logos_class$_ungrouped$WCTimelineMgr, @selector(modifyDataItem:notify:), (IMP)&_logos_method$_ungrouped$WCTimelineMgr$modifyDataItem$notify$, (IMP*)&_logos_orig$_ungrouped$WCTimelineMgr$modifyDataItem$notify$);Class _logos_class$_ungrouped$CGroupMgr = objc_getClass("CGroupMgr"); MSHookMessageEx(_logos_class$_ungrouped$CGroupMgr, @selector(addChatMemberNeedVerifyMsg:ContactList:), (IMP)&_logos_method$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$, (IMP*)&_logos_orig$_ungrouped$CGroupMgr$addChatMemberNeedVerifyMsg$ContactList$);MSHookMessageEx(_logos_class$_ungrouped$CGroupMgr, @selector(addCreateMsg:ContactList:), (IMP)&_logos_method$_ungrouped$CGroupMgr$addCreateMsg$ContactList$, (IMP*)&_logos_orig$_ungrouped$CGroupMgr$addCreateMsg$ContactList$);Class _logos_class$_ungrouped$WCOperateFloatView = objc_getClass("WCOperateFloatView"); { char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(UIButton *), strlen(@encode(UIButton *))); i += strlen(@encode(UIButton *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(m_shareBtn), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$m_shareBtn, _typeEncoding); }{ char _typeEncoding[1024]; unsigned int i = 0; memcpy(_typeEncoding + i, @encode(UIImageView *), strlen(@encode(UIImageView *))); i += strlen(@encode(UIImageView *)); _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(m_lineView2), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$m_lineView2, _typeEncoding); }MSHookMessageEx(_logos_class$_ungrouped$WCOperateFloatView, @selector(showWithItemData:tipPoint:), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$, (IMP*)&_logos_orig$_ungrouped$WCOperateFloatView$showWithItemData$tipPoint$);{ char _typeEncoding[1024]; unsigned int i = 0; _typeEncoding[i] = 'v'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = ':'; i += 1; _typeEncoding[i] = '@'; i += 1; _typeEncoding[i] = '\0'; class_addMethod(_logos_class$_ungrouped$WCOperateFloatView, @selector(forwordTimeLine:), (IMP)&_logos_method$_ungrouped$WCOperateFloatView$forwordTimeLine$, _typeEncoding); }Class _logos_class$_ungrouped$BaseMsgContentViewController = objc_getClass("BaseMsgContentViewController"); MSHookMessageEx(_logos_class$_ungrouped$BaseMsgContentViewController, @selector(viewDidLoad), (IMP)&_logos_method$_ungrouped$BaseMsgContentViewController$viewDidLoad, (IMP*)&_logos_orig$_ungrouped$BaseMsgContentViewController$viewDidLoad);MSHookMessageEx(_logos_class$_ungrouped$BaseMsgContentViewController, @selector(viewWillAppear:), (IMP)&_logos_method$_ungrouped$BaseMsgContentViewController$viewWillAppear$, (IMP*)&_logos_orig$_ungrouped$BaseMsgContentViewController$viewWillAppear$);} } +#line 539 "/Users/zhudekun/mycode/github/DKWechatHelper/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm" diff --git a/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm b/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm index af090bd..63599ad 100644 --- a/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm +++ b/dkhelper/dkhelperDylib/Logos/dkhelperDylib.xm @@ -479,8 +479,60 @@ %end -//%hook ScanQRCodeResultsMgr -//- (void)retryRequetScanResult:(id)arg1 viewController:(id)arg2{ -// %orig; -//} -//%end + +@interface BaseMsgContentViewController:UIViewController +- (id)getMsgTableView; +- (id)getParentTableView; +@end + +%hook BaseMsgContentViewController +- (void)viewDidLoad{ + %orig; +} + +- (void)viewWillAppear:(BOOL)animated{ + %orig; + UIView * annimView = [self.view viewWithTag:66666]; + if (!DKHelperConfig.dkChatBgEnable){ + [annimView removeFromSuperview]; + [annimView stopHWDMP4]; + return; + } + if (annimView == nil){ + annimView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.width *16/9)]; + annimView.tag = 66666; + annimView.backgroundColor = UIColor.clearColor; + [self.view insertSubview:annimView belowSubview:[self getMsgTableView]]; + } + + annimView.center = self.view.center; + NSString *animaName = DKLaunchHelper.animaNames[DKHelperConfig.dkChatBGIndex.intValue][@"name"]; + NSString* path = [NSBundle.mainBundle pathForResource:[NSString stringWithFormat:@"%@Vap", animaName] ofType:@"mp4"]; + [annimView playHWDMP4:path repeatCount:-1 delegate:nil]; + +} + +%end + +@interface MicroMessengerAppDelegate ++ (id)GlobalInstance; +@property(retain, nonatomic) UIWindow *window; +@property (nonatomic, retain) UIWindow *launchWindow; +@end + +%hook MicroMessengerAppDelegate +%property (nonatomic, retain) UIWindow *launchWindow; + +- (_Bool)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2{ + if (!DKHelperConfig.dkLaunchEnable){return %orig; } + BOOL end = %orig; + DKLaunchViewController * launchVC = [[DKLaunchViewController alloc] init]; + UIWindow *launchWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; + self.launchWindow = launchWindow; + launchWindow.windowLevel = UIWindowLevelAlert + 1; + launchWindow.rootViewController = launchVC; + [launchWindow makeKeyAndVisible]; + return end; + +} +%end diff --git a/dkhelper/dkhelperDylib/MyUtils/DKHelper.h b/dkhelper/dkhelperDylib/MyUtils/DKHelper.h index 68fb958..99b783c 100644 --- a/dkhelper/dkhelperDylib/MyUtils/DKHelper.h +++ b/dkhelper/dkhelperDylib/MyUtils/DKHelper.h @@ -16,7 +16,8 @@ #import "DKHelperConfig.h" #import #import "NSArray+Utils.h" - +#import "DKLaunchHelper.h" +#import "DKLaunchViewController.h" //MARK: - quick objc finds #define FUiUtil objc_getClass("UiUtil") #define FMMUICommonUtil objc_getClass("MMUICommonUtil") @@ -45,6 +46,7 @@ typedef void(^BtnBlock)(UIButton *sender); @property (nonatomic,strong)dispatch_group_t checkFriendGroup; + /// 结束好友检测 + (void)endCheck; diff --git a/dkhelper/dkhelperDylib/MyUtils/DKHelperConfig.h b/dkhelper/dkhelperDylib/MyUtils/DKHelperConfig.h index b92773d..71d1322 100644 --- a/dkhelper/dkhelperDylib/MyUtils/DKHelperConfig.h +++ b/dkhelper/dkhelperDylib/MyUtils/DKHelperConfig.h @@ -106,6 +106,23 @@ NS_ASSUME_NONNULL_BEGIN +(BOOL)cleanFriendsEnable; +(void)setCleanFriendsEnable:(BOOL)value; +/// 启用动态启动图 ++(BOOL)dkLaunchEnable; ++(void)setDkLaunchEnable:(BOOL)value; + +/// 启用动态聊天背景 ++(BOOL)dkChatBgEnable; ++(void)setDkChatBgEnable:(BOOL)value; + + +/// 启动图index ++(NSNumber *)dkLaunchIndex; ++(void)setDkLaunchIndex:(NSNumber *)value; + +/// 聊天背景index ++(NSNumber *)dkChatBGIndex; ++(void)setDkChatBGIndex:(NSNumber *)value; + @end diff --git a/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.h b/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.h new file mode 100644 index 0000000..837c5b7 --- /dev/null +++ b/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.h @@ -0,0 +1,84 @@ +// DKLaunchHelper.h +// launchDemo +// Created by ___ORGANIZATIONNAME___ on 2021/1/18 +// +// +// +// ██████╗ ██╗ ██╗ ██╗ ██████╗ ███╗ ██╗███████╗ +// ██╔══██╗██║ ██╔╝ ██║██╔═══██╗████╗ ██║██╔════╝ +// ██║ ██║█████╔╝ ██║██║ ██║██╔██╗ ██║█████╗ +// ██║ ██║██╔═██╗ ██ ██║██║ ██║██║╚██╗██║██╔══╝ +// ██████╔╝██║ ██╗╚█████╔╝╚██████╔╝██║ ╚████║███████╗ +// ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ +// +// + +#import +#import +#import "QGVAPlayer.h" + +@interface DKLaunchHelper : NSObject ++ (void)clearLaunchScreenCache; ++ (NSArray*> *)animaNames; +@end + +/// 自定义校验规则,originImage为原始系统缓存启动图,yourImage为替代的启动图,返回YES代表本次替换,否则本次不会进行替换 +typedef BOOL(^BBACustomValicationBlock)(UIImage *originImage, UIImage *yourImage); + +/** + 动态启动图(请确保替换的图片尺寸与屏幕分辨率一致,否则替换不成功) +*/ +@interface BBADynamicLaunchImage : NSObject + +/// 替换系统缓存启动图,图片压缩质量默认0.8,覆盖原有系统启动图文件 +/// @param replacementImage 替代图片 +/// @return 替换是否成功 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage; + +/// 替换系统缓存启动图,将替换图片以指定的压缩质量写入系统启动图缓存目录,覆盖原有系统启动图文件 +/// @param replacementImage 替代图片 +/// @param quality 压缩质量 +/// @return 替换是否成功 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage compressionQuality:(CGFloat)quality; + +/// 替换系统缓存启动图,按自定义的校验规则将替代图片以指定的压缩质量写入系统启动图缓存目录,覆盖原有系统启动图文件 +/// @param replacementImage 替代图片 +/// @param quality 压缩质量 +/// @param validationBlock 自定义校验回调,校验通过表示本次替换,否则不替换 +/// @return 替换是否成功 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage + compressionQuality:(CGFloat)quality + customValidation:(BBACustomValicationBlock)validationBlock; + + + +/// 获取image对象 ++ (UIImage *)imageFromData:(NSData *)data; + +/// 获取图片大小 ++ (CGSize)getImageSize:(NSData *)imageData; + +@end + + +@interface LaunchImageHelper : NSObject + ++ (UIImage *)snapshotStoryboardForPortrait:(NSString *)sbName; ++ (UIImage *)snapshotStoryboardForLandscape:(NSString *)sbName; + +/// 替换所有的启动图为竖屏 ++ (void)changeAllLaunchImageToPortrait:(UIImage *)image; +/// 替换所有的启动图为横屏 ++ (void)changeAllLaunchImageToLandscape:(UIImage *)image; +/// 使用单独的图片分别替换竖、横屏启动图 ++ (void)changePortraitLaunchImage:(UIImage *)portraitImage + landscapeLaunchImage:(UIImage *)landScapeImage; + +@end + + +@interface UIView (DKScreenShoot) + +- (UIImage *)dkScreenShoot; + +@end diff --git a/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.m b/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.m new file mode 100644 index 0000000..5e8c951 --- /dev/null +++ b/dkhelper/dkhelperDylib/MyUtils/DKLaunchHelper.m @@ -0,0 +1,351 @@ +// DKLaunchHelper.m +// launchDemo +// Created by ___ORGANIZATIONNAME___ on 2021/1/18 +// +// +// +// ██████╗ ██╗ ██╗ ██╗ ██████╗ ███╗ ██╗███████╗ +// ██╔══██╗██║ ██╔╝ ██║██╔═══██╗████╗ ██║██╔════╝ +// ██║ ██║█████╔╝ ██║██║ ██║██╔██╗ ██║█████╗ +// ██║ ██║██╔═██╗ ██ ██║██║ ██║██║╚██╗██║██╔══╝ +// ██████╔╝██║ ██╗╚█████╔╝╚██████╔╝██║ ╚████║███████╗ +// ╚═════╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ +// +// + +#import "DKLaunchHelper.h" +#import + + +@implementation DKLaunchHelper + ++ (void)clearLaunchScreenCache { + // 异步删除缓存 + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSError *error; + [NSFileManager.defaultManager removeItemAtPath:[NSString stringWithFormat:@"%@/Library/SplashBoard",NSHomeDirectory()] error:&error]; + if (error) { + NSLog(@"Failed to delete launch screen cache: %@",error); + } + }); +} + ++ (NSArray *> *)animaNames{ + return @[ + @{@"name":@"earth",@"desc":@"地球",@"color":[UIColor colorWithRed:0.430 green:0.632 blue:0.854 alpha:1.000]}, + @{@"name":@"venus",@"desc":@"金星",@"color":[UIColor colorWithRed:0.662 green:0.392 blue:0.140 alpha:1.000]}, + @{@"name":@"mercury",@"desc":@"水星",@"color":[UIColor colorWithRed:0.629 green:0.594 blue:0.745 alpha:1.000]}, + @{@"name":@"mars",@"desc":@"火星",@"color":[UIColor colorWithRed:0.905 green:0.624 blue:0.519 alpha:1.000]}, + @{@"name":@"moon",@"desc":@"月球",@"color":[UIColor colorWithRed:0.831 green:0.831 blue:0.831 alpha:1.000]},//d4d4d4 + @{@"name":@"jupitre",@"desc":@"木星",@"color":[UIColor colorWithRed:0.629 green:0.542 blue:0.518 alpha:1.000]}, + @{@"name":@"nepture",@"desc":@"海王星",@"color":[UIColor colorWithRed:0.428 green:0.470 blue:0.753 alpha:1.000]}, + @{@"name":@"pluto",@"desc":@"冥王星",@"color":[UIColor colorWithRed:0.611 green:0.539 blue:0.531 alpha:1.000]}, + @{@"name":@"sun",@"desc":@"太阳",@"color":[UIColor colorWithRed:0.624 green:0.206 blue:0.117 alpha:1.000]}, + @{@"name":@"sature",@"desc":@"木星",@"color":[UIColor colorWithRed:0.582 green:0.520 blue:0.429 alpha:1.000]},//94856d + @{@"name":@"uranus",@"desc":@"天王星",@"color":[UIColor colorWithRed:0.436 green:0.677 blue:0.720 alpha:1.000]},//6fadb8 + @{@"name":@"sedna",@"desc":@"塞德娜",@"color":[UIColor colorWithRed:0.496 green:0.117 blue:0.067 alpha:1.000]},//7f1e11 + @{@"name":@"blackhole",@"desc":@"黑洞",@"color":[UIColor colorWithRed:0.688 green:0.398 blue:0.306 alpha:1.000]}//af664e + ]; +} +@end + + +@implementation BBADynamicLaunchImage + +/// 系统启动图缓存路径 ++ (NSString *)launchImageCacheDirectory { + + NSString *bundleID = [NSBundle mainBundle].infoDictionary[@"CFBundleIdentifier"]; + NSFileManager *fm = [NSFileManager defaultManager]; + + // iOS13之前 + NSString *cachesDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; + NSString *snapshotsPath = [[cachesDirectory stringByAppendingPathComponent:@"Snapshots"] stringByAppendingPathComponent:bundleID]; + if ([fm fileExistsAtPath:snapshotsPath]) { + return snapshotsPath; + } + + // iOS13 + NSString *libraryDirectory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]; + snapshotsPath = [NSString stringWithFormat:@"%@/SplashBoard/Snapshots/%@ - {DEFAULT GROUP}", libraryDirectory, bundleID]; + if ([fm fileExistsAtPath:snapshotsPath]) { + return snapshotsPath; + } + + return nil; +} + +/// 系统缓存启动图后缀名 ++ (BOOL)isSnapShotName:(NSString *)name { + // 新系统后缀 + NSString *snapshotSuffixs = @".ktx"; + if ([name hasSuffix:snapshotSuffixs]) { + return YES; + } + // 老系统后缀 + snapshotSuffixs = @".png"; + if ([name hasSuffix:snapshotSuffixs]) { + return YES; + } + return NO; +} + +/// 替换启动图 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage { + return [self replaceLaunchImage:replacementImage compressionQuality:0.8 customValidation:nil]; +} + +/// 替换启动图 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage compressionQuality:(CGFloat)quality { + return [self replaceLaunchImage:replacementImage compressionQuality:quality customValidation:nil]; +} + +/// 替换启动图 ++ (BOOL)replaceLaunchImage:(UIImage *)replacementImage + compressionQuality:(CGFloat)quality + customValidation:(BBACustomValicationBlock)validationBlock { + if (!replacementImage) return NO; + + // 转为jpeg + NSData *data = UIImageJPEGRepresentation(replacementImage, quality); + if (!data) return NO; + + // 检查图片尺寸是否等同屏幕分辨率 +// if (![self checkImageMatchScreenSize:replacementImage]) { +// return NO; +// } + + // 获取系统缓存启动图路径 + NSString *cacheDir = [self launchImageCacheDirectory]; + if (!cacheDir) return NO; + + // 工作目录 + NSString *cachesParentDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; + NSString *tmpDir = [cachesParentDir stringByAppendingPathComponent:@"_tmpLaunchImageCaches"]; + + // 清理工作目录 + NSFileManager *fm = [NSFileManager defaultManager]; + if ([fm fileExistsAtPath:tmpDir]) { + [fm removeItemAtPath:tmpDir error:nil]; + } + + // 移动系统缓存目录内容至工作目录 + BOOL moveResult = [fm moveItemAtPath:cacheDir toPath:tmpDir error:nil]; + if (!moveResult) return NO; + + // 操作工作目录 + // 记录需要操作的图片名 + NSMutableArray *cacheImageNames = [NSMutableArray array]; + for (NSString *name in [fm contentsOfDirectoryAtPath:tmpDir error:nil]) { + if ([self isSnapShotName:name]) { + [cacheImageNames addObject:name]; + } + } + + // 写入替换图片 + for (NSString *name in cacheImageNames) { + NSString *filePath = [tmpDir stringByAppendingPathComponent:name]; + // 自定义校验 + BOOL result = YES; + if (validationBlock) { + NSData *cachedImageData = [NSData dataWithContentsOfFile:filePath]; + UIImage *cachedImage = [self imageFromData:cachedImageData]; + if (cachedImage) { + result = validationBlock(cachedImage, replacementImage); + } + } + if (result) { + [data writeToFile:filePath atomically:YES]; + } + } + + // 还原系统缓存目录 + moveResult = [fm moveItemAtPath:tmpDir toPath:cacheDir error:nil]; + + // 清理工作目录 + if ([fm fileExistsAtPath:tmpDir]) { + [fm removeItemAtPath:tmpDir error:nil]; + } + + return YES; +} + +/// 获取image对象 ++ (UIImage *)imageFromData:(NSData *)data { + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); + if (source) { + CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); + if (imageRef) { + UIImage *originImage = [UIImage imageWithCGImage:imageRef]; + CFRelease(imageRef); + CFRelease(source); + return originImage; + } + } + return nil; +} + +/// 获取图片大小 ++ (CGSize)getImageSize:(NSData *)imageData { + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); + CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); + if (imageRef) { + CGFloat width = CGImageGetWidth(imageRef); + CGFloat height = CGImageGetHeight(imageRef); + CFRelease(imageRef); + CFRelease(source); + return CGSizeMake(width, height); + } + return CGSizeZero; +} + +/// 检查图片大小 ++ (BOOL)checkImageMatchScreenSize:(UIImage *)image { + CGSize screenSize = CGSizeApplyAffineTransform([UIScreen mainScreen].bounds.size, + CGAffineTransformMakeScale([UIScreen mainScreen].scale, + [UIScreen mainScreen].scale)); + CGSize imageSize = CGSizeApplyAffineTransform(image.size, + CGAffineTransformMakeScale(image.scale, image.scale)); + if (CGSizeEqualToSize(imageSize, screenSize)) { + return YES; + } + if (CGSizeEqualToSize(CGSizeMake(imageSize.height, imageSize.width), screenSize)) { + return YES; + } + return NO; +} + +@end + + +@implementation LaunchImageHelper + ++ (UIImage *)snapshotStoryboard:(NSString *)sbName isPortrait:(BOOL)isPortrait { + if (!sbName) { + return nil; + } + + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:sbName bundle:nil]; + UIViewController *vc = storyboard.instantiateInitialViewController; + vc.view.frame = [UIScreen mainScreen].bounds; + if (isPortrait) { + if (vc.view.frame.size.width > vc.view.frame.size.height) { + vc.view.frame = CGRectMake(0, 0, vc.view.frame.size.height, vc.view.frame.size.width); + } + } else { + if (vc.view.frame.size.width < vc.view.frame.size.height) { + vc.view.frame = CGRectMake(0, 0, vc.view.frame.size.height, vc.view.frame.size.width); + } + } + + [vc.view setNeedsLayout]; + [vc.view layoutIfNeeded]; + + UIGraphicsBeginImageContextWithOptions(vc.view.frame.size, NO, [UIScreen mainScreen].scale); + [vc.view.layer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + ++ (UIImage *)snapshotStoryboardForPortrait:(NSString *)sbName { + return [self snapshotStoryboard:sbName isPortrait:YES]; +} + ++ (UIImage *)snapshotStoryboardForLandscape:(NSString *)sbName { + return [self snapshotStoryboard:sbName isPortrait:NO]; +} + ++ (void)changeAllLaunchImageToPortrait:(UIImage *)image { + if (!image) { + return; + } + // 全部替换为竖屏启动图 + image = [self resizeImage:image toPortraitScreenSize:YES]; + [BBADynamicLaunchImage replaceLaunchImage:image]; +} + ++ (void)changeAllLaunchImageToLandscape:(UIImage *)image { + if (!image) { + return; + } + // 全部替换为横屏启动图 + image = [self resizeImage:image toPortraitScreenSize:NO]; + [BBADynamicLaunchImage replaceLaunchImage:image]; +} + ++ (void)changePortraitLaunchImage:(UIImage *)portraitImage + landscapeLaunchImage:(UIImage *)landscapeImage { + if (!portraitImage || !landscapeImage) { + return; + } + + // 替换竖屏启动图 + portraitImage = [self resizeImage:portraitImage toPortraitScreenSize:YES]; + [BBADynamicLaunchImage replaceLaunchImage:portraitImage compressionQuality:0.8 customValidation:^BOOL(UIImage *systemImage, UIImage *yourImage) { + return [self checkImage:systemImage sizeEqualToImage:yourImage]; + }]; + + // 替换横屏启动图 + landscapeImage = [self resizeImage:landscapeImage toPortraitScreenSize:NO]; + [BBADynamicLaunchImage replaceLaunchImage:landscapeImage compressionQuality:0.8 customValidation:^BOOL(UIImage *systemImage, UIImage *yourImage) { + return [self checkImage:systemImage sizeEqualToImage:yourImage]; + }]; +} + +// 通过图片尺寸匹配,竖屏方向图只替换竖屏,横屏方向图只替换横屏 ++ (BOOL)checkImage:(UIImage *)aImage sizeEqualToImage:(UIImage *)bImage { + return CGSizeEqualToSize([self obtainImageSize:aImage], [self obtainImageSize:bImage]); +} + ++ (CGSize)obtainImageSize:(UIImage *)image { + return CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage)); +} + ++ (CGSize)contextSizeForPortrait:(BOOL)isPortrait { + CGFloat screenScale = [UIScreen mainScreen].scale; + CGSize screenSize = [UIScreen mainScreen].bounds.size; + CGFloat width = MIN(screenSize.width, screenSize.height);; + CGFloat height = MAX(screenSize.width, screenSize.height); + if (!isPortrait) { + width = MAX(screenSize.width, screenSize.height); + height = MIN(screenSize.width, screenSize.height); + } + CGSize contextSize = CGSizeMake(width * screenScale, height * screenScale); + return contextSize; +} + ++ (UIImage *)resizeImage:(UIImage *)image toPortraitScreenSize:(BOOL)isPortrait { + CGSize imageSize = CGSizeApplyAffineTransform(image.size, + CGAffineTransformMakeScale(image.scale, image.scale)); + CGSize contextSize = [self contextSizeForPortrait:isPortrait]; + + if (!CGSizeEqualToSize(imageSize, contextSize)) { + UIGraphicsBeginImageContext(contextSize); + CGFloat ratio = MAX((contextSize.width / image.size.width), + (contextSize.height / image.size.height)); + CGRect rect = CGRectMake(0, 0, image.size.width * ratio, image.size.height * ratio); + [image drawInRect:rect]; + UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return resizedImage; + } + + return image; +} + +@end + + +@implementation UIView (DKScreenShoot) + +- (UIImage *)dkScreenShoot{ + UIImage *imageRet = nil; + UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0); + [self.layer renderInContext:UIGraphicsGetCurrentContext()]; + imageRet = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return imageRet; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDMetalShaderSourceDefine.h b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDMetalShaderSourceDefine.h new file mode 100644 index 0000000..6255ff4 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDMetalShaderSourceDefine.h @@ -0,0 +1,224 @@ +// QGHWDMetalShaderSourceDefine.h +// 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. + +#ifndef QGHWDMetalShaderSourceDefine_h +#define QGHWDMetalShaderSourceDefine_h + +#import "VAPMacros.h" +#import "QGHWDShaderTypes.h" + +/* + !!!!!!!!!important!!!!!!!!! + !!所有.metal文件更新,都需要同步到这个文件中!! + !!本文件内着色器代码作为兜底逻辑,当无法找到预编译着色器时使用本文件定义的着色器字符串进行编译! + !!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +//The source may only import the Metal standard library. There is no search path to find other functions. + +//头文件引入 +static NSString * const kQGHWDMetalShaderSourceImports = +@"#include \n#import \n"; + +//类型定义 +static NSString * const kQGHWDMetalShaderTypeDefines = +SHADER_STRING( + typedef struct { + + packed_float4 position; + packed_float2 textureColorCoordinate; + packed_float2 textureAlphaCoordinate; + } QGHWDVertex; + + typedef struct { + packed_float4 position; + packed_float2 textureColorCoordinate; + packed_float2 textureAlphaCoordinate; + packed_float2 textureMaskCoordinate; + } QGVAPVertex; + + struct ColorParameters { + + matrix_float3x3 matrix; + packed_float2 offset; + }; + + struct MaskParameters { + matrix_float3x3 weightMatrix; + int coreSize; + float texelOffset; + }; + + typedef struct { + packed_float4 position; + packed_float2 sourceTextureCoordinate; + packed_float2 maskTextureCoordinate; + } QGHWDAttachmentVertex; + + struct VapAttachmentFragmentParameter { + + int needOriginRGB; + packed_float4 fillColor; + }; +); + +//着色器代码 +static NSString * const kQGHWDMetalShaderSourceString = +SHADER_STRING( + + //QGHWDShaders.metal + using namespace metal; + typedef struct { + + float4 clipSpacePostion [[ position ]]; + float2 textureColorCoordinate; + float2 textureAlphaCoordinate; + } HWDRasterizerData; + + typedef struct { + + float4 clipSpacePostion [[ position ]]; + float2 textureColorCoordinate; + float2 textureAlphaCoordinate; + float2 textureMaskCoordinate; + } VAPRasterizerData; + + typedef struct { + + float4 position [[ position ]]; + float2 sourceTextureCoordinate; + float2 maskTextureCoordinate; + } VAPAttachmentRasterizerData; + + float3 RGBColorFromYuvTextures(sampler textureSampler, float2 coordinate, texture2d texture_luma, texture2d texture_chroma, matrix_float3x3 rotationMatrix, float2 offset) { + + float3 color; + color.x = texture_luma.sample(textureSampler, coordinate).r; + color.yz = texture_chroma.sample(textureSampler, coordinate).rg - offset; + return float3(rotationMatrix * color); + } + + float4 RGBAColor(sampler textureSampler, float2 colorCoordinate, float2 alphaCoordinate, texture2d lumaTexture, texture2d chromaTexture, constant ColorParameters *colorParameters) { + matrix_float3x3 rotationMatrix = colorParameters[0].matrix; + float2 offset = colorParameters[0].offset; + float3 color = RGBColorFromYuvTextures(textureSampler, colorCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + float3 alpha = RGBColorFromYuvTextures(textureSampler, alphaCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + return float4(color, alpha.r); + } + + vertex HWDRasterizerData hwd_vertexShader(uint vertexID [[ vertex_id ]], constant QGHWDVertex *vertexArray [[ buffer(0) ]]) { + + HWDRasterizerData out; + out.clipSpacePostion = vertexArray[vertexID].position; + out.textureColorCoordinate = vertexArray[vertexID].textureColorCoordinate; + out.textureAlphaCoordinate = vertexArray[vertexID].textureAlphaCoordinate; + return out; + } + + fragment float4 hwd_yuvFragmentShader(HWDRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + return RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + } + + vertex VAPRasterizerData vap_vertexShader(uint vertexID [[ vertex_id ]], constant QGVAPVertex *vertexArray [[ buffer(0) ]]) { + + VAPRasterizerData out; + out.clipSpacePostion = vertexArray[vertexID].position; + out.textureColorCoordinate = vertexArray[vertexID].textureColorCoordinate; + out.textureAlphaCoordinate = vertexArray[vertexID].textureAlphaCoordinate; + out.textureMaskCoordinate = vertexArray[vertexID].textureMaskCoordinate; + return out; + } + + fragment float4 vap_yuvFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + return RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + } + + fragment float4 vap_maskFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d maskTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + float4 originColor = RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + float4 maskColor = maskTexture.sample(textureSampler, input.textureMaskCoordinate); + float needMask = maskColor.r * 255; + return float4(originColor.rgb, (1 - needMask) * originColor.a); + } + + fragment float4 vap_maskBlurFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d maskTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]], + constant MaskParameters *maskParameters [[ buffer(1) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + float4 originColor = RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + + int uniform = 255; + float3x3 weightMatrix = maskParameters[0].weightMatrix; + int coreSize = maskParameters[0].coreSize; + float texelOffset = maskParameters[0].texelOffset; + float alphaResult = 0; + + // 循环9次可以写成for循环 + for (int y = 0; y < coreSize; y++) { + for (int x = 0; x < coreSize; x++) { + float2 nearMaskColor = float2(input.textureMaskCoordinate.x + (-1.0 + float(x)) * texelOffset, input.textureMaskCoordinate.y + (-1.0 + float(y)) * texelOffset); + alphaResult += maskTexture.sample(textureSampler, nearMaskColor).r * uniform * weightMatrix[x][y]; + } + } + + int needOrigin = step(alphaResult, 0.01) + step(originColor.a, 0.01); + return float4(originColor.rgb, needOrigin * originColor.a + (1 - needOrigin) * (1 - alphaResult)); + } + + vertex VAPAttachmentRasterizerData vapAttachment_vertexShader(uint vertexID [[ vertex_id ]], constant QGHWDAttachmentVertex *vertexArray [[ buffer(0) ]]) { + + VAPAttachmentRasterizerData out; + out.position = vertexArray[vertexID].position; + out.sourceTextureCoordinate = vertexArray[vertexID].sourceTextureCoordinate; + out.maskTextureCoordinate = vertexArray[vertexID].maskTextureCoordinate; + return out; + } + + fragment float4 vapAttachment_FragmentShader(VAPAttachmentRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d sourceTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]], + constant VapAttachmentFragmentParameter *fillParams [[ buffer(1) ]]) { + + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + matrix_float3x3 rotationMatrix = colorParameters[0].matrix; + float2 offset = colorParameters[0].offset; + float3 mask = RGBColorFromYuvTextures(textureSampler, input.maskTextureCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + float4 source = sourceTexture.sample(textureSampler, input.sourceTextureCoordinate); + return float4(source.rgb, source.a * mask.r); + } + ); + +#endif /* QGHWDMetalShaderSourceDefine_h */ diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaderTypes.h b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaderTypes.h new file mode 100644 index 0000000..c61e88c --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaderTypes.h @@ -0,0 +1,68 @@ +// QGHWDShaderTypes.h +// 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. + +#ifndef QGHWDShaderTypes_h +#define QGHWDShaderTypes_h + +/* + 请注意:更新本文件后请同步到QGHWDMetalShaderSourceDefine.h + */ +#import + +typedef struct { + packed_float4 position; + packed_float2 textureColorCoordinate; + packed_float2 textureAlphaCoordinate; +} QGHWDVertex; + +typedef struct { + packed_float4 position; + packed_float2 textureColorCoordinate; + packed_float2 textureAlphaCoordinate; + packed_float2 textureMaskCoordinate; +} QGVAPVertex; + +struct ColorParameters { + + matrix_float3x3 matrix; + packed_float2 offset; +}; + +struct MaskParameters { + matrix_float3x3 weightMatrix; + int coreSize; + float texelOffset; +}; + +struct VapAttachmentFragmentParameter { + + int needOriginRGB; + packed_float4 fillColor; +}; + +typedef struct { + packed_float4 position; + packed_float2 sourceTextureCoordinate; + packed_float2 maskTextureCoordinate; +} QGHWDAttachmentVertex; + +typedef enum QGHWDYUVFragmentTextureIndex { + + QGHWDYUVFragmentTextureIndexLuma = 0, + QGHWDYUVFragmentTextureIndexChroma = 1, + QGHWDYUVFragmentTextureIndexAttachmentStart = 2, +} QGHWDYUVFragmentTextureIndex; + +#endif /* QGHWDShaderTypes_h */ diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaders.metal b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaders.metal new file mode 100644 index 0000000..fb1efa6 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/Shaders/QGHWDShaders.metal @@ -0,0 +1,163 @@ +// Shaders.metal +// 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. + +#include +#import "QGHWDShaderTypes.h" + +/* + 请注意:更新本文件后请同步到QGHWDMetalShaderSourceDefine.h + */ +using namespace metal; + +typedef struct { + + float4 clipSpacePostion [[ position ]]; + float2 textureColorCoordinate; + float2 textureAlphaCoordinate; +} HWDRasterizerData; + +typedef struct { + + float4 clipSpacePostion [[ position ]]; + float2 textureColorCoordinate; + float2 textureAlphaCoordinate; + float2 textureMaskCoordinate; +} VAPRasterizerData; + +typedef struct { + + float4 position [[ position ]]; + float2 sourceTextureCoordinate; + float2 maskTextureCoordinate; +} VAPAttachmentRasterizerData; + +float3 RGBColorFromYuvTextures(sampler textureSampler, float2 coordinate, texture2d texture_luma, texture2d texture_chroma, matrix_float3x3 rotationMatrix, float2 offset) { + + float3 color; + color.x = texture_luma.sample(textureSampler, coordinate).r; + color.yz = texture_chroma.sample(textureSampler, coordinate).rg - offset; + return float3(rotationMatrix * color); +} + +float4 RGBAColor(sampler textureSampler, float2 colorCoordinate, float2 alphaCoordinate, texture2d lumaTexture, texture2d chromaTexture, constant ColorParameters *colorParameters) { + matrix_float3x3 rotationMatrix = colorParameters[0].matrix; + float2 offset = colorParameters[0].offset; + float3 color = RGBColorFromYuvTextures(textureSampler, colorCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + float3 alpha = RGBColorFromYuvTextures(textureSampler, alphaCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + return float4(color, alpha.r); +} + +vertex HWDRasterizerData hwd_vertexShader(uint vertexID [[ vertex_id ]], constant QGHWDVertex *vertexArray [[ buffer(0) ]]) { + + HWDRasterizerData out; + out.clipSpacePostion = vertexArray[vertexID].position; + out.textureColorCoordinate = vertexArray[vertexID].textureColorCoordinate; + out.textureAlphaCoordinate = vertexArray[vertexID].textureAlphaCoordinate; + return out; +} + +fragment float4 hwd_yuvFragmentShader(HWDRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + return RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); +} + +vertex VAPRasterizerData vap_vertexShader(uint vertexID [[ vertex_id ]], constant QGVAPVertex *vertexArray [[ buffer(0) ]]) { + + VAPRasterizerData out; + out.clipSpacePostion = vertexArray[vertexID].position; + out.textureColorCoordinate = vertexArray[vertexID].textureColorCoordinate; + out.textureAlphaCoordinate = vertexArray[vertexID].textureAlphaCoordinate; + out.textureMaskCoordinate = vertexArray[vertexID].textureMaskCoordinate; + return out; +} + +fragment float4 vap_yuvFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + return RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); +} + +fragment float4 vap_maskFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d maskTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + float4 originColor = RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + float4 maskColor = maskTexture.sample(textureSampler, input.textureMaskCoordinate); + float needMask = maskColor.r * 255; + return float4(originColor.rgb, (1 - needMask) * originColor.a); +} + +fragment float4 vap_maskBlurFragmentShader(VAPRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d maskTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]], + constant MaskParameters *maskParameters [[ buffer(1) ]]) { + //signifies that an expression may be computed at compile-time rather than runtime + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + float4 originColor = RGBAColor(textureSampler, input.textureColorCoordinate, input.textureAlphaCoordinate, lumaTexture, chromaTexture, colorParameters); + + int uniform = 255; + float3x3 weightMatrix = maskParameters[0].weightMatrix; + int coreSize = maskParameters[0].coreSize; + float texelOffset = maskParameters[0].texelOffset; + float alphaResult = 0; + + // 循环9次可以写成for循环 + for (int y = 0; y < coreSize; y++) { + for (int x = 0; x < coreSize; x++) { + float2 nearMaskColor = float2(input.textureMaskCoordinate.x + (-1.0 + float(x)) * texelOffset, input.textureMaskCoordinate.y + (-1.0 + float(y)) * texelOffset); + alphaResult += maskTexture.sample(textureSampler, nearMaskColor).r * uniform * weightMatrix[x][y]; + } + } + + int needOrigin = step(alphaResult, 0.01) + step(originColor.a, 0.01); + return float4(originColor.rgb, needOrigin * originColor.a + (1 - needOrigin) * (1 - alphaResult)); +} + +vertex VAPAttachmentRasterizerData vapAttachment_vertexShader(uint vertexID [[ vertex_id ]], constant QGHWDAttachmentVertex *vertexArray [[ buffer(0) ]]) { + + VAPAttachmentRasterizerData out; + out.position = vertexArray[vertexID].position; + out.sourceTextureCoordinate = vertexArray[vertexID].sourceTextureCoordinate; + out.maskTextureCoordinate = vertexArray[vertexID].maskTextureCoordinate; + return out; +} + +fragment float4 vapAttachment_FragmentShader(VAPAttachmentRasterizerData input [[ stage_in ]], + texture2d lumaTexture [[ texture(0) ]], + texture2d chromaTexture [[ texture(1) ]], + texture2d sourceTexture [[ texture(2) ]], + constant ColorParameters *colorParameters [[ buffer(0) ]], + constant VapAttachmentFragmentParameter *fillParams [[ buffer(1) ]]) { + + constexpr sampler textureSampler (mag_filter::linear, min_filter::linear); + matrix_float3x3 rotationMatrix = colorParameters[0].matrix; + float2 offset = colorParameters[0].offset; + float3 mask = RGBColorFromYuvTextures(textureSampler, input.maskTextureCoordinate, lumaTexture, chromaTexture, rotationMatrix, offset); + float4 source = sourceTexture.sample(textureSampler, input.sourceTextureCoordinate); + return float4(source.rgb, source.a * mask.r); +} + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.h new file mode 100644 index 0000000..436da3b --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.h @@ -0,0 +1,30 @@ +// QGBaseDecoder.h +// 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 +#import "QGAnimatedImageDecodeThread.h" +#import "QGBaseDFileInfo.h" + +@interface QGBaseDecoder : NSObject + +@property (atomic, assign) NSInteger currentDecodeFrame; //正在解码的帧索引 +@property (nonatomic, readonly) QGBaseDFileInfo *fileInfo; //解码文件信息 只能通过初始化方法赋值 + +- (instancetype)initWith:(QGBaseDFileInfo *)fileInfo error:(NSError **)error; +- (void)decodeFrame:(NSInteger)frameIndex buffers:(NSMutableArray *)buffers; +- (BOOL)shouldStopDecode:(NSInteger)nextFrameIndex; +- (BOOL)isFrameIndexBeyondEnd:(NSInteger)frameIndex; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.m new file mode 100644 index 0000000..190c57f --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGBaseDecoder.m @@ -0,0 +1,70 @@ +// QGBaseDecoder.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 "QGBaseDecoder.h" +#import "QGAnimatedImageDecodeThreadPool.h" + +@interface QGBaseDecoder() { + + QGBaseDFileInfo *_fileInfo; +} + +@end + +@implementation QGBaseDecoder + +- (instancetype)initWith:(QGBaseDFileInfo *)fileInfo error:(NSError **)error { + + if (self = [super init]) { + _currentDecodeFrame = -1; + _fileInfo = fileInfo; + _fileInfo.occupiedCount ++; + } + return self; +} + +- (QGBaseDFileInfo *)fileInfo { + + return _fileInfo; +} + +/** + 由具体子类实现 + 该方法在decodeframe方法即将被调用时调用,如果返回YES则停止解码工作 + + @param nextFrameIndex 将要解码的帧索引 + @return 是否需要继续解码 + */ +- (BOOL)shouldStopDecode:(NSInteger)nextFrameIndex { + // No implementation here. Meant to be overriden in subclass. + return NO; +} + +- (BOOL)isFrameIndexBeyondEnd:(NSInteger)frameIndex { + + return NO; +} + +/** + 在专用线程内解码指定帧并放入对应的缓冲区内 + + @param frameIndex 帧索引 + @param buffers 缓冲 + */ +- (void)decodeFrame:(NSInteger)frameIndex buffers:(NSMutableArray *)buffers { + // No implementation here. Meant to be overriden in subclass. +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.h new file mode 100644 index 0000000..77b9cdc --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.h @@ -0,0 +1,42 @@ +// QGMP4FrameHWDecoder.h +// 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 "QGBaseDecoder.h" +#import "QGMP4HWDFileInfo.h" +#import + +/* 数字跳动的动画类型*/ +typedef NS_ENUM(NSInteger, QGMP4HWDErrorCode){ + + QGMP4HWDErrorCode_FileNotExist = 10000, // 文件不存在 + QGMP4HWDErrorCode_InvalidMP4File = 10001, // 非法的mp4文件 + QGMP4HWDErrorCode_CanNotGetStreamInfo = 10002, // 无法获取视频流信息 + QGMP4HWDErrorCode_CanNotGetStream = 10003, // 无法获取视频流 + QGMP4HWDErrorCode_ErrorCreateVTBDesc = 10004, // 创建desc失败 + QGMP4HWDErrorCode_ErrorCreateVTBSession = 10005, // 创建session失败 +}; + +@interface UIDevice (HWD) + +- (BOOL)hwd_isSimulator; + +@end + +@interface QGMP4FrameHWDecoder : QGBaseDecoder + + ++ (NSString *)errorDescriptionForCode:(QGMP4HWDErrorCode)errorCode; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.m new file mode 100644 index 0000000..c914919 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/Decoders/QGMP4FrameHWDecoder.m @@ -0,0 +1,483 @@ +// QGMP4FrameHWDecoder.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 "QGMP4FrameHWDecoder.h" +#import "QGVAPWeakProxy.h" +#import "QGMP4AnimatedImageFrame.h" +#import "QGBaseAnimatedImageFrame+Displaying.h" +#import +#import "QGHWDMP4OpenGLView.h" +#import "QGMP4Parser.h" +#import "QGVAPSafeMutableArray.h" +#import "NSNotificationCenter+VAPThreadSafe.h" +#include +#import + +@implementation UIDevice (HWD) + +- (BOOL)hwd_isSimulator { + static dispatch_once_t token; + static BOOL isSimulator = NO; + dispatch_once(&token, ^{ + NSString *model = [self machineName]; + if ([model isEqualToString:@"x86_64"] || [model isEqualToString:@"i386"]) { + isSimulator = YES; + } + }); + return isSimulator; +} + +- (NSString *)machineName { + static dispatch_once_t token; + static NSString *name; + dispatch_once(&token, ^{ + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char *machineName = malloc(size); + sysctlbyname("hw.machine", machineName, &size, NULL, 0); + name = [NSString stringWithUTF8String:machineName]; + free(machineName); + }); + return name; +} + +@end + +@interface NSArray (SafeOperation) + +@end + +@implementation NSArray (SafeOperation) + +- (id)safeObjectAtIndex:(NSUInteger)index +{ + if (index >= self.count) { + NSAssert(0, @"Error: access to array index which is beyond bounds! "); + return nil; + } + + return self[index]; +} + +@end + +@interface QGMP4FrameHWDecoder() { + + NSMutableArray *_buffers; + + int _videoStream; + int _outputWidth, _outputHeight; + OSStatus _status; + BOOL _isFinish; + VTDecompressionSessionRef _mDecodeSession; + CMFormatDescriptionRef _mFormatDescription; + NSInteger _finishFrameIndex; + NSError *_constructErr; + QGMP4ParserProxy *_mp4Parser; +} + +@property (atomic, strong) dispatch_queue_t decodeQueue; //dispatch decode task +@property (nonatomic, strong) NSData *ppsData; //Picture Parameter Set +@property (nonatomic, strong) NSData *spsData; //Sequence Parameter Set +/** Video Parameter Set */ +@property (nonatomic, strong) NSData *vpsData; + +@end + +NSString *const QGMP4HWDErrorDomain = @"QGMP4HWDErrorDomain"; + +@implementation QGMP4FrameHWDecoder + ++ (NSString *)errorDescriptionForCode:(QGMP4HWDErrorCode)errorCode { + + NSArray *errorDescs = @[@"文件不存在",@"非法文件格式",@"无法获取视频流信息",@"无法获取视频流",@"VTB创建desc失败",@"VTB创建session失败"]; + NSString *desc = @""; + switch (errorCode) { + case QGMP4HWDErrorCode_FileNotExist: + desc = [errorDescs safeObjectAtIndex:0]; + break; + case QGMP4HWDErrorCode_InvalidMP4File: + desc = [errorDescs safeObjectAtIndex:1]; + break; + case QGMP4HWDErrorCode_CanNotGetStreamInfo: + desc = [errorDescs safeObjectAtIndex:2]; + break; + case QGMP4HWDErrorCode_CanNotGetStream: + desc = [errorDescs safeObjectAtIndex:3]; + break; + case QGMP4HWDErrorCode_ErrorCreateVTBDesc: + desc = [errorDescs safeObjectAtIndex:4]; + break; + case QGMP4HWDErrorCode_ErrorCreateVTBSession: + desc = [errorDescs safeObjectAtIndex:5]; + break; + + default: + break; + } + return desc; +} + +- (instancetype)initWith:(QGMP4HWDFileInfo *)fileInfo error:(NSError *__autoreleasing *)error{ + + if (self = [super initWith:fileInfo error:error]) { + _decodeQueue = dispatch_queue_create("com.qgame.vap.decode", DISPATCH_QUEUE_SERIAL); + _mp4Parser = fileInfo.mp4Parser; + BOOL isOpenSuccess = [self onInputStart]; + if (!isOpenSuccess) { + VAP_Event(kQGVAPModuleCommon, @"onInputStart fail!"); + *error = _constructErr; + self = nil; + return nil; + } + [self registerNotification]; + } + return self; +} + +- (void)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]; +} + +- (void)hwd_didReceiveEnterBackgroundNotification:(NSNotification *)notification { + + [self onInputEnd]; +} + +- (void)decodeFrame:(NSInteger)frameIndex buffers:(NSMutableArray *)buffers { + + if (frameIndex == self.currentDecodeFrame) { + VAP_Event(kQGVAPModuleCommon, @"already in decode"); + return ; + } + self.currentDecodeFrame = frameIndex; + _buffers = buffers; + dispatch_async(self.decodeQueue, ^{ + [self _decodeFrame:frameIndex]; + }); +} + +- (void)_decodeFrame:(NSInteger)frameIndex { + + if (_isFinish) { + return ; + } + + if (!_buffers) { + return ; + } + + if (self.spsData == nil || self.ppsData == nil) { + return ; + } + + //解码开始时间 + NSDate *startDate = [NSDate date]; + NSData *packetData = [_mp4Parser readPacketOfSample:frameIndex]; + if (!packetData.length) { + _finishFrameIndex = frameIndex; + [self onInputEnd]; + return; + } + + // 获取当前帧pts,pts是在parse mp4 box时得到的 + uint64_t currentPts = [_mp4Parser.videoSamples[frameIndex] pts]; + + CVPixelBufferRef outputPixelBuffer = NULL; + // 4. get NALUnit payload into a CMBlockBuffer, + CMBlockBufferRef blockBuffer = NULL; + + _status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, + (void *)packetData.bytes, + packetData.length, + kCFAllocatorNull, NULL, 0, + packetData.length, 0, + &blockBuffer); + // 6. create a CMSampleBuffer. + CMSampleBufferRef sampleBuffer = NULL; + const size_t sampleSizeArray[] = {packetData.length}; + _status = CMSampleBufferCreateReady(kCFAllocatorDefault, + blockBuffer, + _mFormatDescription, + 1, 0, NULL, 1, sampleSizeArray, + &sampleBuffer); + + if (blockBuffer) { + CFRelease(blockBuffer); + } + // 7. use VTDecompressionSessionDecodeFrame + if (@available(iOS 9.0, *)) { + __typeof(self) __weak weakSelf = self; + VTDecodeFrameFlags flags = 0; + VTDecodeInfoFlags flagOut = 0; + VTDecompressionSessionDecodeFrameWithOutputHandler(_mDecodeSession, sampleBuffer, flags, &flagOut, ^(OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef _Nullable imageBuffer, CMTime presentationTimeStamp, CMTime presentationDuration) { + CFRelease(sampleBuffer); + __typeof(self) strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + if(status == kVTInvalidSessionErr) { + VAP_Error(kQGVAPModuleCommon, @"decompress fail! frame:%@ kVTInvalidSessionErr error:%@", @(frameIndex), @(status)); + } else if(status == kVTVideoDecoderBadDataErr) { + VAP_Error(kQGVAPModuleCommon, @"decompress fail! frame:%@ kVTVideoDecoderBadDataErr error:%@", @(frameIndex), @(status)); + } else if(status != noErr) { + VAP_Error(kQGVAPModuleCommon, @"decompress fail! frame:%@ error:%@", @(frameIndex), @(status)); + } + + QGMP4AnimatedImageFrame *newFrame = [[QGMP4AnimatedImageFrame alloc] init]; + // imagebuffer会在frame回收时释放 + CVPixelBufferRetain(imageBuffer); + newFrame.pixelBuffer = imageBuffer; + newFrame.frameIndex = frameIndex; //dts顺序 + NSTimeInterval decodeTime = [[NSDate date] timeIntervalSinceDate:startDate]*1000; + newFrame.decodeTime = decodeTime; + newFrame.defaultFps =(int) strongSelf->_mp4Parser.fps; + newFrame.pts = currentPts; + + // 8. insert into buffer + [strongSelf->_buffers addObject:newFrame]; + + // 9. sort + [strongSelf->_buffers sortUsingComparator:^NSComparisonResult(QGMP4AnimatedImageFrame * _Nonnull obj1, QGMP4AnimatedImageFrame * _Nonnull obj2) { + return [@(obj1.pts) compare:@(obj2.pts)]; + }]; + }); + } else { + // 7. use VTDecompressionSessionDecodeFrame + VTDecodeFrameFlags flags = 0; + VTDecodeInfoFlags flagOut = 0; + _status = VTDecompressionSessionDecodeFrame(_mDecodeSession, sampleBuffer, flags, &outputPixelBuffer, &flagOut); + + if(_status == kVTInvalidSessionErr) { + } else if(_status == kVTVideoDecoderBadDataErr) { + } else if(_status != noErr) { + } + CFRelease(sampleBuffer); + + QGMP4AnimatedImageFrame *newFrame = [[QGMP4AnimatedImageFrame alloc] init]; + // imagebuffer会在frame回收时释放 + newFrame.pixelBuffer = outputPixelBuffer; + newFrame.frameIndex = frameIndex; + NSTimeInterval decodeTime = [[NSDate date] timeIntervalSinceDate:startDate]*1000; + newFrame.decodeTime = decodeTime; + newFrame.defaultFps = (int)_mp4Parser.fps; + + // 8. insert into buffer + [_buffers addObject:newFrame]; + + // 9. sort + [_buffers sortUsingComparator:^NSComparisonResult(QGMP4AnimatedImageFrame * _Nonnull obj1, QGMP4AnimatedImageFrame * _Nonnull obj2) { + return [@(obj1.pts) compare:@(obj2.pts)]; + }]; + } +} + +#pragma mark - override + +- (BOOL)shouldStopDecode:(NSInteger)nextFrameIndex { + return _isFinish; +} + +- (BOOL)isFrameIndexBeyondEnd:(NSInteger)frameIndex { + + if (_finishFrameIndex > 0) { + return (frameIndex >= _finishFrameIndex); + } + return NO; +} + +-(void)dealloc { + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self _onInputEnd]; + self.fileInfo.occupiedCount --; + if (self.fileInfo.occupiedCount <= 0) { + + } +} + +#pragma mark - private methods + +- (BOOL)onInputStart { + + NSFileManager *fileMgr = [NSFileManager defaultManager]; + if (![fileMgr fileExistsAtPath:self.fileInfo.filePath]) { + _constructErr = [NSError errorWithDomain:QGMP4HWDErrorDomain code:QGMP4HWDErrorCode_FileNotExist userInfo:[self errorUserInfo]]; + return NO; + } + + _isFinish = NO; + self.vpsData = nil; + self.spsData = nil; + self.ppsData = nil; + _outputWidth = (int)_mp4Parser.picWidth; + _outputHeight = (int)_mp4Parser.picHeight; + + BOOL paramsSetInitSuccess = [self initPPSnSPS]; + return paramsSetInitSuccess; +} + +- (BOOL)initPPSnSPS { + + VAP_Info(kQGVAPModuleCommon, @"initPPSnSPS"); + if (self.spsData && self.ppsData) { + VAP_Error(kQGVAPModuleCommon, @"sps&pps is already has value."); + return YES; + } + + self.spsData = _mp4Parser.spsData; + self.ppsData = _mp4Parser.ppsData; + self.vpsData = _mp4Parser.vpsData; + + // 2. create CMFormatDescription + if (self.spsData != nil && self.ppsData != nil && _mp4Parser.videoCodecID != QGMP4VideoStreamCodecIDUnknown) { + if (_mp4Parser.videoCodecID == QGMP4VideoStreamCodecIDH264) { + const uint8_t* const parameterSetPointers[2] = { (const uint8_t*)[self.spsData bytes], (const uint8_t*)[self.ppsData bytes] }; + const size_t parameterSetSizes[2] = { [self.spsData length], [self.ppsData length] }; + + _status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault, + 2, + parameterSetPointers, + parameterSetSizes, + 4, + &_mFormatDescription); + if (_status != noErr) { + VAP_Event(kQGVAPModuleCommon, @"CMVideoFormatDescription. Creation: %@.", (_status == noErr) ? @"successfully." : @"failed."); + _constructErr = [NSError errorWithDomain:QGMP4HWDErrorDomain code:QGMP4HWDErrorCode_ErrorCreateVTBDesc userInfo:[self errorUserInfo]]; + return NO; + } + } else if (_mp4Parser.videoCodecID == QGMP4VideoStreamCodecIDH265) { + if (@available(iOS 11.0, *)) { + if(VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) { + const uint8_t* const parameterSetPointers[3] = {(const uint8_t*)[self.vpsData bytes], (const uint8_t*)[self.spsData bytes], (const uint8_t*)[self.ppsData bytes]}; + const size_t parameterSetSizes[3] = {[self.vpsData length], [self.spsData length], [self.ppsData length]}; + + _status = CMVideoFormatDescriptionCreateFromHEVCParameterSets(kCFAllocatorDefault, + 3, // parameter_set_count + parameterSetPointers, // ¶meter_set_pointers + parameterSetSizes, // ¶meter_set_sizes + 4, // nal_unit_header_length + NULL, + &_mFormatDescription); + if (_status != noErr) { + VAP_Event(kQGVAPModuleCommon, @"CMVideoFormatDescription. Creation: %@.", (_status == noErr) ? @"successfully." : @"failed."); + _constructErr = [NSError errorWithDomain:QGMP4HWDErrorDomain code:QGMP4HWDErrorCode_ErrorCreateVTBDesc userInfo:[self errorUserInfo]]; + return NO; + } + } else { + VAP_Event(kQGVAPModuleCommon, @"H.265 decoding is un-supported because of the hardware"); + return NO; + } + } else { + VAP_Event(kQGVAPModuleCommon, @"System version is too low to support H.265 decoding"); + return NO; + } + } + } + + // 3. create VTDecompressionSession + CFDictionaryRef attrs = NULL; + const void *keys[] = {kCVPixelBufferPixelFormatTypeKey}; + // kCVPixelFormatType_420YpCbCr8Planar is YUV420 + // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange is NV12 + uint32_t v = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; + const void *values[] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &v) }; + attrs = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); + + if ([UIDevice currentDevice].systemVersion.floatValue >= 9.0) { + _status = VTDecompressionSessionCreate(kCFAllocatorDefault, + _mFormatDescription, + NULL, + attrs, + NULL, + &_mDecodeSession); + if (_status != noErr) { + CFRelease(attrs); + _constructErr = [NSError errorWithDomain:QGMP4HWDErrorDomain code:QGMP4HWDErrorCode_ErrorCreateVTBSession userInfo:[self errorUserInfo]]; + return NO; + } + } else { + VTDecompressionOutputCallbackRecord callBackRecord; + callBackRecord.decompressionOutputCallback = didDecompress; + callBackRecord.decompressionOutputRefCon = NULL; + + _status = VTDecompressionSessionCreate(kCFAllocatorDefault, + _mFormatDescription, + NULL, attrs, + &callBackRecord, + &_mDecodeSession); + if (_status != noErr) { + CFRelease(attrs); + _constructErr = [NSError errorWithDomain:QGMP4HWDErrorDomain code:QGMP4HWDErrorCode_ErrorCreateVTBSession userInfo:[self errorUserInfo]]; + return NO; + } + } + CFRelease(attrs); + return YES; +} + +- (void)_onInputEnd { + if (_isFinish) { + return ; + } + _isFinish = YES; + if (_mDecodeSession) { + VTDecompressionSessionWaitForAsynchronousFrames(_mDecodeSession); + VTDecompressionSessionInvalidate(_mDecodeSession); + CFRelease(_mDecodeSession); + _mDecodeSession = NULL; + } + if (self.spsData || self.ppsData || self.vpsData) { + self.spsData = nil; + self.ppsData = nil; + self.vpsData = nil; + } + if (_mFormatDescription) { + CFRelease(_mFormatDescription); + _mFormatDescription = NULL; + } +} + +- (void)onInputEnd { + + //为确保任务停止,必须同步执行 + __weak __typeof(self) weakSelf = self; + if ([NSThread isMainThread]) { + dispatch_sync(self.decodeQueue, ^{ + [weakSelf _onInputEnd]; + }); + } else { + dispatch_async(self.decodeQueue, ^{ + [weakSelf _onInputEnd]; + }); + } +} + +//decode callback +void didDecompress(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){ + + CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon; + *outputPixelBuffer = CVPixelBufferRetain(pixelBuffer); +} + +- (NSDictionary *)errorUserInfo { + NSDictionary *userInfo = @{@"location" : self.fileInfo.filePath ? : @""}; + return userInfo; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.h new file mode 100644 index 0000000..775475f --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.h @@ -0,0 +1,29 @@ +// QGAnimatedImageBufferManager.h +// 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 +#import "QGAnimatedImageDecodeConfig.h" +#import "QGBaseAnimatedImageFrame.h" + +@interface QGAnimatedImageBufferManager : NSObject + +@property (nonatomic, strong) NSMutableArray *buffers;//缓冲 + +- (instancetype)initWithConfig:(QGAnimatedImageDecodeConfig *)config; +- (QGBaseAnimatedImageFrame *)getBufferedFrame:(NSInteger)frameIndex; +- (BOOL)isBufferFull; +- (QGBaseAnimatedImageFrame *)popVideoFrame; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.m new file mode 100644 index 0000000..2a7bc82 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageBufferManager.m @@ -0,0 +1,101 @@ +// QGAnimatedImageBufferManager.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 "QGAnimatedImageBufferManager.h" +#import "QGVAPSafeMutableArray.h" + +@interface QGAnimatedImageBufferManager() { + + QGAnimatedImageDecodeConfig *_config; //解码配置 +} + +@end + +@implementation QGAnimatedImageBufferManager + +- (instancetype)initWithConfig:(QGAnimatedImageDecodeConfig *)config { + + if (self = [super init]) { + _config = config; + [self createBuffersWithConfig:config]; + } + return self; +} + +- (void)createBuffersWithConfig:(QGAnimatedImageDecodeConfig *)config { + _buffers = [[QGVAPSafeMutableArray alloc] initWithCapacity:config.bufferCount]; +} + +/** + 取出指定的在缓冲区的帧,若不存在于缓冲区则返回空 + + @param frameIndex 目标帧索引 + @return 帧数据 + */ +- (QGBaseAnimatedImageFrame *)getBufferedFrame:(NSInteger)frameIndex { + + if (_buffers.count == 0) { + //NSLog(@"fail buffer is nil"); + return nil; + } + NSInteger bufferIndex = frameIndex%_buffers.count; + if (bufferIndex > _buffers.count-1) { + //NSLog(@"fail"); + return nil; + } + id frame = [_buffers objectAtIndex:bufferIndex]; + if (![frame isKindOfClass:[QGBaseAnimatedImageFrame class]] || ([(QGBaseAnimatedImageFrame*)frame frameIndex] != frameIndex)) { + return nil; + } + return frame; +} + +- (QGBaseAnimatedImageFrame *)popVideoFrame { + if (!_buffers.count) { + return nil; + } + + if (![_buffers.firstObject isKindOfClass:[QGBaseAnimatedImageFrame class]]) { + return nil; + } + + QGBaseAnimatedImageFrame *frame = _buffers.firstObject; + [_buffers removeObjectAtIndex:0]; + + return frame; +} + +/** + 判断当前缓冲区是否被填满 + + @return 只有当缓冲区所有区域都被QGBaseAnimatedImageFrame类型的数据填满才算缓冲区满 + */ +- (BOOL)isBufferFull { + + __block BOOL isFull = YES; + [_buffers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (![obj isKindOfClass:[QGBaseAnimatedImageFrame class]]) { + isFull = NO; + *stop = YES; + } + }]; + return isFull; +} + +- (void)dealloc { + +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.h new file mode 100644 index 0000000..4ebd2cf --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.h @@ -0,0 +1,25 @@ +// QGAnimatedImageDecodeConfig.h +// 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 + +@interface QGAnimatedImageDecodeConfig : NSObject + +@property (nonatomic, assign) NSInteger threadCount;//线程数 +@property (nonatomic, assign) NSInteger bufferCount;//缓冲数 + ++ (instancetype)defaultConfig; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.m new file mode 100644 index 0000000..749323e --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeConfig.m @@ -0,0 +1,28 @@ +// QGAnimatedImageDecodeConfig.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 "QGAnimatedImageDecodeConfig.h" + +@implementation QGAnimatedImageDecodeConfig + ++ (instancetype)defaultConfig { + + QGAnimatedImageDecodeConfig *config = [QGAnimatedImageDecodeConfig new]; + config.threadCount= 1; + config.bufferCount = 5; + return config; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.h new file mode 100644 index 0000000..abf1633 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.h @@ -0,0 +1,57 @@ +// QGAnimatedImageDecodeManager.h +// 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 +#import "QGBaseDecoder.h" +#import "QGBaseAnimatedImageFrame.h" +#import "QGAnimatedImageDecodeConfig.h" + +@class QGAnimatedImageDecodeManager; +@protocol QGAnimatedImageDecoderDelegate + +/** + 必须实现该方法 用以实例化解码器 + + @param manager 解码控制器 + @return class + */ +- (Class)decoderClassForManager:(QGAnimatedImageDecodeManager *)manager; + +@optional + +/** + 到文件末尾时被调用 + + @param decoder 解码器 + */ +- (void)decoderDidFinishDecode:(QGBaseDecoder *)decoder; + +- (void)decoderDidFailDecode:(QGBaseDecoder *)decoder error:(NSError *)error; + +@end + +@interface QGAnimatedImageDecodeManager : NSObject + +@property (nonatomic, weak) id decoderDelegate; + +- (instancetype)initWith:(QGBaseDFileInfo *)fileInfo + config:(QGAnimatedImageDecodeConfig *)config + delegate:(id)delegate; + +- (QGBaseAnimatedImageFrame *)consumeDecodedFrame:(NSInteger)frameIndex; + +- (void)tryToStartAudioPlay; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.m new file mode 100644 index 0000000..e6d8310 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeManager.m @@ -0,0 +1,161 @@ +// QGAnimatedImageDecodeManager.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 "QGAnimatedImageDecodeManager.h" +#import "QGAnimatedImageBufferManager.h" +#import "QGBaseDecoder.h" +#import "QGVAPSafeMutableArray.h" +#import "QGMP4FrameHWDecoder.h" +#import "QGVAPLogger.h" +#import +#import + +@interface QGAnimatedImageDecodeManager() { + + QGAnimatedImageDecodeConfig *_config; //解码配置 + QGBaseDFileInfo *_fileInfo; //sharpP文件信息 + NSMutableArray *_decoders; //解码器 + QGAnimatedImageBufferManager *_bufferManager; //缓冲管理 + AVAudioPlayer *_audioPlayer; +} + +@end + +@implementation QGAnimatedImageDecodeManager + +- (instancetype)initWith:(QGBaseDFileInfo *)fileInfo + config:(QGAnimatedImageDecodeConfig *)config + delegate:(id)delegate { + + if (self = [super init]) { + + _config = config; + _fileInfo = fileInfo; + _decoderDelegate = delegate; + [self createDecodersByConfig:config]; + _bufferManager = [[QGAnimatedImageBufferManager alloc] initWithConfig:config]; + [self initializeBuffers]; + [self setupAudioPlayerIfNeed]; + } + return self; +} + +/** + 取出已解码的一帧并准备下一帧 + + @param frameIndex 帧索引 + @return 帧内容 + */ +- (QGBaseAnimatedImageFrame *)consumeDecodedFrame:(NSInteger)frameIndex { + + @synchronized (self) { + // 控制何时命中第一帧,缓存满了才命中 + if (frameIndex == 0 && _bufferManager.buffers.count < _config.bufferCount) { + return nil; + } + [self checkIfDecodeFinish:frameIndex]; + QGBaseAnimatedImageFrame *frame = [_bufferManager popVideoFrame]; + if (frame) { + // pts顺序 + frame.frameIndex = frameIndex; + [self decodeFrame:frameIndex+_config.bufferCount]; + } + return frame; + } +} + +- (void)tryToStartAudioPlay { + if (!_audioPlayer) { + return ; + } + [_audioPlayer play]; +} + +#pragma mark - private methods + +- (void)checkIfDecodeFinish:(NSInteger)frameIndex { + + NSInteger decoderIndex = _decoders.count==1?0:frameIndex%_decoders.count; + QGBaseDecoder *decoder = _decoders[decoderIndex]; + if ([decoder isFrameIndexBeyondEnd:frameIndex]) { + if ([self.decoderDelegate respondsToSelector:@selector(decoderDidFinishDecode:)]) { + [self.decoderDelegate decoderDidFinishDecode:decoder]; + } + } +} + +- (void)decodeFrame:(NSInteger)frameIndex { + + if (!_decoders || _decoders.count == 0) { + //NSLog(@"error! can't find decoder"); + return ; + } + NSInteger decoderIndex = _decoders.count==1?0:frameIndex%_decoders.count; + QGBaseDecoder *decoder = _decoders[decoderIndex]; + if ([decoder shouldStopDecode:frameIndex]) { + return ; + } + [decoder decodeFrame:frameIndex buffers:_bufferManager.buffers]; +} + +- (void)createDecodersByConfig:(QGAnimatedImageDecodeConfig *)config { + + if (!self.decoderDelegate || ![self.decoderDelegate respondsToSelector:@selector(decoderClassForManager:)]) { + VAP_Event(kQGVAPModuleCommon, @"you MUST implement the delegate in invoker!"); + NSAssert(0, @"you MUST implement the delegate in invoker!"); + return ; + } + + _decoders = [QGVAPSafeMutableArray new]; + for (int i = 0; i < config.threadCount; i ++) { + Class class = [self.decoderDelegate decoderClassForManager:self]; + NSError *error = nil; + QGBaseDecoder *decoder = [class alloc]; + decoder = [decoder initWith:_fileInfo error:&error]; + if (!decoder) { + if ([self.decoderDelegate respondsToSelector:@selector(decoderDidFailDecode:error:)]) { + [self.decoderDelegate decoderDidFailDecode:nil error:error]; + } + break ; + } + [_decoders addObject:decoder]; + } +} + +- (void)initializeBuffers { + + for (int i = 0; i < _config.bufferCount; i++) { + [self decodeFrame:i]; + } +} + +- (void)setupAudioPlayerIfNeed { + + if ([_fileInfo isKindOfClass:[QGMP4HWDFileInfo class]]) { + QGMP4ParserProxy *mp4Parser = [(QGMP4HWDFileInfo *)_fileInfo mp4Parser]; + if (!mp4Parser.audioTrackBox) { + _audioPlayer = nil; + return ; + } + NSError *error; + _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:_fileInfo.filePath] error:&error]; + } +} + +- (void)dealloc { + +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.h new file mode 100644 index 0000000..ab9d929 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.h @@ -0,0 +1,23 @@ +// QGAnimatedImageDecodeThread.h +// 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 + +@interface QGAnimatedImageDecodeThread : NSThread + +@property (nonatomic, assign) BOOL occupied; //是否被解码器占用 +@property (nonatomic, readonly) NSString *sequenceDec; //线程标识信息 + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.m new file mode 100644 index 0000000..c9b89e8 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThread.m @@ -0,0 +1,29 @@ +// QGAnimatedImageDecodeThread.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 "QGAnimatedImageDecodeThread.h" + +@implementation QGAnimatedImageDecodeThread + +- (NSString *)sequenceDec +{ +#ifdef DEBUG + return [NSString stringWithFormat:@"%@",@([[self valueForKeyPath:@"private.seqNum"] integerValue])];// +#else + return [self description]; +#endif +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.h new file mode 100644 index 0000000..d78d979 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.h @@ -0,0 +1,24 @@ +// QGAnimatedImageDecodeThreadPool.h +// 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 +#import "QGAnimatedImageDecodeThread.h" + +@interface QGAnimatedImageDecodeThreadPool : NSObject + ++ (instancetype)sharedPool; +- (QGAnimatedImageDecodeThread *)getDecodeThread; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.m new file mode 100644 index 0000000..8601569 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGAnimatedImageDecodeThreadPool.m @@ -0,0 +1,77 @@ +// QGAnimatedImageDecodeThreadPool.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 "QGAnimatedImageDecodeThreadPool.h" +#import "QGAnimatedImageDecodeThread.h" +#import "QGVAPSafeMutableArray.h" + +@interface QGAnimatedImageDecodeThreadPool (){ + + NSMutableArray *_threads; +} + +@end + +@implementation QGAnimatedImageDecodeThreadPool + ++ (instancetype)sharedPool { + + static QGAnimatedImageDecodeThreadPool *instance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[QGAnimatedImageDecodeThreadPool alloc] init]; + }); + return instance; +} + +- (instancetype)init { + + if (self = [super init]) { + _threads = [QGVAPSafeMutableArray new]; + } + return self; +} + +/** + 从池子中找出没被占用的线程,如果没有则新建一个 + + @return 解码线程 + */ +- (QGAnimatedImageDecodeThread *)getDecodeThread { + + QGAnimatedImageDecodeThread *freeThread = nil; + for (QGAnimatedImageDecodeThread *thread in _threads) { + if (!thread.occupied) { + freeThread = thread; + } + } + if (!freeThread) { + freeThread = [[QGAnimatedImageDecodeThread alloc] initWithTarget:self selector:@selector(run) object:nil]; + [freeThread start]; + [_threads addObject:freeThread]; + } + return freeThread; +} + +- (void)run{ + //线程保活 + @autoreleasepool { + [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop run]; + } +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.h new file mode 100644 index 0000000..9a90de6 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.h @@ -0,0 +1,44 @@ +// QGVAPConfigManager.h +// 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 +#import "QGMP4HWDFileInfo.h" +#import "QGVAPConfigModel.h" +#import "VAPMacros.h" + +@class QGVAPSourceInfo; +@protocol QGVAPConfigDelegate + +- (void)onVAPConfigResourcesLoaded:(QGVAPConfigModel *)config error:(NSError *)error; + +@optional +- (NSString *)vap_contentForTag:(NSString *)tag resource:(QGVAPSourceInfo *)info; //替换配置中的资源占位符(不处理直接返回tag) +- (void)vap_loadImageWithURL:(NSString *)urlStr context:(NSDictionary *)context completion:(VAPImageCompletionBlock)completionBlock; + +@end + +@interface QGVAPConfigManager : NSObject + +@property (nonatomic, weak) id delegate; +@property (nonatomic, assign) BOOL hasValidConfig; +@property (nonatomic, strong) QGVAPConfigModel *model; + +- (instancetype)initWith:(QGMP4HWDFileInfo *)fileInfo; +- (void)loadConfigResources; +- (void)loadMTLTextures:(id)device; +- (void)loadMTLBuffers:(id)device; + +@end + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.m new file mode 100644 index 0000000..5d171ef --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Controllers/QGVAPConfigManager.m @@ -0,0 +1,256 @@ +// QGVAPConfigManager.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 "QGVAPConfigManager.h" +#import "QGMP4Parser.h" +#import "QGVAPLogger.h" +#import "NSDictionary+VAPUtil.h" +#import "UIColor+VAPUtil.h" +#import "NSArray+VAPUtil.h" +#import "QGHWDMetalRenderer.h" +#import "QGVAPTextureLoader.h" + +@interface QGVAPConfigManager () { + + QGMP4HWDFileInfo *_fileInfo; +} + +@end + +@implementation QGVAPConfigManager + +- (instancetype)initWith:(QGMP4HWDFileInfo *)fileInfo { + + if (self = [super init]) { + _fileInfo = fileInfo; + [self setupConfig]; + } + return self; +} + +- (void)setupConfig { + + QGMP4Box *vapc = [_fileInfo.mp4Parser.rootBox subBoxOfType:QGMP4BoxType_vapc]; + if (!vapc) { + self.hasValidConfig = NO; + return ; + } + self.hasValidConfig = YES; + NSData *vapcData = [_fileInfo.mp4Parser readDataOfBox:vapc length:vapc.length-8 offset:8]; + NSError *error = nil; + NSDictionary *configDictionary = [NSJSONSerialization JSONObjectWithData:vapcData options:kNilOptions error:&error]; + if (error) { + VAP_Error(kQGVAPModuleCommon, @"fail to parse config as dictionary file %@", vapc); + } + [self parseConfigDictinary:configDictionary]; +} + +#pragma mark - resource loader + +- (void)loadConfigResources { + + if (self.model.resources.count == 0) { + if ([self.delegate respondsToSelector:@selector(onVAPConfigResourcesLoaded:error:)]) { + [self.delegate onVAPConfigResourcesLoaded:self.model error:nil]; + } + return ; + } + + //tags + if ([self.delegate respondsToSelector:@selector(vap_contentForTag:resource:)]) { + [self.model.resources enumerateObjectsUsingBlock:^(QGVAPSourceInfo * _Nonnull resource, NSUInteger idx, BOOL * _Nonnull stop) { + resource.contentTagValue = [self.delegate vap_contentForTag:resource.contentTag resource:resource]; + }]; + } + + if (![self.delegate respondsToSelector:@selector(vap_loadImageWithURL:context:completion:)]) { + return ; + } + __block NSError *loadError = nil; + dispatch_group_t group = dispatch_group_create(); + [self.model.resources enumerateObjectsUsingBlock:^(QGVAPSourceInfo * _Nonnull resource, NSUInteger idx, BOOL * _Nonnull stop) { + + NSString *tagContent = resource.contentTagValue; + if ([resource.type isEqualToString:kQGAGAttachmentSourceTypeText] && [resource.loadType isEqualToString:QGAGAttachmentSourceLoadTypeLocal]) { + resource.sourceImage = [QGVAPTextureLoader drawingImageForText:tagContent color:resource.color size:resource.size bold:[resource.style isEqualToString:kQGAGAttachmentSourceStyleBoldText]]; + } + + if ([resource.type isEqualToString:kQGAGAttachmentSourceTypeImg] && [resource.loadType isEqualToString:QGAGAttachmentSourceLoadTypeNet]) { + NSString *imageURL = tagContent; + + NSDictionary *context = @{@"resource":resource}; + dispatch_group_enter(group); + [self.delegate vap_loadImageWithURL:imageURL context:context completion:^(UIImage *image, NSError *error, NSString *imageURL) { + if (!image || error) { + VAP_Error(kQGVAPModuleCommon, @"loadImageWithURL %@ error:%@", imageURL, error); + loadError = (loadError ?: (error ?: ([NSError errorWithDomain:[NSString stringWithFormat:@"loadImageError:%@", imageURL] code:-1 userInfo:nil]))); + } + resource.sourceImage = image; + dispatch_group_leave(group); + }]; + } + }]; + + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + if ([self.delegate respondsToSelector:@selector(onVAPConfigResourcesLoaded:error:)]) { + [self.delegate onVAPConfigResourcesLoaded:self.model error:loadError]; + } + }); +} + +- (void)loadMTLTextures:(id)device { + + [self.model.resources enumerateObjectsUsingBlock:^(QGVAPSourceInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + id texture = [QGVAPTextureLoader loadTextureWithImage:obj.sourceImage device:device]; + obj.sourceImage = nil; + obj.texture = texture; + }]; +} + +- (void)loadMTLBuffers:(id)device { + + [self.model.resources enumerateObjectsUsingBlock:^(QGVAPSourceInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + id buffer = [QGVAPTextureLoader loadVapColorFillBufferWith:obj.color device:device]; + obj.colorParamsBuffer = buffer; + }]; +} + + +#pragma mark - parse json + +- (void)parseConfigDictinary:(NSDictionary *)configDic { + + NSDictionary *commonInfoDic = [configDic hwd_dicValue:@"info"]; + NSArray *sourcesArr = [configDic hwd_arrValue:@"src"]; + NSArray *framesArr = [configDic hwd_arrValue:@"frame"]; + + if (!commonInfoDic) { + VAP_Error(kQGVAPModuleCommon, @"has no commonInfoDic:%@", configDic); + return ; + } + QGVAPConfigModel *configModel = [QGVAPConfigModel new]; + //parse + NSInteger version = [commonInfoDic hwd_integerValue:@"v"]; + NSInteger frameCount = [commonInfoDic hwd_integerValue:@"f"]; + CGFloat w = [commonInfoDic hwd_floatValue:@"w"]; + CGFloat h = [commonInfoDic hwd_floatValue:@"h"]; + CGFloat video_w = [commonInfoDic hwd_floatValue:@"videoW"]; + CGFloat video_h = [commonInfoDic hwd_floatValue:@"videoH"]; + CGFloat orientaion = [commonInfoDic hwd_integerValue:@"orien"]; + NSInteger fps = [commonInfoDic hwd_integerValue:@"fps"]; + BOOL isMerged = ([commonInfoDic hwd_integerValue:@"isVapx"] == 1); + NSArray *a_frame = [commonInfoDic hwd_arrValue:@"aFrame"]; + NSArray *rgb_frame = [commonInfoDic hwd_arrValue:@"rgbFrame"]; + self.model = configModel; + //整体信息 + QGVAPCommonInfo *commonInfo = [QGVAPCommonInfo new]; + commonInfo.version = version; + commonInfo.framesCount = frameCount; + commonInfo.size = CGSizeMake(w, h); + commonInfo.videoSize = CGSizeMake(video_w, video_h); + commonInfo.targetOrientaion = orientaion; + commonInfo.fps = fps; + commonInfo.isMerged = isMerged; + commonInfo.alphaAreaRect = a_frame ? [a_frame hwd_rectValue] : CGRectZero; + commonInfo.rgbAreaRect = rgb_frame ? [rgb_frame hwd_rectValue] : CGRectZero; + configModel.info = commonInfo; + + if (!sourcesArr) { + VAP_Error(kQGVAPModuleCommon, @"has no sourcesArr:%@", configDic); + return ; + } + + //源信息 + NSMutableDictionary *sources = [NSMutableDictionary new]; + [sourcesArr enumerateObjectsUsingBlock:^(NSDictionary *sourceDic, NSUInteger idx, BOOL * _Nonnull stop) { + if (![sourceDic isKindOfClass:[NSDictionary class]]) { + VAP_Error(kQGVAPModuleCommon, @"sourceDic is not dic:%@", sourceDic); + return ; + } + NSString *sourceID = [sourceDic hwd_stringValue:@"srcId"]; + if (!sourceID) { + VAP_Error(kQGVAPModuleCommon, @"has no sourceID:%@", sourceDic); + return ; + } + //parse + QGAGAttachmentSourceType sourceType = [sourceDic hwd_stringValue:@"srcType"]; + QGAGAttachmentSourceLoadType loadType = [sourceDic hwd_stringValue:@"loadType"]; + NSString *contentTag = [sourceDic hwd_stringValue:@"srcTag"]; + UIColor *color = [UIColor hwd_colorWithHexString:[sourceDic hwd_stringValue:@"color"]]; + QGAGAttachmentSourceStyle style = [sourceDic hwd_stringValue:@"style"]; + CGFloat width = [sourceDic hwd_floatValue:@"w"]; + CGFloat height = [sourceDic hwd_floatValue:@"h"]; + QGAGAttachmentFitType fitType = [sourceDic hwd_stringValue:@"fitType"]; + QGVAPSourceInfo *sourceInfo = [QGVAPSourceInfo new]; + sourceInfo.type = sourceType; + sourceInfo.style = style; + sourceInfo.contentTag = contentTag; + sourceInfo.color = color; + sourceInfo.size = CGSizeMake(width, height); + sourceInfo.fitType = fitType; + sourceInfo.loadType = loadType; + sources[sourceID] = sourceInfo; + }]; + configModel.resources = sources.allValues; + + //融合信息 + if (!framesArr) { + VAP_Error(kQGVAPModuleCommon, @"has no framesArr:%@", configDic); + return ; + } + NSMutableDictionary *> *mergedConfig = [NSMutableDictionary new]; + [framesArr enumerateObjectsUsingBlock:^(NSDictionary *frameMergedDic, NSUInteger idx, BOOL * _Nonnull stop) { + if (![frameMergedDic isKindOfClass:[NSDictionary class]]) { + VAP_Error(kQGVAPModuleCommon, @"frameMergedDic is not dic:%@", frameMergedDic); + return ; + } + NSInteger frameIndex = [frameMergedDic hwd_integerValue:@"i"]; + + NSMutableArray *mergedInfos = [NSMutableArray new]; + NSArray *mergedObjs = [frameMergedDic hwd_arrValue:@"obj"]; + [mergedObjs enumerateObjectsUsingBlock:^(NSDictionary *mergeInfoDic, NSUInteger idx, BOOL * _Nonnull stop) { + if (![mergeInfoDic isKindOfClass:[NSDictionary class]]) { + VAP_Error(kQGVAPModuleCommon, @"mergeInfoDic is not dic:%@", mergeInfoDic); + return ; + } + NSString *sourceID = [mergeInfoDic hwd_stringValue:@"srcId"]; + QGVAPSourceInfo *sourceInfo = sources[sourceID]; + if (!sourceInfo) { + VAP_Error(kQGVAPModuleCommon, @"sourceInfo is nil:%@", mergeInfoDic); + return ; + } + //parse + NSArray *frame = [mergeInfoDic hwd_arrValue:@"frame"]; + NSArray *m_frame = [mergeInfoDic hwd_arrValue:@"mFrame"]; + NSInteger renderIndex = [mergeInfoDic hwd_integerValue:@"z"]; + NSInteger rotationAngle = [mergeInfoDic hwd_integerValue:@"mt"]; + QGVAPMergedInfo *mergeInfo = [QGVAPMergedInfo new]; + mergeInfo.source = sourceInfo; + mergeInfo.renderIndex = renderIndex; + mergeInfo.needMask = (m_frame != nil); + mergeInfo.renderRect = frame ? [frame hwd_rectValue] : CGRectZero; + mergeInfo.maskRect = m_frame ? [m_frame hwd_rectValue] : CGRectZero; + mergeInfo.maskRotation = rotationAngle; + [mergedInfos addObject:mergeInfo]; + }]; + NSArray *sortedMergeInfos = [mergedInfos sortedArrayUsingComparator:^NSComparisonResult(QGVAPMergedInfo *info1, QGVAPMergedInfo *info2) { + return [@(info1.renderIndex) compare:@(info2.renderIndex)]; + }]; + mergedConfig[@(frameIndex)] = sortedMergeInfos; + }]; + configModel.mergedConfig = mergedConfig; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.h new file mode 100644 index 0000000..2b11b95 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.h @@ -0,0 +1,230 @@ +// QGMP4Box.h +// 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 + +#define ATOM_TYPE(a, b, c, d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) +#define READ32BIT(bytes) ((((bytes)[0]&0xff)<<24)+(((bytes)[1]&0xff)<<16)+(((bytes)[2]&0xff)<<8)+((bytes)[3]&0xff)) + +extern NSInteger const kQGBoxSizeLengthInBytes; +extern NSInteger const kQGBoxTypeLengthInBytes; + +@class QGMP4Box; +typedef NSData* (^QGMp4BoxDataFetcher)(QGMP4Box *box); + +typedef NS_ENUM(NSUInteger, QGMP4CodecType) { + QGMP4CodecTypeUnknown = 0, + QGMP4CodecTypeVideo, + QGMP4CodecTypeAudio +}; + +typedef NS_ENUM(uint32_t, QGMP4TrackType) { + + QGMP4TrackType_Video = ATOM_TYPE('v','i','d','e'), + QGMP4TrackType_Audio = ATOM_TYPE('s','o','u','n'), + QGMP4TrackType_Hint = ATOM_TYPE('h','i','n','t') +}; + +typedef NS_ENUM(NSUInteger, QGMP4BoxType) { + + QGMP4BoxType_unknown = 0x0, + QGMP4BoxType_ftyp = ATOM_TYPE('f','t','y','p'),//0x66747970, + QGMP4BoxType_free = ATOM_TYPE('f','r','e','e'),//0x66726565, + QGMP4BoxType_mdat = ATOM_TYPE('m','d','a','t'),//0x6d646174, + QGMP4BoxType_moov = ATOM_TYPE('m','o','o','v'),//0x6d6f6f76, + QGMP4BoxType_mvhd = ATOM_TYPE('m','v','h','d'),//0x6d766864, + QGMP4BoxType_iods = ATOM_TYPE('i','o','d','s'),//0x696f6473, + QGMP4BoxType_trak = ATOM_TYPE('t','r','a','k'),//0x7472616b, + QGMP4BoxType_tkhd = ATOM_TYPE('t','k','h','d'),//0x746b6864, + QGMP4BoxType_edts = ATOM_TYPE('e','d','t','s'),//0x65647473, + QGMP4BoxType_elst = ATOM_TYPE('e','l','s','t'),//0x656c7374, + QGMP4BoxType_mdia = ATOM_TYPE('m','d','i','a'),//0x6d646961, + QGMP4BoxType_mdhd = ATOM_TYPE('m','d','h','d'),//0x6d646864, + QGMP4BoxType_hdlr = ATOM_TYPE('h','d','l','r'),//0x68646c72, + QGMP4BoxType_minf = ATOM_TYPE('m','i','n','f'),//0x6d696e66, + QGMP4BoxType_vmhd = ATOM_TYPE('v','m','h','d'),//0x766d6864, + QGMP4BoxType_dinf = ATOM_TYPE('d','i','n','f'),//0x64696e66, + QGMP4BoxType_dref = ATOM_TYPE('d','r','e','f'),//0x64726566, + QGMP4BoxType_url = ATOM_TYPE( 0 ,'u','r','l'),//0x75726c, + QGMP4BoxType_stbl = ATOM_TYPE('s','t','b','l'),//0x7374626c, + QGMP4BoxType_stsd = ATOM_TYPE('s','t','s','d'),//0x73747364, + QGMP4BoxType_avc1 = ATOM_TYPE('a','v','c','1'),//0x61766331, + QGMP4BoxType_avcC = ATOM_TYPE('a','v','c','C'),//0x61766343, + QGMP4BoxType_stts = ATOM_TYPE('s','t','t','s'),//0x73747473, + QGMP4BoxType_stss = ATOM_TYPE('s','t','s','s'),//0x73747373, + QGMP4BoxType_stsc = ATOM_TYPE('s','t','s','c'),//0x73747363, + QGMP4BoxType_stsz = ATOM_TYPE('s','t','s','z'),//0x7374737a, + QGMP4BoxType_stco = ATOM_TYPE('s','t','c','o'),//0x7374636f, + QGMP4BoxType_ctts = ATOM_TYPE('c', 't', 't', 's'),//只有视频有,主要⽤来记录pts和dts的差值,通过它可以计算出pts + QGMP4BoxType_udta = ATOM_TYPE('u','d','t','a'),//0x75647461, + QGMP4BoxType_meta = ATOM_TYPE('m','e','t','a'),//0x6d657461, + QGMP4BoxType_ilst = ATOM_TYPE('i','l','s','t'),//0x696c7374, + QGMP4BoxType_data = ATOM_TYPE('d','a','t','a'),//0x64617461, + QGMP4BoxType_wide = ATOM_TYPE('w','i','d','e'),//0x77696465, + QGMP4BoxType_loci = ATOM_TYPE('l','o','c','i'),//0x6c6f6369, + QGMP4BoxType_smhd = ATOM_TYPE('s','m','h','d'),//0x736d6864, + QGMP4BoxType_vapc = ATOM_TYPE('v','a','p','c'),//0x76617063,//vap专属,存储json配置信息 + QGMP4BoxType_hvc1 = ATOM_TYPE('h','v','c','1'), + QGMP4BoxType_hvcC = ATOM_TYPE('h','v','c','C') +}; + +typedef NS_ENUM(NSUInteger, QGMP4VideoStreamCodecID) { + QGMP4VideoStreamCodecIDUnknown = 0, + QGMP4VideoStreamCodecIDH264, + QGMP4VideoStreamCodecIDH265 +}; + +/** + * QGCttsEntry + */ +@interface QGCttsEntry : NSObject + +/** sampleCount */ +@property (nonatomic, assign) uint32_t sampleCount; +/** compositionOffset */ +@property (nonatomic, assign) uint32_t compositionOffset; + +@end + +@interface QGMP4BoxFactory : NSObject + ++ (BOOL)isTypeValueValid:(QGMP4BoxType)type; ++ (Class)boxClassForType:(QGMP4BoxType)type; + ++ (QGMP4Box *)createBoxForType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length; + +@end + +@protocol QGMP4BoxDelegate +@optional +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock; + +@end + +@interface QGMP4Box : NSObject + +@property (nonatomic, assign) QGMP4BoxType type; +@property (nonatomic, assign) unsigned long long length; +@property (nonatomic, assign) unsigned long long startIndexInBytes; +@property (nonatomic, weak) QGMP4Box *superBox; +@property (nonatomic, strong) NSMutableArray *subBoxes; + +- (instancetype)initWithType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length; + +- (id)subBoxOfType:(QGMP4BoxType)type; +- (id)superBoxOfType:(QGMP4BoxType)type; + +@end + +//实际媒体数据,具体的划分等,都在moov⾥⾯。 +@interface QGMP4MdatBox : QGMP4Box +@end + +@interface QGMP4AvccBox : QGMP4Box +@end + +@interface QGMP4HvccBox : QGMP4Box +@end + +@interface QGMP4MvhdBox : QGMP4Box +@end + +//sample description +@interface QGMP4StsdBox : QGMP4Box +@end + +/**Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the samples within a chunk can have different sizes. This table can be used to find the chunk that contains a sample, its position, and the associated sample description. +The table is compactly coded. Each entry gives the index of the first chunk of a run of chunks with the same characteristics. By subtracting one entry here from the previous one, you can compute how many chunks are in this run. You can convert this to a sample count by multiplying by the appropriate samples-per-chunk.*/ +//https://blog.csdn.net/tung214/article/details/30492895 +@interface QGStscEntry : NSObject + +@property (nonatomic, assign) uint32_t firstChunk; +@property (nonatomic, assign) uint32_t samplesPerChunk; +@property (nonatomic, assign) uint32_t sampleDescriptionIndex; + +@end +@interface QGMP4StscBox : QGMP4Box + +@property (nonatomic, strong) NSMutableArray *entries; + +@end + +@interface QGMP4StcoBox : QGMP4Box + +@property (nonatomic, assign) uint32_t chunkCount; +@property (nonatomic, strong) NSMutableArray *chunkOffsets; + +@end + +/** + * ctts + */ +@interface QGMP4CttsBox : QGMP4Box + +/** compositionOffsets */ +@property (nonatomic, strong) NSMutableArray *compositionOffsets; + +@end + +//This box contains a compact version of a table that allows indexing from decoding time to sample number. Other tables give sample sizes and pointers, from the sample number. Each entry in the table gives the number of consecutive samples with the same time delta, and the delta of those samples. By adding the deltas a complete time-to-sample map may be built. +@interface QGSttsEntry : NSObject + +@property (nonatomic, assign) uint32_t sampleCount; +@property (nonatomic, assign) uint32_t sampleDelta; + +@end +@interface QGMP4SttsBox : QGMP4Box + +@property (nonatomic, strong) NSMutableArray *entries; + +@end + +//sample size +@interface QGMP4StszBox : QGMP4Box + +@property (nonatomic, assign) uint32_t sampleCount; +@property (nonatomic, strong) NSMutableArray *sampleSizes; + +@end + +@interface QGMP4TrackBox : QGMP4Box +@end + +@interface QGMP4HdlrBox : QGMP4Box + +@property (nonatomic, assign) QGMP4TrackType trackType; + +@end + +@interface QGMP4Sample : NSObject + +@property (nonatomic, assign) QGMP4CodecType codecType; +@property (nonatomic, assign) uint32_t sampleDelta; +@property (nonatomic, assign) uint32_t sampleSize; +@property (nonatomic, assign) uint32_t sampleIndex; +@property (nonatomic, assign) uint32_t chunkIndex; +@property (nonatomic, assign) uint32_t streamOffset; +@property (nonatomic, assign) uint64_t pts; +@property (nonatomic, assign) uint64_t dts; +@property (nonatomic, assign) BOOL isKeySample; + +@end + +@interface QGChunkOffsetEntry : NSObject + +@property (nonatomic, assign) uint32_t samplesPerChunk; +@property (nonatomic, assign) uint32_t offset; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.m new file mode 100644 index 0000000..68cd5ba --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Box.m @@ -0,0 +1,396 @@ +// QGMP4Box.m +// Tencent is pleased to support the open source community by making vap available. +// +// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed under the License is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions and +// limitations under the License. + +#import "QGMP4Box.h" +#import "QGMP4Parser.h" + +NSInteger const kQGBoxSizeLengthInBytes = 4; +NSInteger const kQGBoxTypeLengthInBytes = 4; + +#pragma mark - boxes +#pragma mark -- base box +@implementation QGMP4Box + +- (instancetype)initWithType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length { + + if (self = [super init]) { + _type = type; + _startIndexInBytes = startIndexInBytes; + _length = length; + } + return self; +} + +/** + 前序遍历递归查找指定类型的子box,不包含自身。 + + @param type box类型 + @return 符合条件的第一个子box + */ +- (id)subBoxOfType:(QGMP4BoxType)type { + + if (self.subBoxes) { + for (QGMP4Box *subBox in self.subBoxes) { + if (subBox.type == type) { + return subBox; + } + QGMP4Box *box = [subBox subBoxOfType:type]; + if (box) { + return box; + } + } + } + return nil; +} + +/** + 向上查找指定类型的box,不包含自身 + + @param type box类型 + @return h符合条件的第一个box + */ +- (id)superBoxOfType:(QGMP4BoxType)type { + + if (self.superBox) { + if (self.superBox.type == type) { + return self.superBox; + } + QGMP4Box *box = [self.superBox superBoxOfType:type]; + if (box) { + return box; + } + } + return nil; +} + +- (NSString *)description { + + return [self descriptionForRecursionLevel:0]; +} + +- (NSString *)descriptionForRecursionLevel:(NSInteger)level { + + __block NSString *des = [NSString stringWithFormat:@"Box:%@ offset:%@ size:%@ ",self.typeString,@(self.startIndexInBytes),@(self.length)]; + for (int i = 0; i < level; i++) { + des = [NSString stringWithFormat:@"|--%@",des]; + } + des = [NSString stringWithFormat:@"\n%@",des]; + [self.subBoxes enumerateObjectsUsingBlock:^(QGMP4Box *obj, NSUInteger idx, BOOL * _Nonnull stop) { + des = [des stringByAppendingString:[obj descriptionForRecursionLevel:(level+1)]]; + }]; + return des; +} + +- (NSString *)typeString { + + NSUInteger value = self.type; + NSString *des = @""; + while (value > 0) { + NSUInteger hexValue = value&0xff; + value = value>>8; + des = [NSString stringWithFormat:@"%c%@",(int)hexValue,des]; + } + return des; +} + +@end + +#pragma mark -- hvcc box +/** + * QGMP4HvccBox + */ +@implementation QGMP4HvccBox +@end + +/** + * QGCttsEntry 通过dts计算pts + */ +@implementation QGCttsEntry + +@end + +/** + * QGMP4CttsBox 通过dts计算pts + */ +@implementation QGMP4CttsBox +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + if (!_compositionOffsets) { + _compositionOffsets = [NSMutableArray new]; + } + + NSData *cttsData = datablock(self); + const char *bytes = cttsData.bytes; + uint32_t entryCount = READ32BIT(&bytes[12]); + + for (int i = 0; i < entryCount; ++i) { + uint32_t sampleCount = READ32BIT(&bytes[16+i*8]); + uint32_t compositionOffset = READ32BIT(&bytes[16+i*8+4]); + for (int j = 0; j < sampleCount; j++) { + [_compositionOffsets addObject:@(compositionOffset)]; + } + } +} + +@end + +#pragma mark -- mdat box +@implementation QGMP4MdatBox + +@end + +#pragma mark -- avcc box +@implementation QGMP4AvccBox + +@end + +@implementation QGMP4MvhdBox + +@end + +/** + 在video track中,stsd包含了sps&pps等编解码数据 + + stsd是⼀个必不可少的atom,在它的 header和version字段后会有⼀个entry count字段, + 根据entry的个数,每个entry会有type信息,根据type不同sample description会提供不 + 同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track + 会有“AudioSampleEntry”类型信息。视频的关键信息如SPS和PPS,编码类型、宽⾼、 + ⻓度,⾳频的声道、采样等信息都会出现在这个box中。 + */ +@implementation QGMP4StsdBox + +@end + +/** + There are two variants of the sample size box. The first variant has a fixed size 32-bit field for representing the sample sizes; it permits defining a constant size for all samples in a track. The second variant permits smaller size fields, to save space when the sizes are varying but small. One of these boxes must be present; the first version is preferred for maximum compatibility. + 记录了每个sample的大小 + */ +@implementation QGMP4StszBox + +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + + if (!_sampleSizes) { + _sampleSizes = [NSMutableArray new]; + } + NSData *stszData = datablock(self); + const char *bytes = stszData.bytes; + uint32_t sampleSize = READ32BIT(&bytes[12]); + uint32_t sampleCount = READ32BIT(&bytes[16]); + self.sampleCount = sampleCount; + for (int i = 0; i < sampleCount; i ++) { + if (sampleSize > 0) { + [self.sampleSizes addObject:@(sampleSize)]; + } else { + uint32_t entryValue = READ32BIT(&bytes[20+i*4]); + [self.sampleSizes addObject:@(entryValue)]; + } + } +} + +@end + +/** + Samples within the media data are grouped into chunks. Chunks can be of different sizes, and the samples within a chunk can have different sizes. This table can be used to find the chunk that contains a sample, its position, and the associated sample description. + stsc记录了每个chunk有多少个Sample https://img-blog.csdn.net/20140613154636296 + */ +@implementation QGStscEntry + +@end + +/* + stsc,记录了每个chunk有多少个sample,通过这个表可以找到指定的sample + */ +@implementation QGMP4StscBox + +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + + if (!_entries) { + _entries = [NSMutableArray new]; + } + NSData *stscData = datablock(self); + const char *bytes = stscData.bytes; + uint32_t entry_count = READ32BIT(&bytes[12]); + for (int i = 0; i < entry_count; ++i) { + QGStscEntry *entry = [QGStscEntry new]; + entry.firstChunk = READ32BIT(&bytes[16+i*12]); + entry.samplesPerChunk = READ32BIT(&bytes[16+i*12+4]); + entry.sampleDescriptionIndex = READ32BIT(&bytes[16+i*12+8]); + [_entries addObject:entry]; + } +} + +@end + +/** + stco 记录每个chunk位置信息,与stsc结合可以算出每个sample的位置和大小 + + entry_count is an integer that gives the number of entries in the following table + chunk_offset is a 32 or 64 bit integer that gives the offset of the start of a chunk into its containing + media file. + */ +@implementation QGMP4StcoBox + +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + + if (!_chunkOffsets) { + _chunkOffsets = [NSMutableArray new]; + } + NSData *stcoData = datablock(self); + const char *bytes = stcoData.bytes; + uint32_t entry_count = READ32BIT(&bytes[12]); + self.chunkCount = entry_count; + for (int i = 0; i < entry_count; ++i) { + [self.chunkOffsets addObject:@(READ32BIT(&bytes[16+i*4]))]; + } +} + +@end + +/** + Decoding Time to Sample Box + 用来计算dts + */ +@implementation QGSttsEntry + +@end + +/* +stts记录了sample的时间信息,⾥⾯有多个entry,每个entry⾥⾯的的sample的时⻓都是 +对应这个entry的delta值,通过这个atom可以得到⼀个时间和sample的映射表。 +*/ +@implementation QGMP4SttsBox + +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + + if (!_entries) { + _entries = [NSMutableArray new]; + } + NSData *sttsData = datablock(self); + const char *bytes = sttsData.bytes; + uint32_t entry_count = READ32BIT(&bytes[12]); + for (int i = 0; i < entry_count; ++i) { + QGSttsEntry *entry = [QGSttsEntry new]; + entry.sampleCount = READ32BIT(&bytes[16+i*8]);; + entry.sampleDelta = READ32BIT(&bytes[16+i*8+4]); + [_entries addObject:entry]; + } +} + +@end + +@implementation QGMP4TrackBox + +@end + +@implementation QGMP4HdlrBox + +- (void)boxDidParsed:(QGMp4BoxDataFetcher)datablock { + + NSData *hdlrData = datablock(self); + const char *bytes = hdlrData.bytes; + uint32_t trackType = READ32BIT(&bytes[16]); + self.trackType = trackType; +} + +@end + +@implementation QGMP4Sample + +@end + +@implementation QGChunkOffsetEntry + +@end + +@implementation QGMP4BoxFactory + ++ (QGMP4Box *)createBoxForType:(QGMP4BoxType)type startIndex:(unsigned long long)startIndexInBytes length:(unsigned long long)length { + + Class boxClass = [self boxClassForType:type] ?: [QGMP4Box class]; + QGMP4Box *box = [[boxClass alloc] initWithType:type startIndex:startIndexInBytes length:length]; + return box; +} + ++ (Class)boxClassForType:(QGMP4BoxType)type { + + switch (type) { + case QGMP4BoxType_ftyp: + case QGMP4BoxType_free: + case QGMP4BoxType_moov: + case QGMP4BoxType_mvhd: + case QGMP4BoxType_trak: + case QGMP4BoxType_tkhd: + case QGMP4BoxType_edts: + case QGMP4BoxType_elst: + case QGMP4BoxType_mdia: + case QGMP4BoxType_minf: + case QGMP4BoxType_vmhd: + case QGMP4BoxType_dinf: + case QGMP4BoxType_dref: + case QGMP4BoxType_url: + case QGMP4BoxType_stbl: + case QGMP4BoxType_avc1: + case QGMP4BoxType_stss: + case QGMP4BoxType_udta: + case QGMP4BoxType_meta: + case QGMP4BoxType_ilst: + case QGMP4BoxType_data: + case QGMP4BoxType_iods: + case QGMP4BoxType_wide: + case QGMP4BoxType_loci: + case QGMP4BoxType_smhd: + return [QGMP4Box class]; + case QGMP4BoxType_mdat: + return [QGMP4MdatBox class]; + case QGMP4BoxType_avcC: + return [QGMP4AvccBox class]; + case QGMP4BoxType_mdhd: + return [QGMP4MvhdBox class]; + case QGMP4BoxType_stsd: + return [QGMP4StsdBox class]; + case QGMP4BoxType_stsz: + return [QGMP4StszBox class]; + case QGMP4BoxType_hdlr: + return [QGMP4HdlrBox class]; + case QGMP4BoxType_stsc: + return [QGMP4StscBox class]; + case QGMP4BoxType_stts: + return [QGMP4SttsBox class]; + case QGMP4BoxType_stco: + return [QGMP4StcoBox class]; + case QGMP4BoxType_hvcC: + return [QGMP4HvccBox class]; + case QGMP4BoxType_ctts: + return [QGMP4CttsBox class]; + default: + return nil; + } +} + +/** + 根据boxClassForType:方法的返回判断是否为一个合法的box类型 + + @param type box类型 + @return 除QGMP4BoxType定义的类型(不包含QGMP4BoxType_unknown)外全部为不合法类型 + */ ++ (BOOL)isTypeValueValid:(QGMP4BoxType)type { + + Class class = [self boxClassForType:type]; + if (class) { + return YES; + } + return NO; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.h new file mode 100644 index 0000000..76cb6b1 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.h @@ -0,0 +1,64 @@ +// QGMP4Parser.h +// 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 +#import "QGMP4Box.h" + +@class QGMP4Parser; +@protocol QGMP4ParserDelegate + +@optional +- (void)didParseMP4Box:(QGMP4Box *)box parser:(QGMP4Parser *)parser; +- (void)MP4FileDidFinishParse:(QGMP4Parser *)parser; + +@end + +@interface QGMP4Parser : NSObject + +@property (nonatomic, strong) QGMP4Box *rootBox; +@property (nonatomic, strong) NSFileHandle *fileHandle; +@property (nonatomic, weak) id delegate; + +- (instancetype)initWithFilePath:(NSString *)filePath; +- (void)parse; +- (NSData *)readDataForBox:(QGMP4Box *)box; +- (NSInteger)readValue:(const char*)bytes length:(NSInteger)length; + +@end + +@interface QGMP4ParserProxy : NSObject + +- (instancetype)initWithFilePath:(NSString *)filePath; + +@property (nonatomic, assign) NSInteger picWidth; //视频宽度 +@property (nonatomic, assign) NSInteger picHeight; //视频高度 +@property (nonatomic, assign) NSInteger fps; //视频fps +@property (nonatomic, assign) double duration; //视频时长 +@property (nonatomic, strong) NSData *spsData; //sps +@property (nonatomic, strong) NSData *ppsData; //pps +@property (nonatomic, strong) NSArray *videoSamples; //所有帧数据,包含了位置和大小等信息 +@property (nonatomic, strong) QGMP4Box *rootBox; //mp4文件根box +@property (nonatomic, strong) QGMP4TrackBox *videoTrackBox; //视频track +@property (nonatomic, strong) QGMP4TrackBox *audioTrackBox; //音频track +/** vps */ +@property (nonatomic, strong) NSData *vpsData; +/** 视频流编码器ID类型 */ +@property (nonatomic, assign) QGMP4VideoStreamCodecID videoCodecID; + +- (void)parse; +- (NSData *)readPacketOfSample:(NSInteger)sampleIndex; +- (NSData *)readDataOfBox:(QGMP4Box *)box length:(NSInteger)length offset:(NSInteger)offset; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.m new file mode 100644 index 0000000..2b28ab9 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/MP4Parser/QGMP4Parser.m @@ -0,0 +1,567 @@ +// QGMP4Parser.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 "QGMP4Parser.h" +#import "QGVAPLogger.h" + +#pragma mark - mp4 parser +@interface QGMP4Parser() { + + QGMp4BoxDataFetcher _boxDataFetcher; +} + +@property (nonatomic, strong) NSString *filePath; + +@end + +@implementation QGMP4Parser + +#pragma mark -- life cycle + +- (instancetype)initWithFilePath:(NSString *)filePath { + + if (self = [super init]) { + _filePath = filePath; + _fileHandle = [NSFileHandle fileHandleForReadingAtPath:_filePath]; + __weak __typeof(self) weakSelf = self; + _boxDataFetcher = ^NSData *(QGMP4Box *box) {return [weakSelf readDataForBox:box];}; + } + return self; +} + +- (void)dealloc { + + [_fileHandle closeFile]; +} + +#pragma mark -- methods + +- (void)parse { + + if (!_filePath || !_fileHandle) { + return ; + } + + unsigned long long fileSize = [_fileHandle seekToEndOfFile]; + [_fileHandle seekToFileOffset:0]; + _rootBox = [QGMP4BoxFactory createBoxForType:QGMP4BoxType_unknown startIndex:0 length:fileSize]; + NSMutableArray *BFSQueue = [NSMutableArray new]; + [BFSQueue addObject:_rootBox]; + + QGMP4Box *calBox = _rootBox; + + //长度包含包含类型码长度+本身长度 + while ((calBox = [BFSQueue firstObject])) { + [BFSQueue removeObjectAtIndex:0]; + + if (calBox.length <= 2*(kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes)) { + //长度限制 + continue ; + } + + unsigned long long offset = 0; + unsigned long long length = 0; + QGMP4BoxType type = QGMP4BoxType_unknown; + + //第一个子box + offset = calBox.superBox ? (calBox.startIndexInBytes + kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes) : 0; + + //avcbox特殊处理 + if (calBox.type == QGMP4BoxType_avc1 || calBox.type == QGMP4BoxType_hvc1 || calBox.type == QGMP4BoxType_stsd) { + unsigned long long avcOffset = calBox.startIndexInBytes+kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes; + unsigned long long avcEdge = calBox.startIndexInBytes+calBox.length-kQGBoxSizeLengthInBytes-kQGBoxTypeLengthInBytes; + unsigned long long avcLength = 0; + QGMP4BoxType avcType = QGMP4BoxType_unknown; + for (; avcOffset < avcEdge; avcOffset++) { + readBoxTypeAndLength(_fileHandle, avcOffset, &avcType, &avcLength); + if (avcType == QGMP4BoxType_avc1 || avcType == QGMP4BoxType_avcC || avcType == QGMP4BoxType_hvc1 || avcType == QGMP4BoxType_hvcC) { + QGMP4Box *avcBox = [QGMP4BoxFactory createBoxForType:avcType startIndex:avcOffset length:avcLength]; + if (!calBox.subBoxes) { + calBox.subBoxes = [NSMutableArray new]; + } + [calBox.subBoxes addObject:avcBox]; + avcBox.superBox = calBox; + [BFSQueue addObject:avcBox]; + offset = (avcBox.startIndexInBytes+avcBox.length); + [self didParseBox:avcBox]; + break ; + } + } + } + do { + //判断是否会越界 + if ((offset+kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes)>(calBox.startIndexInBytes+calBox.length)) { + break ; + } + readBoxTypeAndLength(_fileHandle, offset, &type, &length); + + if ((offset+length)>(calBox.startIndexInBytes+calBox.length)) { + //reach to super box end or not a box + break ; + } + + if (![QGMP4BoxFactory isTypeValueValid:type] && (offset == (calBox.startIndexInBytes + kQGBoxSizeLengthInBytes + kQGBoxTypeLengthInBytes))) { + //目前的策略是 + break ; + } + QGMP4Box *subBox = [QGMP4BoxFactory createBoxForType:type startIndex:offset length:length]; + subBox.superBox = calBox; + if (!calBox.subBoxes) { + calBox.subBoxes = [NSMutableArray new]; + } + //加入box节点 + [calBox.subBoxes addObject:subBox]; + + //进入广度优先遍历队列 + [BFSQueue addObject:subBox]; + [self didParseBox:subBox]; + + //继续兄弟box + offset += length; + } while(1); + } + + [self didFinisheParseFile]; +} + +- (NSData *)readDataForBox:(QGMP4Box *)box { + + if (!box) { + return nil; + } + [_fileHandle seekToFileOffset:box.startIndexInBytes]; + return [_fileHandle readDataOfLength:(NSUInteger)box.length]; +} + +- (NSInteger)readValue:(const char*)bytes length:(NSInteger)length { + + NSInteger value = 0; + for (int i = 0; i < length; i++) { + value += (bytes[i]&0xff)<<((length-i-1)*8); + } + return value; +} + +#pragma mark -- private methods + +- (void)didParseBox:(QGMP4Box *)box { + + if ([box respondsToSelector:@selector(boxDidParsed:)]) { + [box boxDidParsed:_boxDataFetcher]; + } + if ([self.delegate respondsToSelector:@selector(didParseMP4Box:parser:)]) { + [self.delegate didParseMP4Box:box parser:self]; + } +} + +- (void)didFinisheParseFile { + + if ([self.delegate respondsToSelector:@selector(MP4FileDidFinishParse:)]) { + [self.delegate MP4FileDidFinishParse:self]; + } +} + +void readBoxTypeAndLength(NSFileHandle *fileHandle, unsigned long long offset, QGMP4BoxType *type, unsigned long long *length) { + + [fileHandle seekToFileOffset:offset]; + NSData *data = [fileHandle readDataOfLength:kQGBoxSizeLengthInBytes+kQGBoxTypeLengthInBytes]; + const char *bytes = data.bytes; + *length = ((bytes[0]&0xff)<<24)+((bytes[1]&0xff)<<16)+((bytes[2]&0xff)<<8)+(bytes[3]&0xff); + *type = ((bytes[4]&0xff)<<24)+((bytes[5]&0xff)<<16)+((bytes[6]&0xff)<<8)+(bytes[7]&0xff); +} + +@end + +#pragma mark - parser proxy + +@interface QGMP4ParserProxy() { + + QGMP4Parser *_parser; +} + +@end + +@implementation QGMP4ParserProxy + +- (instancetype)initWithFilePath:(NSString *)filePath { + + if (self = [super init]) { + + _parser = [[QGMP4Parser alloc] initWithFilePath:filePath]; + _parser.delegate = self; + } + return self; +} + +- (NSInteger)picWidth { + + if (_picWidth == 0) { + _picWidth = [self readPicWidth]; + } + return _picWidth; +} + +- (NSInteger)picHeight { + + if (_picHeight == 0) { + _picHeight = [self readPicHeight]; + } + return _picHeight; +} + +- (NSInteger)fps { + + if (_fps == 0) { + if (self.videoSamples.count == 0) { + return 0; + } + _fps = self.videoSamples.count/self.duration; + } + return _fps; +} + +- (double)duration { + + if (_duration == 0) { + _duration = [self readDuration]; + } + return _duration; +} + +- (NSArray *)videoSamples { + + if (_videoSamples) { + return _videoSamples; + } + NSMutableArray *videoSamples = [NSMutableArray new]; + + uint64_t start_play_time = 0; + uint64_t tmp = 0; + uint32_t sampIdx = 0; + QGMP4SttsBox *sttsBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stts]; + QGMP4StszBox *stszBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stsz]; + QGMP4StscBox *stscBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stsc]; + QGMP4StcoBox *stcoBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_stco]; + QGMP4CttsBox *cttsBox = [self.videoTrackBox subBoxOfType:QGMP4BoxType_ctts]; + for (int i = 0; i < sttsBox.entries.count; ++i) { + QGSttsEntry *entry = sttsBox.entries[i]; + for (int j = 0; j < entry.sampleCount; ++j) { + QGMP4Sample *sample = [QGMP4Sample new]; + sample.sampleDelta = entry.sampleDelta; + sample.codecType = QGMP4CodecTypeVideo; + sample.sampleIndex = sampIdx; + sample.pts = tmp + [cttsBox.compositionOffsets[j] unsignedLongLongValue]; + if (sampIdx < stszBox.sampleSizes.count) { + sample.sampleSize = (int32_t)[stszBox.sampleSizes[sampIdx] integerValue]; + } + [videoSamples addObject:sample]; + start_play_time += entry.sampleDelta; + sampIdx++; + tmp += entry.sampleDelta; + } + + NSMutableArray *chunkOffsets = [NSMutableArray new]; + uint32_t chunkIndex = 0; + uint32_t totalSample = 0; + for (int j = 0; j < stscBox.entries.count; ++j) { + QGStscEntry *entry = stscBox.entries[j]; + if (j < stscBox.entries.count - 1) { + QGStscEntry *nextEntry = stscBox.entries[j+1]; + for (int k = 0; k < nextEntry.firstChunk - entry.firstChunk; ++k) { + QGChunkOffsetEntry *offsetEntry = [QGChunkOffsetEntry new]; + offsetEntry.samplesPerChunk = entry.samplesPerChunk; + totalSample += entry.samplesPerChunk; + if (chunkIndex < stcoBox.chunkOffsets.count) { + offsetEntry.offset = (uint32_t)[stcoBox.chunkOffsets[chunkIndex] integerValue]; + } + chunkIndex++; + [chunkOffsets addObject:offsetEntry]; + } + } else { + //只有一个或最后一个 + while (chunkIndex < stcoBox.chunkOffsets.count) { + QGChunkOffsetEntry *offsetEntry = [QGChunkOffsetEntry new]; + offsetEntry.samplesPerChunk = entry.samplesPerChunk; + offsetEntry.offset = (uint32_t)[stcoBox.chunkOffsets[chunkIndex] integerValue]; + totalSample += entry.samplesPerChunk; + chunkIndex++; + [chunkOffsets addObject:offsetEntry]; + } + } + } + sampIdx = 0; + for (int i = 0; i < chunkOffsets.count; ++i) { + QGChunkOffsetEntry *offsetEntry = chunkOffsets[i]; + uint32_t offsetChunk = 0; + for (int j = 0; j < offsetEntry.samplesPerChunk; ++j) { + if (sampIdx < videoSamples.count) { + QGMP4Sample *videoSample = videoSamples[sampIdx]; + videoSample.chunkIndex = i; + videoSample.streamOffset = offsetEntry.offset + offsetChunk; + offsetChunk += videoSample.sampleSize; + sampIdx++; + } + } + } + } + _videoSamples = videoSamples; + return _videoSamples; +} + +/** + 调用该方法才会解析mp4文件并得到必要信息。 + */ +- (void)parse { + + [_parser parse]; + _rootBox = _parser.rootBox; + + // 解析视频解码配置信息 + [self parseVideoDecoderConfigRecord]; +} + +#pragma mark - Private + +- (void)parseVideoDecoderConfigRecord { + if (self.videoCodecID == QGMP4VideoStreamCodecIDH264) { + [self parseAvccDecoderConfigRecord]; + } else if (self.videoCodecID == QGMP4VideoStreamCodecIDH265) { + [self parseHvccDecoderConfigRecord]; + } +} + +- (void)parseAvccDecoderConfigRecord { + self.spsData = [self parseAvccSPSData]; + self.ppsData = [self parseAvccPPSData]; +} + +- (void)parseHvccDecoderConfigRecord { + NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_hvcC]]; + if (extraData.length <= 8) { + return; + } + + const char *bytes = extraData.bytes; + int index = 30; // 21 + 4 + 4 + + //int lengthSize = ((bytes[index++] & 0xff) & 0x03) + 1; + int arrayNum = bytes[index++] & 0xff; + + // sps pps vps 种类数量 + for (int i = 0; i < arrayNum; i++) { + int value = bytes[index++] & 0xff; + int naluType = value & 0x3F; + // sps pps vps 各自的数量 + int naluNum = ((bytes[index] & 0xff) << 8) + (bytes[index + 1] & 0xff); + index += 2; + + for (int j = 0; j < naluNum; j++) { + int naluLength = ((bytes[index] & 0xff) << 8) + (bytes[index + 1] & 0xff); + index += 2; + NSData *paramData = [NSData dataWithBytes:&bytes[index] length:naluLength]; + + if (naluType == 32) { + // vps + self.vpsData = paramData; + } else if (naluType == 33) { + // sps + self.spsData = paramData; + } else if (naluType == 34) { + // pps + self.ppsData = paramData; + } + + index += naluLength; + } + } +} + +- (NSData *)parseAvccSPSData { + //boxsize(32)+boxtype(32)+prefix(40)+预留(3)+spsCount(5)+spssize(16)+...+ppscount(8)+ppssize(16)+... + NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_avcC]]; + if (extraData.length <= 8) { + return nil; + } + const char *bytes = extraData.bytes; + //sps数量 默认一个暂无使用 + //NSInteger spsCount = bytes[13]&0x1f; + NSInteger spsLength = ((bytes[14]&0xff)<<8) + (bytes[15]&0xff); + NSInteger naluType = (uint8_t)bytes[16]&0x1F; + if (spsLength + 16 > extraData.length || naluType != 7) { + return nil; + } + NSData *spsData = [NSData dataWithBytes:&bytes[16] length:spsLength]; + return spsData; +} + +- (NSData *)parseAvccPPSData { + NSData *extraData = [_parser readDataForBox:[self.videoTrackBox subBoxOfType:QGMP4BoxType_avcC]]; + if (extraData.length <= 8) { + return nil; + } + const char *bytes = extraData.bytes; + NSInteger spsCount = bytes[13]&0x1f; + NSInteger spsLength = ((bytes[14]&0xff)<<8) + (bytes[15]&0xff); + NSInteger prefixLength = 16 + spsLength; + + while (--spsCount > 0) { + if (prefixLength+2 >= extraData.length) { + return nil; + } + NSInteger nextSpsLength = ((bytes[prefixLength]&0xff)<<8)+bytes[prefixLength+1]&0xff; + prefixLength += nextSpsLength; + } + + //默认1个 + // NSInteger ppsCount = bytes[prefixLength]&0xff; + if (prefixLength+3 >= extraData.length) { + return nil; + } + NSInteger ppsLength = ((bytes[prefixLength+1]&0xff)<<8)+(bytes[prefixLength+2]&0xff); + NSInteger naluType = (uint8_t)bytes[prefixLength+3]&0x1F; + if (naluType != 8 || (ppsLength+prefixLength+3) > extraData.length) { + return nil; + } + + NSData *ppsData = [NSData dataWithBytes:&bytes[prefixLength+3] length:ppsLength]; + return ppsData; +} + +- (NSInteger)readPicWidth { + if (self.videoCodecID == QGMP4VideoStreamCodecIDUnknown) { + return 0; + } + + QGMP4BoxType boxType = self.videoCodecID == QGMP4VideoStreamCodecIDH264 ? QGMP4BoxType_avc1 : QGMP4BoxType_hvc1; + NSInteger sizeIndex = 32; + NSUInteger readLength = 2; + QGMP4Box *avc1 = [self.videoTrackBox subBoxOfType:boxType]; + [_parser.fileHandle seekToFileOffset:avc1.startIndexInBytes+sizeIndex]; + NSData *widthData = [_parser.fileHandle readDataOfLength:readLength]; + + if (widthData.length < readLength) { + return 0; + } + + const char *bytes = widthData.bytes; + NSInteger width = ((bytes[0]&0xff)<<8)+(bytes[1]&0xff); + return width; +} + +- (NSInteger)readPicHeight { + if (self.videoCodecID == QGMP4VideoStreamCodecIDUnknown) { + return 0; + } + + QGMP4BoxType boxType = self.videoCodecID == QGMP4VideoStreamCodecIDH264 ? QGMP4BoxType_avc1 : QGMP4BoxType_hvc1; + NSInteger sizeIndex = 34; + NSUInteger readLength = 2; + QGMP4Box *avc1 = [self.videoTrackBox subBoxOfType:boxType]; + [_parser.fileHandle seekToFileOffset:avc1.startIndexInBytes+sizeIndex]; + NSData *heightData = [_parser.fileHandle readDataOfLength:readLength]; + + if (heightData.length < readLength) { + return 0; + } + + const char *bytes = heightData.bytes; + NSInteger height = ((bytes[0]&0xff)<<8)+(bytes[1]&0xff); + return height; +} + +- (double)readDuration { + + QGMP4MvhdBox *mdhdBox = [self.rootBox subBoxOfType:QGMP4BoxType_mvhd]; + NSData *mvhdData = [_parser readDataForBox:mdhdBox]; + const char *bytes = mvhdData.bytes; + NSInteger version = READ32BIT(&bytes[8]); + NSInteger timescaleIndex = 20; + NSInteger timescaleLength = 4; + NSInteger durationIndex = 24; + NSInteger durationLength = 4; + if (version == 1) { + timescaleIndex = 28; + durationIndex = 32; + durationLength = 8; + } + + NSInteger scale = [_parser readValue:&bytes[timescaleIndex] length:timescaleLength]; + NSInteger duration = [_parser readValue:&bytes[durationIndex] length:durationLength]; + if (scale == 0) { + return 0; + } + double result = duration/(double)scale; + return result; +} + +- (NSData *)readPacketOfSample:(NSInteger)sampleIndex { + + if (sampleIndex >= self.videoSamples.count) { + VAP_Error(kQGVAPModuleCommon, @"readPacketOfSample beyond bounds!:%@ > %@", @(sampleIndex), @(self.videoSamples.count-1)); + return nil; + } + QGMP4Sample *videoSample = self.videoSamples[sampleIndex]; + NSInteger currentSampleSize = videoSample.sampleSize; + [_parser.fileHandle seekToFileOffset:videoSample.streamOffset]; + // 当视频文件有问题时,sampleIndex还没有到最后,sampleIndex < self.videoSamples.count(总帧数)时,readDataOfLength长度可能为0Bytes + NSData *packetData = [_parser.fileHandle readDataOfLength:currentSampleSize]; + return packetData; +} + +- (NSData *)readDataOfBox:(QGMP4Box *)box length:(NSInteger)length offset:(NSInteger)offset { + + if (length <= 0 || offset + length > box.length) { + return nil; + } + [_parser.fileHandle seekToFileOffset:box.startIndexInBytes+offset]; + NSData *data = [_parser.fileHandle readDataOfLength:length]; + return data; +} + +#pragma mark -- delegate + +- (void)MP4FileDidFinishParse:(QGMP4Parser *)parser { + +} + +- (void)didParseMP4Box:(QGMP4Box *)box parser:(QGMP4Parser *)parser { + + switch (box.type) { + case QGMP4BoxType_hdlr: { + QGMP4TrackType trackType = ((QGMP4HdlrBox*)box).trackType; + QGMP4TrackBox *trackBox = (QGMP4TrackBox*)[box superBoxOfType:QGMP4BoxType_trak]; + switch (trackType) { + case QGMP4TrackType_Video: + self.videoTrackBox = trackBox; + break; + case QGMP4TrackType_Audio: + self.audioTrackBox = trackBox; + break; + default: + break; + } + } break; + case QGMP4BoxType_avc1: { + self.videoCodecID = QGMP4VideoStreamCodecIDH264; + } break; + case QGMP4BoxType_hvc1: { + self.videoCodecID = QGMP4VideoStreamCodecIDH265; + } break; + default: + break; + } +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.h new file mode 100644 index 0000000..15d5161 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.h @@ -0,0 +1,25 @@ +// QGBaseAnimatedImageFrame+Displaying.h +// 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 "QGBaseAnimatedImageFrame.h" + +@interface QGBaseAnimatedImageFrame (Displaying) + +@property (nonatomic, strong) NSDate *startDate; //开始播放的时间 +@property (nonatomic, assign) NSTimeInterval decodeTime; //解码时间 + +- (BOOL)shouldFinishDisplaying; //是否需要结束播放(根据播放时长来决定) + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.m new file mode 100644 index 0000000..4677b4f --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame+Displaying.m @@ -0,0 +1,34 @@ +// QGBaseAnimatedImageFrame+Displaying.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 "QGBaseAnimatedImageFrame+Displaying.h" +#import +#import "VAPMacros.h" + +@implementation QGBaseAnimatedImageFrame (Displaying) + +HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(decodeTime, setDecodeTime, NSTimeInterval) +HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(startDate, setStartDate, OBJC_ASSOCIATION_RETAIN); + +- (BOOL)shouldFinishDisplaying { + + if (!self.startDate) { + return YES; + } + NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:self.startDate]; + //每一个VSYNC16ms + return timeInterval*1000 + 10 >= self.duration; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.h new file mode 100644 index 0000000..b519d12 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.h @@ -0,0 +1,25 @@ +// QGBaseAnimatedImageFrame.h +// 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 + +@interface QGBaseAnimatedImageFrame : NSObject + +@property (atomic, assign) NSInteger frameIndex; //当前帧索引 +@property (atomic, assign) NSTimeInterval duration; //播放时长 +/** pts */ +@property (atomic, assign) uint64_t pts; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.m new file mode 100644 index 0000000..78a8584 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseAnimatedImageFrame.m @@ -0,0 +1,20 @@ +// QGBaseAnimatedImageFrame.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 "QGBaseAnimatedImageFrame.h" + +@implementation QGBaseAnimatedImageFrame + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.h new file mode 100644 index 0000000..9b8536a --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.h @@ -0,0 +1,23 @@ +// QGBaseDFileInfo.h +// 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 + +@interface QGBaseDFileInfo : NSObject + +@property (nonatomic, strong) NSString *filePath; //文件路径 +@property (atomic, assign) NSInteger occupiedCount; //作用类似retainCount + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.m new file mode 100644 index 0000000..44bf6ac --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGBaseDFileInfo.m @@ -0,0 +1,20 @@ +// QGBaseDFileInfo.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 "QGBaseDFileInfo.h" + +@implementation QGBaseDFileInfo + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.h new file mode 100644 index 0000000..486fddd --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.h @@ -0,0 +1,24 @@ +// QGMP4AnimatedImageFrame.h +// 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 "QGBaseAnimatedImageFrame.h" +#import + +@interface QGMP4AnimatedImageFrame : QGBaseAnimatedImageFrame + +@property (nonatomic, assign) CVPixelBufferRef pixelBuffer; +@property (nonatomic, assign) int defaultFps; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.m new file mode 100644 index 0000000..4a3aa42 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4AnimatedImageFrame.m @@ -0,0 +1,28 @@ +// QGMP4AnimatedImageFrame.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 "QGMP4AnimatedImageFrame.h" + +@implementation QGMP4AnimatedImageFrame + +- (void)dealloc { + + //释放image buffer + if (self.pixelBuffer) { + CVPixelBufferRelease(self.pixelBuffer); + } +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.h new file mode 100644 index 0000000..3ed94bb --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.h @@ -0,0 +1,23 @@ +// QGMP4HWDFileInfo.h +// 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 "QGBaseDFileInfo.h" +#import "QGMP4Parser.h" + +@interface QGMP4HWDFileInfo : QGBaseDFileInfo + +@property (nonatomic, strong) QGMP4ParserProxy *mp4Parser; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.m new file mode 100644 index 0000000..bb97bfb --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGMP4HWDFileInfo.m @@ -0,0 +1,20 @@ +// QGMP4HWDFileInfo.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 "QGMP4HWDFileInfo.h" + +@implementation QGMP4HWDFileInfo + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.h new file mode 100644 index 0000000..e50e835 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.h @@ -0,0 +1,112 @@ +// QGVAPConfigModel.h +// 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 +#import +#import + +typedef NS_ENUM(NSInteger, QGVAPOrientation){ + + QGVAPOrientation_None = 0, // 兼容 + QGVAPOrientation_Portrait = 1, // 竖屏 + QGVAPOrientation_landscape = 2, // 横屏 +}; + +typedef NSString * QGAGAttachmentSourceType NS_EXTENSIBLE_STRING_ENUM;//资源类型 +typedef NSString * QGAGAttachmentSourceLoadType NS_EXTENSIBLE_STRING_ENUM;//资源加载类型 +typedef NSString * QGAGAttachmentSourceStyle NS_EXTENSIBLE_STRING_ENUM;//字体 +typedef NSString * QGAGAttachmentFitType NS_EXTENSIBLE_STRING_ENUM;//资源适配类型 + +//资源适配类型 +UIKIT_EXTERN QGAGAttachmentFitType const kQGAGAttachmentFitTypeFitXY; //按指定尺寸缩放 +UIKIT_EXTERN QGAGAttachmentFitType const kQGAGAttachmentFitTypeCenterFull; //默认按资源尺寸展示,如果资源尺寸小于遮罩,则等比缩放至可填满 +//资源类型 +UIKIT_EXTERN QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeTextStr; //文字 +UIKIT_EXTERN QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeImgUrl; //图片 + +UIKIT_EXTERN QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeText; +UIKIT_EXTERN QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeImg; +UIKIT_EXTERN QGAGAttachmentSourceLoadType const QGAGAttachmentSourceLoadTypeLocal; +UIKIT_EXTERN QGAGAttachmentSourceLoadType const QGAGAttachmentSourceLoadTypeNet; + +//字体 +UIKIT_EXTERN QGAGAttachmentSourceStyle const kQGAGAttachmentSourceStyleBoldText; //粗体 + +//https://docs.qq.com/sheet/DTGl0bXdidFVkS3pn?tab=7od8yj&c=C25A0I0 +@class QGVAPCommonInfo,QGVAPSourceInfo,QGVAPMergedInfo; +@interface QGVAPConfigModel : NSObject + +@property (nonatomic, strong) QGVAPCommonInfo *info; +@property (nonatomic, strong) NSArray *resources; +@property (nonatomic, strong) NSDictionary *> *mergedConfig; ///@{帧,@[多个融合信息]} + +@end + +#pragma mark - 整体信息 +@interface QGVAPCommonInfo : NSObject + +@property (nonatomic, assign) NSInteger version; +@property (nonatomic, assign) NSInteger framesCount; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, assign) CGSize videoSize; +@property (nonatomic, assign) QGVAPOrientation targetOrientaion; +@property (nonatomic, assign) NSInteger fps; +@property (nonatomic, assign) BOOL isMerged; +@property (nonatomic, assign) CGRect alphaAreaRect; +@property (nonatomic, assign) CGRect rgbAreaRect; + +@end + +#pragma mark - 渲染资源信息 +@interface QGVAPSourceInfo : NSObject + +//原始信息 +@property (nonatomic, strong) QGAGAttachmentSourceType type; +@property (nonatomic, strong) QGAGAttachmentSourceLoadType loadType; +@property (nonatomic, strong) NSString *contentTag; +@property (nonatomic, strong) NSString *contentTagValue; +@property (nonatomic, strong) UIColor *color; +@property (nonatomic, strong) QGAGAttachmentSourceStyle style; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, strong) QGAGAttachmentFitType fitType; + +//加载内容 +@property (nonatomic, strong) UIImage *sourceImage; +@property (nonatomic, strong) id texture; +@property (nonatomic, strong) id colorParamsBuffer; + +@end + +@interface QGVAPSourceDisplayItem : NSObject + +@property (nonatomic, assign) CGRect frame; +@property (nonatomic, strong) QGVAPSourceInfo *sourceInfo; + +@end + +#pragma mark - 融合信息 +@interface QGVAPMergedInfo : NSObject + +@property (nonatomic, strong) QGVAPSourceInfo *source; +@property (nonatomic, assign) NSInteger renderIndex; +@property (nonatomic, assign) CGRect renderRect; +@property (nonatomic, assign) BOOL needMask; +@property (nonatomic, assign) CGRect maskRect; +@property (nonatomic, assign) NSInteger maskRotation; + +//加载内容 +- (id)vertexBufferWithContainerSize:(CGSize)size maskContianerSize:(CGSize)mSize device:(id)device; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.m new file mode 100644 index 0000000..665bc2d --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPConfigModel.m @@ -0,0 +1,108 @@ +// QGVAPConfigModel.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 "QGVAPConfigModel.h" +#import "NSDictionary+VAPUtil.h" +#import "QGVAPMetalUtil.h" +#import "QGVAPLogger.h" +#import "UIDevice+VAPUtil.h" + +//资源适配类型 +QGAGAttachmentFitType const kQGAGAttachmentFitTypeFitXY = @"fitXY"; //按指定尺寸缩放 +QGAGAttachmentFitType const kQGAGAttachmentFitTypeCenterFull = @"centerFull"; //默认按资源尺寸展示,如果资源尺寸小于遮罩,则等比缩放至可填满 +//资源类型 +QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeTextStr = @"textStr"; //文字 +QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeImgUrl = @"imgUrl"; //图片 + +QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeText = @"txt"; //文字 +QGAGAttachmentSourceType const kQGAGAttachmentSourceTypeImg = @"img"; //图片 +QGAGAttachmentSourceLoadType const QGAGAttachmentSourceLoadTypeLocal = @"local"; +QGAGAttachmentSourceLoadType const QGAGAttachmentSourceLoadTypeNet = @"net"; + +//字体 +QGAGAttachmentSourceStyle const kQGAGAttachmentSourceStyleBoldText = @"b"; //粗体 + +@implementation QGVAPConfigModel + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> {info:%@, configs:%@}", self.class, self, _info, _mergedConfig]; +} + +@end + +@implementation QGVAPCommonInfo + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> {version:%@, frames:%@, size:(%@,%@), videoSize:(%@,%@) orien:%@, fps:%@, merged:%@, alpha:(%@,%@,%@,%@), rgb:(%@,%@,%@,%@)}", self.class, self, @(_version), @(_framesCount), @(_size.width), @(_size.height), @(_videoSize.width), @(_videoSize.height), @(_targetOrientaion), @(_fps), @(_isMerged), @(_alphaAreaRect.origin.x), @(_alphaAreaRect.origin.y), @(_alphaAreaRect.size.width), @(_alphaAreaRect.size.height), @(_rgbAreaRect.origin.x), @(_rgbAreaRect.origin.y), @(_rgbAreaRect.size.width), @(_rgbAreaRect.size.height)]; +} + +@end + +@implementation QGVAPSourceInfo + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> {type:%@, tag:%@-%@ color:%@, style:%@, size:(%@,%@), fitType:%@}", self.class, self, _type, _contentTag, _contentTagValue, _color, _style, @(_size.width), @(_size.height), _fitType]; +} + +@end + +@implementation QGVAPSourceDisplayItem + +@end + +@implementation QGVAPMergedInfo + +- (id)vertexBufferWithContainerSize:(CGSize)size maskContianerSize:(CGSize)mSize device:(id)device { + + if (size.width <= 0 || size.height <= 0 || mSize.width <= 0 || mSize.height <= 0) { + VAP_Error(kQGVAPModuleCommon, @"vertexBufferWithContainerSize size error! :%@ - %@", [NSValue valueWithCGSize:size], [NSValue valueWithCGSize:mSize]); + NSAssert(0, @"vertexBufferWithContainerSize size error!"); + return nil; + } + const int colunmCountForVertices = 4, colunmCountForCoordinate = 2, vertexDataLength = 32; + float vertices[16], maskCoordinates[8], sourceCoordinates[8]; + genMTLVertices(self.renderRect, size, vertices, NO); + genMTLTextureCoordinates(self.maskRect, mSize, maskCoordinates,YES, self.maskRotation); + + if ([self.source.fitType isEqualToString:kQGAGAttachmentFitTypeCenterFull]) { + CGRect sourceRect = vapRectForCenterFull(self.source.size, self.renderRect.size); + CGSize sourceSize = vapSourceSizeForCenterFull(self.source.size, self.renderRect.size); + genMTLTextureCoordinates(sourceRect, sourceSize, sourceCoordinates,NO, 0); + } else { + replaceArrayElements(sourceCoordinates, (void*)kVAPMTLTextureCoordinatesIdentity, 8); + } + static float vertexData[vertexDataLength]; + int indexForVertexData = 0; + //顶点数据+纹理坐标+遮罩纹理坐标 + for (int i = 0; i < 16; i ++) { + vertexData[indexForVertexData++] = ((float*)vertices)[i]; + if (i%colunmCountForVertices == colunmCountForVertices-1) { + int row = i/colunmCountForVertices; + vertexData[indexForVertexData++] = ((float*)sourceCoordinates)[row*colunmCountForCoordinate]; + vertexData[indexForVertexData++] = ((float*)sourceCoordinates)[row*colunmCountForCoordinate+1]; + vertexData[indexForVertexData++] = ((float*)maskCoordinates)[row*colunmCountForCoordinate]; + vertexData[indexForVertexData++] = ((float*)maskCoordinates)[row*colunmCountForCoordinate+1]; + } + } + NSUInteger allocationSize = vertexDataLength * sizeof(float); + id vertexBuffer = [device newBufferWithBytes:vertexData length:allocationSize options:kDefaultMTLResourceOption]; + return vertexBuffer; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> {index:%@, rect:(%@,%@,%@,%@), mask:%@, maskRect:(%@,%@,%@,%@), maskRotation:%@, source:%@}", self.class, self, @(_renderIndex), @(_renderRect.origin.x), @(_renderRect.origin.y), @(_renderRect.size.width), @(_renderRect.size.height), @(_needMask), @(_maskRect.origin.x), @(_maskRect.origin.y), @(_maskRect.size.width), @(_maskRect.size.height), @(_maskRotation), _source]; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.h new file mode 100644 index 0000000..02521f0 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.h @@ -0,0 +1,40 @@ +// QGVAPMaskInfo.h +// 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 +#import +#import + +typedef NSUInteger QGVAPMaskValues; + +NS_ASSUME_NONNULL_BEGIN + +// 如果要更新data、rect、size必须重新创建QGVAPMaskInfo对象 +@interface QGVAPMaskInfo : NSObject + +/** mask数据 0/1 Byte */ +@property (nonatomic, strong) NSData *data; +/** 采样范围 与datasize单位一致 */ +@property (nonatomic, assign) CGRect sampleRect; +/** mask 大小 单位pixel */ +@property (nonatomic, assign) CGSize dataSize; +/** 模糊范围,单位pixel */ +@property (nonatomic, assign) NSInteger blurLength; +/** mask纹理 */ +@property (nonatomic, strong, readonly) id texture; + +@end + +NS_ASSUME_NONNULL_END diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.m new file mode 100644 index 0000000..714b412 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPMaskInfo.m @@ -0,0 +1,31 @@ +// QGVAPMaskInfo.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 "QGVAPMaskInfo.h" +#import "QGVAPTextureLoader.h" +#import "QGHWDMetalRenderer.h" + +@implementation QGVAPMaskInfo + +@synthesize texture = _texture; + +- (id)texture { + if (!_texture) { + _texture = [QGVAPTextureLoader loadTextureWithData:self.data device:kQGHWDMetalRendererDevice width:self.dataSize.width height:self.dataSize.height]; + } + return _texture; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.h new file mode 100644 index 0000000..c4c771f --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.h @@ -0,0 +1,31 @@ +// QGVAPTextureLoader.h +// 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 +#import + +@interface QGVAPTextureLoader : NSObject + ++ (id)loadVapColorFillBufferWith:(UIColor *)color device:(id)device; + ++ (id)loadTextureWithImage:(UIImage *)image device:(id)device; + ++ (id)loadTextureWithData:(NSData *)data device:(id)device width:(CGFloat)width height:(CGFloat)height; + ++ (UIImage *)drawingImageForText:(NSString *)textStr color:(UIColor *)color size:(CGSize)size bold:(BOOL)bold; + ++ (UIFont *)getAppropriateFontWith:(NSString *)text rect:(CGRect)fitFrame designedSize:(CGFloat)designedFontSize bold:(BOOL)isBold textSize:(CGSize *)textSize; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.m new file mode 100644 index 0000000..381e6c8 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Models/QGVAPTextureLoader.m @@ -0,0 +1,183 @@ +// QGVAPTextureLoader.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 "QGVAPTextureLoader.h" +#import +#import "QGHWDShaderTypes.h" +#import "QGVAPLogger.h" +#import "UIDevice+VAPUtil.h" + +@implementation QGVAPTextureLoader + +#if TARGET_OS_SIMULATOR//模拟器 ++ (id)loadVapColorFillBufferWith:(UIColor *)color device:(id)device {return nil;} ++ (id)loadTextureWithImage:(UIImage *)image device:(id)device {return nil;} ++ (UIImage *)drawingImageForText:(NSString *)textStr color:(UIColor *)color size:(CGSize)size bold:(BOOL)bold {return nil;} ++ (UIFont *)getAppropriateFontWith:(NSString *)text rect:(CGRect)fitFrame designedSize:(CGFloat)designedFontSize bold:(BOOL)isBold textSize:(CGSize *)textSize {return nil;} +#else + ++ (id)loadVapColorFillBufferWith:(UIColor *)color device:(id)device { + + CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; + if (color) { + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + } + struct VapAttachmentFragmentParameter colorParams[] = {{color != nil ? 0 : 1, {red, green, blue, alpha}}}; + NSUInteger colorParamsSize = sizeof(struct VapAttachmentFragmentParameter); + id buffer = [device newBufferWithBytes:colorParams length:colorParamsSize options:kDefaultMTLResourceOption]; + return buffer; +} + ++ (id)loadTextureWithImage:(UIImage *)image device:(id)device { + + if (!image) { + VAP_Error(kQGVAPModuleCommon, @"attemp to loadTexture with nil image"); + return nil; + } + if (@available(iOS 10.0, *)) { + MTKTextureLoader *loader = [[MTKTextureLoader alloc] initWithDevice:device]; + NSError *error = nil; + id texture = [loader newTextureWithCGImage:image.CGImage options:@{MTKTextureLoaderOptionOrigin : MTKTextureLoaderOriginFlippedVertically} error:&error]; + if (!texture || error) { + VAP_Error(kQGVAPModuleCommon, @"loadTexture error:%@", error); + return nil; + } + return texture; + } + return [self cg_loadTextureWithImage:image device:device]; +} + ++ (UIImage *)drawingImageForText:(NSString *)textStr color:(UIColor *)color size:(CGSize)size bold:(BOOL)bold { + + if (textStr.length == 0) { + VAP_Error(kQGVAPModuleCommon, @"draw text resource fail cuz text is nil !!"); + return nil; + } + if (!color) { + color = [UIColor blackColor]; + } + CGRect rect = CGRectMake(0, 0, size.width/2.0, size.height/2.0); + CGSize textSize = CGSizeZero; + UIFont *font = [QGVAPTextureLoader getAppropriateFontWith:textStr rect:rect designedSize:rect.size.height*0.8 bold:bold textSize:&textSize]; + if (!font) { + VAP_Error(kQGVAPModuleCommon, @"draw text resource:%@ fail cuz font is nil !!", textStr); + return nil; + } + NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; + paragraphStyle.alignment = NSTextAlignmentCenter; + paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; + NSDictionary *attr = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle, NSForegroundColorAttributeName:color}; + UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale); + rect.origin.y = (rect.size.height - font.lineHeight)/2.0; + [textStr drawWithRect:rect options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + if (!image) { + VAP_Error(kQGVAPModuleCommon, @"draw text resource:%@ fail cuz UIGraphics fail.", textStr); + return nil; + } + return image; +} + ++ (id)cg_loadTextureWithImage:(UIImage *)image device:(id)device { + + CGImageRef imageRef = image.CGImage; + if (!device || imageRef == nil) { + VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz device/image is nil-device:%@ imaghe%@", device, imageRef); + return nil; + } + CGFloat width = CGImageGetWidth(imageRef), height = CGImageGetHeight(imageRef); + NSInteger bytesPerPixel = 4, bytesPerRow = bytesPerPixel * width, bitsPerComponent = 8; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + void *rawData = calloc(height * width * bytesPerPixel, sizeof(uint8_t)); + if (rawData == nil) { + VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz alloc mem fail!width:%@ height:%@ bytesPerPixel:%@", @(width), @(height), @(bytesPerPixel)); + CGColorSpaceRelease(colorSpace); + colorSpace = NULL; + return nil; + } + CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGImageByteOrder32Big); + CGColorSpaceRelease(colorSpace); + colorSpace = NULL; + if (context == nil) { + VAP_Error(kQGVAPModuleCommon, @"CGBitmapContextCreate error width:%@ height:%@ bitsPerComponent:%@ bytesPerRow:%@", @(width), @(height), @(bitsPerComponent), @(bytesPerRow)); + free(rawData); + return nil; + } + CGContextTranslateCTM(context, 0, height); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + + MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:width height:height mipmapped:NO]; + id texture = [device newTextureWithDescriptor:textureDescriptor]; + if (!texture) { + VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz fail getting texture"); + free(rawData); + CGContextRelease(context); + return nil; + } + MTLRegion region = MTLRegionMake3D(0, 0, 0, width, height, 1); + [texture replaceRegion:region mipmapLevel:0 withBytes:rawData bytesPerRow:bytesPerRow]; + free(rawData); + CGContextRelease(context); + return texture; +} + ++ (id)loadTextureWithData:(NSData *)data device:(id)device width:(CGFloat)width height:(CGFloat)height { + + if (!data) { + VAP_Error(kQGVAPModuleCommon, @"attemp to loadTexture with nil data"); + return nil; + } + MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm width:width height:height mipmapped:NO]; + id texture = [device newTextureWithDescriptor:textureDescriptor]; + if (!texture) { + VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz fail getting texture"); + return nil; + } + MTLRegion region = MTLRegionMake3D(0, 0, 0, width, height, 1); + + const void *bytes = [data bytes]; + [texture replaceRegion:region mipmapLevel:0 withBytes:bytes bytesPerRow:width]; + return texture; +} + +//根据指定的字符内容和容器大小计算合适的字体 ++ (UIFont *)getAppropriateFontWith:(NSString *)text rect:(CGRect)fitFrame designedSize:(CGFloat)designedFontSize bold:(BOOL)isBold textSize:(CGSize *)textSize { + + UIFont *designedFont = isBold? [UIFont boldSystemFontOfSize:designedFontSize] : [UIFont systemFontOfSize:designedFontSize]; + if (text.length == 0 || CGRectEqualToRect(CGRectZero, fitFrame) || !designedFont) { + *textSize = fitFrame.size; + return designedFont ; + } + CGSize stringSize = [text sizeWithAttributes:@{NSFontAttributeName:designedFont}]; + CGFloat fontSize = designedFontSize; + NSInteger remainExcuteCount = 100; + while (stringSize.width > fitFrame.size.width && fontSize > 2.0 && remainExcuteCount > 0) { + fontSize *= 0.9; + remainExcuteCount -= 1; + designedFont = isBold? [UIFont boldSystemFontOfSize:fontSize] : [UIFont systemFontOfSize:fontSize]; + stringSize = [text sizeWithAttributes:@{NSFontAttributeName:designedFont}]; + } + if (remainExcuteCount < 1 || fontSize < 5.0) { + VAP_Event(kQGVAPModuleCommon, @"data exception content:%@ rect:%@ designedSize:%@ isBold:%@", text, [NSValue valueWithCGRect:fitFrame], @(designedFontSize), @(isBold)); + } + *textSize = stringSize; + return designedFont; +} + +#endif + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/QGVAPlayer.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/QGVAPlayer.h new file mode 100644 index 0000000..8e0a322 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/QGVAPlayer.h @@ -0,0 +1,27 @@ +// QGVAPlayer.h +// 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 +#import "UIView+VAP.h" + +//! Project version number for QGVAPlayer. +FOUNDATION_EXPORT double QGVAPlayerVersionNumber; + +//! Project version string for QGVAPlayer. +FOUNDATION_EXPORT const unsigned char QGVAPlayerVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.h new file mode 100644 index 0000000..8ede750 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.h @@ -0,0 +1,85 @@ +// UIView+VAP.h +// 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 +#import "VAPMacros.h" +#import "QGVAPLogger.h" + +@class QGMP4AnimatedImageFrame,QGVAPConfigModel, QGVAPSourceInfo; +/** 注意:回调方法会在子线程被执行。*/ +@protocol HWDMP4PlayDelegate + +@optional +//即将开始播放时询问,true马上开始播放,false放弃播放 +- (BOOL)shouldStartPlayMP4:(VAPView *)container config:(QGVAPConfigModel *)config; + +- (void)viewDidStartPlayMP4:(VAPView *)container; +- (void)viewDidPlayMP4AtFrame:(QGMP4AnimatedImageFrame*)frame view:(VAPView *)container; +- (void)viewDidStopPlayMP4:(NSInteger)lastFrameIndex view:(VAPView *)container; +- (void)viewDidFinishPlayMP4:(NSInteger)totalFrameCount view:(VAPView *)container; +- (void)viewDidFailPlayMP4:(NSError *)error; + +//vap APIs +- (NSString *)contentForVapTag:(NSString *)tag resource:(QGVAPSourceInfo *)info; //替换配置中的资源占位符(不处理直接返回tag) +- (void)loadVapImageWithURL:(NSString *)urlStr context:(NSDictionary *)context completion:(VAPImageCompletionBlock)completionBlock; //由于组件内不包含网络图片加载的模块,因此需要外部支持图片加载。 + +@end + +@interface UIView (VAP) + +@property (nonatomic, weak) id hwd_Delegate; +@property (nonatomic, readonly) QGMP4AnimatedImageFrame *hwd_currentFrame; +@property (nonatomic, strong) NSString *hwd_MP4FilePath; +@property (nonatomic, assign) NSInteger hwd_fps; //fps for dipslay, each frame's duration would be set by fps value before display. +@property (nonatomic, assign) BOOL hwd_renderByOpenGL; //是否使用opengl渲染,默认使用metal + +- (void)playHWDMp4:(NSString *)filePath; +- (void)playHWDMP4:(NSString *)filePath delegate:(id)delegate; +- (void)playHWDMP4:(NSString *)filePath repeatCount:(NSInteger)repeatCount delegate:(id)delegate; + +- (void)stopHWDMP4; +- (void)pauseHWDMP4; +- (void)resumeHWDMP4; + ++ (void)registerHWDLog:(QGVAPLoggerFunc)logger; + +@end + +@interface UIView (VAPGesture) + +//增加点击的手势识别 +- (void)addVapTapGesture:(VAPGestureEventBlock)handler; + +//手势识别通用接口 +- (void)addVapGesture:(UIGestureRecognizer *)gestureRecognizer callback:(VAPGestureEventBlock)handler; + +@end + +@interface UIView (VAPMask) + +@property (nonatomic, strong) QGVAPMaskInfo *vap_maskInfo; + +@end + +@interface UIView (MP4HWDDeprecated) + +- (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode delegate:(id)delegate __attribute__((deprecated("QGHWDTextureBlendMode is no longer work in vap, use playHWDMP4:delegate: instead"))); +- (void)playHWDMP4:(NSString *)filePath blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id)delegate __attribute__((deprecated("QGHWDTextureBlendMode is no longer work in vap, use playHWDMP4:repeatCount:delegate: instead"))); +- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps delegate:(id)delegate __attribute__((deprecated("customized fps is not recommended, use playHWDMP4:delegate: instead"))); +- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps repeatCount:(NSInteger)repeatCount delegate:(id)delegate __attribute__((deprecated("customized fps is not recommended, use playHWDMP4:repeatCount:delegate: instead"))); +- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode delegate:(id)delegate __attribute__((deprecated("customized fps is not recommended, use playHWDMP4:delegate: instead"))); +- (void)playHWDMP4:(NSString *)filePath fps:(NSInteger)fps blendMode:(QGHWDTextureBlendMode)mode repeatCount:(NSInteger)repeatCount delegate:(id)delegate __attribute__((deprecated("customized fps is not recommended, use playHWDMP4:repeatCount:delegate: instead"))); + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.m new file mode 100644 index 0000000..9d26389 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/UIView+VAP.m @@ -0,0 +1,722 @@ +// 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 +#import +#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 () + +@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)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)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)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 *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)hwd_Delegate { + return objc_getAssociatedObject(self, @"MP4PlayDelegate"); +} + +- (void)setHwd_Delegate:(id)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 *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)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)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)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)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)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)delegate { + [self p_playHWDMP4:filePath fps:fps blendMode:mode repeatCount:repeatCount delegate:delegate]; +} + +@end + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.h new file mode 100644 index 0000000..8656223 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.h @@ -0,0 +1,22 @@ +// NSArray+VAPUtil.h +// 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 + +@interface NSArray (VAPUtil) + +- (CGRect)hwd_rectValue; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.m new file mode 100644 index 0000000..97586d0 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSArray+VAPUtil.m @@ -0,0 +1,37 @@ +// NSArray+VAPUtil.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 "NSArray+VAPUtil.h" + +@implementation NSArray (VAPUtil) + +- (CGRect)hwd_rectValue { + + if (self.count < 4) { + return CGRectZero; + } + for (int i = 0; i < self.count; i++) { + id value = self[i]; + if (i >= 4) { + break ; + } + if (![value isKindOfClass:[NSString class]] && ![value isKindOfClass:[NSNumber class]]) { + return CGRectZero; + } + } + return CGRectMake([self[0] floatValue], [self[1] floatValue], [self[2] floatValue], [self[3] floatValue]); +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.h new file mode 100644 index 0000000..815a13c --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.h @@ -0,0 +1,28 @@ +// NSDictionary+VAPUtil.h +// 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 + + +@interface NSDictionary (VAPUtil) + +- (CGFloat)hwd_floatValue:(NSString *)key; +- (NSInteger)hwd_integerValue:(NSString *)key; +- (NSString *)hwd_stringValue:(NSString *)key; +- (NSDictionary *)hwd_dicValue:(NSString *)key; +- (NSArray *)hwd_arrValue:(NSString *)key; + +@end + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.m new file mode 100644 index 0000000..e4f2b3a --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSDictionary+VAPUtil.m @@ -0,0 +1,71 @@ +// NSDictionary+VAPUtil.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 "NSDictionary+VAPUtil.h" + +#define HWD_RETURN_VALUE(_type_, _default_) \ +if (!key) return _default_; \ +id value = self[key]; \ +if (!value || value == [NSNull null]) return _default_; \ +if ([value isKindOfClass:[NSNumber class]]) return ((NSNumber *)value)._type_; \ +if ([value isKindOfClass:[NSString class]]) return ((NSString *)value)._type_; \ +return _default_; + +@implementation NSDictionary (VAPUtil) + +- (CGFloat)hwd_floatValue:(NSString *)key { + HWD_RETURN_VALUE(floatValue, 0.0); +} + +- (NSInteger)hwd_integerValue:(NSString *)key { + HWD_RETURN_VALUE(integerValue, 0); +} + +- (NSString *)hwd_stringValue:(NSString *)key { + + NSString *defaultValue = @""; + if (!key) return defaultValue; + id value = self[key]; + if (!value || value == [NSNull null]) return defaultValue; + if ([value isKindOfClass:[NSString class]]) return value; + if ([value isKindOfClass:[NSNumber class]]) return ((NSNumber *)value).description; + return defaultValue; +} + +- (NSDictionary *)hwd_dicValue:(NSString *)key { + + if (!key) { + return nil; + } + id value = self[key]; + if (![value isKindOfClass:[NSDictionary class]]) { + return nil; + } + return value; +} + +- (NSArray *)hwd_arrValue:(NSString *)key { + + if (!key) { + return nil; + } + id value = self[key]; + if (![value isKindOfClass:[NSArray class]]) { + return nil; + } + return value; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.h new file mode 100644 index 0000000..27a7cc5 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.h @@ -0,0 +1,54 @@ +// NSNotificationCenter+VAPThreadSafe.h +// 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 + +@interface NSNotificationCenter (VAPThreadSafe) + + +/** + 该方法能够保证通知的执行和移除是线程安全的 + 不需要手动移除通知 + (iOS9以下系统方法addObserver:selector:name:object:通常用法是不安全的。 + + @note 该方法适用于代替原来需要在dealloc内移除通知的场景 + + @param observer Object registering as an observer. This value must not be nil. + @param aSelector Selector that specifies the message the receiver sends observer to notify it of the notification posting. The method specified by aSelector must have one and only one argument (an instance of NSNotification). + @param aName The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer. + If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer. + @param anObject The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. + If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer. + */ +- (void)hwd_addSafeObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject; +- (void)hwd_addWeakObserver:( id)weakObserver name:(NSNotificationName)aName usingBlock:(void (^)(NSNotification *note,id observer))block; + + +/** + 1.设置接收通知的queue + 2.调用- (void)addSafeObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject + + @param observer Object registering as an observer. This value must not be nil. + @param aSelector Selector that specifies the message the receiver sends observer to notify it of the notification posting. The method specified by aSelector must have one and only one argument (an instance of NSNotification). + @param aName The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer. + If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer. + @param anObject The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. + If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer. + @param queue The operation queue to which callbackoperation should be added. + If you pass nil, the block is run asynchronously on queue which hold by this notification. + */ +- (void)hwd_addSafeObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject queue:(NSOperationQueue *)queue; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.m new file mode 100644 index 0000000..b941669 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/NSNotificationCenter+VAPThreadSafe.m @@ -0,0 +1,89 @@ +// NSNotificationCenter+ThreadSafe.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 "NSNotificationCenter+VAPThreadSafe.h" +#import "QGVAPSafeMutableDictionary.h" +#import +#import + +@interface NSObject (SafeNotification) + +@property (nonatomic, strong) NSOperationQueue *notificationOperationQueue; + +@end + +@implementation NSObject (SafeNotification) + +- (NSOperationQueue *)notificationOperationQueue { + @synchronized (self) { + NSOperationQueue *queue = objc_getAssociatedObject(self, @"notificationOperationQueue"); + if (!queue) { + queue = [[NSOperationQueue alloc] init]; + queue.maxConcurrentOperationCount = 1; + self.notificationOperationQueue = queue; + } + return queue; + } +} + +- (void)setNotificationOperationQueue:(NSOperationQueue *)notificationOperationQueue { + @synchronized (self) { + objc_setAssociatedObject(self, @"notificationOperationQueue", notificationOperationQueue, OBJC_ASSOCIATION_RETAIN); + } +} + +@end + +@implementation NSNotificationCenter (VAPThreadSafe) + +- (void)hwd_addSafeObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject { + + double sysVersion = [[[UIDevice currentDevice] systemVersion] doubleValue];; + if (sysVersion >= 9.0) { + return [self addObserver:observer selector:aSelector name:aName object:anObject]; + } + __weak typeof(observer) weakObserver = observer; + __block NSObject *blockObserver = [self addObserverForName:aName object:anObject queue:aName.notificationOperationQueue usingBlock:^(NSNotification * _Nonnull note) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + __strong __typeof__(weakObserver) strongObserver = weakObserver; + [strongObserver performSelector:aSelector withObject:note]; +#pragma clang diagnostic pop + if (!weakObserver) { + [[NSNotificationCenter defaultCenter] removeObserver:blockObserver]; + blockObserver = nil; + } + }]; +} +- (void)hwd_addWeakObserver:( id)Observer name:(NSNotificationName)aName usingBlock:(void (^)(NSNotification *note,id observer))block{ + __weak id weakObserver=Observer; + __block NSObject *blockObserver = [self addObserverForName:aName object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { + __strong id strongObserver = weakObserver; + if(!weakObserver ){ + [[NSNotificationCenter defaultCenter] removeObserver:blockObserver]; + blockObserver = nil; + }else{ + block(note,strongObserver); + } + + }]; +} +- (void)hwd_addSafeObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject queue:(NSOperationQueue *)queue { + + aName.notificationOperationQueue = queue; + [self hwd_addSafeObserver:observer selector:aSelector name:aName object:anObject]; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.h new file mode 100644 index 0000000..285e299 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.h @@ -0,0 +1,26 @@ +// UIColor+VAPUtil.h +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface UIColor (VAPUtil) + ++ (instancetype)hwd_colorWithHexString:(NSString *)hexStr; + +@end + +NS_ASSUME_NONNULL_END diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.m new file mode 100644 index 0000000..881f92c --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIColor+VAPUtil.m @@ -0,0 +1,66 @@ +// UIColor+VAPUtil.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 "UIColor+VAPUtil.h" + +@implementation UIColor (VAPUtil) + +static inline NSUInteger hwd_hexStrToInt(NSString *str) { + uint32_t result = 0; + sscanf([str UTF8String], "%X", &result); + return result; +} + +static BOOL hwd_hexStrToRGBA(NSString *str, + CGFloat *r, CGFloat *g, CGFloat *b, CGFloat *a) { + + NSCharacterSet *cset = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + str = [[str stringByTrimmingCharactersInSet:cset] uppercaseString]; + if ([str hasPrefix:@"#"]) { + str = [str substringFromIndex:1]; + } else if ([str hasPrefix:@"0X"]) { + str = [str substringFromIndex:2]; + } + + NSUInteger length = [str length]; + //RGB||RGBA||RRGGBB||RRGGBBAA + if (length != 3 && length != 4 && length != 6 && length != 8) { + return NO; + } + if (length < 5) { + *r = hwd_hexStrToInt([str substringWithRange:NSMakeRange(0, 1)]) / 255.0f; + *g = hwd_hexStrToInt([str substringWithRange:NSMakeRange(1, 1)]) / 255.0f; + *b = hwd_hexStrToInt([str substringWithRange:NSMakeRange(2, 1)]) / 255.0f; + if (length == 4) *a = hwd_hexStrToInt([str substringWithRange:NSMakeRange(3, 1)]) / 255.0f; + else *a = 1; + } else { + *r = hwd_hexStrToInt([str substringWithRange:NSMakeRange(0, 2)]) / 255.0f; + *g = hwd_hexStrToInt([str substringWithRange:NSMakeRange(2, 2)]) / 255.0f; + *b = hwd_hexStrToInt([str substringWithRange:NSMakeRange(4, 2)]) / 255.0f; + if (length == 8) *a = hwd_hexStrToInt([str substringWithRange:NSMakeRange(6, 2)]) / 255.0f; + else *a = 1; + } + return YES; +} + ++ (instancetype)hwd_colorWithHexString:(NSString *)hexStr { + CGFloat r, g, b, a; + if (hwd_hexStrToRGBA(hexStr, &r, &g, &b, &a)) { + return [UIColor colorWithRed:r green:g blue:b alpha:a]; + } + return nil; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.h new file mode 100644 index 0000000..56e526d --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.h @@ -0,0 +1,45 @@ +// UIDevice+VAPUtil.h +// 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 +#import + +#ifndef kHWDSystemVersion +#define kHWDSystemVersion [UIDevice systemVersionNum] +#endif + +#ifndef kHWDiOS9Later +#define kHWDiOS9Later (kHWDSystemVersion >= 9) +#endif + +#define kDefaultMTLResourceOption getDefaultMTLResourceOption() + +#ifdef __cplusplus +extern "C" { +#endif + MTLResourceOptions getDefaultMTLResourceOption(void); +#ifdef __cplusplus +} +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDevice (VAPUtil) + ++ (double)systemVersionNum; + +@end + +NS_ASSUME_NONNULL_END diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.m new file mode 100644 index 0000000..07b49c6 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIDevice+VAPUtil.m @@ -0,0 +1,38 @@ +// UIDevice+VAPUtil.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 "UIDevice+VAPUtil.h" + +MTLResourceOptions getDefaultMTLResourceOption() { + + if (@available(iOS 9.0, *)) { + return MTLResourceStorageModeShared; + } else { + return MTLResourceCPUCacheModeDefaultCache; + } +} +@implementation UIDevice (VAPUtil) + ++ (double)systemVersionNum { + + static double version; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + version = [UIDevice currentDevice].systemVersion.doubleValue; + }); + return version; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.h new file mode 100644 index 0000000..be16dac --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.h @@ -0,0 +1,26 @@ +// UIGestureRecognizer+VAPUtil.h +// 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 + +@interface UIGestureRecognizer (VAPUtil) + +- (instancetype)initWithVapActionBlock:(void (^)(id sender))block; + +- (void)addVapActionBlock:(void (^)(id sender))block; + +- (void)removeAllVapActionBlocks; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.m new file mode 100644 index 0000000..144fc7b --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIGestureRecognizer+VAPUtil.m @@ -0,0 +1,84 @@ +// UIGestureRecognizer+VAPUtil.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 "UIGestureRecognizer+VAPUtil.h" +#import +#import "QGVAPSafeMutableArray.h" + +static const int vap_block_key; + +@interface _VAPUIGestureRecognizerBlockTarget : NSObject + +@property (nonatomic, copy) void (^block)(id sender); + +- (id)initWithBlock:(void (^)(id sender))block; +- (void)invoke:(id)sender; + +@end + +@implementation _VAPUIGestureRecognizerBlockTarget + +- (id)initWithBlock:(void (^)(id sender))block{ + + if (self = [super init]) { + _block = [block copy]; + } + return self; +} + +- (void)invoke:(id)sender { + if (_block) _block(sender); +} + +@end + +@implementation UIGestureRecognizer (VAPUtil) + +- (instancetype)initWithVapActionBlock:(void (^)(id sender))block { + + if (self = [self init]) { + [self addVapActionBlock:block]; + } + return self; +} + +- (void)addVapActionBlock:(void (^)(id sender))block { + + _VAPUIGestureRecognizerBlockTarget *target = [[_VAPUIGestureRecognizerBlockTarget alloc] initWithBlock:block]; + [self addTarget:target action:@selector(invoke:)]; + NSMutableArray *targets = [self _vap_allUIGestureRecognizerBlockTargets]; + [targets addObject:target]; +} + +- (NSMutableArray *)_vap_allUIGestureRecognizerBlockTargets { + + NSMutableArray *targets = objc_getAssociatedObject(self, &vap_block_key); + if (!targets) { + targets = [QGVAPSafeMutableArray new]; + objc_setAssociatedObject(self, &vap_block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return targets; +} + +- (void)removeAllVapActionBlocks { + + NSMutableArray *targets = [self _vap_allUIGestureRecognizerBlockTargets]; + [targets enumerateObjectsUsingBlock:^(id target, NSUInteger idx, BOOL *stop) { + [self removeTarget:target action:@selector(invoke:)]; + }]; + [targets removeAllObjects]; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.h new file mode 100644 index 0000000..1e58aad --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.h @@ -0,0 +1,25 @@ +// UIView+MP4HWDecode.h +// 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 +#import "UIView+VAP.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIView (MP4HWDecode) + +@end + +NS_ASSUME_NONNULL_END diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.m new file mode 100644 index 0000000..cf9d32b --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Categorys/UIView+MP4HWDecode.m @@ -0,0 +1,20 @@ +// UIView+MP4HWDecode.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 "UIView+MP4HWDecode.h" + +@implementation UIView (MP4HWDecode) + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.h new file mode 100644 index 0000000..8850ad3 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.h @@ -0,0 +1,65 @@ +// QGVAPLogger.h +// 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 + +#define kQGVAPModuleCommon @"kQGVAPModuleCommon" + +NS_ASSUME_NONNULL_BEGIN + +#define VAP_Logger(level, module, format, ...) if(external_VAP_Logger)external_VAP_Logger(level, __FILE__, __LINE__, __FUNCTION__, module, format, ##__VA_ARGS__); else internal_VAP_Logger_handler(level, __FILE__, __LINE__, __FUNCTION__, module, format, ##__VA_ARGS__); + +#define VAP_Error(module, format, ...) VAP_Logger(VAPLogLevelError, module, format, ##__VA_ARGS__) +#define VAP_Event(module, format, ...) VAP_Logger(VAPLogLevelEvent, module, format, ##__VA_ARGS__) +#define VAP_Warn(module, format, ...) VAP_Logger(VAPLogLevelWarn, module, format, ##__VA_ARGS__) +#define VAP_Info(module, format, ...) VAP_Logger(VAPLogLevelInfo, module, format, ##__VA_ARGS__) +#define VAP_Debug(module, format, ...) VAP_Logger(VAPLogLevelDebug, module, format, ##__VA_ARGS__) + +typedef enum { + VAPLogLevelAll = 0, + VAPLogLevelDebug, // Detailed information on the flow through the system. + VAPLogLevelInfo, // Interesting runtime events (startup/shutdown), should be conservative and keep to a minimum. + VAPLogLevelEvent, + VAPLogLevelWarn, // Other runtime situations that are undesirable or unexpected, but not necessarily "wrong". + VAPLogLevelError, // Other runtime errors or unexpected conditions. + VAPLogLevelFatal, // Severe errors that cause premature termination. + VAPLogLevelNone, // Special level used to disable all log messages. +} VAPLogLevel; + +typedef VAPLogLevel HWDLogLevel; + +//void qg_VAP_Logger(VAPLogLevel level, const char* file, int line, const char* func, NSString *MODULE, NSString *format, ...); +typedef void (*QGVAPLoggerFunc)(VAPLogLevel, const char*, int, const char*, NSString *, NSString *, ...); + +#if defined __cplusplus +extern "C" { +#endif + + extern QGVAPLoggerFunc external_VAP_Logger; + void internal_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const char* func, NSString *module, NSString *format, ...); + +#if defined __cplusplus +}; +#endif + +@interface QGVAPLogger : NSObject + ++ (void)registerExternalLog:(QGVAPLoggerFunc)externalLog; + ++ (void)log:(VAPLogLevel)level file:(NSString *)file line:(NSInteger)line func:(NSString *)func module:(NSString *)module message:(NSString *)message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.m new file mode 100644 index 0000000..18e3cf2 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/Logger/QGVAPLogger.m @@ -0,0 +1,53 @@ +// QGVAPLogger.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 "QGVAPLogger.h" + +QGVAPLoggerFunc external_VAP_Logger; + +@implementation QGVAPLogger + +#pragma mark - Extenral log + +void internal_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const char* func, NSString *module, NSString *format, ...) { + +#ifdef DEBUG + va_list arg_list; + va_start (arg_list, format); + NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; + va_end(arg_list); + file = [NSString stringWithUTF8String:file].lastPathComponent.UTF8String; + NSLog(@"<%@> %s(%@):%s [%@] - %@",@(level), file, @(line), func, module, formattedString); +#endif +} + ++ (void)registerExternalLog:(QGVAPLoggerFunc)externalLog { + external_VAP_Logger = externalLog; +} + ++ (void)log:(VAPLogLevel)level file:(NSString *)file line:(NSInteger)line func:(NSString *)func module:(NSString *)module message:(NSString *)message { + + if ([message containsString:@"%"]) { + //此处是为了兼容%进入formmat之后的crash风险 + [message stringByReplacingOccurrencesOfString:@"%" withString:@""]; + } + if (external_VAP_Logger) { + external_VAP_Logger(level, file.UTF8String, (int)line, func.UTF8String, module, message); + } else { + internal_VAP_Logger_handler(level, file.UTF8String, (int)line, func.UTF8String, module, message); + } +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.h new file mode 100644 index 0000000..012469f --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.h @@ -0,0 +1,24 @@ +// QGVAPMetalShaderFunctionLoader.h +// 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 +#import + +@interface QGVAPMetalShaderFunctionLoader : NSObject + +- (instancetype)initWithDevice:(id)device; +- (id)loadFunctionWithName:(NSString *)funcName; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.m new file mode 100644 index 0000000..e37a5af --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalShaderFunctionLoader.m @@ -0,0 +1,92 @@ +// QGVAPMetalShaderFunctionLoader.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 "QGVAPMetalShaderFunctionLoader.h" +#import "QGHWDMetalShaderSourceDefine.h" +#import "QGHWDShaderTypes.h" +#import "QGVAPLogger.h" + +@interface QGVAPMetalShaderFunctionLoader () { + + BOOL _alreadyLoadDefaultLibrary; + BOOL _alreadyLoadHWDLibrary; +} + +@property (nonatomic, strong) id device; +@property (nonatomic, strong) id defaultLibrary; +@property (nonatomic, strong) id hwdLibrary; + +@end + +@implementation QGVAPMetalShaderFunctionLoader + +- (instancetype)initWithDevice:(id)device { + + if (self = [super init]) { + _device = device; + } + return self; +} + +- (id)loadFunctionWithName:(NSString *)funcName { + + id program = nil; + [self loadDefaultLibraryIfNeed]; + program = [self.defaultLibrary newFunctionWithName:funcName]; + //没有找到defaultLibrary文件 || defaultLibrary中不包含对应的fucntion + if (!program) { + [self loadHWDLibraryIfNeed]; + program = [self.hwdLibrary newFunctionWithName:funcName]; + } + return program; +} + +- (void)loadDefaultLibraryIfNeed { + + if (self.defaultLibrary || _alreadyLoadDefaultLibrary) { + return ; + } + NSBundle *bundle = [NSBundle bundleForClass:self.class]; + NSString *metalLibPath = [bundle pathForResource:@"default" ofType:@"metallib"]; + if (metalLibPath.length == 0) { + return ; + } + NSError *error = nil; + id defaultLibrary = [self.device newLibraryWithFile:metalLibPath error:&error]; + if (!defaultLibrary || error) { + VAP_Error(kQGVAPModuleCommon, @"loadDefaultLibrary error!:%@", error); + return ; + } + self.defaultLibrary = defaultLibrary; + _alreadyLoadDefaultLibrary = YES; +} + +- (void)loadHWDLibraryIfNeed { + + if (self.hwdLibrary || _alreadyLoadHWDLibrary) { + return ; + } + NSError *error = nil; + NSString *sourceString = [NSString stringWithFormat:@"%@%@%@", kQGHWDMetalShaderSourceImports, kQGHWDMetalShaderTypeDefines, kQGHWDMetalShaderSourceString]; + id hwdLibrary = [self.device newLibraryWithSource:sourceString options:nil error:&error]; + if (!hwdLibrary || error) { + VAP_Error(kQGVAPModuleCommon, @"loadHWDLibrary error!:%@", error); + return ; + } + self.hwdLibrary = hwdLibrary; + _alreadyLoadHWDLibrary = YES; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.h new file mode 100644 index 0000000..8738761 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.h @@ -0,0 +1,46 @@ +// QGVAPMetalUtil.h +// 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_EXTERN NSString *const kHWDAttachmentVertexFunctionName; +UIKIT_EXTERN NSString *const kVAPAttachmentVertexFunctionName; +UIKIT_EXTERN NSString *const kVAPAttachmentFragmentFunctionName; +UIKIT_EXTERN NSString *const kVAPVertexFunctionName; +UIKIT_EXTERN NSString *const kVAPYUVFragmentFunctionName; +UIKIT_EXTERN NSString *const kVAPMaskFragmentFunctionName; +UIKIT_EXTERN NSString *const kVAPMaskBlurFragmentFunctionName; + +extern float const kVAPMTLVerticesIdentity[16]; +extern float const kVAPMTLTextureCoordinatesIdentity[8]; +extern float const kVAPMTLTextureCoordinatesFor90[8]; + +#ifdef __cplusplus +extern "C" { +#endif + void genMTLVertices(CGRect rect, CGSize containerSize, float vertices[16], BOOL reverse); //生成顶点坐标 + void genMTLTextureCoordinates(CGRect rect, CGSize containerSize, float coordinates[8], BOOL reverse, NSInteger degree); //生成纹理坐标 + void replaceArrayElements(float arr0[], float arr1[], int size); //arr0[0...(size-1)] <- arr1[0...(size-1)] + + CGSize vapSourceSizeForCenterFull(CGSize sourceSize, CGSize renderSize); + CGRect vapRectForCenterFull(CGSize sourceSize, CGSize renderSize); + CGRect vapRectWithContentModeInsideRect(CGRect boundingRect, CGSize aspectRatio, UIViewContentMode contentMode); +#ifdef __cplusplus +} +#endif + +@interface QGVAPMetalUtil : NSObject + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.m new file mode 100644 index 0000000..ab10876 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPMetalUtil.m @@ -0,0 +1,190 @@ +// QGVAPMetalUtil.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 "QGVAPMetalUtil.h" +#import +#import "QGVAPLogger.h" +#import + +NSString *const kVAPAttachmentVertexFunctionName = @"vapAttachment_vertexShader"; +NSString *const kVAPAttachmentFragmentFunctionName = @"vapAttachment_FragmentShader"; +NSString *const kVAPVertexFunctionName = @"vap_vertexShader"; +NSString *const kVAPYUVFragmentFunctionName = @"vap_yuvFragmentShader"; +NSString *const kVAPMaskFragmentFunctionName = @"vap_maskFragmentShader"; +NSString *const kVAPMaskBlurFragmentFunctionName = @"vap_maskBlurFragmentShader"; + +float const kVAPMTLVerticesIdentity[16] = {-1.0, -1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0}; +float const kVAPMTLTextureCoordinatesIdentity[8] = {0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0}; +float const kVAPMTLTextureCoordinatesFor90[8] = {0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0}; + +void replaceArrayElements(float arr0[], float arr1[], int size) { + + if ((arr0 == NULL || arr1 == NULL) && size > 0) { + assert(0); + } + if (size < 0) { + assert(0); + } + for (int i = 0; i < size; i++) { + arr0[i] = arr1[i]; + } +} + +//倒N形 +void genMTLVertices(CGRect rect, CGSize containerSize, float vertices[16], BOOL reverse) { + + if (vertices == NULL) { + VAP_Error(kQGVAPModuleCommon, @"generateMTLVertices params illegal."); + assert(0); + return ; + } + if (containerSize.width <= 0 || containerSize.height <= 0) { + VAP_Error(kQGVAPModuleCommon, @"generateMTLVertices params containerSize illegal."); + assert(0); + return ; + } + float originX, originY, width, height; + originX = -1+2*rect.origin.x/containerSize.width; + originY = 1-2*rect.origin.y/containerSize.height; + width = 2*rect.size.width/containerSize.width; + height = 2*rect.size.height/containerSize.height; + + if (reverse) { + float tempVertices[] = {originX, originY-height, 0.0, 1.0, originX, originY, 0.0, 1.0, originX+width, originY-height, 0.0, 1.0, originX+width, originY, 0.0, 1.0}; + replaceArrayElements(vertices, tempVertices, 16); + return ; + } + float tempVertices[] = {originX, originY, 0.0, 1.0, originX, originY-height, 0.0, 1.0, originX+width, originY, 0.0, 1.0 , originX+width, originY-height, 0.0, 1.0}; + replaceArrayElements(vertices, tempVertices, 16); +} + +//N形 +void genMTLTextureCoordinates(CGRect rect, CGSize containerSize, float coordinates[8], BOOL reverse, NSInteger degree) { + + //degree预留字段,支持旋转纹理 + if (coordinates == NULL) { + VAP_Error(kQGVAPModuleCommon, @"generateMTLTextureCoordinates params coordinates illegal."); + assert(0); + return ; + } + if (containerSize.width <= 0 || containerSize.height <= 0) { + VAP_Error(kQGVAPModuleCommon, @"generateMTLTextureCoordinates params containerSize illegal."); + assert(0); + return ; + } + float originX, originY, width, height; + originX = rect.origin.x/containerSize.width; + originY = rect.origin.y/containerSize.height; + width = rect.size.width/containerSize.width; + height = rect.size.height/containerSize.height; + + if (reverse) { + float tempCoordintes[] = {originX, originY, originX, originY+height , originX+width, originY,originX+width, originY+height}; + replaceArrayElements(coordinates, tempCoordintes, 8); + return ; + } + float tempCoordintes[] = {originX, originY+height, originX, originY, originX+width, originY+height, originX+width, originY}; + replaceArrayElements(coordinates, tempCoordintes, 8); +} + +CGSize vapSourceSizeForCenterFull(CGSize sourceSize, CGSize renderSize) { + + //source大小完全包含render大小,直接返回中间部分 + if (sourceSize.width >= renderSize.width && sourceSize.height >= renderSize.height) { + return sourceSize; + } + CGRect rectForAspectFill = vapRectWithContentModeInsideRect(CGRectMake(0, 0, renderSize.width, renderSize.height), sourceSize, UIViewContentModeScaleAspectFill); + return rectForAspectFill.size; +} + +CGRect vapRectForCenterFull(CGSize sourceSize, CGSize renderSize) { + + //source大小完全包含render大小,直接返回中间部分 + if (sourceSize.width >= renderSize.width && sourceSize.height >= renderSize.height) { + return CGRectMake((sourceSize.width-renderSize.width)/2.0, (sourceSize.height-renderSize.height)/2.0, renderSize.width, renderSize.height); + } + + CGRect rectForAspectFill = vapRectWithContentModeInsideRect(CGRectMake(0, 0, renderSize.width, renderSize.height), sourceSize, UIViewContentModeScaleAspectFill); + + CGRect intersection = CGRectMake(-rectForAspectFill.origin.x, -rectForAspectFill.origin.y, renderSize.width, renderSize.height); + return intersection; +} + +CGRect vapRectWithContentModeInsideRect(CGRect boundingRect, CGSize aspectRatio, UIViewContentMode contentMode) { + + if (aspectRatio.width <= 0 || aspectRatio.height <= 0) { + return boundingRect; + } + CGRect desRect = CGRectZero; + switch (contentMode) { + case UIViewContentModeScaleToFill: { + desRect = boundingRect; + } + break; + case UIViewContentModeScaleAspectFit: { + desRect = AVMakeRectWithAspectRatioInsideRect(aspectRatio, boundingRect); + } + break; + case UIViewContentModeScaleAspectFill: { + CGFloat ratio = MAX(CGRectGetWidth(boundingRect)/aspectRatio.width, CGRectGetHeight(boundingRect)/aspectRatio.height); + CGSize contentSize = CGSizeMake(aspectRatio.width*ratio, aspectRatio.height*ratio); + desRect = CGRectMake(boundingRect.origin.x+(CGRectGetWidth(boundingRect)-contentSize.width)/2.0, boundingRect.origin.y+(CGRectGetHeight(boundingRect)-contentSize.height)/2.0, contentSize.width, contentSize.height); + } + break; + case UIViewContentModeCenter: { + desRect = CGRectMake(boundingRect.origin.x+(CGRectGetWidth(boundingRect)-aspectRatio.width)/2.0, boundingRect.origin.y+(CGRectGetHeight(boundingRect)-aspectRatio.height)/2.0, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeTop: { + desRect = CGRectMake(boundingRect.origin.x+(CGRectGetWidth(boundingRect)-aspectRatio.width)/2.0, boundingRect.origin.y, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeBottom: { + desRect = CGRectMake(boundingRect.origin.x+(CGRectGetWidth(boundingRect)-aspectRatio.width)/2.0, boundingRect.origin.y+CGRectGetHeight(boundingRect)-aspectRatio.height, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeLeft: { + desRect = CGRectMake(boundingRect.origin.x, boundingRect.origin.y+(CGRectGetHeight(boundingRect)-aspectRatio.height)/2.0, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeRight: { + desRect = CGRectMake(boundingRect.origin.x+CGRectGetWidth(boundingRect)-aspectRatio.width, boundingRect.origin.y+(CGRectGetHeight(boundingRect)-aspectRatio.height)/2.0, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeTopLeft: { + desRect = CGRectMake(boundingRect.origin.x, boundingRect.origin.y, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeTopRight: { + desRect = CGRectMake(boundingRect.origin.x+CGRectGetWidth(boundingRect)-aspectRatio.width, boundingRect.origin.y, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeBottomLeft: { + desRect = CGRectMake(boundingRect.origin.x, boundingRect.origin.y+CGRectGetHeight(boundingRect)-aspectRatio.height, aspectRatio.width, aspectRatio.height); + } + break; + case UIViewContentModeBottomRight: { + desRect = CGRectMake(boundingRect.origin.x+CGRectGetWidth(boundingRect)-aspectRatio.width, boundingRect.origin.y+CGRectGetHeight(boundingRect)-aspectRatio.height, aspectRatio.width, aspectRatio.height); + } + break; + default: + break; + } + return desRect; +} + +@implementation QGVAPMetalUtil + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.h new file mode 100644 index 0000000..6e43bde --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.h @@ -0,0 +1,28 @@ +// QGVAPSafeMutableArray.h +// 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 + + +/** + This class inherits from NSMutableArray,make it tread safe and allow Recursive lock. + + @discussion access performance would lower than NSMutableArray, or using semaphore but equal to @sychronized. + + @warning Fast enumerate and enumerator are not thread safe + */ +@interface QGVAPSafeMutableArray : NSMutableArray + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.m new file mode 100644 index 0000000..2110d9d --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableArray.m @@ -0,0 +1,384 @@ +// QGVAPSafeMutableArray.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 "QGVAPSafeMutableArray.h" + +#define VAP_INIT(...) self = super.init; \ +if (!self) return nil; \ +__VA_ARGS__; \ +if (!_arr) return nil; \ +_lock = [[NSRecursiveLock alloc] init]; \ +return self; + +#define VAP_LOCK(...) [_lock lock]; \ +__VA_ARGS__; \ +[_lock unlock]; + +@interface QGVAPSafeMutableArray (){ + + NSMutableArray *_arr; + NSRecursiveLock *_lock; +} + +@end + +@implementation QGVAPSafeMutableArray + + +#pragma mark - init + +-(instancetype)init{ + VAP_INIT(_arr = [NSMutableArray new]); +} + +-(instancetype)initWithCapacity:(NSUInteger)numItems{ + VAP_INIT(_arr = [[NSMutableArray alloc] initWithCapacity:numItems]); +} + +-(instancetype)initWithArray:(NSArray *)array{ + VAP_INIT(_arr = [[NSMutableArray alloc] initWithArray:array]); +} + +-(instancetype)initWithObjects:(id _Nonnull const [])objects count:(NSUInteger)cnt{ + VAP_INIT(_arr = [[NSMutableArray alloc] initWithObjects:objects count:cnt]); +} + +- (instancetype)initWithContentsOfFile:(NSString *)path { + VAP_INIT(_arr = [[NSMutableArray alloc] initWithContentsOfFile:path]); +} + +- (instancetype)initWithContentsOfURL:(NSURL *)url { + VAP_INIT(_arr = [[NSMutableArray alloc] initWithContentsOfURL:url]); +} + +#pragma mark - methods + +- (NSUInteger)count { + VAP_LOCK(NSUInteger count = _arr.count); return count; +} + +- (id)objectAtIndex:(NSUInteger)index { + VAP_LOCK(id obj = [_arr objectAtIndex:index]); return obj; +} + +- (NSArray *)arrayByAddingObject:(id)anObject { + VAP_LOCK(NSArray * arr = [_arr arrayByAddingObject:anObject]); return arr; +} + +- (NSArray *)arrayByAddingObjectsFromArray:(NSArray *)otherArray { + VAP_LOCK(NSArray * arr = [_arr arrayByAddingObjectsFromArray:otherArray]); return arr; +} + +- (NSString *)componentsJoinedByString:(NSString *)separator { + VAP_LOCK(NSString * str = [_arr componentsJoinedByString:separator]); return str; +} + +- (BOOL)containsObject:(id)anObject { + VAP_LOCK(BOOL c = [_arr containsObject:anObject]); return c; +} + +- (NSString *)description { + VAP_LOCK(NSString * d = _arr.description); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale { + VAP_LOCK(NSString * d = [_arr descriptionWithLocale:locale]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level { + VAP_LOCK(NSString * d = [_arr descriptionWithLocale:locale indent:level]); return d; +} + +- (id)firstObjectCommonWithArray:(NSArray *)otherArray { + VAP_LOCK(id o = [_arr firstObjectCommonWithArray:otherArray]); return o; +} + +- (void)getObjects:(id __unsafe_unretained[])objects range:(NSRange)range { + VAP_LOCK([_arr getObjects:objects range:range]); +} + +- (NSUInteger)indexOfObject:(id)anObject { + VAP_LOCK(NSUInteger i = [_arr indexOfObject:anObject]); return i; +} + +- (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range { + VAP_LOCK(NSUInteger i = [_arr indexOfObject:anObject inRange:range]); return i; +} + +- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject { + VAP_LOCK(NSUInteger i = [_arr indexOfObjectIdenticalTo:anObject]); return i; +} + +- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range { + VAP_LOCK(NSUInteger i = [_arr indexOfObjectIdenticalTo:anObject inRange:range]); return i; +} + +- (id)firstObject { + VAP_LOCK(id o = _arr.firstObject); return o; +} + +- (id)lastObject { + VAP_LOCK(id o = _arr.lastObject); return o; +} + +- (NSEnumerator *)objectEnumerator { + VAP_LOCK(NSEnumerator * e = [_arr objectEnumerator]); return e; +} + +- (NSEnumerator *)reverseObjectEnumerator { + VAP_LOCK(NSEnumerator * e = [_arr reverseObjectEnumerator]); return e; +} + +- (NSData *)sortedArrayHint { + VAP_LOCK(NSData * d = [_arr sortedArrayHint]); return d; +} + +- (NSArray *)sortedArrayUsingFunction:(__attribute__((noescape)) NSInteger (*)(id, id, void *))comparator context:(void *)context { + VAP_LOCK(NSArray * arr = [_arr sortedArrayUsingFunction:comparator context:context]) return arr; +} + +- (NSArray *)sortedArrayUsingFunction:(__attribute__((noescape)) NSInteger (*)(id, id, void *))comparator context:(void *)context hint:(NSData *)hint { + VAP_LOCK(NSArray * arr = [_arr sortedArrayUsingFunction:comparator context:context hint:hint]); return arr; +} + +- (NSArray *)sortedArrayUsingSelector:(SEL)comparator { + VAP_LOCK(NSArray * arr = [_arr sortedArrayUsingSelector:comparator]); return arr; +} + +- (NSArray *)subarrayWithRange:(NSRange)range { + VAP_LOCK(NSArray * arr = [_arr subarrayWithRange:range]) return arr; +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector { + VAP_LOCK([_arr makeObjectsPerformSelector:aSelector]); +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument { + VAP_LOCK([_arr makeObjectsPerformSelector:aSelector withObject:argument]); +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + VAP_LOCK(NSArray * arr = [_arr objectsAtIndexes:indexes]); return arr; +} + +- (id)objectAtIndexedSubscript:(NSUInteger)idx { + VAP_LOCK(id o = [_arr objectAtIndexedSubscript:idx]); return o; +} + +- (void)enumerateObjectsUsingBlock:(__attribute__((noescape)) void (^)(id obj, NSUInteger idx, BOOL *stop))block { + VAP_LOCK([_arr enumerateObjectsUsingBlock:block]); +} + +- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(__attribute__((noescape)) void (^)(id obj, NSUInteger idx, BOOL *stop))block { + VAP_LOCK([_arr enumerateObjectsWithOptions:opts usingBlock:block]); +} + +- (void)enumerateObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts usingBlock:(__attribute__((noescape)) void (^)(id obj, NSUInteger idx, BOOL *stop))block { + VAP_LOCK([_arr enumerateObjectsAtIndexes:s options:opts usingBlock:block]); +} + +- (NSUInteger)indexOfObjectPassingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSUInteger i = [_arr indexOfObjectPassingTest:predicate]); return i; +} + +- (NSUInteger)indexOfObjectWithOptions:(NSEnumerationOptions)opts passingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSUInteger i = [_arr indexOfObjectWithOptions:opts passingTest:predicate]); return i; +} + +- (NSUInteger)indexOfObjectAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts passingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSUInteger i = [_arr indexOfObjectAtIndexes:s options:opts passingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsPassingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSIndexSet * i = [_arr indexesOfObjectsPassingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsWithOptions:(NSEnumerationOptions)opts passingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSIndexSet * i = [_arr indexesOfObjectsWithOptions:opts passingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts passingTest:(__attribute__((noescape)) BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { + VAP_LOCK(NSIndexSet * i = [_arr indexesOfObjectsAtIndexes:s options:opts passingTest:predicate]); return i; +} + +- (NSArray *)sortedArrayUsingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK(NSArray * a = [_arr sortedArrayUsingComparator:cmptr]); return a; +} + +- (NSArray *)sortedArrayWithOptions:(NSSortOptions)opts usingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK(NSArray * a = [_arr sortedArrayWithOptions:opts usingComparator:cmptr]); return a; +} + +- (NSUInteger)indexOfObject:(id)obj inSortedRange:(NSRange)r options:(NSBinarySearchingOptions)opts usingComparator:(__attribute__((noescape)) NSComparator)cmp { + VAP_LOCK(NSUInteger i = [_arr indexOfObject:obj inSortedRange:r options:opts usingComparator:cmp]); return i; +} + +#pragma mark - mutable + +- (void)addObject:(id)anObject { + VAP_LOCK([_arr addObject:anObject]); +} + +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { + VAP_LOCK([_arr insertObject:anObject atIndex:index]); +} + +- (void)removeLastObject { + VAP_LOCK([_arr removeLastObject]); +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + VAP_LOCK([_arr removeObjectAtIndex:index]); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + VAP_LOCK([_arr replaceObjectAtIndex:index withObject:anObject]); +} + +- (void)addObjectsFromArray:(NSArray *)otherArray { + VAP_LOCK([_arr addObjectsFromArray:otherArray]); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2 { + VAP_LOCK([_arr exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]); +} + +- (void)removeAllObjects { + VAP_LOCK([_arr removeAllObjects]); +} + +- (void)removeObject:(id)anObject inRange:(NSRange)range { + VAP_LOCK([_arr removeObject:anObject inRange:range]); +} + +- (void)removeObject:(id)anObject { + VAP_LOCK([_arr removeObject:anObject]); +} + +- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range { + VAP_LOCK([_arr removeObjectIdenticalTo:anObject inRange:range]); +} + +- (void)removeObjectIdenticalTo:(id)anObject { + VAP_LOCK([_arr removeObjectIdenticalTo:anObject]); +} + +- (void)removeObjectsInArray:(NSArray *)otherArray { + VAP_LOCK([_arr removeObjectsInArray:otherArray]); +} + +- (void)removeObjectsInRange:(NSRange)range { + VAP_LOCK([_arr removeObjectsInRange:range]); +} + +- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray range:(NSRange)otherRange { + VAP_LOCK([_arr replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]); +} + +- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray { + VAP_LOCK([_arr replaceObjectsInRange:range withObjectsFromArray:otherArray]); +} + +- (void)setArray:(NSArray *)otherArray { + VAP_LOCK([_arr setArray:otherArray]); +} + +- (void)sortUsingFunction:(__attribute__((noescape)) NSInteger (*)(id, id, void *))compare context:(void *)context { + VAP_LOCK([_arr sortUsingFunction:compare context:context]); +} + +- (void)sortUsingSelector:(SEL)comparator { + VAP_LOCK([_arr sortUsingSelector:comparator]); +} + +- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes { + VAP_LOCK([_arr insertObjects:objects atIndexes:indexes]); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + VAP_LOCK([_arr removeObjectsAtIndexes:indexes]); +} + +- (void)replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects { + VAP_LOCK([_arr replaceObjectsAtIndexes:indexes withObjects:objects]); +} + +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx { + VAP_LOCK([_arr setObject:obj atIndexedSubscript:idx]); +} + +- (void)sortUsingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK([_arr sortUsingComparator:cmptr]); +} + +- (void)sortWithOptions:(NSSortOptions)opts usingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK([_arr sortWithOptions:opts usingComparator:cmptr]); +} + +- (BOOL)isEqualToArray:(NSArray *)otherArray { + if (otherArray == self) return YES; + if ([otherArray isKindOfClass:QGVAPSafeMutableArray.class]) { + QGVAPSafeMutableArray *other = (id)otherArray; + BOOL isEqual; + [_lock lock]; + [other->_lock lock]; + isEqual = [_arr isEqualToArray:other->_arr]; + [other->_lock unlock]; + [_lock unlock]; + return isEqual; + } + return NO; +} + +#pragma mark - protocol + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + VAP_LOCK(id copiedArr = [[self.class allocWithZone:zone] initWithArray:_arr]); + return copiedArr; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + VAP_LOCK(NSUInteger count = [_arr countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:[QGVAPSafeMutableArray class]]) { + QGVAPSafeMutableArray *other = object; + BOOL isEqual; + [_lock lock]; + [other->_lock lock]; + isEqual = [_arr isEqual:other->_arr]; + [other->_lock unlock]; + [_lock unlock]; + return isEqual; + } + return NO; +} + +- (NSUInteger)hash { + VAP_LOCK(NSUInteger hash = [_arr hash]); + return hash; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.h new file mode 100644 index 0000000..aeaab73 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.h @@ -0,0 +1,27 @@ +// QGVAPSafeMutableDictionary.h +// 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 + +/** + This class inherits from NSMutableDictionary, make it thread safe and allow Recursive lock. + + @discussion access performance would lower than NSMutableDictionary, or using semaphore but equal to @sychronized. + + @warning Fast enumerate and enumerator are not thread safe + */ +@interface QGVAPSafeMutableDictionary : NSMutableDictionary + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.m new file mode 100644 index 0000000..b6e0f05 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPSafeMutableDictionary.m @@ -0,0 +1,237 @@ +// QGVAPSafeMutableDictionary.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 "QGVAPSafeMutableDictionary.h" + +#define VAP_INIT(...) self = super.init; \ +if (!self) return nil; \ +__VA_ARGS__; \ +if (!_dic) return nil; \ +_lock = [[NSRecursiveLock alloc] init]; \ +return self; + +#define VAP_LOCK(...) [_lock lock]; \ +__VA_ARGS__; \ +[_lock unlock]; + +@interface QGVAPSafeMutableDictionary(){ + + NSMutableDictionary *_dic; + NSRecursiveLock *_lock; +} + +@end + +@implementation QGVAPSafeMutableDictionary + +#pragma mark - init + +- (instancetype)init { + VAP_INIT(_dic = [[NSMutableDictionary alloc] init]); +} + +- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys { + VAP_INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys]); +} + +- (instancetype)initWithCapacity:(NSUInteger)capacity { + VAP_INIT(_dic = [[NSMutableDictionary alloc] initWithCapacity:capacity]); +} + +- (instancetype)initWithObjects:(const id[])objects forKeys:(const id [])keys count:(NSUInteger)cnt { + VAP_INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys count:cnt]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary { + VAP_INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag { + VAP_INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary copyItems:flag]); +} + + +#pragma mark - method + +- (NSUInteger)count { + VAP_LOCK(NSUInteger c = _dic.count); return c; +} + +- (id)objectForKey:(id)aKey { + VAP_LOCK(id o = [_dic objectForKey:aKey]); return o; +} + +- (NSEnumerator *)keyEnumerator { + VAP_LOCK(NSEnumerator * e = [_dic keyEnumerator]); return e; +} + +- (NSArray *)allKeys { + VAP_LOCK(NSArray * a = [_dic allKeys]); return a; +} + +- (NSArray *)allKeysForObject:(id)anObject { + VAP_LOCK(NSArray * a = [_dic allKeysForObject:anObject]); return a; +} + +- (NSArray *)allValues { + VAP_LOCK(NSArray * a = [_dic allValues]); return a; +} + +- (NSString *)description { + VAP_LOCK(NSString * d = [_dic description]); return d; +} + +- (NSString *)descriptionInStringsFileFormat { + VAP_LOCK(NSString * d = [_dic descriptionInStringsFileFormat]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale { + VAP_LOCK(NSString * d = [_dic descriptionWithLocale:locale]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level { + VAP_LOCK(NSString * d = [_dic descriptionWithLocale:locale indent:level]); return d; +} + +- (BOOL)isEqualToDictionary:(NSDictionary *)otherDictionary { + if (otherDictionary == self) return YES; + + if ([otherDictionary isKindOfClass:QGVAPSafeMutableDictionary.class]) { + QGVAPSafeMutableDictionary *other = (id)otherDictionary; + BOOL isEqual; + [_lock lock]; + [other->_lock lock]; + isEqual = [_dic isEqual:other->_dic]; + [other->_lock unlock]; + [_lock unlock]; + return isEqual; + } + return NO; +} + +- (NSEnumerator *)objectEnumerator { + VAP_LOCK(NSEnumerator * e = [_dic objectEnumerator]); return e; +} + +- (NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker { + VAP_LOCK(NSArray * a = [_dic objectsForKeys:keys notFoundMarker:marker]); return a; +} + +- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator { + VAP_LOCK(NSArray * a = [_dic keysSortedByValueUsingSelector:comparator]); return a; +} + +- (void)getObjects:(id __unsafe_unretained[])objects andKeys:(id __unsafe_unretained[])keys { + VAP_LOCK([_dic getObjects:objects andKeys:keys]); +} + +- (id)objectForKeyedSubscript:(id)key { + VAP_LOCK(id o = [_dic objectForKeyedSubscript:key]); return o; +} + +- (void)enumerateKeysAndObjectsUsingBlock:(__attribute__((noescape)) void (^)(id key, id obj, BOOL *stop))block { + VAP_LOCK([_dic enumerateKeysAndObjectsUsingBlock:block]); +} + +- (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(__attribute__((noescape)) void (^)(id key, id obj, BOOL *stop))block { + VAP_LOCK([_dic enumerateKeysAndObjectsWithOptions:opts usingBlock:block]); +} + +- (NSArray *)keysSortedByValueUsingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK(NSArray * a = [_dic keysSortedByValueUsingComparator:cmptr]); return a; +} + +- (NSArray *)keysSortedByValueWithOptions:(NSSortOptions)opts usingComparator:(__attribute__((noescape)) NSComparator)cmptr { + VAP_LOCK(NSArray * a = [_dic keysSortedByValueWithOptions:opts usingComparator:cmptr]); return a; +} + +- (NSSet *)keysOfEntriesPassingTest:(__attribute__((noescape)) BOOL (^)(id key, id obj, BOOL *stop))predicate { + VAP_LOCK(NSSet * a = [_dic keysOfEntriesPassingTest:predicate]); return a; +} + +- (NSSet *)keysOfEntriesWithOptions:(NSEnumerationOptions)opts passingTest:(__attribute__((noescape)) BOOL (^)(id key, id obj, BOOL *stop))predicate { + VAP_LOCK(NSSet * a = [_dic keysOfEntriesWithOptions:opts passingTest:predicate]); return a; +} + +#pragma mark - mutable + +- (void)removeObjectForKey:(id)aKey { + VAP_LOCK([_dic removeObjectForKey:aKey]); +} + +- (void)setObject:(id)anObject forKey:(id )aKey { + VAP_LOCK([_dic setObject:anObject forKey:aKey]); +} + +- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary { + VAP_LOCK([_dic addEntriesFromDictionary:otherDictionary]); +} + +- (void)removeAllObjects { + VAP_LOCK([_dic removeAllObjects]); +} + +- (void)removeObjectsForKeys:(NSArray *)keyArray { + VAP_LOCK([_dic removeObjectsForKeys:keyArray]); +} + +- (void)setDictionary:(NSDictionary *)otherDictionary { + VAP_LOCK([_dic setDictionary:otherDictionary]); +} + +- (void)setObject:(id)obj forKeyedSubscript:(id )key { + VAP_LOCK([_dic setObject:obj forKeyedSubscript:key]); +} + +#pragma mark - protocol + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + VAP_LOCK(id copiedDictionary = [[self.class allocWithZone:zone] initWithDictionary:_dic]); + return copiedDictionary; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + VAP_LOCK(NSUInteger count = [_dic countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:QGVAPSafeMutableDictionary.class]) { + QGVAPSafeMutableDictionary *other = object; + BOOL isEqual; + [_lock lock]; + [other->_lock lock]; + isEqual = [_dic isEqual:other->_dic]; + [other->_lock unlock]; + [_lock unlock]; + return isEqual; + } + return NO; +} + +- (NSUInteger)hash { + VAP_LOCK(NSUInteger hash = [_dic hash]); + return hash; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.h new file mode 100644 index 0000000..8a7240d --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.h @@ -0,0 +1,24 @@ +// QGVAPWeakProxy.h +// 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 + +@interface QGVAPWeakProxy : NSObject + +- (instancetype)initWithTarget:(id)target; + ++ (instancetype)proxyWithTarget:(id)target; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.m new file mode 100644 index 0000000..50d69b9 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Utils/QGVAPWeakProxy.m @@ -0,0 +1,52 @@ +// QGVAPWeakProxy.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 "QGVAPWeakProxy.h" + +@implementation QGVAPWeakProxy { + __weak id _target; +} + +- (instancetype)initWithTarget:(id)target { + if (self = [super init]) { + _target = target; + } + return self; +} + ++ (instancetype)proxyWithTarget:(id)target { + return [[QGVAPWeakProxy alloc] initWithTarget:target]; +} + +// 1. 快速消息转发 +- (id)forwardingTargetForSelector:(SEL)aSelector { + return _target; +} + +// 2. 如果<1>返回nil,到标准消息转发处理,如果不处理为Crash:unrecognized selector. 这里我们直接返回空指针地址. +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { + return [NSObject instanceMethodSignatureForSelector:@selector(init)]; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + void *null = NULL; + [anInvocation setReturnValue:&null]; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + return [_target respondsToSelector:aSelector]; +} + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/VAPMacros.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/VAPMacros.h new file mode 100644 index 0000000..1fe7cdb --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/VAPMacros.h @@ -0,0 +1,78 @@ +// VAPMacros.h +// 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. + +#ifndef VAPMacros_h +#define VAPMacros_h + +#define STRINGIZE(x) #x +#define STRINGIZE2(x) STRINGIZE(x) +#define SHADER_STRING(text) @ STRINGIZE2(text) + +#ifndef HWDSYNTH_DYNAMIC_PROPERTY_OBJECT +#define HWDSYNTH_DYNAMIC_PROPERTY_OBJECT(_dynamic_getter_, _dynamic_setter_, _association_policy_) \ +- (void)_dynamic_setter_ : (id)object { \ +[self willChangeValueForKey:@#_dynamic_getter_]; \ +objc_setAssociatedObject(self, _cmd, object, _association_policy_); \ +[self didChangeValueForKey:@#_dynamic_getter_]; \ +} \ +- (id)_dynamic_getter_ { \ +return objc_getAssociatedObject(self, @selector(_dynamic_setter_:)); \ +} +#endif + +#ifndef HWDSYNTH_DYNAMIC_PROPERTY_CTYPE +#define HWDSYNTH_DYNAMIC_PROPERTY_CTYPE(_dynamic_getter_, _dynamic_setter_, _type_) \ +- (void)_dynamic_setter_ : (_type_)object { \ +[self willChangeValueForKey:@#_dynamic_getter_]; \ +NSValue *value = [NSValue value:&object withObjCType:@encode(_type_)]; \ +objc_setAssociatedObject(self, _cmd, value, OBJC_ASSOCIATION_RETAIN); \ +[self didChangeValueForKey:@#_dynamic_getter_]; \ +} \ +- (_type_)_dynamic_getter_ { \ +_type_ cValue = { 0 }; \ +NSValue *value = objc_getAssociatedObject(self, @selector(_dynamic_setter_:)); \ +[value getValue:&cValue]; \ +return cValue; \ +} +#endif + +#import "QGHWDShaderTypes.h" +#import +#import "QGVAPMaskInfo.h" + +extern NSInteger const kQGHWDMP4DefaultFPS; //默认fps 25 +extern NSInteger const kQGHWDMP4MinFPS; //最小fps 1 +extern NSInteger const QGHWDMP4MaxFPS; //最大fps 60 + +extern NSInteger const VapMaxCompatibleVersion; //最大兼容版本 + +@class QGVAPSourceDisplayItem; + +typedef UIView VAPView; //特效播放容器 + +/* mp4素材中每一帧alpha通道数据的位置*/ +typedef NS_ENUM(NSInteger, QGHWDTextureBlendMode){ + + QGHWDTextureBlendMode_AlphaLeft = 0, // 左侧alpha + QGHWDTextureBlendMode_AlphaRight = 1, // 右侧alpha + QGHWDTextureBlendMode_AlphaTop = 2, // 上侧alpha + QGHWDTextureBlendMode_AlphaBottom = 3, // 下测alpha +}; + +typedef void(^VAPImageCompletionBlock)(UIImage * image, NSError * error,NSString *imageURL); + +typedef void(^VAPGestureEventBlock)(UIGestureRecognizer *gestureRecognizer, BOOL insideSource, QGVAPSourceDisplayItem *source); + +#endif /* VAPMacros_h */ diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.h new file mode 100644 index 0000000..6beae1e --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.h @@ -0,0 +1,54 @@ +// QGHWDMetalRenderer.h +// 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 +#import +#import "VAPMacros.h" + +UIKIT_EXTERN NSString *const kHWDVertexFunctionName; +UIKIT_EXTERN NSString *const kHWDYUVFragmentFunctionName; +extern matrix_float3x3 const kColorConversionMatrix601Default; +extern matrix_float3x3 const kColorConversionMatrix601FullRangeDefault; +extern matrix_float3x3 const kColorConversionMatrix709Default; +extern matrix_float3x3 const kColorConversionMatrix709FullRangeDefault; +extern matrix_float3x3 const kBlurWeightMatrixDefault; +extern id kQGHWDMetalRendererDevice; + +#if TARGET_OS_SIMULATOR//模拟器 + +@interface QGHWDMetalRenderer : NSObject + +@property (nonatomic, assign) QGHWDTextureBlendMode blendMode; + +- (instancetype)initWithMetalLayer:(id)layer blendMode:(QGHWDTextureBlendMode)mode; +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(id)layer; +- (void)dispose; + +@end +#else + +@interface QGHWDMetalRenderer : NSObject + +@property (nonatomic, assign) QGHWDTextureBlendMode blendMode; + +- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer blendMode:(QGHWDTextureBlendMode)mode; + +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer; + +- (void)dispose; + +@end + +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.m new file mode 100644 index 0000000..5cc5ef5 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalRenderer.m @@ -0,0 +1,321 @@ +// QGHWDMetalRenderer.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 "QGHWDMetalRenderer.h" +#import "QGHWDShaderTypes.h" +#import "QGVAPLogger.h" +#import +#import +#import "UIDevice+VAPUtil.h" +#import "QGVAPMetalUtil.h" +#import "QGVAPMetalShaderFunctionLoader.h" + +#pragma mark - constants + +NSString *const kHWDVertexFunctionName = @"hwd_vertexShader"; +NSString *const kHWDYUVFragmentFunctionName = @"hwd_yuvFragmentShader"; + +static NSInteger const kQuadVerticesConstantsRow = 4; +static NSInteger const kQuadVerticesConstantsColumn = 32; +static NSInteger const kHWDVertexCount = 4; + +id kQGHWDMetalRendererDevice; + +// BT.601, which is the standard for SDTV. +matrix_float3x3 const kColorConversionMatrix601Default = {{ + {1.164, 1.164, 1.164}, + {0.0, -0.392, 2.017}, + {1.596, -0.813, 0.0} +}}; + +/*矩阵形式!!! + 1.0 0.0 1.4 + [1.0 -0.343 -0.711 ] + 1.0 1.765 0.0 + */ +//ITU BT.601 Full Range + matrix_float3x3 const kColorConversionMatrix601FullRangeDefault = {{ + {1.0, 1.0, 1.0}, + {0.0, -0.34413, 1.772}, + {1.402, -0.71414, 0.0} +}}; + +// BT.709, which is the standard for HDTV. +matrix_float3x3 const kColorConversionMatrix709Default = {{ + {1.164, 1.164, 1.164}, + {0.0, -0.213, 2.112}, + {1.793, -0.533, 0.0} +}}; + +// BT.709 Full Range. +matrix_float3x3 const kColorConversionMatrix709FullRangeDefault = {{ + {1.0, 1.0, 1.0}, + {0.0, -.18732, 1.8556}, + {1.57481, -.46813, 0.0} +}}; + +// Blur weight matrix. +matrix_float3x3 const kBlurWeightMatrixDefault = {{ + {0.0625, 0.125, 0.0625}, + {0.125, 0.25, 0.125}, + {0.0625, 0.125, 0.0625} +}}; + +//QGHWDVertex 顶点坐标+纹理坐标(rgb+alpha) +static const float kQuadVerticesConstants[kQuadVerticesConstantsRow][kQuadVerticesConstantsColumn] = { + //左侧alpha + {-1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 0.0, 1.0, + -1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 0.0, 0.0, + 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.5, 1.0, + 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 0.0}, + //右侧alpha + {-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.5, 1.0, + -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, + 1.0, -1.0, 0.0, 1.0, 0.5, 1.0, 1.0, 1.0, + 1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0}, + //顶部alpha + {-1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.5, + -1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, + 1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.5, + 1.0, 1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 0.0}, + //底部alpha + {-1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, + -1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.5, + 1.0, -1.0, 0.0, 1.0, 1.0, 0.5, 1.0, 1.0, + 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5} +}; + +#if TARGET_OS_SIMULATOR//模拟器 +#else + +@interface QGHWDMetalRenderer () { + BOOL _renderingResourcesDisposed; //用以标记渲染资源是否被回收 + matrix_float3x3 _currentColorConversionMatrix; +} + +@property (nonatomic, strong) id vertexBuffer; +@property (nonatomic, strong) id yuvMatrixBuffer; +@property (nonatomic, strong) id pipelineState;//This will keep track of the compiled render pipeline you’re about to create. +@property (nonatomic, strong) id commandQueue; +@property (nonatomic, assign) int vertexCount; +@property (nonatomic, assign) CVMetalTextureCacheRef videoTextureCache;//need release +@property (nonatomic, strong) QGVAPMetalShaderFunctionLoader *shaderFuncLoader; + +@end + +@implementation QGHWDMetalRenderer + +#pragma mark - main +- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer blendMode:(QGHWDTextureBlendMode)mode { + self = [super init]; + if (self) { + _blendMode = mode; + if (!kQGHWDMetalRendererDevice) { + kQGHWDMetalRendererDevice = MTLCreateSystemDefaultDevice(); + } + layer.device = kQGHWDMetalRendererDevice; + [self setupConstants]; + [self setupPipelineStatesWithMetalLayer:layer]; + } + return self; +} + +/** + 回收渲染数据,减少内存占用 + */ +- (void)dispose { + + _commandQueue = nil; + _pipelineState = nil; + _vertexBuffer = nil; + _yuvMatrixBuffer = nil; + _shaderFuncLoader = nil; + if (_videoTextureCache) { + CVMetalTextureCacheFlush(_videoTextureCache, 0); + CFRelease(_videoTextureCache); + _videoTextureCache = NULL; + } + _renderingResourcesDisposed = YES; +} + +- (void)dealloc { + [self dispose]; +} + +- (void)setupConstants { + //buffers + const void *vertices = [self suitableQuadVertices]; + NSUInteger allocationSize = kQuadVerticesConstantsColumn * sizeof(float); + _vertexBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:vertices length:allocationSize options:kDefaultMTLResourceOption]; + _vertexCount = kHWDVertexCount; + _currentColorConversionMatrix = kColorConversionMatrix601FullRangeDefault; + struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}}; + NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters); + _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption]; +} + +- (void)updateMetalPropertiesIfNeed:(CVPixelBufferRef)pixelBuffer { + + if (!pixelBuffer) { + return ; + } + CFTypeRef yCbCrMatrixType = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL); + matrix_float3x3 matrix = kColorConversionMatrix601FullRangeDefault; + if (CFStringCompare(yCbCrMatrixType, kCVImageBufferYCbCrMatrix_ITU_R_709_2, 0) == kCFCompareEqualTo) { + matrix = kColorConversionMatrix709FullRangeDefault; + } + if (simd_equal(_currentColorConversionMatrix, matrix)) { + return ; + } + _currentColorConversionMatrix = matrix; + struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}}; + NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters); + _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption]; +} + +- (void)setupPipelineStatesWithMetalLayer:(CAMetalLayer *)metalLayer { + + self.shaderFuncLoader = [[QGVAPMetalShaderFunctionLoader alloc] initWithDevice:kQGHWDMetalRendererDevice]; + id vertexProgram = [self.shaderFuncLoader loadFunctionWithName:kHWDVertexFunctionName]; + id fragmentProgram = [self.shaderFuncLoader loadFunctionWithName:kHWDYUVFragmentFunctionName]; + + if (!vertexProgram || !fragmentProgram) { + VAP_Error(kQGVAPModuleCommon, @"setupPipelineStatesWithMetalLayer fail! cuz: shader load fail"); + NSAssert(0, @"check if .metal files been compiled to correct target!"); + return ; + } + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [MTLRenderPipelineDescriptor new]; + pipelineStateDescriptor.vertexFunction = vertexProgram; + pipelineStateDescriptor.fragmentFunction = fragmentProgram; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat; + NSError *psError = nil; + id pipelineState = [kQGHWDMetalRendererDevice newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&psError]; + if (!pipelineState || psError) { + VAP_Error(kQGVAPModuleCommon, @"newRenderPipelineStateWithDescriptor error!:%@", psError); + return ; + } + self.pipelineState = pipelineState; + self.commandQueue = [kQGHWDMetalRendererDevice newCommandQueue]; + CVReturn textureCacheError = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, kQGHWDMetalRendererDevice, nil, &_videoTextureCache); + if (textureCacheError != kCVReturnSuccess) { + VAP_Error(kQGVAPModuleCommon, @"create texture cache fail!:%@", textureCacheError); + return ; + } +} + +/** + 使用metal渲染管线渲染CVPixelBufferRef,若有需要融合的图层则通过对应的管线一并渲染 + + @param pixelBuffer 图像数据 + @param layer metalLayer + */ +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer { + + if (!layer.superlayer || layer.bounds.size.width <= 0 || layer.bounds.size.height <= 0) { + //https://forums.developer.apple.com/thread/26278 + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz layer.superlayer or size error is nil! superlayer:%@ height:%@ width:%@", layer.superlayer, @(layer.bounds.size.height), @(layer.bounds.size.width)); + return ; + } + [self reconstructIfNeed:layer]; + if (pixelBuffer == NULL || !self.commandQueue || !self.pipelineState) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz pixelbuffer is nil!"); + return ; + } + [self updateMetalPropertiesIfNeed:pixelBuffer]; + CVMetalTextureCacheFlush(_videoTextureCache, 0); + CVMetalTextureRef yTextureRef = nil, uvTextureRef = nil; + size_t yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + size_t yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + size_t uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); + size_t uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); + //注意格式!r8Unorm + CVReturn yStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, yWidth, yHeight, 0, &yTextureRef); + //注意格式!rg8Unorm + CVReturn uvStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, uvWidth, uvHeight, 1, &uvTextureRef); + if (yStatus != kCVReturnSuccess || uvStatus != kCVReturnSuccess) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz failing getting yuv texture-yStatus%@:uvStatus%@", @(yStatus), @(uvStatus)); + return ; + } + id yTexture = CVMetalTextureGetTexture(yTextureRef); + id uvTexture = CVMetalTextureGetTexture(uvTextureRef); + CVBufferRelease(yTextureRef); + CVBufferRelease(uvTextureRef); + CVMetalTextureCacheFlush(_videoTextureCache, 0); + yTextureRef = NULL; + uvTextureRef = NULL; + if (!yTexture || !uvTexture || !layer) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz content is nil! y:%@ uv:%@, layer:%@", @(yTexture != nil), @(uvTexture != nil), @(layer != nil)); + return ; + } + if (layer.drawableSize.width <= 0 || layer.drawableSize.height <= 0) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz drawableSize is 0"); + return ; + } + id drawable = layer.nextDrawable; + if (!drawable) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz nextDrawable is nil!"); + return ; + } + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor new]; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; //which returns the texture in which you need to draw in order for something to appear on the screen. + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; //“set the texture to the clear color before doing any drawing,” + renderPassDescriptor.colorAttachments[0].clearColor =MTLClearColorMake(1.0, 1.0, 1.0, 1.0); + id commandBuffer = [self.commandQueue commandBuffer]; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + [renderEncoder setRenderPipelineState:self.pipelineState]; + [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma]; + [renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma]; + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:self.vertexCount instanceCount:1]; + [renderEncoder endEncoding]; + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; +} + +#pragma mark - private + +/** + 在必要的时候重建渲染数据,以便渲染 + + @param layer metalLayer + */ +- (void)reconstructIfNeed:(CAMetalLayer *)layer { + if (_renderingResourcesDisposed) { + [self setupConstants]; + [self setupPipelineStatesWithMetalLayer:layer]; + _renderingResourcesDisposed = NO; + } +} + +- (const void *)suitableQuadVertices { + + switch (self.blendMode) { + case QGHWDTextureBlendMode_AlphaLeft: + return kQuadVerticesConstants[0]; + case QGHWDTextureBlendMode_AlphaRight: + return kQuadVerticesConstants[1]; + case QGHWDTextureBlendMode_AlphaTop: + return kQuadVerticesConstants[2]; + case QGHWDTextureBlendMode_AlphaBottom: + return kQuadVerticesConstants[3]; + default: + break; + } + return kQuadVerticesConstants[0]; +} + +@end +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.h new file mode 100644 index 0000000..aee3642 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.h @@ -0,0 +1,47 @@ +// QGHWDMetalView.h +// 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 +#import "VAPMacros.h" + +//https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Device/Device.html#//apple_ref/doc/uid/TP40014221-CH2-SW1 +/* + 2018-12-31 00:01:51.349229+0800 MetalTest[28134:2050088] [DYMTLInitPlatform] platform initialization successful + 2018-12-31 00:01:51.413574+0800 MetalTest[28134:2050043] Metal GPU Frame Capture Enabled + 2018-12-31 00:01:51.414037+0800 MetalTest[28134:2050043] Metal API Validation Enabled + 2018-12-31 00:01:54.008682+0800 MetalTest[28134:2050086] Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6) + 2018-12-31 00:01:54.009053+0800 MetalTest[28134:2050086] Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6) + 2018-12-31 00:01:54.011370+0800 MetalTest[28134:2050086] Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6) + 2018-12-31 00:01:54.011710+0800 MetalTest[28134:2050086] Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6) + */ +@protocol QGHWDMetelViewDelegate + +- (void)onMetalViewUnavailable; + +@end + +@interface QGHWDMetalView : UIView + +@property (nonatomic, weak) id delegate; +@property (nonatomic, assign) QGHWDTextureBlendMode blendMode; + +- (instancetype)initWithFrame:(CGRect)frame blendMode:(QGHWDTextureBlendMode)mode; + +- (void)display:(CVPixelBufferRef)pixelBuffer; + +- (void)dispose; + +@end + diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.m new file mode 100644 index 0000000..7ab00e8 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/QGHWDMetalView.m @@ -0,0 +1,134 @@ +// QGHWDMetalView.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 "QGHWDMetalView.h" +#import "QGVAPLogger.h" +#import "QGHWDMetalRenderer.h" + +#if TARGET_OS_SIMULATOR//模拟器 + +@implementation QGHWDMetalView + +- (instancetype)initWithFrame:(CGRect)frame blendMode:(QGHWDTextureBlendMode)mode { + return [self initWithFrame:frame]; +} + +- (void)display:(CVPixelBufferRef)pixelBuffer {} + +-(void)dispose {} + +@end + +#else + +@interface QGHWDMetalView () + +@property (nonatomic, strong) CAMetalLayer *metalLayer; +@property (nonatomic, strong) QGHWDMetalRenderer *renderer; +@property (nonatomic, assign) BOOL drawableSizeShouldUpdate; + +@end + +@implementation QGHWDMetalView + +#pragma mark - override + ++ (Class)layerClass { + return [CAMetalLayer class]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + NSAssert(0, @"initWithCoder: has not been implemented"); + return nil; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + if (self = [super initWithFrame:frame]) { + _drawableSizeShouldUpdate = YES; + _blendMode = QGHWDTextureBlendMode_AlphaLeft; + } + return self; +} + +- (void)didMoveToWindow { + [super didMoveToWindow]; + self.drawableSizeShouldUpdate = YES; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + self.drawableSizeShouldUpdate = YES; +} + +- (void)dealloc { + [self onMetalViewUnavailable]; +} + +#pragma mark - main + +- (instancetype)initWithFrame:(CGRect)frame blendMode:(QGHWDTextureBlendMode)mode { + + if (self = [super initWithFrame:frame]) { + _drawableSizeShouldUpdate = YES; + _blendMode = QGHWDTextureBlendMode_AlphaLeft; + _metalLayer = (CAMetalLayer *)self.layer; + _metalLayer.frame = self.frame; + _metalLayer.opaque = NO; + _blendMode = mode; + _renderer = [[QGHWDMetalRenderer alloc] initWithMetalLayer:_metalLayer blendMode:mode]; + _metalLayer.contentsScale = [UIScreen mainScreen].scale; + _metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; + _metalLayer.framebufferOnly = YES; + } + return self; +} + +- (void)display:(CVPixelBufferRef)pixelBuffer { + if (!self.window) { + VAP_Event(kQGVAPModuleCommon, @"quit display pixelbuffer, cuz window is nil!"); + [self onMetalViewUnavailable]; + return ; + } + if (self.drawableSizeShouldUpdate) { + CGFloat nativeScale = [UIScreen mainScreen].nativeScale; + CGSize drawableSize = CGSizeMake(CGRectGetWidth(self.bounds)*nativeScale, CGRectGetHeight(self.bounds)*nativeScale); + self.metalLayer.drawableSize = drawableSize; + VAP_Event(kQGVAPModuleCommon, @"update drawablesize :%@", [NSValue valueWithCGSize:drawableSize]); + self.drawableSizeShouldUpdate = NO; + } + self.renderer.blendMode = self.blendMode; + [self.renderer renderPixelBuffer:pixelBuffer metalLayer:self.metalLayer]; +} + +/** + 资源回收 + */ +- (void)dispose { + [self.renderer dispose]; +} + +#pragma mark - private + +- (void)onMetalViewUnavailable{ + + if ([self.delegate respondsToSelector:@selector(onMetalViewUnavailable)]) { + [self.delegate onMetalViewUnavailable]; + } +} + +@end + +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.h new file mode 100644 index 0000000..1a9f6a4 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.h @@ -0,0 +1,48 @@ +// QGVAPMetalRenderer.h +// 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 +#import "QGVAPConfigModel.h" +#import +#import "VAPMacros.h" + +#if TARGET_OS_SIMULATOR//模拟器 + +@interface QGVAPMetalRenderer : NSObject + +@property (nonatomic, strong) QGVAPCommonInfo *commonInfo; + +- (instancetype)initWithMetalLayer:(id)layer; +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(id)layer mergeInfos:(NSArray *)infos; +- (void)dispose; + +@end + +#else + +@interface QGVAPMetalRenderer : NSObject + +@property (nonatomic, strong) QGVAPCommonInfo *commonInfo; +@property (nonatomic, strong) QGVAPMaskInfo *maskInfo; + +- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer; + +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer mergeInfos:(NSArray *)infos; + +- (void)dispose; + +@end + +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.m new file mode 100644 index 0000000..48b295b --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalRenderer.m @@ -0,0 +1,425 @@ +// QGVAPMetalRenderer.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 "QGVAPMetalRenderer.h" +#import "QGHWDMetalRenderer.h" +#import +#import "QGVAPLogger.h" +#import +#import "UIDevice+VAPUtil.h" +#import "QGVAPMetalUtil.h" +#import "QGVAPMetalShaderFunctionLoader.h" + +#if TARGET_OS_SIMULATOR//模拟器 +#else + +@interface QGVAPMetalRenderer () { + BOOL _renderingResourcesDisposed; //用以标记渲染资源是否被回收 + matrix_float3x3 _currentColorConversionMatrix; +} + +@property (nonatomic, strong) id vertexBuffer; +@property (nonatomic, strong) id yuvMatrixBuffer; +@property (nonatomic, strong) id maskBlurBuffer; +@property (nonatomic, strong) id attachmentPipelineState; +@property (nonatomic, strong) id defaultMainPipelineState; +@property (nonatomic, strong) id mainPipelineStateForMask; //带遮罩处理的主流程管线 +@property (nonatomic, strong) id mainPipelineStateForMaskBlur; //带遮罩模糊处理的主流程管线 +@property (nonatomic, strong) id commandQueue; +@property (nonatomic, assign) CVMetalTextureCacheRef videoTextureCache;//need release +@property (nonatomic, strong) QGVAPMetalShaderFunctionLoader *shaderFuncLoader; +@property (nonatomic, weak) CAMetalLayer *metalLayer; + +@end + +@implementation QGVAPMetalRenderer + +- (instancetype)initWithMetalLayer:(CAMetalLayer *)layer { + + if (self = [super init]) { + if (!kQGHWDMetalRendererDevice) { + kQGHWDMetalRendererDevice = MTLCreateSystemDefaultDevice(); + } + layer.device = kQGHWDMetalRendererDevice; + _metalLayer = layer; + [self setupRenderContext]; + } + return self; +} + +#pragma mark - main + +- (void)renderPixelBuffer:(CVPixelBufferRef)pixelBuffer metalLayer:(CAMetalLayer *)layer mergeInfos:(NSArray *)infos { + + if (!layer.superlayer || layer.bounds.size.width <= 0 || layer.bounds.size.height <= 0) { + //https://forums.developer.apple.com/thread/26278 + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz layer.superlayer or size error is nil! superlayer:%@ height:%@ width:%@", layer.superlayer, @(layer.bounds.size.height), @(layer.bounds.size.width)); + return ; + } + [self reconstructIfNeed:layer]; + if (pixelBuffer == NULL || !self.commandQueue) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz pixelbuffer is nil!"); + return ; + } + [self updateMetalPropertiesIfNeed:pixelBuffer]; + CVMetalTextureCacheFlush(_videoTextureCache, 0); + CVMetalTextureRef yTextureRef = nil, uvTextureRef = nil; + size_t yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + size_t yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + size_t uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); + size_t uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); + //注意格式!r8Unorm + CVReturn yStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatR8Unorm, yWidth, yHeight, 0, &yTextureRef); + //注意格式!rg8Unorm + CVReturn uvStatus = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _videoTextureCache, pixelBuffer, nil, MTLPixelFormatRG8Unorm, uvWidth, uvHeight, 1, &uvTextureRef); + if (yStatus != kCVReturnSuccess || uvStatus != kCVReturnSuccess) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz failing getting yuv texture-yStatus%@:uvStatus%@", @(yStatus), @(uvStatus)); + return ; + } + id yTexture = CVMetalTextureGetTexture(yTextureRef); + id uvTexture = CVMetalTextureGetTexture(uvTextureRef); + CVBufferRelease(yTextureRef); + CVBufferRelease(uvTextureRef); + CVMetalTextureCacheFlush(_videoTextureCache, 0); + yTextureRef = NULL; + uvTextureRef = NULL; + if (!yTexture || !uvTexture || !layer) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz content is nil! y:%@ uv:%@, layer:%@", @(yTexture != nil), @(uvTexture != nil), @(layer != nil)); + return ; + } + if (layer.drawableSize.width <= 0 || layer.drawableSize.height <= 0) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz drawableSize is 0"); + return ; + } + id drawable = layer.nextDrawable; + if (!drawable) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz nextDrawable is nil!"); + return ; + } + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor new]; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; //which returns the texture in which you need to draw in order for something to appear on the screen. + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; //“set the texture to the clear color before doing any drawing,” + renderPassDescriptor.colorAttachments[0].clearColor =MTLClearColorMake(0.0, 0.0, 0.0, 0.0); + id commandBuffer = [self.commandQueue commandBuffer]; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + if (renderEncoder == nil) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz renderEncoder:%p or self.pipelineState:%p is nil!", renderEncoder); + return ; + } + if (self.vertexBuffer == nil || self.yuvMatrixBuffer == nil) { + VAP_Error(kQGVAPModuleCommon, @"quit rendering cuz vertexBuffer:%p or yuvMatrixBuffer:%p is nil!", self.vertexBuffer, self.yuvMatrixBuffer); + return ; + } + + [self drawBackground:yTexture uvTexture:uvTexture encoder:renderEncoder]; + [self drawMergedAttachments:infos yTexture:yTexture uvTexture:uvTexture renderEncoder:renderEncoder metalLayer:layer]; + [renderEncoder endEncoding]; + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; +} + +- (void)drawBackground:(id)yTexture uvTexture:(id)uvTexture encoder:(id)renderEncoder { + + if (self.maskInfo) { + id maskTexture = self.maskInfo.texture; + if (!maskTexture) { + VAP_Error(kQGVAPModuleCommon, @"maskTexture error! maskTexture is nil"); + return; + } + + if (!self.mainPipelineStateForMask) { + VAP_Error(kQGVAPModuleCommon, @"maskPipelineState error! maskTexture is nil"); + return; + } + + if (self.maskInfo.blurLength > 0) { + [renderEncoder setRenderPipelineState:self.mainPipelineStateForMaskBlur]; + [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentBuffer:self.maskBlurBuffer offset:0 atIndex:1]; + [renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma]; + [renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma]; + [renderEncoder setFragmentTexture:maskTexture atIndex:QGHWDYUVFragmentTextureIndexAttachmentStart]; + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4 instanceCount:1]; + } else { + [renderEncoder setRenderPipelineState:self.mainPipelineStateForMask]; + [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma]; + [renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma]; + [renderEncoder setFragmentTexture:maskTexture atIndex:QGHWDYUVFragmentTextureIndexAttachmentStart]; + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4 instanceCount:1]; + } + } else { + if (!self.defaultMainPipelineState) { + VAP_Error(kQGVAPModuleCommon, @"yuvPipelineState error! maskTexture is nil"); + return; + } + [renderEncoder setRenderPipelineState:self.defaultMainPipelineState]; + [renderEncoder setVertexBuffer:self.vertexBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentBuffer:self.yuvMatrixBuffer offset:0 atIndex:0]; + [renderEncoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma]; + [renderEncoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma]; + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4 instanceCount:1]; + } +} + +- (void)dispose { + + _commandQueue = nil; + _vertexBuffer = nil; + _yuvMatrixBuffer = nil; + _attachmentPipelineState = nil; + _shaderFuncLoader = nil; + if (_videoTextureCache) { + CVMetalTextureCacheFlush(_videoTextureCache, 0); + CFRelease(_videoTextureCache); + _videoTextureCache = NULL; + } + _renderingResourcesDisposed = YES; + _mainPipelineStateForMask = nil; + _defaultMainPipelineState = nil; +} + +-(void)dealloc { + [self dispose]; +} + +- (void)setupRenderContext { + + //constants + _currentColorConversionMatrix = kColorConversionMatrix601FullRangeDefault; + struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}}; + NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters); + _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption]; + + //function loader + self.shaderFuncLoader = [[QGVAPMetalShaderFunctionLoader alloc] initWithDevice:kQGHWDMetalRendererDevice]; + + //command queue + self.commandQueue = [kQGHWDMetalRendererDevice newCommandQueue]; + + //texture cache + CVReturn textureCacheError = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, kQGHWDMetalRendererDevice, nil, &_videoTextureCache); + if (textureCacheError != kCVReturnSuccess) { + VAP_Error(kQGVAPModuleCommon, @"create texture cache fail!:%@", textureCacheError); + } +} + +- (void)drawMergedAttachments:(NSArray *)infos + yTexture:(id)yTexture + uvTexture:(id)uvTexture + renderEncoder:(id)encoder + metalLayer:(CAMetalLayer *)layer { + + if (infos.count == 0) { + return ; + } + if (!encoder || !self.commonInfo || !self.attachmentPipelineState) { + VAP_Error(kQGVAPModuleCommon, @"renderMergedAttachments error! infos:%@ encoder:%p commonInfo:%@ attachmentPipelineState:%p", @(infos.count), encoder, @(self.commonInfo != nil), self.attachmentPipelineState); + return ; + } + if (yTexture == nil || uvTexture == nil) { + VAP_Error(kQGVAPModuleCommon, @"renderMergedAttachments error! cuz yTexture:%p or uvTexture:%p is nil!", yTexture, uvTexture); + return ; + } + [infos enumerateObjectsUsingBlock:^(QGVAPMergedInfo * _Nonnull mergeInfo, NSUInteger idx, BOOL * _Nonnull stop) { + + [encoder setRenderPipelineState:self.attachmentPipelineState]; + id sourceTexture = mergeInfo.source.texture;//图片纹理 + id vertexBuffer = [mergeInfo vertexBufferWithContainerSize:self.commonInfo.size maskContianerSize:self.commonInfo.videoSize device:kQGHWDMetalRendererDevice]; + id colorParamsBuffer = mergeInfo.source.colorParamsBuffer; + id yuvMatrixBuffer = self.yuvMatrixBuffer; + if (!sourceTexture || !vertexBuffer || !colorParamsBuffer || !yuvMatrixBuffer) { + //VAP_Error(kQGVAPModuleCommon, @"quit attachment:%p cuz-source:%p vertex:%p",mergeInfo, sourceTexture, vertexBuffer); + return ; + } + [encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0]; + [encoder setFragmentBuffer:yuvMatrixBuffer offset:0 atIndex:0]; + [encoder setFragmentBuffer:colorParamsBuffer offset:0 atIndex:1]; + //遮罩信息在视频流中 + [encoder setFragmentTexture:yTexture atIndex:QGHWDYUVFragmentTextureIndexLuma]; + [encoder setFragmentTexture:uvTexture atIndex:QGHWDYUVFragmentTextureIndexChroma]; + [encoder setFragmentTexture:sourceTexture atIndex:QGHWDYUVFragmentTextureIndexAttachmentStart]; + [encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4 instanceCount:1]; + }]; +} + +#pragma mark - setter&getter + +- (id)maskBlurBuffer { + if (!_maskBlurBuffer) { + struct MaskParameters parameters[] = {{kBlurWeightMatrixDefault, 3, 0.01}}; + NSUInteger parametersSize = sizeof(struct MaskParameters); + _maskBlurBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:parameters length:parametersSize options:kDefaultMTLResourceOption]; + } + return _maskBlurBuffer; +} + +- (void)setCommonInfo:(QGVAPCommonInfo *)commonInfo { + _commonInfo = commonInfo; + [self updateMainVertexBuffer]; +} + +- (void)setMaskInfo:(QGVAPMaskInfo *)maskInfo { + + if (maskInfo && (!maskInfo.data || maskInfo.dataSize.width <= 0 || maskInfo.dataSize.height <= 0)) { + VAP_Error(kQGVAPModuleCommon, @"setMaskInfo fail: data:%@, size:%@", maskInfo.data, NSStringFromCGSize(maskInfo.dataSize)); + return; + } + if (_maskInfo == maskInfo) { + return ; + } + _maskInfo = maskInfo; + if (_vertexBuffer) { + [self updateMainVertexBuffer]; + } +} + +#pragma mark - pipelines + +- (id)createPipelineState:(NSString *)vertexFunction fragmentFunction:(NSString *)fragmentFunction { + + id vertexProgram = [self.shaderFuncLoader loadFunctionWithName:vertexFunction]; + id fragmentProgram = [self.shaderFuncLoader loadFunctionWithName:fragmentFunction]; + + if (!vertexProgram || !fragmentProgram) { + VAP_Error(kQGVAPModuleCommon, @"setupPipelineStatesWithMetalLayer fail! cuz: shader load fail!"); + NSAssert(0, @"check if .metal files been compiled to correct target!"); + return nil; + } + + //融混方程 + //https://objccn.io/issue-3-1/ + //https://www.andersriggelsen.dk/glblendfunc.php + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [MTLRenderPipelineDescriptor new]; + pipelineStateDescriptor.vertexFunction = vertexProgram; + pipelineStateDescriptor.fragmentFunction = fragmentProgram; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = _metalLayer.pixelFormat; + [pipelineStateDescriptor.colorAttachments[0] setBlendingEnabled:YES]; + pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; + pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; + pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + NSError *psError = nil; + id pipelineState = [kQGHWDMetalRendererDevice newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&psError]; + if (!pipelineState || psError) { + VAP_Error(kQGVAPModuleCommon, @"newRenderPipelineStateWithDescriptor error!:%@", psError); + return nil; + } + + return pipelineState; +} + +- (id)defaultMainPipelineState { + if (!_defaultMainPipelineState) { + _defaultMainPipelineState = [self createPipelineState:kVAPVertexFunctionName fragmentFunction:kVAPYUVFragmentFunctionName]; + } + return _defaultMainPipelineState; +} + +- (id)mainPipelineStateForMask { + if (!_mainPipelineStateForMask) { + _mainPipelineStateForMask = [self createPipelineState:kVAPVertexFunctionName fragmentFunction:kVAPMaskFragmentFunctionName]; + } + return _mainPipelineStateForMask; +} + +- (id)attachmentPipelineState { + if (!_attachmentPipelineState) { + _attachmentPipelineState = [self createPipelineState:kVAPAttachmentVertexFunctionName fragmentFunction:kVAPAttachmentFragmentFunctionName]; + } + return _attachmentPipelineState; +} + +- (id)mainPipelineStateForMaskBlur { + if (!_mainPipelineStateForMaskBlur) { + _mainPipelineStateForMaskBlur = [self createPipelineState:kVAPVertexFunctionName fragmentFunction:kVAPMaskBlurFragmentFunctionName]; + } + return _mainPipelineStateForMaskBlur; +} + +#pragma mark - private + +- (void)reconstructIfNeed:(CAMetalLayer *)layer { + + if (_renderingResourcesDisposed) { + [self setupRenderContext]; + _renderingResourcesDisposed = NO; + } +} + +- (void)updateMetalPropertiesIfNeed:(CVPixelBufferRef)pixelBuffer { + + if (!pixelBuffer) { + return ; + } + CFTypeRef yCbCrMatrixType = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL); + matrix_float3x3 matrix = kColorConversionMatrix601FullRangeDefault; + if (CFStringCompare(yCbCrMatrixType, kCVImageBufferYCbCrMatrix_ITU_R_709_2, 0) == kCFCompareEqualTo) { + matrix = kColorConversionMatrix709FullRangeDefault; + } + if (simd_equal(_currentColorConversionMatrix, matrix)) { + return ; + } + _currentColorConversionMatrix = matrix; + struct ColorParameters yuvMatrixs[] = {{_currentColorConversionMatrix,{0.5, 0.5}}}; + NSUInteger yuvMatrixsDataSize = sizeof(struct ColorParameters); + _yuvMatrixBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:yuvMatrixs length:yuvMatrixsDataSize options:kDefaultMTLResourceOption]; +} + +- (void)updateMainVertexBuffer { + + const int colunmCountForVertices = 4, colunmCountForCoordinate = 2, vertexDataLength = 40; //顶点(x,y,z,w),纹理坐标(x,x),数组长度 + static float vertexData[vertexDataLength]; //顶点+纹理坐标数据 + float rgbCoordinates[8], alphaCoordinates[8]; + float maskCoordinates[8] = {0}; + const void *vertices = kVAPMTLVerticesIdentity; + + genMTLTextureCoordinates(self.commonInfo.rgbAreaRect, self.commonInfo.videoSize, rgbCoordinates, NO, 0); + genMTLTextureCoordinates(self.commonInfo.alphaAreaRect, self.commonInfo.videoSize, alphaCoordinates, NO, 0); + if (self.maskInfo) { + genMTLTextureCoordinates(self.maskInfo.sampleRect, self.maskInfo.dataSize, maskCoordinates, NO, 0); + } + + int indexForVertexData = 0; + //顶点数据+坐标。==> 这里的写法需有优化一下 + for (int i = 0; i < 4 * colunmCountForVertices; i ++) { + //顶点数据 + vertexData[indexForVertexData++] = ((float*)vertices)[i]; + //逐行处理 + if (i%colunmCountForVertices == colunmCountForVertices-1) { + int row = i/colunmCountForVertices; + //rgb纹理坐标 + vertexData[indexForVertexData++] = ((float*)rgbCoordinates)[row*colunmCountForCoordinate]; + vertexData[indexForVertexData++] = ((float*)rgbCoordinates)[row*colunmCountForCoordinate+1]; + //alpha纹理坐标 + vertexData[indexForVertexData++] = ((float*)alphaCoordinates)[row*colunmCountForCoordinate]; + vertexData[indexForVertexData++] = ((float*)alphaCoordinates)[row*colunmCountForCoordinate+1]; + //mask纹理坐标 + vertexData[indexForVertexData++] = ((float*)maskCoordinates)[row*colunmCountForCoordinate]; + vertexData[indexForVertexData++] = ((float*)maskCoordinates)[row*colunmCountForCoordinate+1]; + } + } + NSUInteger allocationSize = vertexDataLength * sizeof(float); + id vertexBuffer = [kQGHWDMetalRendererDevice newBufferWithBytes:vertexData length:allocationSize options:kDefaultMTLResourceOption]; + _vertexBuffer = vertexBuffer; +} + +@end + +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.h new file mode 100644 index 0000000..79b65da --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.h @@ -0,0 +1,37 @@ +// QGVAPMetalView.h +// 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 +#import "QGVAPConfigModel.h" + +@class QGVAPMaskInfo; + +@protocol QGVAPMetalViewDelegate + +- (void)onMetalViewUnavailable; + +@end + +@interface QGVAPMetalView : UIView + +@property (nonatomic, weak) id delegate; +@property (nonatomic, strong) QGVAPCommonInfo *commonInfo; +@property (nonatomic, strong) QGVAPMaskInfo *maskInfo; + +- (void)display:(CVPixelBufferRef)pixelBuffer mergeInfos:(NSArray *)infos; + +- (void)dispose; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.m new file mode 100644 index 0000000..ed056fb --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/Metal/Vapx/QGVAPMetalView.m @@ -0,0 +1,132 @@ +// QGVAPMetalView.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 "QGVAPMetalView.h" +#import "QGVAPMetalRenderer.h" +#import "QGVAPLogger.h" + +#if TARGET_OS_SIMULATOR//模拟器 + +@implementation QGVAPMetalView + +- (void)display:(CVPixelBufferRef)pixelBuffer mergeInfos:(NSArray *)infos {} + +- (void)dispose {} + +@end + +#else + +@interface QGVAPMetalView () + +@property (nonatomic, strong) CAMetalLayer *metalLayer; +@property (nonatomic, strong) QGVAPMetalRenderer *renderer; +@property (nonatomic, assign) BOOL drawableSizeShouldUpdate; + +@end + +@implementation QGVAPMetalView + +#pragma mark - override + ++ (Class)layerClass { + return [CAMetalLayer class]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + NSAssert(0, @"initWithCoder: has not been implemented"); + if (self = [super initWithCoder:aDecoder]) { + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + if (self = [super initWithFrame:frame]) { + _drawableSizeShouldUpdate = YES; + _metalLayer = (CAMetalLayer *)self.layer; + _metalLayer.frame = self.frame; + _metalLayer.opaque = NO; + _renderer = [[QGVAPMetalRenderer alloc] initWithMetalLayer:_metalLayer]; + _metalLayer.contentsScale = [UIScreen mainScreen].scale; + _metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; + _metalLayer.framebufferOnly = YES; + } + return self; +} + +- (void)didMoveToWindow { + [super didMoveToWindow]; + self.drawableSizeShouldUpdate = YES; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + self.drawableSizeShouldUpdate = YES; +} + +- (void)dealloc { + [self onMetalViewUnavailable]; +} + +#pragma mark - getter&setter + +- (QGVAPCommonInfo *)commonInfo { + return self.renderer.commonInfo; +} + +- (void)setCommonInfo:(QGVAPCommonInfo *)commonInfo { + [self.renderer setCommonInfo:commonInfo]; +} + +- (void)setMaskInfo:(QGVAPMaskInfo *)maskInfo { + [self.renderer setMaskInfo:maskInfo]; +} + +#pragma mark - main + +- (void)display:(CVPixelBufferRef)pixelBuffer mergeInfos:(NSArray *)infos { + + if (!self.window) { + VAP_Event(kQGVAPModuleCommon, @"quit display pixelbuffer, cuz window is nil!"); + [self onMetalViewUnavailable]; + return ; + } + if (self.drawableSizeShouldUpdate) { + CGFloat nativeScale = [UIScreen mainScreen].nativeScale; + CGSize drawableSize = CGSizeMake(CGRectGetWidth(self.bounds)*nativeScale, CGRectGetHeight(self.bounds)*nativeScale); + self.metalLayer.drawableSize = drawableSize; + VAP_Event(kQGVAPModuleCommon, @"update drawablesize :%@", [NSValue valueWithCGSize:drawableSize]); + self.drawableSizeShouldUpdate = NO; + } + [self.renderer renderPixelBuffer:pixelBuffer metalLayer:self.metalLayer mergeInfos:infos]; +} + +- (void)dispose { + [self.renderer dispose]; +} + +#pragma mark - private + +- (void)onMetalViewUnavailable{ + + if ([self.delegate respondsToSelector:@selector(onMetalViewUnavailable)]) { + [self.delegate onMetalViewUnavailable]; + } +} + +@end + +#endif diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.h b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.h new file mode 100644 index 0000000..88a46df --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.h @@ -0,0 +1,41 @@ +// QGHWDMP4OpenGLView.h +// 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 +#import +#import +#import "UIView+VAP.h" + +@protocol QGHWDMP4OpenGLViewDelegate + +- (void)onViewUnavailableStatus; + +@end + +@interface QGHWDMP4OpenGLView : UIView + +@property (nonatomic, strong) EAGLContext *glContext; +@property (nonatomic, weak) id displayDelegate; +@property (nonatomic, assign) QGHWDTextureBlendMode blendMode; +@property (nonatomic, assign) BOOL pause; + +- (void)setupGL; +- (void)displayPixelBuffer:(CVPixelBufferRef)pixelBuffer; +- (void)dispose; + +//update glcontext's viewport size by layer bounds +- (void)updateBackingSize; + +@end diff --git a/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.m b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.m new file mode 100644 index 0000000..7804074 --- /dev/null +++ b/dkhelper/dkhelperDylib/QGVAPlayer/class/Views/OpenGL/QGHWDMP4OpenGLView.m @@ -0,0 +1,645 @@ +// QGHWDMP4OpenGLView.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 "QGHWDMP4OpenGLView.h" +#import +#import +#import +#import +#import "VAPMacros.h" + +// Uniform index. +enum { + HWD_UNIFORM_Y, + HWD_UNIFORM_UV, + HWD_UNIFORM_COLOR_CONVERSION_MATRIX, + HWD_NUM_UNIFORMS +}; +GLint hwd_uniforms[HWD_NUM_UNIFORMS]; + +// Attribute index. +enum { + ATTRIB_VERTEX, + ATTRIB_TEXCOORD_RGB, + ATTRIB_TEXCOORD_ALPHA, + NUM_ATTRIBUTES +}; + +// BT.709-HDTV. +static const GLfloat kColorConversion709[] = { + 1.164, 1.164, 1.164, + 0.0, -0.213, 2.112, + 1.793, -0.533, 0.0, +}; + +// BT.601 full range-http://www.equasys.de/colorconversion.html +const GLfloat kColorConversion601FullRange[] = { + 1.0, 1.0, 1.0, + 0.0, -0.343, 1.765, + 1.4, -0.711, 0.0, +}; + + +// texture coords for blend + +const GLfloat textureCoordLeft[] = { // 左侧 + 0.5, 0.0, + 0.0, 0.0, + 0.5, 1.0, + 0.0, 1.0 +}; + +const GLfloat textureCoordRight[] = { // 右侧 + 1.0, 0.0, + 0.5, 0.0, + 1.0, 1.0, + 0.5, 1.0 +}; + +const GLfloat textureCoordTop[] = { // 上侧 + 1.0, 0.0, + 0.0, 0.0, + 1.0, 0.5, + 0.0, 0.5 +}; + +const GLfloat textureCoordBottom[] = { // 下侧 + 1.0, 0.5, + 0.0, 0.5, + 1.0, 1.0, + 0.0, 1.0 +}; + +#undef cos +#undef sin +NSString *const kVertexShaderSource = SHADER_STRING +( + attribute vec4 position; + attribute vec2 RGBTexCoord; + attribute vec2 alphaTexCoord; + + varying vec2 RGBTexCoordVarying; + varying vec2 alphaTexCoordVarying; + + void main() +{ + float preferredRotation = 3.14; + mat4 rotationMatrix = mat4(cos(preferredRotation), -sin(preferredRotation), 0.0, 0.0,sin(preferredRotation),cos(preferredRotation), 0.0, 0.0,0.0,0.0,1.0,0.0,0.0,0.0, 0.0,1.0); + gl_Position = rotationMatrix * position; + RGBTexCoordVarying = RGBTexCoord; + alphaTexCoordVarying = alphaTexCoord; +} + ); + +NSString *const kFragmentShaderSource = SHADER_STRING +( + varying highp vec2 RGBTexCoordVarying; + varying highp vec2 alphaTexCoordVarying; + precision mediump float; + + uniform sampler2D SamplerY; + uniform sampler2D SamplerUV; + uniform mat3 colorConversionMatrix; + + void main() +{ + mediump vec3 yuv_rgb; + lowp vec3 rgb_rgb; + + mediump vec3 yuv_alpha; + lowp vec3 rgb_alpha; + + // Subtract constants to map the video range start at 0 + yuv_rgb.x = (texture2D(SamplerY, RGBTexCoordVarying).r);// - (16.0/255.0)); + yuv_rgb.yz = (texture2D(SamplerUV, RGBTexCoordVarying).ra - vec2(0.5, 0.5)); + + rgb_rgb = colorConversionMatrix * yuv_rgb; + + + yuv_alpha.x = (texture2D(SamplerY, alphaTexCoordVarying).r);// - (16.0/255.0)); + yuv_alpha.yz = (texture2D(SamplerUV, alphaTexCoordVarying).ra - vec2(0.5, 0.5)); + + rgb_alpha = colorConversionMatrix * yuv_alpha; + + + gl_FragColor = vec4(rgb_rgb,rgb_alpha.r); + // gl_FragColor = vec4(1, 0, 0, 1); +} + ); + + +@interface QGHWDMP4OpenGLView() { + + GLint _backingWidth; + GLint _backingHeight; + CVOpenGLESTextureRef _lumaTexture; + CVOpenGLESTextureRef _chromaTexture; + CVOpenGLESTextureCacheRef _textureCache; + GLuint _frameBufferHandle; + GLuint _colorBufferHandle; + const GLfloat *_preferredConversion; +} + +@property GLuint program; + +- (void)setupBuffers; +- (void)cleanupTextures; + +- (BOOL)isValidateProgram:(GLuint)prog; + +- (BOOL)loadShaders; +- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL; +- (BOOL)linkProgram:(GLuint)prog; + +@end + +@implementation QGHWDMP4OpenGLView + ++ (Class)layerClass { + + return [CAEAGLLayer class]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + if ((self = [super initWithCoder:aDecoder])) { + if (![self commonInit]) { + return nil; + } + } + return self; +} + +- (instancetype)init { + + if (self = [super init]) { + if (![self commonInit]) { + return nil; + } + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + if (self = [super initWithFrame:frame]) { + if (![self commonInit]) { + return nil; + } + } + return self; +} + +- (BOOL)commonInit { + + self.contentScaleFactor = [[UIScreen mainScreen] scale]; + CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; + eaglLayer.opaque = NO; + eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO], + kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8}; + _glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + if (!_glContext || ![EAGLContext setCurrentContext:_glContext] || ![self loadShaders]) { + return NO; + } + _preferredConversion = kColorConversion709; + return YES; +} + +- (void)dealloc { + + [self cleanupTextures]; + if(_textureCache) { + CFRelease(_textureCache); + } + if ([self.displayDelegate respondsToSelector:@selector(onViewUnavailableStatus)]) { + [self.displayDelegate onViewUnavailableStatus]; + } +} + +# pragma mark - OpenGL setup +/** + 初始化opengl环境 + */ +- (void)setupGL { + + VAP_Info(kQGVAPModuleCommon, @"setupGL"); + [EAGLContext setCurrentContext:_glContext]; + [self setupBuffers]; + [self loadShaders]; + glUseProgram(self.program); + glUniform1i(hwd_uniforms[HWD_UNIFORM_Y], 0); + glUniform1i(hwd_uniforms[HWD_UNIFORM_UV], 1); + glUniformMatrix3fv(hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion); + // Create CVOpenGLESTextureCacheRef for optimal CVPixelBufferRef to GLES texture conversion. + if (!_textureCache) { + CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _glContext, NULL, &_textureCache); + if (err != noErr) { + VAP_Event(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreate %d", err); + return; + } + } + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +} + +#pragma mark - Utilities + +- (void)setupBuffers { + + glDisable(GL_DEPTH_TEST); + glEnableVertexAttribArray(ATTRIB_VERTEX); + glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0); + glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB); + glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0); + glEnableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA); + glVertexAttribPointer(ATTRIB_TEXCOORD_ALPHA, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0); + glGenFramebuffers(1, &_frameBufferHandle); + glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle); + glGenRenderbuffers(1, &_colorBufferHandle); + glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle); + [_glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer]; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBufferHandle); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + VAP_Event(kQGVAPModuleCommon, @"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } +} + +- (void)layoutSubviews { + + [super layoutSubviews]; + [self updateBackingSize]; +} + + +/** + 根据容器大小更新渲染尺寸 + */ +- (void)updateBackingSize { + + if ([EAGLContext currentContext] != _glContext) { + [EAGLContext setCurrentContext:_glContext]; + } + [_glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer]; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight); +} + +- (void)cleanupTextures { + + if (_lumaTexture) { + CFRelease(_lumaTexture); + _lumaTexture = NULL; + } + if (_chromaTexture) { + CFRelease(_chromaTexture); + _chromaTexture = NULL; + } + CVOpenGLESTextureCacheFlush(_textureCache, 0); +} + +#pragma mark - OpenGLES drawing + +/** + 上屏 + + @param pixelBuffer 硬解出来的samplebuffer数据 + */ +- (void)displayPixelBuffer:(CVPixelBufferRef)pixelBuffer { + + if (!self.window && [self.displayDelegate respondsToSelector:@selector(onViewUnavailableStatus)]) { + [self.displayDelegate onViewUnavailableStatus]; + return ; + } + + if ([EAGLContext currentContext] != _glContext) { + [EAGLContext setCurrentContext:_glContext]; + } + + CVReturn err; + if (pixelBuffer != NULL) { + int frameWidth = (int)CVPixelBufferGetWidth(pixelBuffer); + int frameHeight = (int)CVPixelBufferGetHeight(pixelBuffer); + + if (!_textureCache) { + VAP_Event(kQGVAPModuleCommon, @"No video texture cache"); + return; + } + [self cleanupTextures]; + + _preferredConversion = kColorConversion601FullRange; + + //y + glActiveTexture(GL_TEXTURE0); + err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, + _textureCache, + pixelBuffer, + NULL, + GL_TEXTURE_2D, + GL_LUMINANCE, + frameWidth, + frameHeight, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + 0, + &_lumaTexture); + if (err) { + VAP_Event(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err); + } + + glBindTexture(CVOpenGLESTextureGetTarget(_lumaTexture), CVOpenGLESTextureGetName(_lumaTexture)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // uv + glActiveTexture(GL_TEXTURE1); + err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, + _textureCache, + pixelBuffer, + NULL, + GL_TEXTURE_2D, + GL_LUMINANCE_ALPHA, + frameWidth / 2.0, + frameHeight / 2.0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + 1, + &_chromaTexture); + if (err) { + VAP_Error(kQGVAPModuleCommon, @"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err); + } + + glBindTexture(CVOpenGLESTextureGetTarget(_chromaTexture), CVOpenGLESTextureGetName(_chromaTexture)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle); + + // Set the view port to the entire view. + glViewport(0, 0, _backingWidth, _backingHeight); + } + + // glClearColor(0.1f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(self.program); + glUniformMatrix3fv(hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion); + + // 根据视频的方向和高宽比设置四个顶点。 + CGRect vertexRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(_backingWidth, _backingHeight), self.layer.bounds); + + // 计算归一化四坐标来绘制坐标系。 + CGSize normalizedSamplingSize = CGSizeMake(0.0, 0.0); + CGSize cropScaleAmount = CGSizeMake(vertexRect.size.width/self.layer.bounds.size.width, vertexRect.size.height/self.layer.bounds.size.height); + + if (cropScaleAmount.width > cropScaleAmount.height) { + normalizedSamplingSize.width = 1.0; + normalizedSamplingSize.height = cropScaleAmount.height/cropScaleAmount.width; + } else { + normalizedSamplingSize.width = 1.0; + normalizedSamplingSize.height = cropScaleAmount.width/cropScaleAmount.height; + } + + GLfloat quadVertexData [] = { + -1 * normalizedSamplingSize.width, -1 * normalizedSamplingSize.height, + normalizedSamplingSize.width, -1 * normalizedSamplingSize.height, + -1 * normalizedSamplingSize.width, normalizedSamplingSize.height, + normalizedSamplingSize.width, normalizedSamplingSize.height, + }; + + // 更新顶点数据 + glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData); + glEnableVertexAttribArray(ATTRIB_VERTEX); + glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, 0, 0, [self quadTextureRGBData]); + glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB); + glVertexAttribPointer(ATTRIB_TEXCOORD_ALPHA, 2, GL_FLOAT, 0, 0, [self quedTextureAlphaData]); + glEnableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle); + if ([EAGLContext currentContext] == _glContext && !self.pause && self.window && [UIApplication sharedApplication].applicationState != UIApplicationStateBackground) { + [_glContext presentRenderbuffer:GL_RENDERBUFFER]; + } +} + +- (const void *)quedTextureAlphaData { + + switch (self.blendMode) { + case QGHWDTextureBlendMode_AlphaLeft: + return textureCoordLeft; + case QGHWDTextureBlendMode_AlphaRight: + return textureCoordRight; + case QGHWDTextureBlendMode_AlphaTop: + return textureCoordTop; + case QGHWDTextureBlendMode_AlphaBottom: + return textureCoordBottom; + default: + return textureCoordLeft; + } +} + +- (const void *)quadTextureRGBData { + + switch (self.blendMode) { + case QGHWDTextureBlendMode_AlphaLeft: + return textureCoordRight; + case QGHWDTextureBlendMode_AlphaRight: + return textureCoordLeft; + case QGHWDTextureBlendMode_AlphaTop: + return textureCoordBottom; + case QGHWDTextureBlendMode_AlphaBottom: + return textureCoordTop; + default: + return textureCoordRight; + } +} + +#pragma mark - OpenGL ES 2 shader compilation + +- (BOOL)loadShaders { + + GLuint vShader, fShader; + self.program = glCreateProgram(); + // Create and compile the vertex shader. + if (![self compileShader:&vShader type:GL_VERTEX_SHADER source:kVertexShaderSource]) { + VAP_Error(kQGVAPModuleCommon, @"Failed to compile vertex shader"); + return NO; + } + // Create and compile fragment shader. + if (![self compileShader:&fShader type:GL_FRAGMENT_SHADER source:kFragmentShaderSource]) { + VAP_Error(kQGVAPModuleCommon, @"Failed to compile fragment shader"); + return NO; + } + // Attach vertex shader to program. + glAttachShader(self.program, vShader); + // Attach fragment shader to program. + glAttachShader(self.program, fShader); + // Bind attribute locations. This needs to be done prior to linking. + glBindAttribLocation(self.program, ATTRIB_VERTEX, "position"); + glBindAttribLocation(self.program, ATTRIB_TEXCOORD_RGB, "RGBTexCoord"); + glBindAttribLocation(self.program, ATTRIB_TEXCOORD_ALPHA, "alphaTexCoord"); + // Link the program. + if (![self linkProgram:self.program]) { + VAP_Event(kQGVAPModuleCommon, @"Failed to link program: %d", self.program); + if (vShader) { + glDeleteShader(vShader); + vShader = 0; + } + if (fShader) { + glDeleteShader(fShader); + fShader = 0; + } + if (self.program) { + glDeleteProgram(self.program); + self.program = 0; + } + return NO; + } + + // Get uniforms' location. + hwd_uniforms[HWD_UNIFORM_Y] = glGetUniformLocation(self.program, "SamplerY"); + hwd_uniforms[HWD_UNIFORM_UV] = glGetUniformLocation(self.program, "SamplerUV"); + hwd_uniforms[HWD_UNIFORM_COLOR_CONVERSION_MATRIX] = glGetUniformLocation(self.program, "colorConversionMatrix"); + + // Release vertex and fragment shaders. + if (vShader) { + glDetachShader(self.program, vShader); + glDeleteShader(vShader); + } + if (fShader) { + glDetachShader(self.program, fShader); + glDeleteShader(fShader); + } + return YES; +} + +- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type source:(const NSString *)sourceString { + + GLint status; + const GLchar *source; + source = (GLchar *)[sourceString UTF8String]; + *shader = glCreateShader(type); + glShaderSource(*shader, 1, &source, NULL); + glCompileShader(*shader); +#if defined(DEBUG) + GLint lengthOfLog; + glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog); + if (lengthOfLog > 0) { + GLchar *log = (GLchar *)malloc(lengthOfLog); + glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log); + VAP_Info(kQGVAPModuleCommon, @"MODULE_DECODE Shader compile log:\n%s", log) + free(log); + } +#endif + glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); + if (status == 0) { + glDeleteShader(*shader); + return NO; + } + return YES; +} + +- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL { + + VAP_Info(kQGVAPModuleCommon, @"compileShader"); + NSError *error; + NSString *sourceString = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error]; + if (sourceString == nil) { + VAP_Event(kQGVAPModuleCommon, @"Failed to load vertex shader: %@", [error localizedDescription]); + return NO; + } + + const GLchar *source; + source = (GLchar *)[sourceString UTF8String]; + *shader = glCreateShader(type); + glShaderSource(*shader, 1, &source, NULL); + glCompileShader(*shader); + +#if defined(DEBUG) + GLint lengthOfLog; + glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &lengthOfLog); + if (lengthOfLog > 0) { + GLchar *log = (GLchar *)malloc(lengthOfLog); + glGetShaderInfoLog(*shader, lengthOfLog, &lengthOfLog, log); + VAP_Info(kQGVAPModuleCommon, @"Shader compile log:\n%s", log); + free(log); + } +#endif + + GLint status; + glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); + if (status == 0) { + glDeleteShader(*shader); + return NO; + } + + return YES; +} + +- (BOOL)linkProgram:(GLuint)prog { + + GLint status; + glLinkProgram(prog); + +#if defined(DEBUG) + GLint lengthOfLog; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &lengthOfLog); + if (lengthOfLog > 0) { + GLchar *log = (GLchar *)malloc(lengthOfLog); + glGetProgramInfoLog(prog, lengthOfLog, &lengthOfLog, log); + VAP_Info(kQGVAPModuleCommon, @"Program link log:\n%s", log); + free(log); + } +#endif + + glGetProgramiv(prog, GL_LINK_STATUS, &status); + if (status == 0) { + return NO; + } + + return YES; +} + +- (BOOL)isValidateProgram:(GLuint)prog { + + GLint logLength, status; + glValidateProgram(prog); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) { + GLchar *log = (GLchar *)malloc(logLength); + glGetProgramInfoLog(prog, logLength, &logLength, log); + VAP_Info(kQGVAPModuleCommon, @"Program validate log:\n%s", log); + free(log); + } + + glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); + if (status == 0) { + VAP_Event(kQGVAPModuleCommon, @"program is not valid:%@",@(status)); + return NO; + } + VAP_Info(kQGVAPModuleCommon, @"programe is valid"); + return YES; +} + +- (void)dispose { + + glDisableVertexAttribArray(ATTRIB_VERTEX); + glDisableVertexAttribArray(ATTRIB_TEXCOORD_RGB); + glDisableVertexAttribArray(ATTRIB_TEXCOORD_ALPHA); +} + +@end diff --git a/dkhelper/dkhelperDylib/vapFiles/blackhole.jpg b/dkhelper/dkhelperDylib/vapFiles/blackhole.jpg new file mode 100644 index 0000000..187a42c Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/blackhole.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/blackholeVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/blackholeVap.mp4 new file mode 100644 index 0000000..5e1a908 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/blackholeVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/earth.jpg b/dkhelper/dkhelperDylib/vapFiles/earth.jpg new file mode 100644 index 0000000..441ccd2 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/earth.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/earthVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/earthVap.mp4 new file mode 100644 index 0000000..4eafd90 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/earthVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/jupitre.jpg b/dkhelper/dkhelperDylib/vapFiles/jupitre.jpg new file mode 100644 index 0000000..c5eb061 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/jupitre.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/jupitreVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/jupitreVap.mp4 new file mode 100644 index 0000000..8fe7bc5 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/jupitreVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/launchBgm.mp3 b/dkhelper/dkhelperDylib/vapFiles/launchBgm.mp3 new file mode 100644 index 0000000..a8cd99f Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/launchBgm.mp3 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/mars.jpg b/dkhelper/dkhelperDylib/vapFiles/mars.jpg new file mode 100644 index 0000000..c32b1ae Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/mars.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/marsVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/marsVap.mp4 new file mode 100644 index 0000000..3a2d899 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/marsVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/mercury.jpg b/dkhelper/dkhelperDylib/vapFiles/mercury.jpg new file mode 100644 index 0000000..ee258d0 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/mercury.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/mercuryVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/mercuryVap.mp4 new file mode 100644 index 0000000..7b9c6af Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/mercuryVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/moon.jpg b/dkhelper/dkhelperDylib/vapFiles/moon.jpg new file mode 100644 index 0000000..545b733 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/moon.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/moonVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/moonVap.mp4 new file mode 100644 index 0000000..3376052 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/moonVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/nepture.jpg b/dkhelper/dkhelperDylib/vapFiles/nepture.jpg new file mode 100644 index 0000000..12cacf2 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/nepture.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/neptureVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/neptureVap.mp4 new file mode 100644 index 0000000..6c2f15f Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/neptureVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/pluto.jpg b/dkhelper/dkhelperDylib/vapFiles/pluto.jpg new file mode 100644 index 0000000..4f66417 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/pluto.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/plutoVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/plutoVap.mp4 new file mode 100644 index 0000000..ff69399 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/plutoVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/sature.jpg b/dkhelper/dkhelperDylib/vapFiles/sature.jpg new file mode 100644 index 0000000..4998ffb Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/sature.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/satureVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/satureVap.mp4 new file mode 100644 index 0000000..8c6ff73 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/satureVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/sedna.jpg b/dkhelper/dkhelperDylib/vapFiles/sedna.jpg new file mode 100644 index 0000000..4f74f07 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/sedna.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/sednaVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/sednaVap.mp4 new file mode 100644 index 0000000..92b107f Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/sednaVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/sun.jpg b/dkhelper/dkhelperDylib/vapFiles/sun.jpg new file mode 100644 index 0000000..0de3100 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/sun.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/sunVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/sunVap.mp4 new file mode 100644 index 0000000..31e4621 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/sunVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/uranus.jpg b/dkhelper/dkhelperDylib/vapFiles/uranus.jpg new file mode 100644 index 0000000..294c44c Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/uranus.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/uranusVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/uranusVap.mp4 new file mode 100644 index 0000000..a493314 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/uranusVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/vapFiles/venus.jpg b/dkhelper/dkhelperDylib/vapFiles/venus.jpg new file mode 100644 index 0000000..14635d7 Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/venus.jpg differ diff --git a/dkhelper/dkhelperDylib/vapFiles/venusVap.mp4 b/dkhelper/dkhelperDylib/vapFiles/venusVap.mp4 new file mode 100644 index 0000000..ee3dadd Binary files /dev/null and b/dkhelper/dkhelperDylib/vapFiles/venusVap.mp4 differ diff --git a/dkhelper/dkhelperDylib/wechatHeaders/WechatHeaders.h b/dkhelper/dkhelperDylib/wechatHeaders/WechatHeaders.h index 334c44a..1ee1379 100644 --- a/dkhelper/dkhelperDylib/wechatHeaders/WechatHeaders.h +++ b/dkhelper/dkhelperDylib/wechatHeaders/WechatHeaders.h @@ -460,18 +460,15 @@ @end @interface MMLoadingView : UIView -- (void)ShowTipView:(id)arg1 Title:(id)arg2 Delay:(double)arg3; +@property(retain, nonatomic) NSString *text; - (void)stopLoadingAndShowOK; - (void)stopLoadingAndShowError:(id)arg1 withDelay:(double)arg2; - (void)stopLoadingAndShowError:(id)arg1; - (void)stopLoadingAndShowOK:(id)arg1 withDelay:(double)arg2; - (void)stopLoadingAndShowOK:(id)arg1; -- (void)StopLoadingTimerFired:(id)arg1; - (void)stopLoading; -- (void)startLoadingButAnimatingAfterDelay:(double)arg1; - (void)startLoading; -- (void)setLabelText:(id)arg1; -- (void)ShowTipView:(id)arg1 Title:(id)arg2 Delay:(double)arg3; + @end