├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── a.mp4 │ ├── car.mp4 │ ├── city1.mp4 │ ├── dynamic_264_mid.mp4 │ ├── effect.mp4 │ ├── normal_264_mid.mp4 │ └── test_dynamic_264_mid.mp4 │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── yy │ │ └── yyeva │ │ └── player │ │ ├── EvaDemoActivity.kt │ │ ├── EvaDownloadDemoActivity.kt │ │ ├── EvaKeyDemoActivity.kt │ │ ├── EvaKeyListDemoActivity.kt │ │ ├── EvaKeyRecyclerAdapter.kt │ │ ├── FileUtil.kt │ │ ├── MainActivity.kt │ │ ├── bean │ │ └── VideoInfo.kt │ │ └── util │ │ ├── EvaCache.kt │ │ └── EvaDownloader.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xhdpi │ ├── bg.png │ ├── chang.png │ └── img.webp │ ├── drawable │ ├── ball_1.png │ ├── ball_2.png │ ├── ball_3.png │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_anim_demo_recycle.xml │ ├── activity_anim_simple_demo_p.xml │ ├── activity_main.xml │ └── item_animview.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── values-night │ └── themes.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── resource └── out_3.gif ├── settings.gradle └── yyevac ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro ├── publish.gradle └── src └── main ├── AndroidManifest.xml ├── cpp ├── bean │ ├── data.h │ ├── datas.h │ ├── descript.h │ ├── effect.cpp │ ├── effect.h │ ├── evaanimeconfig.cpp │ ├── evaanimeconfig.h │ ├── evaframe.cpp │ ├── evaframe.h │ ├── evaframeall.cpp │ ├── evaframeall.h │ ├── evaframeset.cpp │ ├── evaframeset.h │ ├── evasrc.cpp │ ├── evasrc.h │ ├── evasrcmap.cpp │ ├── evasrcmap.h │ └── pointrect.h ├── egl │ ├── eglcore.cpp │ ├── eglcore.h │ └── glbase.h ├── engine │ ├── bgrender.cpp │ ├── bgrender.h │ ├── evaengine.cpp │ ├── evaengine.h │ ├── irender.h │ ├── mp4render.cpp │ ├── mp4render.h │ ├── render.cpp │ ├── render.h │ ├── rendercontroller.cpp │ ├── rendercontroller.h │ ├── yuvrender.cpp │ └── yuvrender.h ├── mix │ ├── evamixrender.cpp │ ├── evamixrender.h │ ├── mixshader.cpp │ └── mixshader.h ├── util │ ├── glfloatarray.cpp │ ├── glfloatarray.h │ ├── parson.c │ ├── parson.h │ ├── shaderutil.cpp │ ├── shaderutil.h │ ├── stb_image.h │ ├── stb_image_write.h │ ├── texcoordsutil.cpp │ ├── texcoordsutil.h │ ├── textureloadutil.cpp │ ├── textureloadutil.h │ ├── vertexutil.cpp │ └── vertexutil.h ├── vulkan_wrapper │ ├── vulkan_wrapper.cpp │ └── vulkan_wrapper.h └── yyevajni.cpp ├── java └── com │ └── yy │ └── yyeva │ ├── EvaAnimConfig.kt │ ├── EvaAnimConfigManager.kt │ ├── EvaAnimPlayer.kt │ ├── IEvaRenderListener.kt │ ├── decoder │ ├── EvaDecoder.kt │ └── EvaHardDecoder.kt │ ├── file │ ├── EvaAssetsEvaFileContainer.kt │ ├── EvaFileContainer.kt │ ├── EvaPref.kt │ ├── EvaStreamContainerEva.kt │ ├── EvaStreamMediaDataSource.kt │ ├── FileUtil.kt │ └── IEvaFileContainer.kt │ ├── inter │ ├── IEvaAnimListener.kt │ ├── IEvaFetchResource.kt │ └── OnEvaResourceClickListener.kt │ ├── mix │ ├── EvaFrame.kt │ ├── EvaMixAnimPlugin.kt │ ├── EvaMixTouch.kt │ ├── EvaResource.kt │ └── EvaSrc.kt │ ├── plugin │ ├── EvaAnimPluginManager.kt │ └── IEvaAnimPlugin.kt │ ├── util │ ├── ELog.kt │ ├── EvaBitmapUtil.kt │ ├── EvaConstant.kt │ ├── EvaJniUtil.kt │ ├── EvaMediaUtil.kt │ ├── EvaVideoEntity.kt │ ├── PointRect.kt │ ├── ScaleTypeUtil.kt │ └── SpeedControlUtil.kt │ └── view │ ├── EvaAnimView.kt │ ├── EvaAnimViewV3.kt │ ├── EvaAudioPlayer.kt │ ├── IEvaAnimView.kt │ ├── InnerSurfaceView.kt │ └── InnerTextureView.kt └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | /.idea 11 | .DS_Store 12 | /build 13 | /*/jniLibs 14 | /captures 15 | .externalNativeBuild 16 | .cxx 17 | local.properties 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YY-EVA Android 2 | 3 | 简体中文 | [English](./README_en.md) 4 | 5 | > 轻量级 高性能 跨平台 MP4 礼物播放器 6 | 7 | ## 支持本项目 8 | 9 | 请支持我们的项目,点击[⭐⭐⭐](https://github.com/yylive/YYEVA), 让更多的人看到该项目 10 | 11 | 12 | ## 案例演示 13 | 14 | 15 | 16 | ## 介绍 17 | 18 | + YYEVAPlayer 是一个轻量的动画渲染库。通过[这里](https://github.com/yylive/YYEVA/blob/main/YYEVA%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83.md)导出动画文件 19 | + 通过[这里](https://github.com/yylive/YYEVA/tree/main/AEP/demo_aep)可以获取设计的测试资源文件 20 | + YYEVA-Android 使用Native Opengles 渲染视频,为你提供高性能、低开销的动画体验。 21 | 22 | ## 平台支持 23 | + 支持 [Android](https://github.com/yylive/YYEVA-Android)、[IOS](https://github.com/yylive/YYEVA-iOS)、[Web](https://github.com/yylive/YYEVA-Web) 点击了解详细接入 24 | + 资源制作的AE插件使用规范 [详情](https://github.com/yylive/YYEVA/tree/main/AEP) 25 | + 数据结构定义 [详情](https://github.com/yylive/YYEVA/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.md) 26 | + 项目相关文章、设计规范等 [详情](https://github.com/yylive/YYEVA) 27 | 28 | ## 用法 29 | 30 | 我们在这里介绍 YYEVA-Android 的用法。想要知道如何导出动画,点击[这里](https://github.com/yylive/YYEVA/blob/main/YYEVA%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83.md)。 31 | 32 | ### 使用Gradle安装依赖 33 | build.gradle 34 | ```groovy 35 | allprojects { 36 | repositories { 37 | maven { url 'https://jitpack.io' } 38 | } 39 | } 40 | ``` 41 | 42 | ```groovy 43 | dependencies { 44 | implementation 'com.github.yylive.YYEVA-Android:yyeva:1.0.17' 45 | } 46 | // 2.0.0-beta版本 47 | dependencies { 48 | implementation 'com.github.yylive.YYEVA-Android:yyeva:2.0.0-beta' 49 | } 50 | ``` 51 | 52 | ### 放置混合 mp4 文件 在Assets中 53 | 如需要使用SurfaceView可以使用EvaAnimView,需要使用TextureView可以使用EvaAnimViewV3,demo使用EvaAnimViewV3 54 | 55 | 替换元素接口 56 | ```kotlin 57 | interface IEvaFetchResource { 58 | // 获取图片 (暂时不支持Bitmap.Config.ALPHA_8 主要是因为一些机型opengl兼容问题) 59 | fun setImage(resource: EvaResource, result:(Bitmap?) -> Unit) 60 | 61 | // 获取文字 62 | fun setText(resource: EvaResource, result:(String?) -> Unit) 63 | 64 | // 资源释放通知 65 | fun releaseSrc(resources: List) 66 | 67 | //speed为倍速 68 | fun setVideoFps(fps: Int, speed: Float = 1.0f) 69 | //设置资源颜色区域和透明区域的对齐方式 70 | /* 71 | const val VIDEO_MODE_SPLIT_HORIZONTAL = 1 // 视频左右对齐(alpha左\rgb右) 72 | const val VIDEO_MODE_SPLIT_VERTICAL = 2 // 视频上下对齐(alpha上\rgb下) 73 | const val VIDEO_MODE_SPLIT_HORIZONTAL_REVERSE = 3 // 视频左右对齐(rgb左\alpha右) 74 | const val VIDEO_MODE_SPLIT_VERTICAL_REVERSE = 4 // 视频上下对齐(rgb上\alpha下) 75 | const val YYEVAColorRegion_AlphaMP4_alphaHalfRightTop = 5 // 视频上下对齐(rgb上\alpha下)1/4 elpha 76 | */ 77 | fun setVideoMode(mode: Int) 78 | //设置起始播放位置 毫秒 79 | //硬解某些机型会有跳帧前几帧解析异常的问题,不建议使用。 80 | fun setStartPoint(startPoint: Long) 81 | //设置静音 82 | fun setMute(isMute: Boolean) 83 | //是否不透明度mp4 84 | fun setNormalMp4(isNormalMp4: Boolean) 85 | //是否停留在最后一帧 86 | fun setLastFrame(isSetLastFrame: Boolean) 87 | //设置日志打印 88 | fun setLog(log: IELog) 89 | } 90 | ``` 91 | 具体实现可以参照EvaDemoActivity的代码实验,替换自身mp4中的元素。 92 | 93 | 播放使用IEvaAnimView接口 94 | ```kotlin 95 | interface IEvaAnimView { 96 | ... 97 | //播放文件 98 | fun startPlay(file: File) 99 | //播放本地文件 100 | fun startPlay(assetManager: AssetManager, assetsPath: String) 101 | //停止播放 102 | fun stopPlay() 103 | //是否正在运行 104 | fun isRunning(): Boolean 105 | //循环播放 106 | fun setLoop(playLoop: Int) 107 | //设置背景图 108 | fun setBgImage(bg: Bitmap) 109 | ... 110 | } 111 | ``` 112 | 113 | 114 | 2.0.0-beta播放使用OptionParams类配置 115 | ```kotlin 116 | class OptionParams { 117 | var frameRate = 30 118 | var playCount = 1 119 | var isMute = false 120 | var isRemoteService = true //使用多进程 121 | var mp4Address = "" 122 | var scaleType = 1 // 1=>裁剪居中, 2=>全屏拉伸 3=>原资源大小 123 | var filterType = "" //高清算法 hermite lagrange 124 | } 125 | ``` 126 | 127 | ### 代码 128 | 129 | ## QQ交流群 130 |  131 | 132 | ## 鸣谢 133 | + 感谢 [vap](https://github.com/Tencent/vap) 优秀的混合渲染方案、项目Render混合部分重用了 vap的方案 134 | 135 | ## Dev Team 136 | 137 | 138 | 139 | 140 | 141 | 142 | Cangwang 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | # YY-EVA Android 2 | > Lightweight,High Performance,Cross Platform,MP4 Gift Player 3 | 4 | ## Product show 5 | 6 | 7 | 8 | ## Intruduction 9 | + YYEVAPlayer is a lightweight animation library with a simple yet powerful API。Reference [here](https://github.com/yylive/YYEVA/blob/main/YYEVA%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83.md) can easily export animation resources 10 | + YYEVA-iOS render with Metal library , providing you with a high-performance, low-cost animation experience. 11 | 12 | ## Platform support 13 | + Platform:[Android](https://github.com/yylive/YYEVA-Android), [iOS](https://github.com/yylive/YYEVA-iOS), [Web](https://github.com/yylive/YYEVA-Web) 14 | + Generation Tool : [AE plguin](https://github.com/yylive/YYEVA/tree/main/AEP) 15 | + [Data structure](https://github.com/yylive/YYEVA/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.md) 16 | + [Docs](https://github.com/yylive/YYEVA) 17 | 18 | ## Usage 19 | 20 | 我们在这里介绍 YYEVA-Android 的用法。想要知道如何导出动画,点击[这里](https://github.com/yylive/YYEVA/blob/main/YYEVA%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83.md)。 21 | 22 | ### Installation with Gradle 23 | build.gradle 24 | ```groovy 25 | allprojects { 26 | repositories { 27 | maven { url 'https://jitpack.io' } 28 | } 29 | } 30 | ``` 31 | 32 | ```groovy 33 | dependencies { 34 | implementation 'com.github.yylive.YYEVA-Android:yyeva:1.0.17' 35 | } 36 | // 2.0.0-beta 37 | dependencies { 38 | implementation 'com.github.yylive.YYEVA-Android:yyeva:2.0.0-beta' 39 | } 40 | ``` 41 | 42 | ### Animation with Key 43 | For SurfaceView can use EvaAnimView,For TextureView can useEvaAnimViewV3,demo show use EvaAnimViewV3 44 | 45 | change property interface 46 | ```kotlin 47 | interface IEvaFetchResource { 48 | // 获取图片 (暂时不支持Bitmap.Config.ALPHA_8 主要是因为一些机型opengl兼容问题) 49 | fun setImage(resource: EvaResource, result:(Bitmap?) -> Unit) 50 | 51 | // 获取文字 52 | fun setText(resource: EvaResource, result:(String?) -> Unit) 53 | 54 | // 资源释放通知 55 | fun releaseSrc(resources: List) 56 | } 57 | ``` 58 | You can find the example int the project in app module. 59 | 60 | Play with IEvaAnimView interface. 61 | ```kotlin 62 | interface IEvaAnimView { 63 | ... 64 | //播放文件 65 | fun startPlay(file: File) 66 | //播放本地文件 67 | fun startPlay(assetManager: AssetManager, assetsPath: String) 68 | //停止播放 69 | fun stopPlay() 70 | //是否正在运行 71 | fun isRunning(): Boolean 72 | //循环播放 73 | fun setLoop(playLoop: Int) 74 | //设置背景图 75 | fun setBgImage(bg: Bitmap) 76 | //speed为倍速 77 | fun setVideoFps(fps: Int, speed: Float = 1.0f) 78 | //设置资源颜色区域和透明区域的对齐方式 79 | /* 80 | const val VIDEO_MODE_SPLIT_HORIZONTAL = 1 // 视频左右对齐(alpha左\rgb右) 81 | const val VIDEO_MODE_SPLIT_VERTICAL = 2 // 视频上下对齐(alpha上\rgb下) 82 | const val VIDEO_MODE_SPLIT_HORIZONTAL_REVERSE = 3 // 视频左右对齐(rgb左\alpha右) 83 | const val VIDEO_MODE_SPLIT_VERTICAL_REVERSE = 4 // 视频上下对齐(rgb上\alpha下) 84 | const val YYEVAColorRegion_AlphaMP4_alphaHalfRightTop = 5 // 视频上下对齐(rgb上\alpha下)1/4 elpha 85 | */ 86 | fun setVideoMode(mode: Int) 87 | //设置起始播放位置 毫秒 88 | //硬解某些机型会有跳帧前几帧解析异常的问题,不建议使用。 89 | fun setStartPoint(startPoint: Long) 90 | //设置静音 91 | fun setMute(isMute: Boolean) 92 | //是否不透明度mp4 93 | fun setNormalMp4(isNormalMp4: Boolean) 94 | //是否停留在最后一帧 95 | fun setLastFrame(isSetLastFrame: Boolean) 96 | //设置日志打印 97 | fun setLog(log: IELog) 98 | ... 99 | } 100 | ``` 101 | 2.0.0-beta use OptionParams to set 102 | ```kotlin 103 | class OptionParams { 104 | var frameRate = 30 105 | var playCount = 1 106 | var isMute = false 107 | var isRemoteService = true //使用多进程 108 | var mp4Address = "" 109 | var scaleType = 1 // 1=>裁剪居中, 2=>全屏拉伸 3=>原资源大小 110 | var filterType = "" //高清算法 hermite lagrange 111 | } 112 | ``` 113 | 114 | ## QQexchange group 115 |  116 | 117 | ## 鸣谢 118 | + Thanks [vap](https://github.com/Tencent/vap) , our decoder module with good experiences of it. 119 | 120 | ## Dev Team 121 | 122 | 123 | 124 | 125 | 126 | 127 | Cangwang 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-android-extensions' 5 | } 6 | 7 | android { 8 | compileSdk 31 9 | 10 | defaultConfig { 11 | applicationId "com.yy.yyeva" 12 | minSdk 21 13 | targetSdk 31 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | externalNativeBuild { 19 | cmake { 20 | cppFlags "-std=c++11 -fexceptions" 21 | // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 22 | abiFilters = ["armeabi-v7a","arm64-v8a", 'x86', 'x86_64'] 23 | } 24 | } 25 | ndk { 26 | // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 27 | setAbiFilters(['armeabi-v7a', 'arm64-v8a','x86', 'x86_64']) 28 | // abiFilters.clear() 29 | } 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | kotlinOptions { 43 | jvmTarget = '1.8' 44 | } 45 | lintOptions { 46 | abortOnError false 47 | } 48 | ndkVersion '21.0.6113669' 49 | 50 | // splits { 51 | // 52 | // // Configures multiple APKs based on ABI. 53 | // abi { 54 | // 55 | // // Enables building multiple APKs per ABI. 56 | // enable true 57 | // 58 | // // By default all ABIs are included, so use reset() and include to specify that we only 59 | // // want APKs for x86 and x86_64. 60 | // 61 | // // Resets the list of ABIs that Gradle should create APKs for to none. 62 | // reset() 63 | // 64 | // // Specifies a list of ABIs that Gradle should create APKs for. 65 | // include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 66 | // 67 | // // Specifies that we do not want to also generate a universal APK that includes all ABIs. 68 | // universalApk false 69 | // } 70 | // } 71 | } 72 | 73 | dependencies { 74 | implementation fileTree(dir: 'libs', include: ['*.jar']) 75 | api project(':yyevac') 76 | // implementation 'com.github.yylive.YYEVA-Android:yyeva:1.1.11' 77 | implementation 'androidx.appcompat:appcompat:1.4.1' 78 | api 'com.google.android.material:material:1.5.0' 79 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 33 | 34 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/assets/a.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/a.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/car.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/car.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/city1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/city1.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/dynamic_264_mid.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/dynamic_264_mid.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/effect.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/effect.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/normal_264_mid.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/normal_264_mid.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/test_dynamic_264_mid.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/assets/test_dynamic_264_mid.mp4 -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/yy/yyeva/player/EvaKeyListDemoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.player 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.net.LinkAddress 6 | import android.os.Bundle 7 | import android.os.Environment 8 | import android.os.Handler 9 | import android.os.Looper 10 | import com.yy.yyeva.EvaAnimConfig 11 | import com.yy.yyeva.inter.IEvaAnimListener 12 | import com.yy.yyeva.util.ELog 13 | import com.yy.yyeva.util.IELog 14 | import android.util.Log 15 | import android.view.View 16 | import androidx.recyclerview.widget.LinearLayoutManager 17 | import androidx.recyclerview.widget.RecyclerView 18 | import com.yy.yyeva.player.bean.VideoInfo 19 | import kotlinx.android.synthetic.main.activity_anim_demo_recycle.* 20 | 21 | /** 22 | * 23 | * 必须使用YYEVA插件生成对应的mp4才能播放 24 | */ 25 | class EvaKeyListDemoActivity : Activity(), IEvaAnimListener { 26 | 27 | companion object { 28 | private const val TAG = "EvaDemoActivity" 29 | } 30 | 31 | private val dir by lazy { 32 | // 存放在sdcard应用缓存文件中 33 | getExternalFilesDir(null)?.absolutePath ?: Environment.getExternalStorageDirectory().path 34 | } 35 | 36 | // 视频信息 37 | private val videoInfo = VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be") 38 | 39 | private var adapter: EvaKeyRecyclerAdapter? = null 40 | private lateinit var layoutManager: LinearLayoutManager 41 | private var findLastVisibleItemPosition = 0 42 | private var findFirstVisibleItemPosition = 0 43 | 44 | private val uiHandler by lazy { 45 | Handler(Looper.getMainLooper()) 46 | } 47 | 48 | override fun onCreate(savedInstanceState: Bundle?) { 49 | super.onCreate(savedInstanceState) 50 | setContentView(R.layout.activity_anim_demo_recycle) 51 | layoutManager = LinearLayoutManager(this) 52 | eva_recycler.layoutManager = layoutManager 53 | eva_recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { 54 | var dy = 0 55 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 56 | super.onScrolled(recyclerView, dx, dy) 57 | this.dy = dy 58 | } 59 | 60 | override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { 61 | super.onScrollStateChanged(recyclerView, newState) 62 | if (newState == RecyclerView.SCROLL_STATE_IDLE) { 63 | findLastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() 64 | findFirstVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition() 65 | for (i in findFirstVisibleItemPosition..findLastVisibleItemPosition) { 66 | val viewHolder = eva_recycler.findViewHolderForAdapterPosition(i) as? EvaKeyRecyclerAdapter.EvaKeyHolder 67 | viewHolder?.play() 68 | } 69 | } 70 | } 71 | }) 72 | adapter = EvaKeyRecyclerAdapter() 73 | eva_recycler.adapter = adapter 74 | loadFile() 75 | } 76 | 77 | private fun init() { 78 | // 初始化日志 79 | initLog() 80 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 81 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 82 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 83 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 84 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 85 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 86 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 87 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 88 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 89 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 90 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 91 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 92 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 93 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 94 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 95 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 96 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 97 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 98 | adapter?.addBean(VideoInfo("effect.mp4", "400a778f258ed6bd02ec32defe8ca8be")) 99 | adapter?.notifyDataSetChanged() 100 | eva_recycler.post { 101 | val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() 102 | val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() 103 | ELog.i(TAG, "firstVisibleItemPosition $firstVisibleItemPosition, lastVisibleItemPosition $lastVisibleItemPosition") 104 | for (i in firstVisibleItemPosition..lastVisibleItemPosition) { 105 | val viewHolder = eva_recycler.findViewHolderForAdapterPosition(i) as? EvaKeyRecyclerAdapter.EvaKeyHolder 106 | viewHolder?.play() 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * 视频信息准备好后的回调,用于检查视频准备好后是否继续播放 113 | * @return true 继续播放 false 停止播放 114 | */ 115 | override fun onVideoConfigReady(config: EvaAnimConfig): Boolean { 116 | return true 117 | } 118 | 119 | /** 120 | * 视频开始回调 121 | */ 122 | override fun onVideoStart() { 123 | ELog.i(TAG, "onVideoStart") 124 | } 125 | 126 | override fun onVideoRestart() { 127 | ELog.i(TAG, "onVideoReStart") 128 | } 129 | 130 | /** 131 | * 视频渲染每一帧时的回调 132 | * @param frameIndex 帧索引 133 | */ 134 | override fun onVideoRender(frameIndex: Int, config: EvaAnimConfig?) { 135 | } 136 | 137 | /** 138 | * 视频播放结束(失败也会回调) 139 | */ 140 | override fun onVideoComplete(lastFrame: Boolean) { 141 | ELog.i(TAG, "onVideoComplete") 142 | } 143 | 144 | /** 145 | * 播放器被销毁情况下会调用 146 | */ 147 | override fun onVideoDestroy() { 148 | ELog.i(TAG, "onVideoDestroy") 149 | } 150 | 151 | /** 152 | * 失败回调 153 | * 一次播放时可能会调用多次 154 | * @param errorType 错误类型 155 | * @param errorMsg 错误消息 156 | */ 157 | override fun onFailed(errorType: Int, errorMsg: String?) { 158 | ELog.i(TAG, "onFailed errorType=$errorType errorMsg=$errorMsg") 159 | } 160 | 161 | private fun initLog() { 162 | ELog.isDebug = false 163 | ELog.log = object : IELog { 164 | override fun i(tag: String, msg: String) { 165 | Log.i(tag, msg) 166 | } 167 | 168 | override fun d(tag: String, msg: String) { 169 | Log.d(tag, msg) 170 | } 171 | 172 | override fun e(tag: String, msg: String) { 173 | Log.e(tag, msg) 174 | } 175 | 176 | override fun e(tag: String, msg: String, tr: Throwable) { 177 | Log.e(tag, msg, tr) 178 | } 179 | } 180 | } 181 | 182 | private fun loadFile() { 183 | val files = Array(1) { 184 | videoInfo.fileName 185 | } 186 | FileUtil.copyAssetsToStorage(this, dir, files) { 187 | uiHandler.post { 188 | init() 189 | } 190 | } 191 | } 192 | } 193 | 194 | -------------------------------------------------------------------------------- /app/src/main/java/com/yy/yyeva/player/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.player 2 | 3 | import android.content.Context 4 | import com.yy.yyeva.util.ELog 5 | import java.io.* 6 | import java.security.MessageDigest 7 | 8 | object FileUtil { 9 | 10 | private val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') 11 | private val TAG = "FileUtil" 12 | 13 | fun copyAssetsToStorage(context: Context, dir: String, files: Array, loadSuccess:()->Unit) { 14 | Thread { 15 | var outputStream: OutputStream 16 | var inputStream: InputStream 17 | val buf = ByteArray(4096) 18 | val names = context.assets.list("") 19 | if (names == null) { 20 | ELog.e(TAG,"assets has no file") 21 | return@Thread 22 | } 23 | files.forEach { 24 | try { 25 | if (File("$dir/$it").exists()) { 26 | return@forEach 27 | } 28 | if (!names.contains(it)) { 29 | ELog.e(TAG,"assets has not $it") 30 | return@Thread 31 | } 32 | 33 | inputStream = context.assets.open(it) 34 | outputStream = FileOutputStream("$dir/$it") 35 | var length = inputStream.read(buf) 36 | while (length > 0) { 37 | outputStream.write(buf, 0, length) 38 | length = inputStream.read(buf) 39 | } 40 | outputStream.close() 41 | inputStream.close() 42 | } catch (e: IOException) { 43 | e.printStackTrace() 44 | return@Thread 45 | } 46 | } 47 | loadSuccess.invoke() 48 | }.start() 49 | 50 | } 51 | 52 | fun getFileMD5(file: File): String? { 53 | if (!file.exists() || !file.isFile || file.length() <= 0) { 54 | return null 55 | } 56 | var inputStream: InputStream? = null 57 | try { 58 | val md = MessageDigest.getInstance("MD5") 59 | inputStream = FileInputStream(file) 60 | val dataBytes = ByteArray(4096) 61 | var iRd: Int 62 | iRd = inputStream.read(dataBytes) 63 | while (iRd != -1) { 64 | md.update(dataBytes, 0, iRd) 65 | iRd = inputStream.read(dataBytes) 66 | } 67 | inputStream.close() 68 | val digest = md.digest() 69 | if (digest != null) { 70 | return bufferToHex(digest) 71 | } 72 | } catch (t: Throwable) { 73 | t.printStackTrace() 74 | } finally { 75 | if (inputStream != null) { 76 | try { 77 | inputStream.close() 78 | } catch (e: Throwable) { 79 | e.printStackTrace() 80 | } 81 | 82 | } 83 | } 84 | return null 85 | } 86 | 87 | private fun bufferToHex(bytes: ByteArray): String { 88 | return bufferToHex(bytes, 0, bytes.size) 89 | } 90 | 91 | private fun bufferToHex(bytes: ByteArray, m: Int, n: Int): String { 92 | val sb = StringBuffer(2 * n) 93 | val k = m + n 94 | for (l in m until k) { 95 | appendHexPair(bytes[l], sb) 96 | } 97 | return sb.toString() 98 | } 99 | 100 | private fun appendHexPair(bt: Byte, sb: StringBuffer) { 101 | val c0 = hexDigits[bt.toInt() and 0xf0 ushr 4] 102 | val c1 = hexDigits[bt.toInt() and 0x0f] 103 | sb.append(c0) 104 | sb.append(c1) 105 | } 106 | } -------------------------------------------------------------------------------- /app/src/main/java/com/yy/yyeva/player/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.player 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import kotlinx.android.synthetic.main.activity_main.* 7 | 8 | class MainActivity : AppCompatActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.activity_main) 12 | btn1.setOnClickListener { 13 | startActivity(Intent(this, EvaDemoActivity::class.java)) 14 | } 15 | 16 | btn2.setOnClickListener { 17 | startActivity(Intent(this, EvaKeyDemoActivity::class.java)) 18 | } 19 | 20 | btn3.setOnClickListener { 21 | startActivity(Intent(this, EvaDownloadDemoActivity::class.java)) 22 | } 23 | btn4.setOnClickListener { 24 | startActivity(Intent(this, EvaKeyListDemoActivity::class.java)) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/yy/yyeva/player/bean/VideoInfo.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.player.bean 2 | 3 | data class VideoInfo(val fileName: String, val md5: String) -------------------------------------------------------------------------------- /app/src/main/java/com/yy/yyeva/player/util/EvaCache.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.player.util 2 | 3 | import android.content.Context 4 | import android.os.Environment 5 | import com.yy.yyeva.util.ELog 6 | import java.io.File 7 | import java.net.URL 8 | import java.security.MessageDigest 9 | 10 | /** 11 | * EVA 缓存管理 12 | */ 13 | object EvaCache { 14 | enum class Type { 15 | DEFAULT, 16 | FILE 17 | } 18 | 19 | private const val TAG = "EvaCache" 20 | private var type: Type = Type.DEFAULT 21 | 22 | private var cacheDir: String = "/" 23 | get() { 24 | if (field != "/") { 25 | val dir = File(field) 26 | if (!dir.exists()) { 27 | dir.mkdirs() 28 | } 29 | } 30 | return field 31 | } 32 | 33 | 34 | fun onCreate(context: Context?) { 35 | onCreate(context, Type.DEFAULT) 36 | } 37 | 38 | fun onCreate(context: Context?, type: Type) { 39 | if (isInitialized()) return 40 | context ?: return 41 | // cacheDir = "${context.cacheDir.absolutePath}/eva/" 42 | cacheDir = context.getExternalFilesDir(null)?.absolutePath ?: Environment.getExternalStorageDirectory().path 43 | cacheDir = "$cacheDir/" 44 | File(cacheDir).takeIf { !it.exists() }?.mkdirs() 45 | EvaCache.type = type 46 | } 47 | 48 | /** 49 | * 清理缓存 50 | */ 51 | fun clearCache() { 52 | if (!isInitialized()) { 53 | ELog.e(TAG, "EVACache is not init!") 54 | return 55 | } 56 | EvaDownloader.threadPoolExecutor.execute { 57 | clearDir(cacheDir) 58 | ELog.i(TAG, "Clear eva cache done!") 59 | } 60 | } 61 | 62 | // 清除目录下的所有文件 63 | internal fun clearDir(path: String) { 64 | try { 65 | val dir = File(path) 66 | dir.takeIf { it.exists() }?.let { parentDir -> 67 | parentDir.listFiles()?.forEach { file -> 68 | if (!file.exists()) { 69 | return@forEach 70 | } 71 | if (file.isDirectory) { 72 | clearDir(file.absolutePath) 73 | } 74 | file.delete() 75 | } 76 | } 77 | } catch (e: Exception) { 78 | ELog.e(TAG, "Clear eva cache path: $path fail", e) 79 | } 80 | } 81 | 82 | fun isInitialized(): Boolean { 83 | return "/" != cacheDir && File(cacheDir).exists() 84 | } 85 | 86 | fun isDefaultCache(): Boolean = type == Type.DEFAULT 87 | 88 | fun isCached(cacheKey: String): Boolean { 89 | return if (isDefaultCache()) { 90 | buildCacheDir(cacheKey) 91 | } else { 92 | buildMp4File( 93 | cacheKey 94 | ) 95 | }.exists() 96 | } 97 | 98 | fun buildCacheKey(str: String): String { 99 | val messageDigest = MessageDigest.getInstance("MD5") 100 | messageDigest.update(str.toByteArray(charset("UTF-8"))) 101 | val digest = messageDigest.digest() 102 | var sb = "" 103 | for (b in digest) { 104 | sb += String.format("%02x", b) 105 | } 106 | return sb 107 | } 108 | 109 | fun buildCacheKey(url: URL): String = buildCacheKey(url.toString()) 110 | 111 | fun buildCacheDir(cacheKey: String): File { 112 | return File("$cacheDir$cacheKey/") 113 | } 114 | 115 | fun buildMp4File(cacheKey: String): File { 116 | return File("$cacheDir$cacheKey.mp4") 117 | } 118 | 119 | fun buildAudioFile(audio: String): File { 120 | return File("$cacheDir$audio.mp3") 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable-xhdpi/bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/chang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable-xhdpi/chang.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/img.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable-xhdpi/img.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ball_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable/ball_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ball_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable/ball_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ball_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/drawable/ball_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_anim_demo_recycle.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_anim_simple_demo_p.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 15 | 16 | 23 | 28 | 33 | 34 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_animview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 21 | 26 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | yyeva 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | 4 | ext.kotlin_version = '1.5.20' 5 | repositories { 6 | google() 7 | mavenCentral() 8 | jcenter() // Warning: this repository is going to shut down soon 9 | } 10 | dependencies { 11 | classpath "com.android.tools.build:gradle:7.0.3" 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | maven { url 'https://jitpack.io' } 22 | google() 23 | mavenCentral() 24 | jcenter() // Warning: this repository is going to shut down soon 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 01 15:15:40 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk11 -------------------------------------------------------------------------------- /resource/out_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yylive/YYEVA-Android/1ae13e67953fa597200d662fd476db4f9ed1bcf0/resource/out_3.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "yyeva" 2 | include ':app' 3 | include ':yyevac' 4 | -------------------------------------------------------------------------------- /yyevac/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.cxx 3 | 4 | -------------------------------------------------------------------------------- /yyevac/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/jniLibs/${ANDROID_ABI}) 13 | include_directories(${CMAKE_SOURCE_DIR} src/main/cpp) 14 | add_library( # Sets the name of the library. 15 | yyeva 16 | 17 | # Sets the library as a shared library. 18 | SHARED 19 | 20 | # Provides a relative path to your source file(s). 21 | src/main/cpp/yyevajni.cpp 22 | src/main/cpp/bean/effect.cpp 23 | src/main/cpp/bean/evaanimeconfig.cpp 24 | src/main/cpp/bean/evasrc.cpp 25 | src/main/cpp/bean/evasrcmap.cpp 26 | src/main/cpp/bean/evaframe.cpp 27 | src/main/cpp/bean/evaframeall.cpp 28 | src/main/cpp/bean/evaframeset.cpp 29 | src/main/cpp/egl/eglcore.cpp 30 | src/main/cpp/engine/yuvrender.cpp 31 | src/main/cpp/engine/render.cpp 32 | src/main/cpp/engine/mp4render.cpp 33 | src/main/cpp/engine/bgrender.cpp 34 | src/main/cpp/engine/rendercontroller.cpp 35 | src/main/cpp/engine/evaengine.cpp 36 | src/main/cpp/util/shaderutil.cpp 37 | src/main/cpp/util/glfloatarray.cpp 38 | src/main/cpp/util/vertexutil.cpp 39 | src/main/cpp/util/texcoordsutil.cpp 40 | src/main/cpp/util/textureloadutil.cpp 41 | src/main/cpp/mix/evamixrender.cpp 42 | src/main/cpp/mix/mixshader.cpp 43 | src/main/cpp/vulkan_wrapper/vulkan_wrapper.cpp 44 | ) 45 | 46 | add_library(parson 47 | STATIC 48 | src/main/cpp/util/parson.c) 49 | 50 | # Searches for a specified prebuilt library and stores the path as a 51 | # variable. Because CMake includes system libraries in the search path by 52 | # default, you only need to specify the name of the public NDK library 53 | # you want to add. CMake verifies that the library exists before 54 | # completing its build. 55 | 56 | find_library( # Sets the name of the path variable. 57 | log-lib 58 | 59 | # Specifies the name of the NDK library that 60 | # you want CMake to locate. 61 | log ) 62 | 63 | # Specifies libraries CMake should link to your target library. You 64 | # can link multiple libraries, such as libraries you define in this 65 | # build script, prebuilt third-party libraries, or system libraries. 66 | 67 | 68 | target_link_libraries( # Specifies the target library. 69 | yyeva 70 | android 71 | # GLESv3 72 | GLESv2 73 | EGL 74 | jnigraphics 75 | parson 76 | 77 | # Links the target library to the log library 78 | # included in the NDK. 79 | ${log-lib} ) -------------------------------------------------------------------------------- /yyevac/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'maven-publish' 5 | 6 | android { 7 | compileSdkVersion 31 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 31 12 | versionCode 1 13 | versionName "1.0" 14 | externalNativeBuild { 15 | cmake { 16 | cppFlags "-std=c++11 -fexceptions" 17 | // abiFilters 'armeabi-v7a', 'arm64-v8a','x86', 'x86_64' 18 | abiFilters = ["armeabi-v7a","arm64-v8a", 'x86', 'x86_64'] 19 | } 20 | } 21 | ndk { 22 | // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 23 | setAbiFilters(['armeabi-v7a', 'arm64-v8a','x86', 'x86_64']) 24 | // abiFilters.clear() 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | externalNativeBuild { 35 | cmake { 36 | path "CMakeLists.txt" 37 | } 38 | } 39 | ndkVersion '22.1.7171670' 40 | 41 | lintOptions { 42 | abortOnError false 43 | } 44 | } 45 | 46 | dependencies { 47 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") { 48 | exclude module: 'annotations' 49 | } 50 | } 51 | 52 | afterEvaluate { 53 | publishing { 54 | publications { 55 | // Creates a Maven publication called "release". 56 | release(MavenPublication) { 57 | // Applies the component for the release build variant. 58 | from components.release 59 | 60 | // You can then customize attributes of the publication as shown below. 61 | groupId = 'com.yy.yyeva' 62 | artifactId = 'yyeva' 63 | version = '1.0.0' 64 | } 65 | // Creates a Maven publication called “debug”. 66 | debug(MavenPublication) { 67 | // Applies the component for the debug build variant. 68 | from components.debug 69 | 70 | groupId = 'com.yy.yyeva' 71 | artifactId = 'yyeva-debug' 72 | version = '1.0.0' 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /yyevac/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /yyevac/publish.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | // 此处填写刚才建立的maven仓库的仓库名称 3 | bintrayRepo = 'maven' 4 | // library的group id 5 | publishedGroupId = 'com.yy.yyeva' 6 | // library网站地址 7 | siteUrl = 'https://github.com/YY-LIVE/YYEVA-Android' 8 | // library仓库地址 9 | gitUrl = 'https://github.com/YY-LIVE/YYEVA-Android' 10 | 11 | // 注册时候的bintray username 12 | developerId = 'hexleo' 13 | // 开发者名称 14 | developerName = 'hexleo' 15 | // 开发者邮箱 16 | developerEmail = 'wanghailiang333@gmail.com' 17 | 18 | // 开源许可证 19 | licenseName = 'MIT' 20 | licenseUrl = 'http://opensource.org/licenses/MIT' 21 | allLicenses = ["MIT"] 22 | 23 | // library artifact(单个module一般就填写library name) 24 | artifact = 'yyeva' 25 | libraryName = 'yyeva' 26 | libraryVersion = '1.0.0' 27 | libraryDescription = '' 28 | // bintrayName 是你在网页Repository页面能看到的名称 29 | bintrayName = 'yyeva' 30 | } 31 | 32 | apply from: '../installv1.gradle' 33 | apply from: '../bintrayv1.gradle' 34 | -------------------------------------------------------------------------------- /yyevac/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/data.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "pointrect.h" 5 | #ifndef YYEVA_DATA_H 6 | #define YYEVA_DATA_H 7 | 8 | 9 | class Data { 10 | public: 11 | Data(){}; 12 | ~Data(){ 13 | renderFrame = nullptr; 14 | outputFrame = nullptr; 15 | }; 16 | PointRect* renderFrame; 17 | int effectId; 18 | PointRect* outputFrame; 19 | }; 20 | 21 | 22 | #endif //YYEVA_DATA_H 23 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/datas.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "list" 5 | #include "data.h" 6 | 7 | #ifndef YYEVA_DATAS_H 8 | #define YYEVA_DATAS_H 9 | 10 | class Datas { 11 | public: 12 | int frameIndex; 13 | std::list data; 14 | Datas() { 15 | 16 | } 17 | ~Datas() { 18 | data.clear(); 19 | } 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/descript.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | 5 | #include "pointrect.h" 6 | 7 | #ifndef YYEVA_DESCRIPT_H 8 | #define YYEVA_DESCRIPT_H 9 | 10 | class Descript { 11 | public: 12 | Descript(){}; 13 | ~Descript(){ 14 | rgbFrame = nullptr; 15 | alphaFrame = nullptr; 16 | }; 17 | int width; 18 | int height; 19 | bool isEffect; 20 | int version; 21 | PointRect* rgbFrame; 22 | PointRect* alphaFrame; 23 | }; 24 | 25 | 26 | #endif //YYEVA_DESCRIPT_H 27 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/effect.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "effect.h" 5 | 6 | Effect::Effect(){ 7 | effectTag = ""; 8 | effectType = ""; 9 | fontColor = ""; 10 | } 11 | Effect::~Effect(){ 12 | effectTag.clear(); 13 | effectType.clear(); 14 | fontColor.clear(); 15 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/effect.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "string" 5 | 6 | #ifndef YYEVA_EFFECT_H 7 | #define YYEVA_EFFECT_H 8 | 9 | class Effect { 10 | public: 11 | int effectWidth; 12 | int effectHeight; 13 | int effectId; 14 | std::string effectTag; 15 | std::string effectType; 16 | std::string fontColor; 17 | std::string textAlign; 18 | std::string scaleMode; //scaleFill ,aspectFit, aspectFill 19 | int fontSize; 20 | 21 | Effect(); 22 | ~Effect(); 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaanimeconfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "datas.h" 5 | #include "descript.h" 6 | #include "effect.h" 7 | #include 8 | #include 9 | 10 | #ifndef YYEVA_EVAANIMECONFIG_H 11 | #define YYEVA_EVAANIMECONFIG_H 12 | 13 | using namespace std; 14 | class EvaAnimeConfig { 15 | public: 16 | EvaAnimeConfig() {}; 17 | ~EvaAnimeConfig() { 18 | descript = nullptr; 19 | effects.clear(); 20 | datas.clear(); 21 | }; 22 | int width = 0; 23 | int height = 0; 24 | int videoWidth = 0; 25 | int videoHeight = 0; 26 | Descript *descript; 27 | list effects; 28 | list datas; 29 | 30 | PointRect *alphaPointRect; 31 | PointRect *rgbPointRect; 32 | bool isMix = false; 33 | 34 | static EvaAnimeConfig* parse(const char* json); 35 | static EvaAnimeConfig* defaultConfig(int _videoWidth, int _videoHeight, int defaultVideoMode); 36 | }; 37 | 38 | 39 | 40 | 41 | 42 | #endif //YYEVA_EVAANIMECONFIG_H 43 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframe.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include "evaframe.h" 5 | 6 | EvaFrame::EvaFrame(){ 7 | 8 | } 9 | 10 | EvaFrame::EvaFrame(int index, Data d) { 11 | srcId = to_string(d.effectId); 12 | z = index; 13 | frame = d.renderFrame; 14 | mFrame = d.outputFrame; 15 | } 16 | 17 | EvaFrame::~EvaFrame(){ 18 | srcId.clear(); 19 | frame = nullptr; 20 | mFrame = nullptr; 21 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframe.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include "string" 5 | #include "pointrect.h" 6 | #include "data.h" 7 | 8 | 9 | #ifndef YYEVA_EVAFRAME_H 10 | #define YYEVA_EVAFRAME_H 11 | 12 | using namespace std; 13 | class EvaFrame { 14 | public: 15 | string srcId = ""; 16 | int z = 0; 17 | PointRect* frame; //渲染frame 18 | PointRect* mFrame; //输出帧 19 | int mt = 0; // 遮罩旋转角度v2 版本只支持 0 与 90度 20 | 21 | EvaFrame(); 22 | 23 | EvaFrame(int index, Data d); 24 | 25 | ~EvaFrame(); 26 | }; 27 | 28 | 29 | #endif //YYEVA_EVAFRAME_H 30 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframeall.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "evaframeall.h" 3 | 4 | EvaFrameAll::EvaFrameAll(){ 5 | 6 | } 7 | 8 | EvaFrameAll::EvaFrameAll(std::list datas) { 9 | // list::iterator it; 10 | // for (it = datas.begin(); it != datas.end(); ++it) { 11 | // EvaFrameSet *frameSet = new EvaFrameSet(*it); 12 | // map.insert(make_pair(frameSet->index, *frameSet)); 13 | // } 14 | 15 | for (Datas d: datas) { 16 | EvaFrameSet *frameSet = new EvaFrameSet(d); 17 | map[frameSet->index] = *frameSet; 18 | } 19 | } 20 | 21 | EvaFrameAll::~EvaFrameAll() { 22 | map.clear(); 23 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframeall.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include 5 | #include "list" 6 | #include "evaframeset.h" 7 | 8 | #ifndef YYEVA_EVAFRAMEALL_H 9 | #define YYEVA_EVAFRAMEALL_H 10 | 11 | using namespace std; 12 | class EvaFrameAll { 13 | public: 14 | map map; 15 | EvaFrameAll(); 16 | 17 | EvaFrameAll(std::list datas); 18 | 19 | ~EvaFrameAll(); 20 | }; 21 | 22 | 23 | #endif //YYEVA_EVAFRAMEALL_H 24 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframeset.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include "evaframeset.h" 5 | 6 | EvaFrameSet::EvaFrameSet() { 7 | 8 | } 9 | 10 | EvaFrameSet::EvaFrameSet(Datas datas) { 11 | index = datas.frameIndex; 12 | int i = 0; 13 | std::list::iterator it; 14 | for (it = datas.data.begin(); it != datas.data.end(); ++it) { 15 | auto *frame = new EvaFrame(i, *it); 16 | list.push_back(*frame); 17 | i++; 18 | } 19 | } 20 | 21 | EvaFrameSet::~EvaFrameSet() { 22 | list.clear(); 23 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evaframeset.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include "evaframe.h" 5 | #include "list" 6 | #include "datas.h" 7 | 8 | #ifndef YYEVA_EVAFRAMESET_H 9 | #define YYEVA_EVAFRAMESET_H 10 | 11 | 12 | class EvaFrameSet { 13 | public: 14 | int index = 0; 15 | std::list list; 16 | 17 | EvaFrameSet(); 18 | 19 | EvaFrameSet(Datas datas); 20 | 21 | ~EvaFrameSet(); 22 | }; 23 | 24 | 25 | #endif //YYEVA_EVAFRAMESET_H 26 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evasrc.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | 5 | #include "evasrc.h" 6 | 7 | EvaSrc::EvaSrc() { 8 | 9 | } 10 | 11 | EvaSrc::EvaSrc(Effect* effect) { 12 | srcId = to_string(effect->effectId); 13 | w = effect->effectWidth; 14 | h = effect->effectHeight; 15 | fontColor = effect->fontColor; 16 | if (effect->effectType == "img") { 17 | srcType = SrcType(1); 18 | } else if (effect->effectType == "txt") { 19 | srcType = SrcType(2); 20 | } 21 | if (effect->scaleMode == "aspectFit") { 22 | fitType = FitType::CENTER_FIT; 23 | } else if (effect->scaleMode == "aspectFill") { 24 | fitType = FitType::CENTER_FULL; 25 | } else { 26 | fitType = FitType::FIX_XY; 27 | } 28 | 29 | // srcId = to_string(effect.effectId); 30 | // w = effect.effectWidth; 31 | // h = effect.effectHeight; 32 | // fontColor = effect.fontColor; 33 | // if (effect.effectType == "img") { 34 | // srcType = SrcType(1); 35 | // } else if (effect.effectType == "txt") { 36 | // srcType = SrcType(2); 37 | // } 38 | } 39 | 40 | EvaSrc::~EvaSrc() { 41 | srcTag.clear(); 42 | txt.clear(); 43 | fontColor.clear(); 44 | bitmap = nullptr; 45 | bitmapInfo = nullptr; 46 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evasrc.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include "src/main/cpp/bean/effect.h" 8 | 9 | #ifndef YYEVA_EVASRC_H 10 | #define YYEVA_EVASRC_H 11 | 12 | using namespace std; 13 | 14 | class EvaSrc { 15 | public: 16 | enum SrcType { 17 | UNKNOWN, 18 | IMG, 19 | TXT, 20 | }; 21 | enum LoadType { 22 | _UNKNOWN, 23 | NET, // 网络加载的图片 24 | LOCAL, // 本地加载的图片 25 | }; 26 | 27 | enum FitType { 28 | FIX_XY, // 按原始大小填充纹理 scaleFill 29 | CENTER_FULL, // 以纹理中心点放置 aspectFill 30 | CENTER_FIT, // aspectFit 31 | }; 32 | enum Style { 33 | DEFAULT, 34 | BLOD, //文字粗体 35 | }; 36 | 37 | string srcId = ""; 38 | int w = 0; 39 | int h = 0; 40 | SrcType srcType = UNKNOWN; 41 | LoadType loadType = _UNKNOWN; 42 | unsigned char* bitmap = nullptr; 43 | string saveAddress = ""; 44 | AndroidBitmapInfo* bitmapInfo = nullptr; 45 | int bitmapWidth; 46 | int bitmapHeight; 47 | int bitmapFormat; 48 | string srcTag = ""; 49 | string txt = ""; 50 | Style style = DEFAULT; 51 | string fontColor = ""; 52 | FitType fitType = FIX_XY; 53 | GLuint srcTextureId; 54 | int fontSize = 0; 55 | 56 | EvaSrc(); 57 | 58 | EvaSrc(Effect* effect); 59 | 60 | ~EvaSrc(); 61 | }; 62 | 63 | 64 | #endif //YYEVA_EVASRC_H 65 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evasrcmap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | 5 | #include "evasrcmap.h" 6 | 7 | EvaSrcMap::EvaSrcMap() { 8 | 9 | } 10 | 11 | EvaSrcMap::EvaSrcMap(list effects) { 12 | for (Effect effect: effects) { 13 | EvaSrc *src = new EvaSrc(&effect); 14 | if (src->srcType != EvaSrc::UNKNOWN) { 15 | map[src->srcId] = *src; 16 | } else { 17 | src = nullptr; 18 | } 19 | } 20 | // list::iterator it; 21 | // for (it = effects.begin(); it != effects.end(); ++it) { 22 | // EvaSrc *src = new EvaSrc(it); 23 | // if (src->srcType != EvaSrc::UNKNOWN) { 24 | // map[src->srcId] = *src; 25 | // } else { 26 | // src = nullptr; 27 | // } 28 | // } 29 | } 30 | 31 | EvaSrcMap::~EvaSrcMap() { 32 | map.clear(); 33 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/evasrcmap.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/19. 3 | // 4 | #include "map" 5 | #include "string" 6 | #include "list" 7 | #include "evasrc.h" 8 | #include "effect.h" 9 | 10 | #ifndef YYEVA_EVASRCMAP_H 11 | #define YYEVA_EVASRCMAP_H 12 | 13 | using namespace std; 14 | class EvaSrcMap { 15 | public: 16 | map map; 17 | EvaSrcMap(); 18 | EvaSrcMap(list effects); 19 | 20 | ~EvaSrcMap(); 21 | }; 22 | 23 | 24 | #endif //YYEVA_EVASRCMAP_H 25 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/bean/pointrect.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | 5 | #ifndef YYEVA_POINTRECT_H 6 | #define YYEVA_POINTRECT_H 7 | 8 | 9 | class PointRect { 10 | public: 11 | int x; 12 | int y; 13 | int w; 14 | int h; 15 | PointRect(int x, int y, int w, int h) { 16 | this->x = x; 17 | this->y = y; 18 | this->w = w; 19 | this->h = h; 20 | } 21 | }; 22 | 23 | 24 | #endif //YYEVA_POINTRECT_H 25 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/egl/eglcore.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "eglcore.h" 3 | 4 | #define LOG_TAG "EGLCore" 5 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 6 | #define ELOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 7 | 8 | /** 9 | * EGL是介于诸如OpenGL 或OpenVG的Khronos渲染API与底层本地平台窗口系统的接口。它被用于处理图形管理、表面/缓冲捆绑、渲染同步及支援使用其他Khronos API进行的高效、加速、混合模式2D和3D渲染。 10 | * cangwang 2018.12.1 11 | */ 12 | EGLCore::EGLCore():mDisplay(EGL_NO_DISPLAY),mSurface(EGL_NO_SURFACE),mContext(EGL_NO_CONTEXT) { 13 | 14 | } 15 | 16 | EGLCore::~EGLCore() { 17 | mDisplay = EGL_NO_DISPLAY; 18 | mSurface = EGL_NO_SURFACE; 19 | mContext = EGL_NO_CONTEXT; 20 | } 21 | 22 | void EGLCore::start(ANativeWindow *window) { 23 | mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 24 | GLint majorVersion; 25 | GLint minorVersion; 26 | //获取支持最低和最高版本 27 | if (!eglInitialize(mDisplay,&majorVersion,&minorVersion)){ 28 | ELOGE("eglInitialize failed: %d",eglGetError()); 29 | return; 30 | } 31 | ELOGD("eglInitialize success"); 32 | EGLConfig config = chooseConfig(); 33 | ELOGD("chooseConfig success"); 34 | 35 | mContext = createContext(mDisplay, config); 36 | ELOGD("createContext success"); 37 | EGLint format = 0; 38 | if (!eglGetConfigAttrib(mDisplay,config,EGL_NATIVE_VISUAL_ID,&format)){ 39 | ELOGE("eglGetConfigAttrib failed: %d",eglGetError()); 40 | } 41 | ELOGD("eglGetConfigAttrib success"); 42 | ANativeWindow_setBuffersGeometry(window,0,0,format); 43 | ELOGD("setBuffersGeometry success"); 44 | //创建On-Screen 渲染区域 45 | mSurface = eglCreateWindowSurface(mDisplay,config,window,0); 46 | if (mSurface == nullptr || mSurface == EGL_NO_SURFACE){ 47 | ELOGE("eglCreateWindowSurface failed: %d",eglGetError()); 48 | return; 49 | } 50 | ELOGD("eglCreateWindowSurface success"); 51 | if (eglMakeCurrent(mDisplay, mSurface, mSurface, mContext) == false) { 52 | ELOGE("make current error:${Integer.toHexString(egl?.eglGetError() ?: 0)}"); 53 | } 54 | ELOGD("eglMakeCurrent success"); 55 | } 56 | 57 | EGLConfig EGLCore::chooseConfig() { 58 | int configsCount = 0; 59 | EGLConfig configs; 60 | // EGLint* attributes = getAttributes(); 61 | EGLint attributes[] = 62 | {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //指定渲染api类别 63 | EGL_RED_SIZE, 8, 64 | EGL_GREEN_SIZE, 8, 65 | EGL_BLUE_SIZE, 8, 66 | EGL_ALPHA_SIZE, 8, 67 | EGL_DEPTH_SIZE, 0, 68 | EGL_STENCIL_SIZE, 0, 69 | EGL_NONE 70 | }; 71 | int configSize = 1; 72 | if (eglChooseConfig(mDisplay, attributes, &configs, configSize, &configsCount) == true) { 73 | return configs; 74 | } else { 75 | ELOGE("eglChooseConfig failed: %d",eglGetError()); 76 | } 77 | return nullptr; 78 | } 79 | 80 | EGLint* EGLCore::getAttributes() { 81 | EGLint attribList[] = 82 | {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //指定渲染api类别 83 | EGL_RED_SIZE, 8, 84 | EGL_GREEN_SIZE, 8, 85 | EGL_BLUE_SIZE, 8, 86 | EGL_ALPHA_SIZE, 8, 87 | EGL_DEPTH_SIZE, 0, 88 | EGL_STENCIL_SIZE, 0, 89 | EGL_NONE 90 | }; 91 | return attribList; 92 | } 93 | 94 | EGLContext EGLCore::createContext(EGLDisplay eglDisplay, EGLConfig eglConfig) { 95 | //创建渲染上下文 96 | //只使用opengles2 97 | GLint contextAttrib[] = {EGL_CONTEXT_CLIENT_VERSION, 3 , 98 | EGL_NONE}; 99 | // EGL_NO_CONTEXT表示不向其它的context共享资源 100 | mContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttrib); 101 | if (mContext == EGL_NO_CONTEXT){ 102 | ELOGE("eglCreateContext failed: %d",eglGetError()); 103 | return GL_FALSE; 104 | } 105 | return mContext; 106 | } 107 | 108 | GLboolean EGLCore::buildContext(ANativeWindow *window) { 109 | //与本地窗口通信 110 | mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 111 | if (mDisplay == EGL_NO_DISPLAY){ 112 | ELOGE("eglGetDisplay failed: %d",eglGetError()); 113 | return GL_FALSE; 114 | } 115 | 116 | GLint majorVersion; 117 | GLint minorVersion; 118 | //获取支持最低和最高版本 119 | if (!eglInitialize(mDisplay,&majorVersion,&minorVersion)){ 120 | ELOGE("eglInitialize failed: %d",eglGetError()); 121 | return GL_FALSE; 122 | } 123 | 124 | EGLConfig config; 125 | EGLint numConfigs = 0; 126 | //颜色使用565,读写类型需要egl扩展 127 | EGLint attribList[] = { 128 | EGL_RED_SIZE,5, //指定RGB中的R大小(bits) 129 | EGL_GREEN_SIZE,6, //指定G大小 130 | EGL_BLUE_SIZE,5, //指定B大小 131 | // EGL_RENDERABLE_TYPE,EGL_OPENGL_ES3_BIT_KHR, //渲染类型,为相机扩展类型 132 | EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, //绘图类型, 133 | EGL_NONE 134 | }; 135 | 136 | //让EGL推荐匹配的EGLConfig 137 | if(!eglChooseConfig(mDisplay,attribList,&config,1,&numConfigs)){ 138 | ELOGE("eglChooseConfig failed: %d",eglGetError()); 139 | return GL_FALSE; 140 | } 141 | 142 | //找不到匹配的 143 | if (numConfigs <1){ 144 | ELOGE("eglChooseConfig get config number less than one"); 145 | return GL_FALSE; 146 | } 147 | 148 | //创建渲染上下文 149 | //只使用opengles3 150 | GLint contextAttrib[] = {EGL_CONTEXT_CLIENT_VERSION,3,EGL_NONE}; 151 | // EGL_NO_CONTEXT表示不向其它的context共享资源 152 | mContext = eglCreateContext(mDisplay,config,EGL_NO_CONTEXT,contextAttrib); 153 | if (mContext == EGL_NO_CONTEXT){ 154 | ELOGE("eglCreateContext failed: %d",eglGetError()); 155 | return GL_FALSE; 156 | } 157 | 158 | EGLint format = 0; 159 | if (!eglGetConfigAttrib(mDisplay,config,EGL_NATIVE_VISUAL_ID,&format)){ 160 | ELOGE("eglGetConfigAttrib failed: %d",eglGetError()); 161 | return GL_FALSE; 162 | } 163 | ANativeWindow_setBuffersGeometry(window,0,0,format); 164 | 165 | //创建On-Screen 渲染区域 166 | mSurface = eglCreateWindowSurface(mDisplay,config,window,0); 167 | if (mSurface == EGL_NO_SURFACE){ 168 | ELOGE("eglCreateWindowSurface failed: %d",eglGetError()); 169 | return GL_FALSE; 170 | } 171 | 172 | //把EGLContext和EGLSurface关联起来,单缓冲只使用了一个surface 173 | if (!eglMakeCurrent(mDisplay,mSurface,mSurface,mContext)){ 174 | ELOGE("eglMakeCurrent failed: %d",eglGetError()); 175 | return GL_FALSE; 176 | } 177 | 178 | ELOGD("buildContext Succeed"); 179 | return GL_TRUE; 180 | } 181 | 182 | /** 183 | * 现在只使用单缓冲绘制 184 | */ 185 | void EGLCore::swapBuffer() { 186 | //双缓冲绘图,原来是检测出前台display和后台缓冲的差别的dirty区域,然后再区域替换buffer 187 | //1)首先计算非dirty区域,然后将非dirty区域数据从上一个buffer拷贝到当前buffer; 188 | //2)完成buffer内容的填充,然后将previousBuffer指向buffer,同时queue buffer。 189 | //3)Dequeue一块新的buffer,并等待fence。如果等待超时,就将buffer cancel掉。 190 | //4)按需重新计算buffer 191 | //5)Lock buffer,这样就实现page flip,也就是swapbuffer 192 | if (mDisplay != nullptr && mSurface != nullptr) { 193 | eglSwapBuffers(mDisplay, mSurface); 194 | } 195 | } 196 | 197 | void EGLCore::release() { 198 | ELOGD("release"); 199 | eglDestroySurface(mDisplay,mSurface); 200 | eglMakeCurrent(mDisplay,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); 201 | eglDestroyContext(mDisplay,mContext); 202 | eglReleaseThread(); 203 | eglTerminate(mDisplay); 204 | mDisplay = EGL_NO_DISPLAY; 205 | mContext = EGL_NO_CONTEXT; 206 | mSurface = EGL_NO_SURFACE; 207 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/egl/eglcore.h: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef YYEVA_EGLCORE_H 8 | #define YYEVA_EGLCORE_H 9 | 10 | /** 11 | * cangwang 2018.12.1 12 | */ 13 | class EGLCore { 14 | public: 15 | EGLCore(); 16 | 17 | ~EGLCore(); 18 | 19 | void start(ANativeWindow *window); 20 | 21 | EGLConfig chooseConfig(); 22 | 23 | EGLint* getAttributes(); 24 | 25 | EGLContext createContext(EGLDisplay eglDisplay, EGLConfig eglConfig); 26 | 27 | GLboolean buildContext(ANativeWindow *window); 28 | 29 | void swapBuffer(); 30 | 31 | void release(); 32 | 33 | protected: 34 | 35 | private: 36 | EGLDisplay mDisplay; 37 | EGLSurface mSurface; 38 | EGLContext mContext; 39 | }; 40 | #endif -------------------------------------------------------------------------------- /yyevac/src/main/cpp/egl/glbase.h: -------------------------------------------------------------------------------- 1 | //#include 2 | #include 3 | 4 | #ifndef YYEVA_GLBASE_H 5 | #define YYEVA_GLBASE_H 6 | 7 | /** 8 | * cangwang 2018.12.1 9 | */ 10 | class GLBase{ 11 | public: 12 | GLBase(): mProgram(0), mWidth(0), mHeight(0){ 13 | 14 | } 15 | 16 | void resize(int width,int height){ 17 | mWidth = width; 18 | mHeight = height; 19 | } 20 | 21 | protected: 22 | GLuint mProgram; 23 | GLint mWidth; 24 | GLint mHeight; 25 | 26 | private: 27 | 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/bgrender.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/10/11. 3 | // 4 | 5 | #include "bgrender.h" 6 | 7 | BgRender::BgRender() { 8 | initRender(); 9 | } 10 | 11 | BgRender::~BgRender() { 12 | vertexArray = nullptr; 13 | rgbaArray = nullptr; 14 | } 15 | 16 | void BgRender::setBgImage(unsigned char *bitmap, AndroidBitmapInfo *bitmapInfo) { 17 | textureId = TextureLoadUtil::loadTexture(bitmap, bitmapInfo); 18 | } 19 | 20 | 21 | void BgRender::initRender() { 22 | char VERTEX_SHADER[] = "attribute vec4 vPosition;\n" 23 | "attribute vec4 vTexCoordinate;\n" 24 | "varying vec2 v_TexCoordinate;\n" 25 | "\n" 26 | "void main() {\n" 27 | " v_TexCoordinate = vec2(vTexCoordinate.x, vTexCoordinate.y);\n" 28 | " gl_Position = vPosition;\n" 29 | "}"; 30 | 31 | char FRAGMENT_SHADER[] = "precision mediump float;\n" 32 | "uniform sampler2D texture;\n" 33 | "varying vec2 v_TexCoordinate;\n" 34 | "\n" 35 | "void main () {\n" 36 | " gl_FragColor = texture2D(texture, v_TexCoordinate);\n" 37 | // " gl_FragColor = vec4(1.0,0.2,0.5,1.0);\n" 38 | "}"; 39 | shaderProgram = ShaderUtil::createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 40 | uTextureLocation = glGetUniformLocation(shaderProgram, "texture"); 41 | positionLocation = glGetAttribLocation(shaderProgram, "vPosition"); 42 | textureLocation = glGetAttribLocation(shaderProgram, "vTexCoordinate"); 43 | } 44 | 45 | void BgRender::renderFrame() { 46 | if (surfaceSizeChanged && surfaceWidth > 0 && surfaceHeight > 0) { 47 | surfaceSizeChanged = false; 48 | glViewport(0, 0, surfaceWidth, surfaceHeight); 49 | } 50 | draw(); 51 | } 52 | 53 | void BgRender::clearFrame() { 54 | glClearColor(0, 0, 0, 0); 55 | 56 | glClear(GL_COLOR_BUFFER_BIT); 57 | } 58 | 59 | void BgRender::destroyRender() { 60 | releaseTexture(); 61 | } 62 | 63 | void BgRender::setAnimeConfig(EvaAnimeConfig *config) { 64 | vertexArray->setArray(VertexUtil::create(config->width, config->height, new PointRect(0, 0, config->width, config->height), vertexArray->array)); 65 | float* rgba = TexCoordsUtil::create(config->width, config->height, new PointRect(0, 0, config->width, config->height), rgbaArray->array); 66 | rgbaArray->setArray(rgba); 67 | } 68 | 69 | GLuint BgRender::getExternalTexture() { 70 | return textureId; 71 | } 72 | 73 | void BgRender::releaseTexture() { 74 | glDeleteTextures(1, &textureId); 75 | } 76 | 77 | void BgRender::swapBuffers() { 78 | 79 | } 80 | 81 | void BgRender::updateViewPort(int width, int height) { 82 | if (width <= 0 || height <= 0) return; 83 | surfaceSizeChanged = true; 84 | surfaceWidth = width; 85 | surfaceHeight = height; 86 | } 87 | 88 | void BgRender::draw() { 89 | if (textureId != -1) { 90 | glUseProgram(shaderProgram); 91 | vertexArray->setVertexAttribPointer(positionLocation); 92 | glActiveTexture(GL_TEXTURE0); 93 | glBindTexture(GL_TEXTURE_2D, textureId); 94 | //加载纹理 95 | glUniform1i(uTextureLocation, 0); 96 | rgbaArray->setVertexAttribPointer(textureLocation); 97 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/bgrender.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/10/11. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifndef YYEVA_BGRENDER_H 18 | #define YYEVA_BGRENDER_H 19 | 20 | #define LOG_TAG "BgRender" 21 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 22 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 23 | 24 | 25 | using namespace std; 26 | class BgRender: public IRender { 27 | public: 28 | BgRender(); 29 | ~BgRender(); 30 | void initRender(); 31 | void setBgImage(unsigned char *bitmap, AndroidBitmapInfo *bitmapInfo); 32 | void renderFrame(); 33 | void clearFrame(); 34 | void destroyRender(); 35 | void setAnimeConfig(EvaAnimeConfig* config); 36 | GLuint getExternalTexture(); 37 | void releaseTexture(); 38 | void swapBuffers(); 39 | void updateViewPort(int width, int height); 40 | void setHasBg(bool hasBg){}; 41 | void draw(); 42 | 43 | private: 44 | GlFloatArray *vertexArray = new GlFloatArray(); 45 | GlFloatArray *rgbaArray = new GlFloatArray(); 46 | 47 | GLuint shaderProgram; 48 | //shader 49 | GLuint textureId; 50 | //顶点位置 51 | GLint uTextureLocation; 52 | //纹理位置 53 | GLint positionLocation; 54 | //纹理位置 55 | GLint textureLocation; 56 | 57 | int surfaceWidth = 0; 58 | int surfaceHeight = 0; 59 | bool surfaceSizeChanged = false; 60 | }; 61 | 62 | 63 | #endif //YYEVA_BGRENDER_H 64 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/evaengine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | 5 | #include "evaengine.h" 6 | //#include 7 | //#include 8 | 9 | 10 | 11 | EvaEngine::EvaEngine(ANativeWindow *window): mWindow(window), mEGLCore(new EGLCore()), mTextureId(0), mTextureLoc(0), mMatrixLoc(0){ 12 | 13 | } 14 | 15 | EvaEngine::~EvaEngine() { 16 | if (mWindow) { 17 | ANativeWindow_release(mWindow); 18 | mWindow = nullptr; 19 | } 20 | if (mEGLCore) { 21 | delete mEGLCore; 22 | mEGLCore = nullptr; 23 | } 24 | } 25 | int EvaEngine::create() { 26 | 27 | return mTextureId; 28 | } 29 | void EvaEngine::draw() { 30 | 31 | } 32 | void EvaEngine::stop() { 33 | 34 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/evaengine.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "src/main/cpp/egl/glbase.h" 5 | #include "src/main/cpp/egl/eglcore.h" 6 | #include 7 | #include 8 | //#include 9 | #include 10 | 11 | 12 | class EvaEngine: public GLBase { 13 | public: 14 | EvaEngine(ANativeWindow *window); 15 | ~EvaEngine(); 16 | int create(); 17 | void draw(); 18 | void stop(); 19 | 20 | private: 21 | ANativeWindow * mWindow; 22 | GLuint mTextureId; 23 | GLint mTextureLoc; 24 | GLint mMatrixLoc; 25 | GLfloat mMatrix[16]; 26 | EGLCore *mEGLCore; 27 | }; 28 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/irender.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by asus on 2022/4/15. 3 | // 4 | 5 | #include 6 | #include "src/main/cpp/bean/evaanimeconfig.h" 7 | 8 | #ifndef YYEVA_IRENDER_H 9 | #define YYEVA_IRENDER_H 10 | 11 | class IRender { 12 | public: 13 | virtual void initRender() = 0; 14 | virtual void renderFrame() = 0; 15 | virtual void clearFrame() = 0; 16 | virtual void destroyRender() = 0; 17 | virtual void setAnimeConfig(EvaAnimeConfig* config) = 0; 18 | virtual void updateViewPort(int width, int height){}; 19 | virtual GLuint getExternalTexture() = 0; 20 | virtual void releaseTexture() = 0; 21 | virtual void swapBuffers() = 0; 22 | void setYUVData(int width, int height, char *y, char *u, char *v){}; 23 | //如果有背景需要开启混合 24 | virtual void setHasBg(bool hasBg)=0; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/mp4render.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/10/11. 3 | // 4 | 5 | #include "mp4render.h" 6 | 7 | Mp4Render::Mp4Render() { 8 | initRender(); 9 | } 10 | 11 | Mp4Render::~Mp4Render() { 12 | vertexArray = nullptr; 13 | rgbaArray = nullptr; 14 | } 15 | 16 | void Mp4Render::setBgImage(unsigned char *bitmap, AndroidBitmapInfo *bitmapInfo) { 17 | // textureId = TextureLoadUtil::loadTexture(bitmap, bitmapInfo); 18 | } 19 | 20 | 21 | void Mp4Render::initRender() { 22 | char VERTEX_SHADER[] = "attribute vec4 vPosition;\n" 23 | "attribute vec4 vTexCoordinate;\n" 24 | "varying vec2 v_TexCoordinate;\n" 25 | "\n" 26 | "void main() {\n" 27 | " v_TexCoordinate = vec2(vTexCoordinate.x, vTexCoordinate.y);\n" 28 | " gl_Position = vPosition;\n" 29 | "}"; 30 | 31 | char FRAGMENT_SHADER[] = 32 | "#extension GL_OES_EGL_image_external : require\n" 33 | "precision mediump float;\n" 34 | "uniform samplerExternalOES texture;\n" 35 | "varying vec2 v_TexCoordinate;\n" 36 | "\n" 37 | "void main () {\n" 38 | " gl_FragColor = texture2D(texture, v_TexCoordinate);\n" 39 | // " gl_FragColor = vec4(1.0,0.2,0.5,1.0);\n" 40 | "}"; 41 | shaderProgram = ShaderUtil::createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 42 | uTextureLocation = glGetUniformLocation(shaderProgram, "texture"); 43 | positionLocation = glGetAttribLocation(shaderProgram, "vPosition"); 44 | textureLocation = glGetAttribLocation(shaderProgram, "vTexCoordinate"); 45 | 46 | glGenTextures(1, &textureId); 47 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); 48 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 49 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 50 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 51 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 52 | } 53 | 54 | void Mp4Render::renderFrame() { 55 | if (surfaceSizeChanged && surfaceWidth > 0 && surfaceHeight > 0) { 56 | surfaceSizeChanged = false; 57 | glViewport(0, 0, surfaceWidth, surfaceHeight); 58 | } 59 | draw(); 60 | } 61 | 62 | void Mp4Render::clearFrame() { 63 | glClearColor(0, 0, 0, 0); 64 | 65 | glClear(GL_COLOR_BUFFER_BIT); 66 | } 67 | 68 | void Mp4Render::destroyRender() { 69 | releaseTexture(); 70 | } 71 | 72 | void Mp4Render::setAnimeConfig(EvaAnimeConfig *config) { 73 | vertexArray->setArray(VertexUtil::create(config->width, config->height, new PointRect(0, 0, config->width, config->height), vertexArray->array)); 74 | float* rgba = TexCoordsUtil::create(config->width, config->height, new PointRect(0, 0, config->width, config->height), rgbaArray->array); 75 | rgbaArray->setArray(rgba); 76 | } 77 | 78 | GLuint Mp4Render::getExternalTexture() { 79 | return textureId; 80 | } 81 | 82 | void Mp4Render::releaseTexture() { 83 | glDeleteTextures(1, &textureId); 84 | } 85 | 86 | void Mp4Render::swapBuffers() { 87 | 88 | } 89 | 90 | void Mp4Render::updateViewPort(int width, int height) { 91 | if (width <= 0 || height <= 0) return; 92 | surfaceSizeChanged = true; 93 | surfaceWidth = width; 94 | surfaceHeight = height; 95 | } 96 | 97 | void Mp4Render::draw() { 98 | if (textureId != -1) { 99 | glUseProgram(shaderProgram); 100 | vertexArray->setVertexAttribPointer(positionLocation); 101 | glActiveTexture(GL_TEXTURE0); 102 | glBindTexture(GL_TEXTURE_2D, textureId); 103 | //加载纹理 104 | glUniform1i(uTextureLocation, 0); 105 | rgbaArray->setVertexAttribPointer(textureLocation); 106 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/mp4render.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/10/11. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifndef YYEVA_Mp4Render_H 18 | #define YYEVA_Mp4Render_H 19 | 20 | #define LOG_TAG "Mp4Render" 21 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 22 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 23 | 24 | 25 | using namespace std; 26 | class Mp4Render: public IRender { 27 | public: 28 | Mp4Render(); 29 | ~Mp4Render(); 30 | void initRender(); 31 | void setBgImage(unsigned char *bitmap, AndroidBitmapInfo *bitmapInfo); 32 | void renderFrame(); 33 | void clearFrame(); 34 | void destroyRender(); 35 | void setAnimeConfig(EvaAnimeConfig* config); 36 | GLuint getExternalTexture(); 37 | void releaseTexture(); 38 | void swapBuffers(); 39 | void updateViewPort(int width, int height); 40 | void setHasBg(bool hasBg){}; 41 | void draw(); 42 | 43 | private: 44 | GlFloatArray *vertexArray = new GlFloatArray(); 45 | GlFloatArray *rgbaArray = new GlFloatArray(); 46 | 47 | GLuint shaderProgram; 48 | //shader 49 | GLuint textureId; 50 | //顶点位置 51 | GLint uTextureLocation; 52 | //纹理位置 53 | GLint positionLocation; 54 | //纹理位置 55 | GLint textureLocation; 56 | 57 | int surfaceWidth = 0; 58 | int surfaceHeight = 0; 59 | bool surfaceSizeChanged = false; 60 | }; 61 | 62 | 63 | #endif //YYEVA_Mp4Render_H 64 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/render.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | 5 | #include "render.h" 6 | 7 | #define LOG_TAG "Render" 8 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 9 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 10 | 11 | Render::Render() { 12 | initRender(); 13 | } 14 | 15 | Render::~Render() { 16 | vertexArray = nullptr; 17 | alphaArray = nullptr; 18 | rgbArray = nullptr; 19 | } 20 | 21 | void Render::initRender() { 22 | char VERTEX_SHADER[] = "attribute vec4 vPosition;\n" 23 | "attribute vec4 vTexCoordinateAlpha;\n" 24 | "attribute vec4 vTexCoordinateRgb;\n" 25 | "varying vec2 v_TexCoordinateAlpha;\n" 26 | "varying vec2 v_TexCoordinateRgb;\n" 27 | "\n" 28 | "void main() {\n" 29 | " v_TexCoordinateAlpha = vec2(vTexCoordinateAlpha.x, vTexCoordinateAlpha.y);\n" 30 | " v_TexCoordinateRgb = vec2(vTexCoordinateRgb.x, vTexCoordinateRgb.y);\n" 31 | " gl_Position = vPosition;\n" 32 | "}"; 33 | 34 | char FRAGMENT_SHADER[] = "#extension GL_OES_EGL_image_external : require\n" 35 | "precision mediump float;\n" 36 | "uniform samplerExternalOES texture;\n" 37 | "varying vec2 v_TexCoordinateAlpha;\n" 38 | "varying vec2 v_TexCoordinateRgb;\n" 39 | "\n" 40 | "void main () {\n" 41 | " vec4 alphaColor = texture2D(texture, v_TexCoordinateAlpha);\n" 42 | " vec4 rgbColor = texture2D(texture, v_TexCoordinateRgb);\n" 43 | " gl_FragColor = vec4(rgbColor.r, rgbColor.g, rgbColor.b, alphaColor.r);\n" 44 | // " gl_FragColor = vec4(1.0,0.2,0.5,1.0);\n" 45 | "}"; 46 | shaderProgram = ShaderUtil::createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 47 | uTextureLocation = glGetUniformLocation(shaderProgram, "texture"); 48 | aPositionLocation = glGetAttribLocation(shaderProgram, "vPosition"); 49 | aTextureAlphaLocation = glGetAttribLocation(shaderProgram, "vTexCoordinateAlpha"); 50 | aTextureRgbLocation = glGetAttribLocation(shaderProgram, "vTexCoordinateRgb"); 51 | 52 | glGenTextures(1,&textureId); 53 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); 54 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 55 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 56 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 57 | glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 58 | } 59 | 60 | void Render::renderFrame() { 61 | if (surfaceSizeChanged && surfaceWidth > 0 && surfaceHeight > 0) { 62 | surfaceSizeChanged = false; 63 | glViewport(0, 0, surfaceWidth, surfaceHeight); 64 | } 65 | draw(); 66 | } 67 | 68 | void Render::clearFrame() { 69 | glClearColor(0, 0, 0, 0); 70 | // glClearColor(1.0, 0, 0.5, 1.0); 71 | glClear(GL_COLOR_BUFFER_BIT); 72 | } 73 | 74 | void Render::destroyRender() { 75 | releaseTexture(); 76 | } 77 | 78 | void Render::setAnimeConfig(EvaAnimeConfig* config) { 79 | vertexArray->setArray(VertexUtil::create(config->width, config->height, new PointRect(0, 0, config->width, config->height), vertexArray->array)); 80 | float* alpha = TexCoordsUtil::create(config->videoWidth, config->videoHeight, config->alphaPointRect, alphaArray->array); 81 | float* rgb = TexCoordsUtil::create(config->videoWidth, config->videoHeight, config->rgbPointRect, rgbArray->array); 82 | alphaArray->setArray(alpha); 83 | rgbArray->setArray(rgb); 84 | } 85 | 86 | GLuint Render::getExternalTexture() { 87 | return textureId; 88 | } 89 | 90 | void Render::releaseTexture() { 91 | glDeleteTextures(1, &textureId); 92 | } 93 | 94 | void Render::updateViewPort(int width, int height) { 95 | if (width <= 0 || height <= 0) return; 96 | surfaceSizeChanged = true; 97 | surfaceWidth = width; 98 | surfaceHeight = height; 99 | } 100 | 101 | void Render::swapBuffers() { 102 | 103 | } 104 | 105 | void Render::draw() { 106 | if (textureId != -1) { 107 | glUseProgram(shaderProgram); 108 | vertexArray->setVertexAttribPointer(aPositionLocation); 109 | glActiveTexture(GL_TEXTURE0); 110 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); 111 | //加载纹理 112 | glUniform1i(uTextureLocation, 0); 113 | 114 | alphaArray->setVertexAttribPointer(aTextureAlphaLocation); 115 | rgbArray->setVertexAttribPointer(aTextureRgbLocation); 116 | //启动混合 117 | if (hasBg) { //如果有背景需要开启混合 118 | glEnable(GL_BLEND); 119 | } 120 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 121 | //关闭混合 122 | if (hasBg) { 123 | glDisable(GL_BLEND); 124 | } 125 | } 126 | } 127 | 128 | //如果有背景需要开启混合 129 | void Render::setHasBg(bool hasBg) { 130 | this->hasBg = hasBg; 131 | } 132 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/render.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | 5 | #ifndef YYEVA_RENDER_H 6 | #define YYEVA_RENDER_H 7 | 8 | 9 | #include "irender.h" 10 | #include "src/main/cpp/egl/eglcore.h" 11 | #include "src/main/cpp/util/shaderutil.h" 12 | #include "src/main/cpp/util/glfloatarray.h" 13 | #include 14 | #include 15 | #include "src/main/cpp/util/vertexutil.h" 16 | #include "src/main/cpp/util/texcoordsutil.h" 17 | 18 | class Render: public IRender { 19 | public: 20 | Render(); 21 | ~Render(); 22 | void initRender(); 23 | void renderFrame(); 24 | void clearFrame(); 25 | void destroyRender(); 26 | void setAnimeConfig(EvaAnimeConfig* config); 27 | GLuint getExternalTexture(); 28 | void releaseTexture(); 29 | void swapBuffers(); 30 | void updateViewPort(int width, int height); 31 | void setHasBg(bool hasBg); 32 | void draw(); 33 | 34 | private: 35 | GlFloatArray *vertexArray = new GlFloatArray(); 36 | GlFloatArray *alphaArray = new GlFloatArray(); 37 | GlFloatArray *rgbArray = new GlFloatArray(); 38 | 39 | GLuint shaderProgram; 40 | //shader 41 | GLuint textureId; 42 | //顶点位置 43 | GLint uTextureLocation; 44 | //rgb纹理位置 45 | GLint aPositionLocation; 46 | //alpha纹理位置 47 | GLint aTextureAlphaLocation; 48 | GLint aTextureRgbLocation; 49 | 50 | int surfaceWidth = 0; 51 | int surfaceHeight = 0; 52 | bool surfaceSizeChanged = false; 53 | bool hasBg = false; 54 | 55 | // string VERTEX_SHADER = string("attribute vec4 vPosition;\n") + 56 | // "attribute vec4 vTexCoordinateAlpha;\n" + 57 | // "attribute vec4 vTexCoordinateRgb;\n" + 58 | // "varying vec2 v_TexCoordinateAlpha;\n" + 59 | // "varying vec2 v_TexCoordinateRgb;\n" + 60 | // "\n" + 61 | // "void main() {\n" + 62 | // " v_TexCoordinateAlpha = vec2(vTexCoordinateAlpha.x, vTexCoordinateAlpha.y);\n" + 63 | // " v_TexCoordinateRgb = vec2(vTexCoordinateRgb.x, vTexCoordinateRgb.y);\n" + 64 | // " gl_Position = vPosition;\n" + 65 | // "}"; 66 | 67 | // char VERTEX_SHADER[] = "attribute vec4 vPosition;\n" 68 | // "attribute vec4 vTexCoordinateAlpha;\n" 69 | // "attribute vec4 vTexCoordinateRgb;\n" 70 | // "varying vec2 v_TexCoordinateAlpha;\n" 71 | // "varying vec2 v_TexCoordinateRgb;\n" 72 | // "\n" 73 | // "void main() {\n" 74 | // " v_TexCoordinateAlpha = vec2(vTexCoordinateAlpha.x, vTexCoordinateAlpha.y);\n" 75 | // " v_TexCoordinateRgb = vec2(vTexCoordinateRgb.x, vTexCoordinateRgb.y);\n" 76 | // " gl_Position = vPosition;\n" 77 | // "}"; 78 | 79 | 80 | // string FRAGMENT_SHADER = string("#extension GL_OES_EGL_image_external : require\n") + 81 | // "precision mediump float;\n" + 82 | // "uniform samplerExternalOES texture;\n" + 83 | // "varying vec2 v_TexCoordinateAlpha;\n" + 84 | // "varying vec2 v_TexCoordinateRgb;\n" + 85 | // "\n" + 86 | // "void main () {\n" + 87 | // " vec4 alphaColor = texture2D(texture, v_TexCoordinateAlpha);\n" + 88 | // " vec4 rgbColor = texture2D(texture, v_TexCoordinateRgb);\n" + 89 | // " gl_FragColor = vec4(rgbColor.r, rgbColor.g, rgbColor.b, alphaColor.r);\n" + 90 | // "}"; 91 | }; 92 | 93 | 94 | #endif //YYEVA_RENDER_H 95 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/rendercontroller.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/22. 3 | // 4 | 5 | #ifndef YYEVA_RENDERCONTROLLER_H 6 | #define YYEVA_RENDERCONTROLLER_H 7 | 8 | 9 | #include "irender.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace std; 21 | class RenderController { 22 | public: 23 | RenderController(); 24 | ~RenderController(); 25 | GLuint initRender(ANativeWindow *window, bool isNeedYUV, bool isNormalMp4); 26 | void destroyRender(); 27 | void updateViewPoint(int width, int height); 28 | int getExternalTexture(); 29 | void videoSizeChange(int newWidth, int newHeight); 30 | void setRenderConfig(EvaAnimeConfig* config); 31 | void renderFrame(); 32 | void renderSwapBuffers(); 33 | void renderClearFrame(); 34 | void releaseTexture(); 35 | 36 | void mixConfigCreate(EvaAnimeConfig* config); 37 | void mixRenderCreate(); 38 | void mixRendering(int frameIndex); 39 | void mixRenderRelease(int textureId); 40 | void mixRenderDestroy(); 41 | 42 | void parseFrame(EvaAnimeConfig* config); 43 | void parseSrc(EvaAnimeConfig* config); 44 | void setSrcBitmap(const char* srcId, unsigned char* bitmap, AndroidBitmapInfo* bitmapInfo, string scaleMode); 45 | void setSrcTxt(const char* srcId, const char* txt); 46 | void setBgImage(unsigned char* bitmap, AndroidBitmapInfo* bitmapInfo); 47 | 48 | private: 49 | BgRender* bgRender; 50 | IRender* render; 51 | EvaMixRender* mixRender; 52 | EvaAnimeConfig* config; 53 | int curFrameIndex; 54 | EvaFrameAll* frameAll; 55 | EvaSrcMap* srcMap; 56 | EGLCore *eglCore; 57 | }; 58 | 59 | 60 | #endif //YYEVA_RENDERCONTROLLER_H 61 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/engine/yuvrender.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/15. 3 | // 4 | #include "irender.h" 5 | #include "src/main/cpp/egl/eglcore.h" 6 | #include "src/main/cpp/util/shaderutil.h" 7 | #include "src/main/cpp/util/glfloatarray.h" 8 | #include 9 | //#include 10 | //#include 11 | #include 12 | #include 13 | #include "src/main/cpp/util/vertexutil.h" 14 | #include "src/main/cpp/util/texcoordsutil.h" 15 | 16 | 17 | class YUVRender: public IRender { 18 | public: 19 | YUVRender(); 20 | ~YUVRender(); 21 | void initRender(); 22 | void renderFrame(); 23 | void clearFrame(); 24 | void destroyRender(); 25 | void setAnimeConfig(EvaAnimeConfig* config); 26 | GLuint getExternalTexture(); 27 | void releaseTexture(); 28 | void swapBuffers(); 29 | void setHasBg(bool hasBg) {} 30 | void setYUVData(int width, int height, char *y, char *u, char *v); 31 | 32 | private: 33 | GlFloatArray *vertexArray = new GlFloatArray(); 34 | GlFloatArray *alphaArray = new GlFloatArray(); 35 | GlFloatArray *rgbArray = new GlFloatArray(); 36 | 37 | GLuint shaderProgram; 38 | //顶点位置 39 | GLint avPosition; 40 | //rgb纹理位置 41 | GLint rgbPosition; 42 | //alpha纹理位置 43 | GLint alphaPosition; 44 | //shader yuv变量 45 | GLint samplerY = 0; 46 | GLint samplerU = 0; 47 | GLint samplerV = 0; 48 | GLuint textureId[3]; 49 | GLint convertMatrixUniform; 50 | GLint convertOffsetUniform; 51 | //YUV数据 52 | int widthYUV = 0; 53 | int heightYUV = 0; 54 | char* y; 55 | char* u; 56 | char* v; 57 | 58 | // 像素数据向GPU传输时默认以4字节对齐 59 | int unpackAlign = 4; 60 | 61 | GLfloat YUV_OFFSET[3] = { 62 | 0, -0.501960814, -0.501960814 63 | }; 64 | 65 | 66 | // GLint YUV_OFFSET[] = { 67 | // 0,1,2 68 | // }; 69 | 70 | GLfloat YUV_MATRIX[9] = { 71 | 1.0f, 1.0f, 1.0f, 72 | 0.0f, -0.3441f, 1.772f, 73 | 1.402f, -0.7141f, 0.0f 74 | }; 75 | 76 | void draw(); 77 | }; 78 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/mix/evamixrender.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | 5 | #include "evamixrender.h" 6 | 7 | #define LOG_TAG "EvaMixRender" 8 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 9 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 10 | 11 | EvaMixRender::EvaMixRender() { 12 | 13 | } 14 | 15 | EvaMixRender::~EvaMixRender() { 16 | 17 | } 18 | 19 | void EvaMixRender::init(EvaSrcMap* evaSrcMap) { 20 | shader = new MixShader(); 21 | glDisable(GL_DEPTH_TEST); //关闭深度测试 22 | 23 | if (!evaSrcMap->map.empty()) { 24 | map::iterator it; 25 | for (it = evaSrcMap->map.begin(); it != evaSrcMap->map.end(); it++) { 26 | // cout << (*it).first << " " << (*it).second << endl; 27 | ELOGV("init srcId = %s", it->second.srcId.c_str()); 28 | TextureLoadUtil::loadTexture(&it->second); 29 | if (shader != nullptr && shader->program != 0) { 30 | ELOGV("textureProgram=%d, textureId=%d", shader->program, it->second.srcTextureId); 31 | } else { 32 | ELOGE("shader program error"); 33 | } 34 | } 35 | } 36 | } 37 | 38 | void EvaMixRender::rendFrame(GLuint videoTextureId, EvaAnimeConfig* config, EvaFrame* frame, EvaSrc* src) { 39 | if (videoTextureId <= 0) { 40 | ELOGE("rendFrame videoTextureId = 0"); 41 | return; 42 | } 43 | if (shader == nullptr) { 44 | ELOGE("rendFrame shader is null"); 45 | return; 46 | } 47 | if (config == nullptr) { 48 | ELOGE("rendFrame config is null"); 49 | return; 50 | } 51 | 52 | if (frame == nullptr) { 53 | ELOGE("rendFrame frame is null"); 54 | return; 55 | } 56 | 57 | if (src == nullptr) { 58 | ELOGE("rendFrame src is null"); 59 | return; 60 | } 61 | shader->useProgram(); 62 | //定点坐标 坐标归一 63 | vertexArray->setArray(VertexUtil::create(config->width, config->height, frame->frame, vertexArray->array)); 64 | vertexArray->setVertexAttribPointer(shader->aPositionLocation); 65 | 66 | //src纹理坐标 坐标归一 67 | float* array = genSrcCoordsArray(srcArray->array, frame->frame->w, frame->frame->h, src->bitmapWidth, src->bitmapHeight, src->fitType); 68 | srcArray->setArray(array); 69 | srcArray->setVertexAttribPointer(shader->aTextureSrcCoordinatesLocation); 70 | 71 | //绑定src纹理 72 | glActiveTexture(GL_TEXTURE0); 73 | glBindTexture(GL_TEXTURE_2D, src->srcTextureId); 74 | glUniform1i(shader->uTextureSrcUnitLocation, 0); 75 | 76 | //mask纹理 只有r通道为透明值 77 | maskArray->setArray(TexCoordsUtil::create(config->videoWidth, config->videoHeight, frame->mFrame, maskArray->array)); 78 | if (frame->mt == 90) { 79 | maskArray->setArray(TexCoordsUtil::rotate90(maskArray->array)); 80 | } 81 | 82 | maskArray->setVertexAttribPointer(shader->aTextureMaskCoordinatesLocation); 83 | //绑定mask纹理 84 | glActiveTexture(GL_TEXTURE1); 85 | glBindTexture(GL_TEXTURE_EXTERNAL_OES, videoTextureId); 86 | glUniform1i(shader->uTextureMaskUnitLocation, 1); 87 | 88 | //属性处理 89 | glUniform1i(shader->uIsFillLocation, 0); 90 | glUniform4f(shader->uColorLocation, 0, 0, 0, 0); 91 | //启动混合 92 | glEnable(GL_BLEND); 93 | //基于alpha通道的半透明混合函数 94 | //void glBlendFuncSeparate(GLenum srcRGB, 95 | // GLenum dstRGB, 96 | // GLenum srcAlpha, 97 | // GLenum dstAlpha); 98 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 99 | //绘制 100 | glDrawArrays(GL_TRIANGLE_STRIP,0, 4); 101 | //关闭混合 102 | glDisable(GL_BLEND); 103 | } 104 | 105 | void EvaMixRender::release(GLuint textureId) { 106 | if (textureId != 0) { 107 | glDeleteTextures(1, &textureId); 108 | } 109 | } 110 | 111 | /** 112 | * 113 | * @param array 顶点数据 114 | * @param fw 帧宽 115 | * @param fh 帧高 116 | * @param sw 元素宽 117 | * @param sh 元素高 118 | * @param fitType 缩放模型 119 | * @return 120 | */ 121 | float *EvaMixRender::genSrcCoordsArray(float *array, int fw, int fh, int sw, int sh, 122 | EvaSrc::FitType fitType) { 123 | float* srcArray = nullptr; 124 | if (fitType == EvaSrc::FitType::CENTER_FULL) { //aspectFill 125 | if (fw ==0 || fh == 0) { 126 | //中心对齐,不拉伸 127 | int gw = (sw - fw) / 2; 128 | int gh = (sh - fh) / 2; 129 | srcArray = TexCoordsUtil::create(sw, sh, new PointRect(gw, gh, fw, fh), array); 130 | } else { //centerCrop 131 | float fScale = float(fw) / fh; 132 | float sScale = float(sw) / sh; 133 | PointRect *srcRect; 134 | if (fScale > sScale) { 135 | int w = sw; 136 | int x = 0; 137 | int h = int(sw / fScale); 138 | int y = (sh - h) / 2; 139 | srcRect = new PointRect(x, y, w, h); 140 | } else { 141 | int h = sh; 142 | int y = 0; 143 | int w = int(sh * fScale); 144 | int x = (sw - w) / 2; 145 | srcRect = new PointRect(x, y, w, h); 146 | } 147 | srcArray = TexCoordsUtil::create(sw, sh, srcRect, array); 148 | } 149 | } else if (fitType == EvaSrc::FitType::CENTER_FIT) { //aspectFit 150 | if (fw ==0 || fh == 0) { 151 | //中心对齐,不拉伸 152 | int gw = (sw - fw) / 2; 153 | int gh = (sh - fh) / 2; 154 | srcArray = TexCoordsUtil::create(sw, sh, new PointRect(gw, gh, fw, fh), array); 155 | } else { 156 | float fScale = float(fw) / fh; 157 | float sScale = float(sw) / sh; 158 | PointRect *srcRect; 159 | if (fScale < sScale) { 160 | int w = sw; 161 | int x = 0; 162 | int h = int(sw / fScale); 163 | int y = (sh - h) / 2; 164 | srcRect = new PointRect(x, y, w, h); 165 | } else { 166 | int h = sh; 167 | int y = 0; 168 | int w = int(sh * fScale); 169 | int x = (sw - w) / 2; 170 | srcRect = new PointRect(x, y, w, h); 171 | } 172 | srcArray = TexCoordsUtil::create(sw, sh, srcRect, array); 173 | } 174 | } else { //scaleFill 175 | srcArray = TexCoordsUtil::create(fw, fh, new PointRect(0, 0, fw, fh), array); 176 | } 177 | return srcArray; 178 | } 179 | 180 | float *EvaMixRender::transColor(int color) { 181 | auto* argb = new float[4]; 182 | unsigned int c = color; 183 | argb[0] = float((c >> 24) & 0x000000ff) / 255.0; 184 | argb[1] = float((c >> 16) & 0x000000ff) / 255.0; 185 | argb[2] = float((c >> 8) & 0x000000ff) / 255.0; 186 | argb[3] = float(c & 0x000000ff) / 255.0; 187 | return argb; 188 | } 189 | 190 | //转换RGB颜色 color string转为color RGB色值 191 | float *EvaMixRender::transColor(std::string color) { 192 | if (color == "") { 193 | auto* argb = new float[4]{1.0f}; 194 | return argb; 195 | } 196 | // int num = std::stoi(color, NULL, 16); 197 | // int num = std::stoi(color, nullptr, 16); 198 | // auto* argb = new float[4]; 199 | // argb[0] = float((num >> 24) & 0xff) / 255.0; 200 | // argb[0] = float((num >> 16) & 0xff) / 255.0; 201 | // argb[0] = float((num >> 8) & 0xff) / 255.0; 202 | // argb[0] = float(num & 0xff) / 255.0; 203 | 204 | if (color[0] == '#') { 205 | color.erase(0, 1); 206 | } 207 | unsigned long num = stoul(color, nullptr,16); 208 | auto* argb = new float[4]; 209 | argb[0] = float((num >> 24) & 0xff) / 255.0; 210 | argb[1] = float((num >> 16) & 0xff) / 255.0; 211 | argb[2] = float((num >> 8) & 0xff) / 255.0; 212 | argb[3] = float(num & 0xff) / 255.0; 213 | return argb; 214 | } 215 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/mix/evamixrender.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | #include 5 | #include 6 | #include "bean/evasrcmap.h" 7 | #include "util/texcoordsutil.h" 8 | #include "util/textureloadutil.h" 9 | #include "util/vertexutil.h" 10 | #include 11 | #include "src/main/cpp/bean/evaanimeconfig.h" 12 | #include "mixshader.h" 13 | 14 | 15 | #ifndef YYEVA_EVAMIXRENDER_H 16 | #define YYEVA_EVAMIXRENDER_H 17 | 18 | using namespace std; 19 | class EvaMixRender { 20 | public: 21 | MixShader* shader; 22 | GlFloatArray* vertexArray = new GlFloatArray(); 23 | GlFloatArray* srcArray = new GlFloatArray(); 24 | GlFloatArray* maskArray = new GlFloatArray(); 25 | 26 | EvaMixRender(); 27 | ~EvaMixRender(); 28 | void init(EvaSrcMap* evaSrcMap); 29 | void rendFrame(GLuint videoTextureId, EvaAnimeConfig* config, EvaFrame* frame, EvaSrc* src); 30 | void release(GLuint textureId); 31 | 32 | private: 33 | float* genSrcCoordsArray(float* array, int fw, int fh, int sw, int sh, EvaSrc::FitType fitType); 34 | float* transColor(int color); 35 | float* transColor(string color); 36 | }; 37 | 38 | 39 | #endif //YYEVA_EVAMIXRENDER_H 40 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/mix/mixshader.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/20. 3 | // 4 | 5 | #include "mixshader.h" 6 | 7 | MixShader::MixShader() { 8 | char VERTEX[] = "attribute vec4 a_Position; \n" 9 | "attribute vec2 a_TextureSrcCoordinates;\n" 10 | "attribute vec2 a_TextureMaskCoordinates;\n" 11 | "varying vec2 v_TextureSrcCoordinates;\n" 12 | "varying vec2 v_TextureMaskCoordinates;\n" 13 | "void main()\n" 14 | "{\n" 15 | " v_TextureSrcCoordinates = a_TextureSrcCoordinates;\n" 16 | " v_TextureMaskCoordinates = a_TextureMaskCoordinates;\n" 17 | " gl_Position = a_Position;\n" 18 | "}"; 19 | 20 | char FRAGMENT[] = "#extension GL_OES_EGL_image_external : require\n" 21 | "precision mediump float; \n" 22 | "uniform sampler2D u_TextureSrcUnit;\n" 23 | "uniform samplerExternalOES u_TextureMaskUnit;\n" 24 | "uniform int u_isFill;\n" 25 | "uniform vec4 u_Color;\n" 26 | "varying vec2 v_TextureSrcCoordinates;\n" 27 | "varying vec2 v_TextureMaskCoordinates;\n" 28 | "void main()\n" 29 | "{\n" 30 | " vec4 srcRgba = texture2D(u_TextureSrcUnit, v_TextureSrcCoordinates);\n" 31 | " vec4 maskRgba = texture2D(u_TextureMaskUnit, v_TextureMaskCoordinates);\n" 32 | " float isFill = step(0.5, float(u_isFill));\n" 33 | " vec4 srcRgbaCal = isFill * vec4(u_Color.r, u_Color.g, u_Color.b, srcRgba.a) + (1.0 - isFill) * srcRgba;\n" 34 | " gl_FragColor = vec4(srcRgbaCal.r, srcRgbaCal.g, srcRgbaCal.b, srcRgba.a * maskRgba.r);\n" 35 | "}"; 36 | 37 | program = ShaderUtil::createProgram(VERTEX, FRAGMENT); 38 | uTextureSrcUnitLocation = glGetUniformLocation(program, U_TEXTURE_SRC_UNIT); 39 | uTextureMaskUnitLocation = glGetUniformLocation(program, U_TEXTURE_MASK_UNIT); 40 | //是否填充字体颜色 41 | uIsFillLocation = glGetUniformLocation(program, U_IS_FILL); 42 | //填充颜色 43 | uColorLocation = glGetUniformLocation(program, U_COLOR); 44 | 45 | aPositionLocation = glGetAttribLocation(program, A_POSITION); 46 | aTextureSrcCoordinatesLocation = glGetAttribLocation(program, A_TEXTURE_SRC_COORDINATES); 47 | aTextureMaskCoordinatesLocation = glGetAttribLocation(program, A_TEXTURE_MASK_COORDINATES); 48 | } 49 | 50 | MixShader::~MixShader() { 51 | 52 | } 53 | 54 | void MixShader::useProgram() { 55 | glUseProgram(program); 56 | } 57 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/mix/mixshader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/20. 3 | // 4 | 5 | #include "string" 6 | #include "src/main/cpp/util/shaderutil.h" 7 | 8 | #ifndef YYEVA_MIXSHADER_H 9 | #define YYEVA_MIXSHADER_H 10 | 11 | using namespace std; 12 | class MixShader { 13 | private: 14 | // Uniform constants 15 | const GLchar *U_TEXTURE_SRC_UNIT = "u_TextureSrcUnit"; 16 | const GLchar *U_TEXTURE_MASK_UNIT = "u_TextureMaskUnit"; 17 | const GLchar *U_IS_FILL = "u_isFill"; 18 | const GLchar *U_COLOR = "u_Color"; 19 | 20 | // Attribute constants 21 | const GLchar *A_POSITION = "a_Position"; 22 | const GLchar *A_TEXTURE_SRC_COORDINATES = "a_TextureSrcCoordinates"; 23 | const GLchar *A_TEXTURE_MASK_COORDINATES = "a_TextureMaskCoordinates"; 24 | 25 | public: 26 | GLuint program; 27 | GLint uTextureSrcUnitLocation; 28 | GLint uTextureMaskUnitLocation; 29 | GLint uIsFillLocation; 30 | GLint uColorLocation; 31 | 32 | GLint aPositionLocation; 33 | GLint aTextureSrcCoordinatesLocation; 34 | GLint aTextureMaskCoordinatesLocation; 35 | 36 | 37 | MixShader(); 38 | ~MixShader(); 39 | void useProgram(); 40 | }; 41 | 42 | 43 | #endif //YYEVA_MIXSHADER_H 44 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/glfloatarray.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by asus on 2022/4/17. 3 | // 4 | 5 | #include "glfloatarray.h" 6 | 7 | GlFloatArray::GlFloatArray() { 8 | 9 | } 10 | 11 | GlFloatArray::~GlFloatArray() { 12 | 13 | } 14 | 15 | void GlFloatArray::setArray(GLfloat* a) { 16 | *array = *a; 17 | } 18 | void GlFloatArray::setVertexAttribPointer(GLuint attributeLocation) { 19 | glVertexAttribPointer(attributeLocation, 2, GL_FLOAT, GL_FALSE, 0, array); 20 | glEnableVertexAttribArray(attributeLocation); 21 | } 22 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/glfloatarray.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by asus on 2022/4/17. 3 | // 4 | 5 | //#include 6 | //#include 7 | #include 8 | #include 9 | 10 | #ifndef YYEVA_GLFLOATARRAY_H 11 | #define YYEVA_GLFLOATARRAY_H 12 | 13 | class GlFloatArray { 14 | public: 15 | GlFloatArray(); 16 | ~GlFloatArray(); 17 | void setArray(float array[]); 18 | void setVertexAttribPointer(GLuint attributeLocation); 19 | GLfloat array[8]; 20 | 21 | private: 22 | char* floatBuffer; 23 | }; 24 | #endif -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/shaderutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by asus on 2022/4/17. 3 | // 4 | #include "shaderutil.h" 5 | #include 6 | 7 | #define LOG_TAG "ShareUtil" 8 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 9 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 10 | 11 | GLuint ShaderUtil::createProgram(string vertexSource, string fragmentSource) { 12 | GLuint vertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexSource.c_str()); 13 | GLuint fragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentSource.c_str()); 14 | return createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle); 15 | } 16 | 17 | GLuint ShaderUtil::createProgram(char* vertexSource, char* fragmentSource) { 18 | GLuint vertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexSource); 19 | GLuint fragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentSource); 20 | return createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle); 21 | } 22 | 23 | GLuint ShaderUtil::compileShader(GLenum shaderType, const char* shaderSource) { 24 | GLint shaderHandle = glCreateShader(shaderType); 25 | if (shaderHandle != 0) { 26 | GLint compiled; 27 | glShaderSource(shaderHandle, 1, 28 | &shaderSource, nullptr); 29 | glCompileShader(shaderHandle); 30 | glGetShaderiv(shaderHandle,GL_COMPILE_STATUS,&compiled); 31 | if(!compiled){ 32 | GLint infoLen = 0; 33 | glGetShaderiv(shaderHandle,GL_INFO_LOG_LENGTH,&infoLen); 34 | 35 | if(infoLen >1){ 36 | char *infoLog= (char*)malloc(sizeof(char*) *infoLen); 37 | glGetShaderInfoLog(shaderHandle,infoLen,NULL,infoLog); 38 | ELOGE("Error compiling shader:[%s]",infoLog); 39 | free(infoLog); 40 | } 41 | glDeleteShader(shaderHandle); 42 | return 0; 43 | } 44 | return shaderHandle; 45 | } else { 46 | GLint infoLen = 0; 47 | glGetShaderiv(shaderHandle,GL_INFO_LOG_LENGTH,&infoLen); 48 | if(infoLen >1){ 49 | char *infoLog= (char*)malloc(sizeof(char*) *infoLen); 50 | glGetShaderInfoLog(shaderHandle,infoLen,NULL,infoLog); 51 | ELOGE("Error create shader:[%s]",infoLog); 52 | free(infoLog); 53 | } 54 | ELOGE("Error create shader"); 55 | } 56 | return 0; 57 | } 58 | 59 | GLuint ShaderUtil::createAndLinkProgram(GLuint vertexShaderHandle, GLuint fragmentShaderHandle) { 60 | GLuint iProgId = glCreateProgram(); 61 | if (iProgId == 0){ 62 | ELOGE("create program failed"); 63 | return 0; 64 | } 65 | glAttachShader(iProgId,vertexShaderHandle); 66 | glAttachShader(iProgId,fragmentShaderHandle); 67 | 68 | //链接shader到程序 69 | glLinkProgram(iProgId); 70 | 71 | GLint linked; 72 | glGetProgramiv(iProgId,GL_LINK_STATUS,&linked); 73 | if (!linked) { 74 | GLint infoLen = 0; 75 | glGetProgramiv(iProgId, GL_INFO_LOG_LENGTH, &infoLen); 76 | if (infoLen > 1) { 77 | char *infoLog = (char *) malloc(sizeof(char) * infoLen); 78 | glGetProgramInfoLog(iProgId, infoLen, nullptr, infoLog); 79 | ELOGE("loadProgram failed: %s", infoLog); 80 | free(infoLog); 81 | } 82 | 83 | glDeleteProgram(iProgId); 84 | return 0; 85 | } 86 | glDeleteShader(vertexShaderHandle); 87 | glDeleteShader(fragmentShaderHandle); 88 | return iProgId; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/shaderutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by asus on 2022/4/17. 3 | // 4 | #include 5 | //#include 6 | //#include 7 | #include 8 | #include 9 | 10 | #ifndef YYEVA_SHADERUTIL_H 11 | #define YYEVA_SHADERUTIL_H 12 | 13 | using namespace std; 14 | class ShaderUtil { 15 | public: 16 | static GLuint createProgram(string vertexSource, string fragmentSource); 17 | 18 | static GLuint createProgram(char* vertexSource, char* fragmentSource); 19 | 20 | static GLuint compileShader(GLuint shaderType, const char* shaderSource); 21 | 22 | static GLuint createAndLinkProgram(GLuint vertexShaderHandle, GLuint fragmentShaderHandle); 23 | }; 24 | 25 | #endif -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/texcoordsutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | 5 | #include "texcoordsutil.h" 6 | 7 | float* TexCoordsUtil::create(int width, int height, PointRect* rect, float* array) { 8 | //x0 9 | array[0] = (float) rect->x / width; 10 | //y0 11 | array[1] = (float) rect->y / height; 12 | //x1 13 | array[2] = (float) rect->x / width; 14 | //y1 15 | array[3] = ((float)rect->y + rect->h) / height; 16 | 17 | //x2 18 | array[4] = ((float)rect->x + rect->w) / width; 19 | //y2 20 | array[5] = (float) rect->y / height; 21 | //x3 22 | array[6] = ((float)rect->x + rect->w) / width; 23 | //y3 24 | array[7] = ((float)rect->y + rect->h) / height; 25 | 26 | return array; 27 | } 28 | 29 | float* TexCoordsUtil::rotate90(float* array) { 30 | // 0->2 1->0 3->1 2->3 31 | float tx = array[0]; 32 | float ty = array[1]; 33 | // 1->0 34 | array[0] = array[2]; 35 | array[1] = array[3]; 36 | 37 | // 3->1 38 | array[2] = array[6]; 39 | array[3] = array[7]; 40 | 41 | // 2->3 42 | array[6] = array[4]; 43 | array[7] = array[5]; 44 | 45 | // 0->2 46 | array[4] = tx; 47 | array[5] = ty; 48 | 49 | return array; 50 | } 51 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/texcoordsutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | 5 | #include "src/main/cpp/bean/pointrect.h" 6 | 7 | #ifndef YYEVA_TEXCOORDSUTIL_H 8 | #define YYEVA_TEXCOORDSUTIL_H 9 | 10 | class TexCoordsUtil { 11 | public: 12 | 13 | static float* create(int width, int height, PointRect* rect, float* array); 14 | 15 | static float* rotate90(float* array); 16 | 17 | }; 18 | 19 | #endif //YYEVA_TEXCOORDSUTIL_H 20 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/textureloadutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/21. 3 | // 4 | 5 | 6 | 7 | #include "textureloadutil.h" 8 | #define STB_IMAGE_WRITE_IMPLEMENTATION 9 | 10 | 11 | #define LOG_TAG "TextureLoadUtil" 12 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 13 | #define ELOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) 14 | 15 | GLuint TextureLoadUtil::loadTexture(unsigned char *bitmap, AndroidBitmapInfo* info) { 16 | if (bitmap == nullptr) { 17 | ELOGE("bitmap = null"); 18 | return 0; 19 | } 20 | 21 | GLuint textureObjectIds = 0; 22 | glGenTextures(1, &textureObjectIds); 23 | if (textureObjectIds == 0) { 24 | ELOGE("textureObjectIds = 0"); 25 | return 0; 26 | } 27 | 28 | glBindTexture(GL_TEXTURE_2D, textureObjectIds); 29 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 30 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 31 | //纹理放大缩小使用线性插值 32 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 33 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 34 | //超出的部份会重复纹理坐标的边缘,产生一种边缘被拉伸的效果,s/t相当于x/y轴坐标 35 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); 36 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); 37 | 38 | GLint format = GL_RGB; 39 | if (info->format == ANDROID_BITMAP_FORMAT_RGB_565) { //RGB三通道,例如jpg格式 40 | format = GL_RGB; 41 | } else if (info->format == ANDROID_BITMAP_FORMAT_RGBA_8888) { //RGBA四通道,例如png格式 android图片是ARGB排列,所以还是要做图片转换的 42 | format = GL_RGBA; 43 | } 44 | //将图片数据生成一个2D纹理 45 | glTexImage2D(GL_TEXTURE_2D, 0, format, info->width, info->height, 0, format, GL_UNSIGNED_BYTE, 46 | bitmap); 47 | 48 | glGenerateMipmap(GL_TEXTURE_2D); 49 | glBindTexture(GL_TEXTURE_2D, 0); 50 | return textureObjectIds; 51 | } 52 | 53 | GLuint TextureLoadUtil::loadTexture(EvaSrc* src) { 54 | if (src->bitmap == nullptr) { 55 | ELOGE("bitmap = null"); 56 | return 0; 57 | } 58 | 59 | GLuint textureObjectIds = 0; 60 | glGenTextures(1, &textureObjectIds); 61 | if (textureObjectIds == 0) { 62 | ELOGE("textureObjectIds = 0"); 63 | return 0; 64 | } 65 | 66 | glBindTexture(GL_TEXTURE_2D, textureObjectIds); 67 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 68 | // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 69 | //纹理放大缩小使用线性插值 70 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 71 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 72 | //超出的部份会重复纹理坐标的边缘,产生一种边缘被拉伸的效果,s/t相当于x/y轴坐标 73 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER_EXT); 74 | glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER_EXT); 75 | 76 | GLint format = GL_RGB; 77 | if (src->bitmapFormat == ANDROID_BITMAP_FORMAT_RGB_565) { //RGB三通道,例如jpg格式 78 | format = GL_RGB; 79 | } else if (src->bitmapFormat == ANDROID_BITMAP_FORMAT_RGBA_8888) { //RGBA四通道,例如png格式 android图片是ARGB排列,所以还是要做图片转换的 80 | format = GL_RGBA; 81 | } 82 | //像素对齐,不然jpg图片会显示错位 83 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 84 | //将图片数据生成一个2D纹理 85 | glTexImage2D(GL_TEXTURE_2D, 0, format, src->bitmapWidth, src->bitmapHeight, 0, format, GL_UNSIGNED_BYTE, 86 | src->bitmap); 87 | 88 | glGenerateMipmap(GL_TEXTURE_2D); 89 | glBindTexture(GL_TEXTURE_2D, 0); 90 | // 91 | // if (stbi_write_png(src->saveAddress.c_str(), src->bitmapWidth, src->bitmapHeight, 4, 92 | // src->bitmap, 0)) { 93 | // ELOGV("save address = %s success", src->saveAddress.c_str()); 94 | // } else { 95 | // ELOGE("save address = %s fail", src->saveAddress.c_str()); 96 | // } 97 | src->srcTextureId = textureObjectIds; 98 | return textureObjectIds; 99 | } 100 | 101 | void TextureLoadUtil::releaseTexture(GLuint textureId) { 102 | if (textureId != 0) { 103 | glDeleteTextures(1,&textureId); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/textureloadutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/21. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef YYEVA_TEXTURELOADUTIL_H 14 | #define YYEVA_TEXTURELOADUTIL_H 15 | 16 | 17 | class TextureLoadUtil { 18 | public: 19 | static GLuint loadTexture(unsigned char* bitmap, AndroidBitmapInfo* info); 20 | static GLuint loadTexture(EvaSrc* src); 21 | static void releaseTexture(GLuint textureId); 22 | }; 23 | 24 | 25 | #endif //YYEVA_TEXTURELOADUTIL_H 26 | -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/vertexutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | #include "src/main/cpp/util/vertexutil.h" 5 | #include "android/log.h" 6 | 7 | #define LOG_TAG "VertexUtil" 8 | #define ELOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 9 | 10 | float* VertexUtil::create(int width, int height, PointRect* rect, float *array) { 11 | if (array == nullptr) { 12 | ELOGE("array is null"); 13 | return array; 14 | } 15 | //x0 16 | array[0] = switchX((float)rect->x / width); 17 | //y0 18 | array[1] = switchY((float)rect->y / height); 19 | //x1 20 | array[2] = switchX((float)rect->x / width); 21 | //y1 22 | array[3] = switchY(((float)rect->y + rect->h) / height); 23 | 24 | //x2 25 | array[4] = switchX(((float)rect->x + rect->w) / width); 26 | //y2 27 | array[5] = switchY( (float)rect->y / height); 28 | //x3 29 | array[6] = switchX(((float)rect->x + rect->w) / width); 30 | //y3 31 | array[7] = switchY(((float)rect->y + rect->h) / height); 32 | 33 | return array; 34 | } 35 | 36 | //顶点区域为-1~1 37 | float VertexUtil::switchX(float x) { 38 | return x * 2 - 1; 39 | } 40 | 41 | //顶点区域-1~1 42 | float VertexUtil::switchY(float y) { 43 | return ((y*2 - 2) * -1) - 1; 44 | } -------------------------------------------------------------------------------- /yyevac/src/main/cpp/util/vertexutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zengjiale on 2022/4/18. 3 | // 4 | #include "src/main/cpp/bean/pointrect.h" 5 | 6 | 7 | #ifndef YYEVA_VETEXUTIL_HPP 8 | #define YYEVA_VETEXUTIL_HPP 9 | 10 | class VertexUtil { 11 | public: 12 | static float* create(int width, int height, PointRect* rect, float* array); 13 | static float switchX(float x); 14 | static float switchY(float y); 15 | }; 16 | 17 | 18 | #endif //YYEVA_VETEXUTIL_HPP 19 | -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/EvaAnimPlayer.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva 2 | 3 | import android.util.Log 4 | import com.yy.yyeva.decoder.Decoder 5 | import com.yy.yyeva.decoder.EvaHardDecoder 6 | import com.yy.yyeva.file.IEvaFileContainer 7 | import com.yy.yyeva.inter.IEvaAnimListener 8 | import com.yy.yyeva.plugin.EvaAnimPluginManager 9 | import com.yy.yyeva.util.EvaConstant 10 | import com.yy.yyeva.util.ELog 11 | import com.yy.yyeva.view.EvaAudioPlayer 12 | import com.yy.yyeva.view.IEvaAnimView 13 | 14 | class EvaAnimPlayer(val evaAnimView: IEvaAnimView) { 15 | 16 | companion object { 17 | private const val TAG = "AnimPlayer" 18 | } 19 | @Volatile var controllerId = -1 //底层Native管理器id 20 | var evaAnimListener: IEvaAnimListener? = null 21 | var decoder: Decoder? = null 22 | var evaAudioPlayer: EvaAudioPlayer? = null 23 | var fps: Int = 0 24 | set(value) { 25 | decoder?.fps = value 26 | field = value 27 | } 28 | // 设置默认的fps <= 0 表示以mp4内配置为准 > 0 表示以此设置为准 29 | var defaultFps: Int = 30 30 | var isSetFps = false 31 | var audioSpeed = 1.0f //音频速度 32 | var playLoop: Int = 0 33 | set(value) { 34 | decoder?.playLoop = value 35 | evaAudioPlayer?.playLoop = value 36 | field = value 37 | } 38 | var supportMaskBoolean : Boolean = false 39 | var maskEdgeBlurBoolean : Boolean = false 40 | // 是否兼容老版本 默认不兼容 41 | var enableVersion1 : Boolean = false 42 | // 视频模式 43 | var videoMode: Int = EvaConstant.VIDEO_MODE_NORMAL_MP4 // 正常mp4 44 | var isDetachedFromWindow = false 45 | var isSurfaceAvailable = false 46 | var startRunnable: Runnable? = null 47 | var isStartRunning = false // 启动时运行状态 48 | var isMute = false // 是否静音 49 | var startPoint = 0L // 开启播放位置 50 | var sampleTime = 0L // sampleTime实际播放时间 51 | 52 | var isNormalMp4 = false //是否正常的不透明的mp4 53 | 54 | var isSetLastFrame = false //是否停留在最后一帧 55 | 56 | // val configManager = AnimConfigManager(this) 57 | val configManager = EvaAnimConfigManager(this) 58 | val pluginManager = EvaAnimPluginManager(this) 59 | 60 | fun onSurfaceTextureDestroyed() { 61 | isSurfaceAvailable = false 62 | isStartRunning = false 63 | startPoint = 0L 64 | decoder?.destroy() 65 | evaAudioPlayer?.destroy() 66 | } 67 | 68 | fun onSurfaceTextureAvailable(width: Int, height: Int) { 69 | isSurfaceAvailable = true 70 | startRunnable?.run() 71 | startRunnable = null 72 | } 73 | 74 | 75 | fun onSurfaceTextureSizeChanged(width: Int, height: Int) { 76 | decoder?.onSurfaceSizeChanged(width, height) 77 | } 78 | 79 | fun startPlay(evaFileContainer: IEvaFileContainer) { 80 | isStartRunning = true 81 | prepareDecoder() 82 | if (decoder?.prepareThread() == false) { 83 | isStartRunning = false 84 | decoder?.onFailed(EvaConstant.REPORT_ERROR_TYPE_CREATE_THREAD, EvaConstant.ERROR_MSG_CREATE_THREAD) 85 | decoder?.onVideoComplete() 86 | return 87 | } 88 | // 在线程中解析配置 89 | decoder?.renderThread?.handler?.post { 90 | val result = configManager.parseConfig(evaFileContainer, enableVersion1, videoMode, defaultFps) 91 | if (result != EvaConstant.OK) { 92 | isStartRunning = false 93 | decoder?.onFailed(result, EvaConstant.getErrorMsg(result)) 94 | decoder?.onVideoComplete() 95 | return@post 96 | } 97 | ELog.i(TAG, "parse ${configManager.config}") 98 | val config = configManager.config 99 | // 如果是默认配置,因为信息不完整onVideoConfigReady不会被调用 100 | if (config != null && (config.isDefaultConfig || evaAnimListener?.onVideoConfigReady(config) == true)) { 101 | innerStartPlay(evaFileContainer) 102 | } else { 103 | ELog.i(TAG, "onVideoConfigReady return false") 104 | } 105 | } 106 | } 107 | 108 | private fun innerStartPlay(evaFileContainer: IEvaFileContainer) { 109 | synchronized(EvaAnimPlayer::class.java) { 110 | if (isSurfaceAvailable) { 111 | Log.i(TAG, "decoder start") 112 | isStartRunning = false 113 | decoder?.start(evaFileContainer) 114 | if (!isMute) { 115 | evaAudioPlayer?.start(evaFileContainer) 116 | } 117 | } else { 118 | startRunnable = Runnable { 119 | innerStartPlay(evaFileContainer) 120 | } 121 | evaAnimView.prepareTextureView() 122 | } 123 | } 124 | } 125 | 126 | fun pause() { 127 | evaAudioPlayer?.pause() 128 | decoder?.pause() 129 | } 130 | 131 | fun resume() { 132 | evaAudioPlayer?.resume() 133 | decoder?.resume() 134 | } 135 | 136 | fun stopPlay() { 137 | decoder?.stop() 138 | evaAudioPlayer?.stop() 139 | } 140 | 141 | fun isRunning(): Boolean { 142 | ELog.i(TAG, "isStartRunning $isStartRunning, decoderRunning ${decoder?.isRunning?:false}") 143 | return isStartRunning // 启动过程运行状态 144 | || (decoder?.isRunning ?: false) // 解码过程运行状态 145 | } 146 | 147 | private fun prepareDecoder() { 148 | if (decoder == null) { 149 | decoder = EvaHardDecoder(this).apply { 150 | playLoop = this@EvaAnimPlayer.playLoop 151 | fps = this@EvaAnimPlayer.fps 152 | } 153 | } 154 | if (evaAudioPlayer == null) { 155 | evaAudioPlayer = EvaAudioPlayer(this).apply { 156 | playLoop = this@EvaAnimPlayer.playLoop 157 | } 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/IEvaRenderListener.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva 2 | 3 | interface IEvaRenderListener { 4 | 5 | /** 6 | * 初始化渲染环境,获取shader字段,创建绑定纹理 7 | */ 8 | fun initRender() 9 | 10 | /** 11 | * 渲染上屏 12 | */ 13 | fun renderFrame() 14 | 15 | fun clearFrame() 16 | 17 | /** 18 | * 释放纹理 19 | */ 20 | fun destroyRender() 21 | 22 | /** 23 | * 设置视频配置 24 | */ 25 | fun setAnimConfig(config: EvaAnimConfig) 26 | 27 | /** 28 | * 显示区域大小变化 29 | */ 30 | fun updateViewPort(width: Int, height: Int) {} 31 | 32 | fun getExternalTexture(): Int 33 | 34 | fun releaseTexture() 35 | 36 | fun swapBuffers() 37 | 38 | fun setYUVData(width: Int, height: Int, y: ByteArray?, u: ByteArray?, v: ByteArray?) {} 39 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/decoder/EvaDecoder.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.decoder 2 | 3 | import android.os.Build 4 | import android.os.HandlerThread 5 | import android.os.Handler 6 | import android.util.Log 7 | import com.yy.yyeva.EvaAnimConfig 8 | import com.yy.yyeva.EvaAnimPlayer 9 | import com.yy.yyeva.file.IEvaFileContainer 10 | import com.yy.yyeva.inter.IEvaAnimListener 11 | import com.yy.yyeva.util.EvaConstant 12 | import com.yy.yyeva.util.ELog 13 | import com.yy.yyeva.util.EvaJniUtil 14 | import com.yy.yyeva.util.SpeedControlUtil 15 | import org.json.JSONObject 16 | 17 | 18 | abstract class Decoder(val playerEva: EvaAnimPlayer) : IEvaAnimListener { 19 | 20 | companion object { 21 | private const val TAG = "${EvaConstant.TAG}.Decoder" 22 | 23 | fun createThread(handlerHolder: HandlerHolder, name: String): Boolean { 24 | try { 25 | if (handlerHolder.thread == null || handlerHolder.thread?.isAlive == false) { 26 | handlerHolder.thread = HandlerThread(name).apply { 27 | start() 28 | handlerHolder.handler = Handler(looper) 29 | } 30 | } 31 | return true 32 | } catch (e: OutOfMemoryError) { 33 | ELog.e(TAG, "createThread OOM", e) 34 | } 35 | return false 36 | } 37 | 38 | fun quitSafely(thread: HandlerThread?): HandlerThread? { 39 | thread?.apply { 40 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 41 | thread.quitSafely() 42 | } else { 43 | thread.quit() 44 | } 45 | } 46 | return null 47 | } 48 | } 49 | 50 | // var render: IRenderListener? = null 51 | val renderThread = HandlerHolder(null, null) 52 | val decodeThread = HandlerHolder(null, null) 53 | private var surfaceWidth = 0 54 | private var surfaceHeight = 0 55 | var fps: Int = 0 56 | set(value) { 57 | //设置帧率 58 | speedControlUtil.setFixedPlaybackRate(value) 59 | field = value 60 | } 61 | var playLoop = 0 // 循环播放次数 62 | var isRunning = false // 是否正在运行 63 | var isStopReq = false // 是否需要停止 64 | val speedControlUtil by lazy { SpeedControlUtil() } 65 | 66 | abstract fun start(evaFileContainer: IEvaFileContainer) 67 | 68 | fun stop() { 69 | Log.i(TAG, "stop true") 70 | isStopReq = true 71 | } 72 | 73 | abstract fun pause() 74 | 75 | abstract fun resume() 76 | 77 | abstract fun destroy() 78 | 79 | fun prepareThread(): Boolean { 80 | return createThread(renderThread, "anim_render_thread") && createThread(decodeThread, "anim_decode_thread") 81 | } 82 | 83 | fun prepareRender(needYUV: Boolean): Boolean { 84 | ELog.i(TAG, "prepareRender") 85 | playerEva.evaAnimView.getSurface()?.apply { 86 | playerEva.controllerId = EvaJniUtil.initRender(playerEva.controllerId, this, needYUV, playerEva.isNormalMp4) 87 | return true 88 | } 89 | return false 90 | } 91 | 92 | fun preparePlay(videoWidth: Int, videoHeight: Int) { 93 | playerEva.configManager.defaultConfig(videoWidth, videoHeight) 94 | playerEva.configManager.config?.apply { 95 | // render?.setAnimConfig(this) 96 | if (isDefaultConfig) { 97 | if (playerEva.isNormalMp4) { //正常MP4播放 98 | EvaJniUtil.defaultConfig( 99 | playerEva.controllerId, 100 | videoWidth, 101 | videoHeight, 102 | -1 //正常宽高 103 | ) 104 | } else { 105 | EvaJniUtil.defaultConfig( 106 | playerEva.controllerId, 107 | videoWidth, 108 | videoHeight, 109 | defaultVideoMode 110 | ) 111 | } 112 | playerEva.evaAnimListener?.onVideoConfigReady(this) 113 | playerEva.evaAnimView.updateTextureViewLayout() 114 | } else if (jsonConfig != null) { 115 | EvaJniUtil.setRenderConfig(playerEva.controllerId, jsonConfig.toString()) 116 | } 117 | } 118 | 119 | playerEva.pluginManager.onRenderCreate() 120 | } 121 | 122 | /** 123 | * decode过程中视频尺寸变化 124 | * 主要是没有16进制对齐的老视频 125 | */ 126 | fun videoSizeChange(newWidth: Int, newHeight: Int) { 127 | EvaJniUtil.videoSizeChange(playerEva.controllerId, newWidth,newHeight) 128 | } 129 | 130 | 131 | fun destroyThread() { 132 | if (playerEva.isDetachedFromWindow) { 133 | ELog.i(TAG, "destroyThread") 134 | renderThread.handler?.removeCallbacksAndMessages(null) 135 | decodeThread.handler?.removeCallbacksAndMessages(null) 136 | renderThread.thread = quitSafely(renderThread.thread) 137 | decodeThread.thread = quitSafely(decodeThread.thread) 138 | renderThread.handler = null 139 | decodeThread.handler = null 140 | } 141 | } 142 | 143 | fun onSurfaceSizeChanged(width: Int, height: Int) { 144 | surfaceWidth = width 145 | surfaceHeight = height 146 | EvaJniUtil.updateViewPoint(playerEva.controllerId, width,height) 147 | Log.i(TAG, "updateViewPoint $width, $height") 148 | } 149 | 150 | override fun onVideoStart() { 151 | ELog.i(TAG, "onVideoStart") 152 | playerEva.evaAnimListener?.onVideoStart() 153 | } 154 | 155 | override fun onVideoRestart() { 156 | ELog.i(TAG, "onVideoRestart") 157 | playerEva.evaAnimListener?.onVideoRestart() 158 | } 159 | 160 | override fun onVideoRender(frameIndex: Int, config: EvaAnimConfig?) { 161 | ELog.d(TAG, "onVideoRender") 162 | playerEva.evaAnimListener?.onVideoRender(frameIndex, config) 163 | } 164 | 165 | override fun onVideoComplete(lastFrame: Boolean) { 166 | ELog.i(TAG, "onVideoComplete") 167 | playerEva.evaAnimListener?.onVideoComplete(lastFrame) 168 | } 169 | 170 | override fun onVideoDestroy() { 171 | ELog.i(TAG, "onVideoDestroy") 172 | playerEva.evaAnimListener?.onVideoDestroy() 173 | } 174 | 175 | override fun onFailed(errorType: Int, errorMsg: String?) { 176 | ELog.e(TAG, "onFailed errorType=$errorType, errorMsg=$errorMsg") 177 | playerEva.evaAnimListener?.onFailed(errorType, errorMsg) 178 | } 179 | } 180 | 181 | data class HandlerHolder(var thread: HandlerThread?, var handler: Handler?) -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/EvaAssetsEvaFileContainer.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.content.res.AssetFileDescriptor 4 | import android.content.res.AssetManager 5 | import android.media.MediaExtractor 6 | import com.yy.yyeva.util.EvaConstant 7 | import com.yy.yyeva.util.ELog 8 | import java.io.File 9 | 10 | class EvaAssetsEvaFileContainer(assetManager: AssetManager, val assetsPath: String): IEvaFileContainer { 11 | 12 | companion object { 13 | private const val TAG = "${EvaConstant.TAG}.FileContainer" 14 | } 15 | 16 | private val assetFd: AssetFileDescriptor = assetManager.openFd(assetsPath) 17 | private val assetsInputStream: AssetManager.AssetInputStream = 18 | assetManager.open(assetsPath, AssetManager.ACCESS_STREAMING) as AssetManager.AssetInputStream 19 | var f: File 20 | private var md5 = "" 21 | 22 | init { 23 | ELog.i(TAG, "AssetsFileContainer init") 24 | f = File(assetsPath) 25 | } 26 | 27 | override fun setDataSource(extractor: MediaExtractor) { 28 | if (assetFd.declaredLength < 0) { 29 | extractor.setDataSource(assetFd.fileDescriptor) 30 | } else { 31 | extractor.setDataSource(assetFd.fileDescriptor, assetFd.startOffset, assetFd.declaredLength) 32 | } 33 | } 34 | 35 | override fun startRandomRead() { 36 | } 37 | 38 | override fun read(b: ByteArray, off: Int, len: Int): Int { 39 | return assetsInputStream.read(b, off, len) 40 | } 41 | 42 | override fun skip(pos: Long) { 43 | assetsInputStream.skip(pos) 44 | } 45 | 46 | override fun closeRandomRead() { 47 | assetsInputStream.close() 48 | } 49 | 50 | override fun close() { 51 | assetFd.close() 52 | assetsInputStream.close() 53 | } 54 | 55 | override fun getFile(): File { 56 | return f 57 | } 58 | 59 | override fun getMd5(): String { 60 | if (md5.isEmpty()) { 61 | md5 = FileUtil.getFileMD5(f)?: "" 62 | } 63 | return md5 64 | } 65 | 66 | override fun setEvaJson(json: String) { 67 | EvaPref.setEvaJson(f.name, getMd5(), json) 68 | } 69 | 70 | override fun getEvaJson(): String? { 71 | return EvaPref.getEvaJson(getMd5()) 72 | } 73 | 74 | override fun setEvaMp4Type(type: Int) { 75 | EvaPref.setEvaMp4Type(f.name, getMd5(), type) 76 | } 77 | 78 | override fun getEvaMp4Type(): Int { 79 | return EvaPref.getEvaMp4Type(getMd5()) 80 | } 81 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/EvaFileContainer.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.media.MediaExtractor 4 | import com.yy.yyeva.util.EvaConstant 5 | import com.yy.yyeva.util.ELog 6 | import java.io.File 7 | import java.io.FileNotFoundException 8 | import java.io.RandomAccessFile 9 | 10 | class EvaFileContainer(private val file: File) : IEvaFileContainer { 11 | 12 | companion object { 13 | private const val TAG = "${EvaConstant.TAG}.EvaFileContainer" 14 | } 15 | 16 | private var randomAccessFile: RandomAccessFile? = null 17 | private var md5 = "" 18 | 19 | init { 20 | ELog.i(TAG, "FileContainer init") 21 | if (!(file.exists() && file.isFile && file.canRead())) throw FileNotFoundException("Unable to read $file") 22 | } 23 | 24 | override fun setDataSource(extractor: MediaExtractor) { 25 | extractor.setDataSource(file.toString()) 26 | } 27 | 28 | override fun startRandomRead() { 29 | randomAccessFile = RandomAccessFile(file, "r") 30 | } 31 | 32 | override fun read(b: ByteArray, off: Int, len: Int): Int { 33 | return randomAccessFile?.read(b, off, len) ?: -1 34 | } 35 | 36 | override fun skip(pos: Long) { 37 | randomAccessFile?.skipBytes(pos.toInt()) 38 | } 39 | 40 | override fun closeRandomRead() { 41 | randomAccessFile?.close() 42 | } 43 | 44 | override fun close() { 45 | } 46 | 47 | override fun getFile(): File { 48 | return file 49 | } 50 | 51 | override fun getMd5(): String { 52 | if (md5.isEmpty()) { 53 | md5 = FileUtil.getFileMD5(file)?: "" 54 | } 55 | return md5 56 | } 57 | 58 | override fun setEvaJson(json: String) { 59 | EvaPref.setEvaJson(file.name, getMd5(), json) 60 | } 61 | 62 | override fun getEvaJson(): String? { 63 | return EvaPref.getEvaJson(getMd5()) 64 | } 65 | 66 | override fun setEvaMp4Type(type: Int) { 67 | EvaPref.setEvaMp4Type(file.name, getMd5(), type) 68 | } 69 | 70 | override fun getEvaMp4Type(): Int { 71 | return EvaPref.getEvaMp4Type(getMd5()) 72 | } 73 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/EvaPref.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import com.yy.yyeva.util.ELog 6 | import com.yy.yyeva.util.EvaConstant.VIDEO_MODE_NORMAL_MP4_NONE 7 | 8 | object EvaPref { 9 | val TAG = "EvaPref" 10 | 11 | var sp: SharedPreferences? = null 12 | 13 | fun init(c: Context) { 14 | if (sp == null) { 15 | sp = c.getSharedPreferences(TAG, Context.MODE_PRIVATE) 16 | } 17 | } 18 | 19 | fun getEvaJson(md5: String?): String? { 20 | if (sp == null) { 21 | ELog.e(TAG, "EvaPref not init") 22 | return null 23 | } 24 | 25 | if (md5 == null) { 26 | ELog.e(TAG, "md5$md5 is null") 27 | return null 28 | } 29 | 30 | return sp?.getString("yyeva_json_$md5", "") 31 | } 32 | 33 | fun setEvaJson(fileName: String, md5: String, json: String) { 34 | ELog.i(TAG, "setEvaJson fileName: $fileName, md5: $md5") 35 | sp?.edit()?.putString("yyeva_json_$md5", json)?.apply() 36 | } 37 | 38 | fun setEvaMp4Type(fileName: String, md5: String, type: Int) { 39 | ELog.i(TAG, "setEvaMp4Type fileName: $fileName, md5: $md5, type: $type") 40 | sp?.edit()?.putInt("yyeva_mp4_type_$md5", type)?.apply() 41 | } 42 | 43 | fun getEvaMp4Type(md5: String?): Int { 44 | if (sp == null) { 45 | ELog.e(TAG, "EvaPref not init") 46 | return VIDEO_MODE_NORMAL_MP4_NONE 47 | } 48 | 49 | if (md5 == null) { 50 | ELog.e(TAG, "md5$md5 is null") 51 | return VIDEO_MODE_NORMAL_MP4_NONE 52 | } 53 | 54 | return sp?.getInt("yyeva_mp4_type_$md5", VIDEO_MODE_NORMAL_MP4_NONE) ?: VIDEO_MODE_NORMAL_MP4_NONE 55 | } 56 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/EvaStreamContainerEva.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.annotation.TargetApi 4 | import android.media.MediaExtractor 5 | import android.os.Build 6 | import java.io.ByteArrayInputStream 7 | import java.io.File 8 | 9 | @TargetApi(Build.VERSION_CODES.M) 10 | class EvaStreamContainerEva(private val bytes: ByteArray) : IEvaFileContainer { 11 | 12 | private var stream: ByteArrayInputStream = ByteArrayInputStream(bytes) 13 | 14 | override fun setDataSource(extractor: MediaExtractor) { 15 | val dataSource = EvaStreamMediaDataSource(bytes) 16 | extractor.setDataSource(dataSource) 17 | } 18 | 19 | override fun startRandomRead() { 20 | } 21 | 22 | override fun read(b: ByteArray, off: Int, len: Int): Int { 23 | return stream.read(b, off, len) 24 | } 25 | 26 | override fun skip(pos: Long) { 27 | stream.skip(pos) 28 | } 29 | 30 | override fun closeRandomRead() { 31 | } 32 | 33 | override fun close() { 34 | stream.close() 35 | } 36 | 37 | override fun getFile(): File? { 38 | return null 39 | } 40 | 41 | override fun getMd5(): String { 42 | return "" 43 | } 44 | 45 | override fun setEvaJson(json: String) { 46 | } 47 | 48 | override fun getEvaJson(): String? { 49 | return "" 50 | } 51 | 52 | override fun setEvaMp4Type(type: Int) { 53 | } 54 | 55 | override fun getEvaMp4Type(): Int { 56 | return -2 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/EvaStreamMediaDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.annotation.TargetApi 4 | import android.media.MediaDataSource 5 | import android.os.Build 6 | 7 | @TargetApi(Build.VERSION_CODES.M) 8 | class EvaStreamMediaDataSource(val bytes: ByteArray) : MediaDataSource() { 9 | 10 | override fun close() { 11 | } 12 | 13 | override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int { 14 | var newSize = size 15 | synchronized(EvaStreamMediaDataSource::class) { 16 | val length = bytes.size 17 | if (position >= length) { 18 | return -1 19 | } 20 | if (position + newSize > length) { 21 | newSize -= (position + newSize).toInt() - length 22 | } 23 | System.arraycopy(bytes, position.toInt(), buffer, offset, newSize) 24 | return newSize 25 | } 26 | 27 | } 28 | 29 | override fun getSize(): Long { 30 | synchronized(EvaStreamMediaDataSource::class) { 31 | return bytes.size.toLong() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.content.Context 4 | import com.yy.yyeva.util.ELog 5 | import java.io.* 6 | import java.security.MessageDigest 7 | 8 | object FileUtil { 9 | 10 | private val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') 11 | private val TAG = "FileUtil" 12 | 13 | fun copyAssetsToStorage(context: Context, dir: String, files: Array, loadSuccess:()->Unit) { 14 | Thread { 15 | var outputStream: OutputStream 16 | var inputStream: InputStream 17 | val buf = ByteArray(4096) 18 | val names = context.assets.list("") 19 | if (names == null) { 20 | ELog.e(TAG,"assets has no file") 21 | return@Thread 22 | } 23 | files.forEach { 24 | try { 25 | if (File("$dir/$it").exists()) { 26 | return@forEach 27 | } 28 | if (!names.contains(it)) { 29 | ELog.e(TAG,"assets has not $it") 30 | return@Thread 31 | } 32 | 33 | inputStream = context.assets.open(it) 34 | outputStream = FileOutputStream("$dir/$it") 35 | var length = inputStream.read(buf) 36 | while (length > 0) { 37 | outputStream.write(buf, 0, length) 38 | length = inputStream.read(buf) 39 | } 40 | outputStream.close() 41 | inputStream.close() 42 | } catch (e: IOException) { 43 | e.printStackTrace() 44 | return@Thread 45 | } 46 | } 47 | loadSuccess.invoke() 48 | }.start() 49 | 50 | } 51 | 52 | fun getFileMD5(file: File): String? { 53 | if (!file.exists() || !file.isFile || file.length() <= 0) { 54 | return null 55 | } 56 | var inputStream: InputStream? = null 57 | try { 58 | val md = MessageDigest.getInstance("MD5") 59 | inputStream = FileInputStream(file) 60 | val dataBytes = ByteArray(4096) 61 | var iRd: Int 62 | iRd = inputStream.read(dataBytes) 63 | while (iRd != -1) { 64 | md.update(dataBytes, 0, iRd) 65 | iRd = inputStream.read(dataBytes) 66 | } 67 | inputStream.close() 68 | val digest = md.digest() 69 | if (digest != null) { 70 | return bufferToHex(digest) 71 | } 72 | } catch (t: Throwable) { 73 | t.printStackTrace() 74 | } finally { 75 | if (inputStream != null) { 76 | try { 77 | inputStream.close() 78 | } catch (e: Throwable) { 79 | e.printStackTrace() 80 | } 81 | } 82 | } 83 | return null 84 | } 85 | 86 | private fun bufferToHex(bytes: ByteArray): String { 87 | return bufferToHex(bytes, 0, bytes.size) 88 | } 89 | 90 | private fun bufferToHex(bytes: ByteArray, m: Int, n: Int): String { 91 | val sb = StringBuffer(2 * n) 92 | val k = m + n 93 | for (l in m until k) { 94 | appendHexPair(bytes[l], sb) 95 | } 96 | return sb.toString() 97 | } 98 | 99 | private fun appendHexPair(bt: Byte, sb: StringBuffer) { 100 | val c0 = hexDigits[bt.toInt() and 0xf0 ushr 4] 101 | val c1 = hexDigits[bt.toInt() and 0x0f] 102 | sb.append(c0) 103 | sb.append(c1) 104 | } 105 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/file/IEvaFileContainer.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.file 2 | 3 | import android.media.MediaExtractor 4 | import java.io.File 5 | 6 | interface IEvaFileContainer { 7 | 8 | fun setDataSource(extractor: MediaExtractor) 9 | 10 | fun startRandomRead() 11 | 12 | fun read(b: ByteArray, off: Int, len: Int): Int 13 | 14 | fun skip(pos: Long) 15 | 16 | fun closeRandomRead() 17 | 18 | fun close() 19 | 20 | fun getFile(): File? 21 | 22 | fun getMd5(): String 23 | 24 | fun setEvaJson(json: String) 25 | 26 | fun getEvaJson(): String? 27 | 28 | fun setEvaMp4Type(type: Int) 29 | 30 | fun getEvaMp4Type(): Int 31 | 32 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/inter/IEvaAnimListener.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.inter 2 | 3 | import com.yy.yyeva.EvaAnimConfig 4 | 5 | interface IEvaAnimListener { 6 | 7 | /** 8 | * 配置准备好后回调 9 | * @return true 继续播放 false 结束播放 10 | */ 11 | fun onVideoConfigReady(config: EvaAnimConfig): Boolean { 12 | return true // 默认继续播放 13 | } 14 | 15 | /** 16 | * 开始播放 17 | */ 18 | fun onVideoStart() 19 | 20 | /** 21 | * 循环播放开始 22 | */ 23 | fun onVideoRestart() 24 | 25 | 26 | /** 27 | * 视频渲染每一帧时的回调 28 | * @param frameIndex 帧索引 29 | */ 30 | fun onVideoRender(frameIndex: Int, config: EvaAnimConfig?) 31 | 32 | /** 33 | * 视频播放结束 34 | */ 35 | fun onVideoComplete(lastFrame: Boolean = false) 36 | 37 | /** 38 | * 视频被销毁 39 | */ 40 | fun onVideoDestroy() 41 | 42 | /** 43 | * 失败回调 44 | * @param errorType 错误类型 45 | * @param errorMsg 错误消息 46 | */ 47 | fun onFailed(errorType: Int, errorMsg: String?) 48 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/inter/IEvaFetchResource.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.inter 2 | 3 | import android.graphics.Bitmap 4 | import com.yy.yyeva.mix.EvaResource 5 | import com.yy.yyeva.mix.EvaSrc 6 | 7 | interface IEvaFetchResource { 8 | // 获取图片 (暂时不支持Bitmap.Config.ALPHA_8 主要是因为一些机型opengl兼容问题) 9 | fun setImage(resource: EvaResource, result:(Bitmap?, EvaSrc.FitType?) -> Unit) 10 | 11 | // 获取文字 12 | fun setText(resource: EvaResource, result:(EvaResource) -> Unit) 13 | 14 | // 资源释放通知 15 | fun releaseSrc(resources: List) 16 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/inter/OnEvaResourceClickListener.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.inter 2 | 3 | import com.yy.yyeva.mix.EvaResource 4 | 5 | interface OnEvaResourceClickListener { 6 | 7 | // 返回被点击的资源 8 | fun onClick(resource: EvaResource) 9 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/mix/EvaFrame.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.mix 2 | 3 | import android.util.SparseArray 4 | import com.yy.yyeva.EvaAnimConfig 5 | import com.yy.yyeva.util.PointRect 6 | 7 | /** 8 | * 单帧描述 9 | */ 10 | class EvaFrame(index: Int, d: EvaAnimConfig.Data) { 11 | var srcId = "" 12 | var z = 0 13 | var frame: PointRect 14 | var mFrame: PointRect 15 | var mt = 0 // 遮罩旋转角度v2 版本只支持 0 与 90度 16 | 17 | init { 18 | srcId = d.effectId.toString() 19 | z = index 20 | frame = d.renderFrame 21 | mFrame = d.outputFrame 22 | } 23 | } 24 | 25 | 26 | /** 27 | * 一帧的集合 28 | */ 29 | class EvaFrameSet(data: EvaAnimConfig.Datas) { 30 | var index = 0 // 哪一帧 31 | val list = ArrayList() 32 | init { 33 | index = data.frameIndex 34 | for (i in 0 until data.data.size) { 35 | val frame = EvaFrame(i, data.data[i]) 36 | list.add(frame) 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * 所有帧 43 | */ 44 | class EvaFrameAll(datas: MutableList) { 45 | // 每一帧的集合 46 | val map = SparseArray() 47 | 48 | init { 49 | for (data in datas) { 50 | val frameSet = EvaFrameSet(data) 51 | map.put(frameSet.index, frameSet) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/mix/EvaMixTouch.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.mix 2 | 3 | import android.view.MotionEvent 4 | import com.yy.yyeva.util.PointRect 5 | 6 | /** 7 | * 触摸事件拦截 8 | */ 9 | class EvaMixTouch(private val mixAnimPlugin: EvaMixAnimPlugin) { 10 | 11 | fun onTouchEvent(ev: MotionEvent): EvaResource? { 12 | val (viewWith, viewHeight) = mixAnimPlugin.playerEva.evaAnimView.getRealSize() 13 | val videoWith = mixAnimPlugin.playerEva.configManager.config?.width ?: return null 14 | val videoHeight = mixAnimPlugin.playerEva.configManager.config?.height ?: return null 15 | 16 | if (viewWith == 0 || viewHeight == 0) return null 17 | 18 | when(ev.action) { 19 | MotionEvent.ACTION_DOWN -> { 20 | val x = ev.x * videoWith / viewWith.toFloat() 21 | val y = ev.y * videoHeight / viewHeight.toFloat() 22 | val list = mixAnimPlugin.frameAll?.map?.get(mixAnimPlugin.curFrameIndex)?.list 23 | list?.forEach {frame -> 24 | val src = mixAnimPlugin.srcMap?.map?.get(frame.srcId) ?: return@forEach 25 | if (calClick(x.toInt(), y.toInt(), frame.frame)) { 26 | return EvaResource(src) 27 | } 28 | } 29 | } 30 | } 31 | return null 32 | } 33 | 34 | 35 | private fun calClick(x: Int, y: Int, frame: PointRect): Boolean { 36 | return x >= frame.x && x <= (frame.x + frame.w) 37 | && y >= frame.y && y <= (frame.y + frame.h) 38 | } 39 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/mix/EvaResource.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.mix 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Typeface 5 | 6 | /** 7 | * 资源描述类 8 | */ 9 | class EvaResource(src: EvaSrc) { 10 | var id = "" 11 | var type = EvaSrc.SrcType.UNKNOWN 12 | var loadType = EvaSrc.LoadType.UNKNOWN 13 | var tag = "" 14 | var scaleMode: String = "" // scaleFill, aspectFit, aspectFill 15 | var bitmap: Bitmap? = null 16 | 17 | var text: String? = null //文字,用户定制 18 | var textColor = -1 //文字颜色,用户定制 19 | var fontSize = 0 //文字大小,用户定制 20 | var textAlign: String? = null //文字对齐方式,用户定制 21 | var typeFace: Typeface? = null //字体样式,用户定制 22 | 23 | init { 24 | id = src.srcId 25 | type = src.srcType 26 | loadType = src.loadType 27 | tag = src.srcTag 28 | bitmap = src.bitmap 29 | if (src.scaleMode.isNotEmpty()) { 30 | scaleMode = src.scaleMode 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/mix/EvaSrc.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.mix 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Color 5 | import com.yy.yyeva.util.EvaConstant 6 | import com.yy.yyeva.EvaAnimConfig 7 | import com.yy.yyeva.util.ELog 8 | import org.json.JSONObject 9 | 10 | class EvaSrc { 11 | companion object { 12 | private const val TAG = "${EvaConstant.TAG}.EvaSrc" 13 | } 14 | 15 | enum class SrcType(val type: String) { 16 | UNKNOWN("unknown"), 17 | IMG("img"), 18 | TXT("txt"), 19 | } 20 | 21 | enum class LoadType(val type: String) { 22 | UNKNOWN("unknown"), 23 | NET("net"), // 网络加载的图片 24 | LOCAL("local"), // 本地加载的图片 25 | } 26 | 27 | enum class FitType(val type: String) { 28 | FIT_XY("fitXY"), // 按原始大小填充纹理 29 | CENTER_FULL("centerFull"), // 以纹理中心点放置 30 | CENTER_FIT("centerFit"), // 以纹理中心点显示整个放置 31 | } 32 | 33 | enum class Style(val style: String) { 34 | DEFAULT("default"), 35 | BOLD("b"), // 文字粗体 36 | } 37 | 38 | var srcId = "" 39 | var w = 0 40 | var h = 0 41 | var srcType = SrcType.UNKNOWN 42 | var loadType = LoadType.UNKNOWN 43 | var srcTag = "" 44 | var scaleMode = "scaleFill" //scaleFill aspectFit aspectFill 45 | var bitmap: Bitmap? = null 46 | var txt = "" 47 | var style = Style.DEFAULT 48 | var color: Int = 0 49 | var fitType = FitType.FIT_XY 50 | var frontSize = 0 51 | var textAlign = "center" // center, right, left 52 | 53 | constructor(effect: EvaAnimConfig.Effect) { 54 | srcId = effect.effectId.toString() 55 | w = effect.effectWidth 56 | h = effect.effectHeight 57 | var colorStr = effect.fontColor 58 | if (colorStr.isEmpty()) { 59 | colorStr = "#000000" 60 | } 61 | color = Color.parseColor(colorStr) 62 | srcTag = effect.effectTag 63 | txt= srcTag 64 | frontSize = effect.fontSize 65 | textAlign = effect.textAlign 66 | srcType = when(effect.effectType) { 67 | SrcType.IMG.type -> SrcType.IMG 68 | SrcType.TXT.type -> SrcType.TXT 69 | else -> SrcType.UNKNOWN 70 | } 71 | fitType = FitType.FIT_XY 72 | ELog.i(TAG, "${toString()} color=$colorStr") 73 | } 74 | 75 | constructor(json: JSONObject) { 76 | srcId = json.getString("srcId") 77 | w = json.getInt("w") 78 | h = json.getInt("h") 79 | // 可选 80 | var colorStr = json.optString("color", "#000000") 81 | if (colorStr.isEmpty()) { 82 | colorStr = "#000000" 83 | } 84 | color = Color.parseColor(colorStr) 85 | srcTag = json.getString("srcTag") 86 | txt = srcTag 87 | 88 | srcType = when(json.getString("srcType")) { 89 | SrcType.IMG.type -> SrcType.IMG 90 | SrcType.TXT.type -> SrcType.TXT 91 | else -> SrcType.UNKNOWN 92 | } 93 | loadType = when(json.getString("loadType")) { 94 | LoadType.NET.type -> LoadType.NET 95 | LoadType.LOCAL.type -> LoadType.LOCAL 96 | else -> LoadType.UNKNOWN 97 | } 98 | fitType = when(json.getString("fitType")) { 99 | FitType.CENTER_FULL.type -> FitType.CENTER_FULL 100 | else -> FitType.FIT_XY 101 | } 102 | 103 | // 可选 104 | style = when(json.optString("style", "")) { 105 | Style.BOLD.style -> Style.BOLD 106 | else -> Style.DEFAULT 107 | } 108 | ELog.i(TAG, "${toString()} color=$colorStr") 109 | } 110 | 111 | 112 | 113 | override fun toString(): String { 114 | return "Src(srcId='$srcId', srcType=$srcType, loadType=$loadType, srcTag='$srcTag', bitmap=$bitmap, txt='$txt, textAlign = $textAlign)" 115 | } 116 | 117 | } 118 | 119 | class EvaSrcMap(effects: MutableList) { 120 | val map = HashMap() 121 | 122 | init { 123 | for (i in 0 until effects.size) { 124 | val src = EvaSrc(effects[i]) 125 | if (src.srcType != EvaSrc.SrcType.UNKNOWN) { 126 | map[src.srcId] = src 127 | } 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/plugin/EvaAnimPluginManager.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.plugin 2 | 3 | import android.view.MotionEvent 4 | import com.yy.yyeva.EvaAnimPlayer 5 | import com.yy.yyeva.util.EvaConstant 6 | import com.yy.yyeva.EvaAnimConfig 7 | import com.yy.yyeva.mix.EvaMixAnimPlugin 8 | import com.yy.yyeva.util.ELog 9 | 10 | /** 11 | * 动画插件管理 12 | */ 13 | class EvaAnimPluginManager(val playerEva: EvaAnimPlayer) { 14 | companion object { 15 | private const val TAG = "${EvaConstant.TAG}.AnimPluginManager" 16 | private const val DIFF_TIMES = 4 17 | } 18 | 19 | private val mixAnimPlugin = EvaMixAnimPlugin(playerEva) 20 | 21 | private val plugins = listOf(mixAnimPlugin) 22 | 23 | // 当前渲染的帧 24 | private var frameIndex = 0 25 | // 当前解码的帧 26 | private var decodeIndex = 0 27 | // 帧不相同的次数, 连续多次不同则直接使用decodeIndex 28 | private var frameDiffTimes = 0 29 | 30 | fun getMixAnimPlugin(): EvaMixAnimPlugin? { 31 | return mixAnimPlugin 32 | } 33 | 34 | fun onConfigCreate(config: EvaAnimConfig): Int { 35 | ELog.i(TAG, "onConfigCreateEva") 36 | plugins.forEach { 37 | val res = it.onConfigCreate(config) 38 | if (res != EvaConstant.OK) { 39 | return res 40 | } 41 | } 42 | return EvaConstant.OK 43 | } 44 | 45 | fun onRenderCreate() { 46 | ELog.i(TAG, "onRenderCreate") 47 | frameIndex = 0 48 | decodeIndex = 0 49 | plugins.forEach { 50 | it.onRenderCreate() 51 | } 52 | } 53 | 54 | fun onDecoding(decodeIndex: Int) { 55 | ELog.i(TAG, "onDecoding decodeIndex=$decodeIndex") 56 | this.decodeIndex = decodeIndex 57 | plugins.forEach { 58 | it.onDecoding(decodeIndex) 59 | } 60 | } 61 | 62 | // 开始循环调用 63 | fun onLoopStart() { 64 | ELog.i(TAG, "onLoopStart") 65 | frameIndex = 0 66 | decodeIndex = 0 67 | } 68 | 69 | fun onRendering() { 70 | if (playerEva.configManager.config?.isMix == false) return 71 | if (decodeIndex > frameIndex + 1 || frameDiffTimes >= DIFF_TIMES) { 72 | ELog.i(TAG, "jump frameIndex= $frameIndex,decodeIndex=$decodeIndex,frameDiffTimes=$frameDiffTimes") 73 | frameIndex = decodeIndex 74 | } 75 | if (decodeIndex != frameIndex) { 76 | frameDiffTimes++ 77 | } else { 78 | frameDiffTimes = 0 79 | } 80 | ELog.d(TAG, "onRendering frameIndex=$frameIndex") 81 | plugins.forEach { 82 | it.onRendering(frameIndex) // 第一帧 0 83 | } 84 | frameIndex++ 85 | } 86 | 87 | fun onRelease() { 88 | ELog.i(TAG, "onRelease") 89 | plugins.forEach { 90 | it.onRelease() 91 | } 92 | } 93 | 94 | fun onDestroy() { 95 | ELog.i(TAG, "onDestroy") 96 | plugins.forEach { 97 | it.onDestroy() 98 | } 99 | } 100 | 101 | fun onDispatchTouchEvent(ev: MotionEvent): Boolean { 102 | plugins.forEach { 103 | if (it.onDispatchTouchEvent(ev)) return true 104 | } 105 | return false 106 | } 107 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/plugin/IEvaAnimPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.plugin 2 | 3 | import android.view.MotionEvent 4 | import com.yy.yyeva.util.EvaConstant 5 | import com.yy.yyeva.EvaAnimConfig 6 | 7 | interface IEvaAnimPlugin { 8 | 9 | // 配置生成 10 | fun onConfigCreate(config: EvaAnimConfig): Int { 11 | return EvaConstant.OK 12 | } 13 | 14 | // 渲染初始化 15 | fun onRenderCreate() {} 16 | 17 | // 解码通知 18 | fun onDecoding(decodeIndex: Int) {} 19 | 20 | // 每一帧渲染 21 | fun onRendering(frameIndex: Int) {} 22 | 23 | // 每次播放完毕 24 | fun onRelease() {} 25 | 26 | // 销毁 27 | fun onDestroy() {} 28 | 29 | /** 触摸事件 30 | * @return false 不拦截 true 拦截 31 | */ 32 | fun onDispatchTouchEvent(ev: MotionEvent): Boolean {return false} 33 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/ELog.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | object ELog { 4 | 5 | var isDebug = false 6 | 7 | var log: IELog? = null 8 | 9 | fun i(tag: String, msg: String) { 10 | log?.i(tag, msg) 11 | } 12 | 13 | fun d(tag: String, msg: String) { 14 | if (isDebug) { 15 | log?.d(tag, msg) 16 | } 17 | } 18 | 19 | fun e(tag: String, msg: String) { 20 | log?.e(tag, msg) 21 | } 22 | 23 | fun e(tag: String, msg: String, tr: Throwable) { 24 | log?.e(tag, msg, tr) 25 | } 26 | } 27 | 28 | 29 | interface IELog { 30 | fun i(tag: String, msg: String) {} 31 | 32 | fun d(tag: String, msg: String) {} 33 | 34 | fun e(tag: String, msg: String) {} 35 | 36 | fun e(tag: String, msg: String, tr: Throwable) {} 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/EvaBitmapUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | import android.graphics.* 4 | import android.text.TextPaint 5 | import com.yy.yyeva.mix.EvaSrc 6 | 7 | object EvaBitmapUtil { 8 | 9 | fun createEmptyBitmap() : Bitmap { 10 | return Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888).apply { 11 | eraseColor(Color.TRANSPARENT) 12 | } 13 | } 14 | 15 | fun createTxtBitmap(src: EvaSrc, tf: Typeface? = null): Bitmap { 16 | val w = src.w 17 | val h = src.h 18 | // 这里使用ALPHA_8 在opengl渲染的时候图像出现错位 19 | val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) 20 | val canvas = Canvas(bitmap) 21 | val rect = Rect(0, 0, w, h) 22 | val bounds = Rect() 23 | var sizeR = 0.8f 24 | val paint = TextPaint().apply { 25 | // textSize = h * sizeR 26 | textSize = src.frontSize.toFloat() 27 | textAlign = when (src.textAlign) { 28 | "right" -> { 29 | Paint.Align.RIGHT 30 | } 31 | "left" -> { 32 | Paint.Align.LEFT 33 | } 34 | else -> { 35 | Paint.Align.CENTER 36 | } 37 | } 38 | style = Paint.Style.FILL 39 | isAntiAlias = true 40 | if (tf != null) { 41 | typeface = tf 42 | } else if (src.style == EvaSrc.Style.BOLD) { 43 | typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) 44 | } 45 | color = src.color 46 | } 47 | val text = src.txt 48 | while (sizeR > 0.1f) { 49 | paint.getTextBounds(text, 0, text.length, bounds) 50 | if (bounds.width() <= rect.width()) { 51 | break 52 | } 53 | sizeR -= 0.1f 54 | // paint.textSize = h * sizeR 55 | paint.textSize = src.frontSize.toFloat() 56 | } 57 | val fontMetrics = paint.fontMetricsInt 58 | val top = fontMetrics.top 59 | val bottom = fontMetrics.bottom 60 | val baseline = rect.centerY() - top/2 - bottom/2 61 | when (src.textAlign) { 62 | "left" -> { 63 | canvas.drawText(text, rect.left.toFloat(), baseline.toFloat(), paint) 64 | } 65 | "right" -> { 66 | canvas.drawText(text, rect.right.toFloat(), baseline.toFloat(), paint) 67 | } 68 | else -> { 69 | canvas.drawText(text, rect.centerX().toFloat(), baseline.toFloat(), paint) 70 | } 71 | } 72 | 73 | return bitmap 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/EvaConstant.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | object EvaConstant { 4 | const val TAG = "EvaAnimPlayer" 5 | 6 | const val VIDEO_MODE_NORMAL_MP4_NONE = -2 // 无检测状态 7 | const val VIDEO_MODE_NORMAL_MP4 = -1 // 正常mp4 8 | // 视频对齐方式 (兼容老版本视频模式) 9 | const val VIDEO_MODE_SPLIT_HORIZONTAL = 1 // 视频左右对齐(alpha左\rgb右) 10 | const val VIDEO_MODE_SPLIT_VERTICAL = 2 // 视频上下对齐(alpha上\rgb下) 11 | const val VIDEO_MODE_SPLIT_HORIZONTAL_REVERSE = 3 // 视频左右对齐(rgb左\alpha右) 12 | const val VIDEO_MODE_SPLIT_VERTICAL_REVERSE = 4 // 视频上下对齐(rgb上\alpha下) 13 | 14 | const val OK = 0 // 成功 15 | 16 | const val REPORT_ERROR_TYPE_EXTRACTOR_EXC = 10001 // MediaExtractor exception 17 | const val REPORT_ERROR_TYPE_DECODE_EXC = 10002 // MediaCodec exception 18 | const val REPORT_ERROR_TYPE_CREATE_THREAD = 10003 // 线程创建失败 19 | const val REPORT_ERROR_TYPE_CREATE_RENDER = 10004 // render创建失败 20 | const val REPORT_ERROR_TYPE_PARSE_CONFIG = 10005 // 配置解析失败 21 | const val REPORT_ERROR_TYPE_CONFIG_PLUGIN_MIX = 10006 // 融合动画资源获取失败 22 | const val REPORT_ERROR_TYPE_FILE_ERROR = 10007 // 文件无法读取 23 | const val REPORT_ERROR_TYPE_HEVC_NOT_SUPPORT = 10008 // 不支持h265 24 | 25 | const val ERROR_MSG_EXTRACTOR_EXC = "0x1 MediaExtractor exception" // MediaExtractor exception 26 | const val ERROR_MSG_DECODE_EXC = "0x2 MediaCodec exception" // MediaCodec exception 27 | const val ERROR_MSG_CREATE_THREAD = "0x3 thread create fail" // 线程创建失败 28 | const val ERROR_MSG_CREATE_RENDER = "0x4 render create fail" // render创建失败 29 | const val ERROR_MSG_PARSE_CONFIG = "0x5 parse config fail" // 配置解析失败 30 | const val ERROR_MSG_CONFIG_PLUGIN_MIX = "0x6 yyeva fail" // 融合动画资源获取失败 31 | const val ERROR_MSG_FILE_ERROR = "0x7 file can't read" // 文件无法读取 32 | const val ERROR_MSG_HEVC_NOT_SUPPORT = "0x8 hevc not support" // 不支持h265 33 | 34 | 35 | fun getErrorMsg(errorType: Int, errorMsg: String? = null): String { 36 | return when(errorType) { 37 | REPORT_ERROR_TYPE_EXTRACTOR_EXC -> ERROR_MSG_EXTRACTOR_EXC 38 | REPORT_ERROR_TYPE_DECODE_EXC -> ERROR_MSG_DECODE_EXC 39 | REPORT_ERROR_TYPE_CREATE_THREAD -> ERROR_MSG_CREATE_THREAD 40 | REPORT_ERROR_TYPE_CREATE_RENDER -> ERROR_MSG_CREATE_RENDER 41 | REPORT_ERROR_TYPE_PARSE_CONFIG -> ERROR_MSG_PARSE_CONFIG 42 | REPORT_ERROR_TYPE_CONFIG_PLUGIN_MIX -> ERROR_MSG_CONFIG_PLUGIN_MIX 43 | else -> "unknown" 44 | } + " ${errorMsg ?: ""}" 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/EvaJniUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | import android.graphics.Bitmap 4 | import android.view.Surface 5 | 6 | object EvaJniUtil { 7 | init { 8 | System.loadLibrary("yyeva") 9 | } 10 | 11 | external fun nativeProcessBitmap(bitmap: Bitmap) 12 | 13 | /** 14 | * 获取底层的textureId,用于创建和绑定surfaceView或textureView 15 | */ 16 | external fun getExternalTexture(controllerId: Int): Int 17 | 18 | /** 19 | * 更新渲染尺寸 20 | */ 21 | external fun updateViewPoint(controllerId: Int, width: Int, height: Int) 22 | 23 | /** 24 | * 视频大小变化 25 | */ 26 | external fun videoSizeChange(controllerId: Int, newWidth: Int, newHeight: Int) 27 | 28 | /** 29 | * 初始化mp4渲染 30 | * 返回管理器id 31 | */ 32 | external fun initRender(controllerId: Int, surface: Surface, isNeedYuv: Boolean, isNormalMp4: Boolean): Int 33 | 34 | /** 35 | * 设置动效json数据 36 | */ 37 | external fun setRenderConfig(controllerId: Int, json: String) 38 | 39 | external fun defaultConfig(controllerId: Int, width: Int, height: Int, defaultVideoMode: Int = EvaConstant.VIDEO_MODE_SPLIT_HORIZONTAL_REVERSE) 40 | 41 | /** 42 | * 设置背景bitmap数据 43 | */ 44 | external fun setBgBitmap(controllerId: Int, bitmap: Bitmap?) 45 | 46 | /** 47 | * 设置动效元素bitmap数据 48 | */ 49 | external fun setSrcBitmap(controllerId: Int, srcId: String, bitmap: Bitmap?, scaleMode: String) 50 | 51 | /** 52 | * 设置动效元素txt 53 | */ 54 | external fun setSrcTxt(controllerId: Int, srcId: String, txt: String) 55 | 56 | /** 57 | * 进行MP4渲染 58 | */ 59 | external fun renderFrame(controllerId: Int) 60 | 61 | /** 62 | * 清空MP4渲染 63 | */ 64 | external fun renderClearFrame(controllerId: Int) 65 | 66 | /** 67 | * 清空纹理 68 | */ 69 | external fun releaseTexture(controllerId: Int) 70 | 71 | /** 72 | * 交换渲染mp4纹理数据 73 | */ 74 | external fun renderSwapBuffers(controllerId: Int) 75 | 76 | /** 77 | * 销毁mp4纹理渲染 78 | */ 79 | external fun destroyRender(controllerId: Int) 80 | 81 | /** 82 | * 动效元素配置 83 | */ 84 | external fun mixConfigCreate(controllerId: Int, json: String): Int 85 | 86 | /** 87 | * 动效元素创建 88 | */ 89 | external fun mixRenderCreate(controllerId: Int) 90 | 91 | /** 92 | * 动效元素渲染 93 | */ 94 | external fun mixRendering(controllerId: Int, frameIndex: Int) 95 | 96 | /** 97 | * 销毁动效元素 98 | */ 99 | external fun mixRenderDestroy(controllerId: Int) 100 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/EvaMediaUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | import android.media.MediaCodecList 4 | import android.media.MediaExtractor 5 | import android.media.MediaFormat 6 | import com.yy.yyeva.file.IEvaFileContainer 7 | import kotlin.collections.HashMap 8 | 9 | 10 | object EvaMediaUtil { 11 | 12 | private const val TAG = "${EvaConstant.TAG}.MediaUtil" 13 | 14 | private var isTypeMapInit = false 15 | private val supportTypeMap = HashMap() 16 | 17 | const val MIME_HEVC = "video/hevc" 18 | 19 | fun getExtractor(evaFile: IEvaFileContainer): MediaExtractor { 20 | val extractor = MediaExtractor() 21 | evaFile.setDataSource(extractor) 22 | return extractor 23 | } 24 | 25 | /** 26 | * 是否为h265的视频 27 | */ 28 | fun checkIsHevc(videoFormat: MediaFormat):Boolean { 29 | val mime = videoFormat.getString(MediaFormat.KEY_MIME) ?: "" 30 | return mime.contains("hevc") 31 | } 32 | 33 | fun selectVideoTrack(extractor: MediaExtractor): Int { 34 | val numTracks = extractor.trackCount 35 | for (i in 0 until numTracks) { 36 | val format = extractor.getTrackFormat(i) 37 | val mime = format.getString(MediaFormat.KEY_MIME) ?: "" 38 | if (mime.startsWith("video/")) { 39 | ELog.i(TAG, "Extractor selected track $i ($mime): $format") 40 | return i 41 | } 42 | } 43 | return -1 44 | } 45 | 46 | fun selectAudioTrack(extractor: MediaExtractor): Int { 47 | val numTracks = extractor.trackCount 48 | for (i in 0 until numTracks) { 49 | val format = extractor.getTrackFormat(i) 50 | val mime = format.getString(MediaFormat.KEY_MIME) ?: "" 51 | if (mime.startsWith("audio/")) { 52 | ELog.i(TAG, "Extractor selected track $i ($mime): $format") 53 | return i 54 | } 55 | } 56 | return -1 57 | } 58 | 59 | /** 60 | * 检查设备解码支持类型 61 | */ 62 | @Synchronized 63 | fun checkSupportCodec(mimeType: String): Boolean { 64 | if (!isTypeMapInit) { 65 | isTypeMapInit = true 66 | getSupportType() 67 | } 68 | return supportTypeMap.containsKey(mimeType.toLowerCase()) 69 | } 70 | 71 | 72 | private fun getSupportType() { 73 | try { 74 | val numCodecs = MediaCodecList.getCodecCount() 75 | for (i in 0 until numCodecs) { 76 | val codecInfo = MediaCodecList.getCodecInfoAt(i) 77 | if (!codecInfo.isEncoder) { 78 | continue 79 | } 80 | val types = codecInfo.supportedTypes 81 | for (j in types.indices) { 82 | supportTypeMap[types[j].toLowerCase()] = true 83 | } 84 | } 85 | ELog.i(TAG, "supportType=${supportTypeMap.keys}") 86 | } catch (t: Throwable) { 87 | ELog.e(TAG, "getSupportType $t") 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/EvaVideoEntity.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | import java.io.File 4 | 5 | class EvaVideoEntity { 6 | 7 | var mCacheDir: File 8 | var mFrameHeight = 0 9 | var mFrameWidth = 0 10 | 11 | constructor(cacheDir: File) : this(cacheDir, 0, 0) 12 | 13 | constructor(cacheDir: File, frameWidth: Int, frameHeight: Int) { 14 | mFrameWidth = frameWidth 15 | mFrameHeight = frameHeight 16 | mCacheDir = cacheDir 17 | } 18 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/PointRect.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | data class PointRect(val x: Int, val y: Int, val w: Int, val h: Int) -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/ScaleTypeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | import android.view.Gravity 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.FrameLayout 7 | 8 | enum class ScaleType { 9 | FIT_XY, // 完整填充整个布局 default 10 | FIT_CENTER, // 按视频比例在布局中间完整显示 11 | CENTER_CROP, // 按视频比例完整填充布局(多余部分不显示) 12 | } 13 | 14 | interface IScaleType { 15 | 16 | fun getLayoutParam( 17 | layoutWidth: Int, 18 | layoutHeight: Int, 19 | videoWidth: Int, 20 | videoHeight: Int, 21 | layoutParams: FrameLayout.LayoutParams 22 | ): FrameLayout.LayoutParams 23 | 24 | fun getRealSize(): Pair 25 | } 26 | 27 | class ScaleTypeFitXY : IScaleType { 28 | 29 | private var realWidth = 0 30 | private var realHeight = 0 31 | 32 | override fun getLayoutParam( 33 | layoutWidth: Int, 34 | layoutHeight: Int, 35 | videoWidth: Int, 36 | videoHeight: Int, 37 | layoutParams: FrameLayout.LayoutParams 38 | ): FrameLayout.LayoutParams { 39 | layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT 40 | layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT 41 | realWidth = layoutWidth 42 | realHeight = layoutHeight 43 | return layoutParams 44 | } 45 | 46 | override fun getRealSize(): Pair { 47 | return Pair(realWidth, realHeight) 48 | } 49 | } 50 | 51 | class ScaleTypeFitCenter : IScaleType { 52 | 53 | private var realWidth = 0 54 | private var realHeight = 0 55 | 56 | override fun getLayoutParam( 57 | layoutWidth: Int, 58 | layoutHeight: Int, 59 | videoWidth: Int, 60 | videoHeight: Int, 61 | layoutParams: FrameLayout.LayoutParams 62 | ): FrameLayout.LayoutParams { 63 | val (w, h) = getFitCenterSize(layoutWidth, layoutHeight, videoWidth, videoHeight) 64 | if (w <= 0 && h <= 0) return layoutParams 65 | realWidth = w 66 | realHeight = h 67 | layoutParams.width = w 68 | layoutParams.height = h 69 | layoutParams.gravity = Gravity.CENTER 70 | return layoutParams 71 | } 72 | 73 | override fun getRealSize(): Pair { 74 | return Pair(realWidth, realHeight) 75 | } 76 | 77 | private fun getFitCenterSize( 78 | layoutWidth: Int, 79 | layoutHeight: Int, 80 | videoWidth: Int, 81 | videoHeight: Int 82 | ): Pair { 83 | 84 | val layoutRatio = layoutWidth.toFloat() / layoutHeight 85 | val videoRatio = videoWidth.toFloat() / videoHeight 86 | 87 | val realWidth: Int 88 | val realHeight: Int 89 | if (layoutRatio > videoRatio) { 90 | realHeight = layoutHeight 91 | realWidth = (videoRatio * realHeight).toInt() 92 | } else { 93 | realWidth = layoutWidth 94 | realHeight = (realWidth / videoRatio).toInt() 95 | } 96 | 97 | return Pair(realWidth, realHeight) 98 | } 99 | } 100 | 101 | class ScaleTypeCenterCrop : IScaleType { 102 | 103 | private var realWidth = 0 104 | private var realHeight = 0 105 | 106 | override fun getLayoutParam( 107 | layoutWidth: Int, 108 | layoutHeight: Int, 109 | videoWidth: Int, 110 | videoHeight: Int, 111 | layoutParams: FrameLayout.LayoutParams 112 | ): FrameLayout.LayoutParams { 113 | val (w, h) = getCenterCropSize(layoutWidth, layoutHeight, videoWidth, videoHeight) 114 | if (w <= 0 && h <= 0) return layoutParams 115 | realWidth = w 116 | realHeight = h 117 | layoutParams.width = w 118 | layoutParams.height = h 119 | layoutParams.gravity = Gravity.CENTER 120 | return layoutParams 121 | } 122 | 123 | override fun getRealSize(): Pair { 124 | return Pair(realWidth, realHeight) 125 | } 126 | 127 | private fun getCenterCropSize( 128 | layoutWidth: Int, 129 | layoutHeight: Int, 130 | videoWidth: Int, 131 | videoHeight: Int 132 | ): Pair { 133 | 134 | val layoutRatio = layoutWidth.toFloat() / layoutHeight 135 | val videoRatio = videoWidth.toFloat() / videoHeight 136 | 137 | val realWidth: Int 138 | val realHeight: Int 139 | if (layoutRatio > videoRatio) { 140 | realWidth = layoutWidth 141 | realHeight = (realWidth / videoRatio).toInt() 142 | } else { 143 | realHeight = layoutHeight 144 | realWidth = (videoRatio * realHeight).toInt() 145 | } 146 | 147 | return Pair(realWidth, realHeight) 148 | } 149 | } 150 | 151 | 152 | class ScaleTypeUtil { 153 | 154 | companion object { 155 | private const val TAG = "${EvaConstant.TAG}.ScaleTypeUtil" 156 | } 157 | 158 | private val scaleTypeFitXY by lazy { ScaleTypeFitXY() } 159 | private val scaleTypeFitCenter by lazy { ScaleTypeFitCenter() } 160 | private val scaleTypeCenterCrop by lazy { ScaleTypeCenterCrop() } 161 | private var layoutWidth = 0 162 | private var layoutHeight = 0 163 | private var videoWidth = 0 164 | private var videoHeight = 0 165 | 166 | var currentScaleType = ScaleType.FIT_XY 167 | var scaleTypeImpl: IScaleType? = null 168 | 169 | fun setLayoutSize(w: Int, h: Int) { 170 | layoutWidth = w 171 | layoutHeight = h 172 | } 173 | 174 | fun setVideoSize(w: Int, h: Int) { 175 | videoWidth = w 176 | videoHeight = h 177 | } 178 | 179 | /** 180 | * 获取实际视频容器宽高 181 | * @return w h 182 | */ 183 | fun getRealSize(): Pair { 184 | val size = getCurrentScaleType().getRealSize() 185 | ELog.i(TAG, "get real size (${size.first}, ${size.second})") 186 | return size 187 | } 188 | 189 | fun getLayoutParam(view: View?): FrameLayout.LayoutParams { 190 | val layoutParams = (view?.layoutParams as? FrameLayout.LayoutParams) 191 | ?: FrameLayout.LayoutParams( 192 | ViewGroup.LayoutParams.MATCH_PARENT, 193 | ViewGroup.LayoutParams.MATCH_PARENT 194 | ) 195 | if (!checkParams()) { 196 | ELog.e( 197 | TAG, 198 | "params error: layoutWidth=$layoutWidth, layoutHeight=$layoutHeight, videoWidth=$videoWidth, videoHeight=$videoHeight" 199 | ) 200 | return layoutParams 201 | } 202 | 203 | return getCurrentScaleType().getLayoutParam( 204 | layoutWidth, 205 | layoutHeight, 206 | videoWidth, 207 | videoHeight, 208 | layoutParams 209 | ) 210 | } 211 | 212 | private fun getCurrentScaleType(): IScaleType { 213 | val tmpScaleType = scaleTypeImpl 214 | return if (tmpScaleType != null) { 215 | ELog.i(TAG, "custom scaleType") 216 | tmpScaleType 217 | } else { 218 | ELog.i(TAG, "scaleType=$currentScaleType") 219 | when (currentScaleType) { 220 | ScaleType.FIT_XY -> scaleTypeFitXY 221 | ScaleType.FIT_CENTER -> scaleTypeFitCenter 222 | ScaleType.CENTER_CROP -> scaleTypeCenterCrop 223 | } 224 | } 225 | } 226 | 227 | 228 | private fun checkParams(): Boolean { 229 | return layoutWidth > 0 230 | && layoutHeight > 0 231 | && videoWidth > 0 232 | && videoHeight > 0 233 | } 234 | 235 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/util/SpeedControlUtil.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.util 2 | 3 | class SpeedControlUtil { 4 | 5 | companion object { 6 | private const val TAG = "${EvaConstant.TAG}.SpeedControlUtil" 7 | } 8 | private val ONE_MILLION = 1000000L 9 | 10 | private var prevPresentUsec: Long = 0 11 | private var prevMonoUsec: Long = 0 12 | private var fixedFrameDurationUsec: Long = 0 13 | private var loopReset = true 14 | 15 | //设置帧率 16 | fun setFixedPlaybackRate(fps: Int) { 17 | if (fps <=0) return 18 | fixedFrameDurationUsec = ONE_MILLION / fps 19 | } 20 | //控制帧率时间 21 | fun preRender(presentationTimeUsec: Long) { 22 | if (prevMonoUsec == 0L) { 23 | prevMonoUsec = System.nanoTime() / 1000 24 | prevPresentUsec = presentationTimeUsec 25 | } else { 26 | var frameDelta: Long 27 | if (loopReset) { 28 | prevPresentUsec = presentationTimeUsec - ONE_MILLION / 30 29 | loopReset = false 30 | } 31 | frameDelta = if (fixedFrameDurationUsec != 0L) { 32 | fixedFrameDurationUsec 33 | } else { 34 | presentationTimeUsec - prevPresentUsec 35 | } 36 | when { 37 | frameDelta < 0 -> frameDelta = 0 38 | frameDelta > 10 * ONE_MILLION -> frameDelta = 5 * ONE_MILLION 39 | } 40 | 41 | val desiredUsec = prevMonoUsec + frameDelta 42 | var nowUsec = System.nanoTime() / 1000 43 | while (nowUsec < desiredUsec - 100 ) { 44 | var sleepTimeUsec = desiredUsec - nowUsec 45 | if (sleepTimeUsec > 500000) { 46 | sleepTimeUsec = 500000 47 | } 48 | try { 49 | Thread.sleep(sleepTimeUsec / 1000, (sleepTimeUsec % 1000).toInt() * 1000) 50 | } catch (e: InterruptedException) { 51 | ELog.e(TAG, "e=$e", e) 52 | } 53 | nowUsec = System.nanoTime() / 1000 54 | } 55 | 56 | prevMonoUsec += frameDelta 57 | prevPresentUsec += frameDelta 58 | } 59 | } 60 | 61 | fun reset() { 62 | prevPresentUsec = 0 63 | prevMonoUsec = 0 64 | } 65 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/view/IEvaAnimView.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.view 2 | 3 | import android.content.res.AssetManager 4 | import android.graphics.Bitmap 5 | import android.graphics.SurfaceTexture 6 | import android.view.Surface 7 | import com.yy.yyeva.file.IEvaFileContainer 8 | import com.yy.yyeva.inter.IEvaAnimListener 9 | import com.yy.yyeva.inter.IEvaFetchResource 10 | import com.yy.yyeva.inter.OnEvaResourceClickListener 11 | import com.yy.yyeva.util.IScaleType 12 | import com.yy.yyeva.util.ScaleType 13 | import java.io.File 14 | 15 | interface IEvaAnimView { 16 | 17 | fun prepareTextureView() 18 | 19 | fun getSurfaceTexture(): SurfaceTexture? 20 | 21 | fun getSurface(): Surface? 22 | 23 | fun setAnimListener(evaAnimListener: IEvaAnimListener?) 24 | 25 | fun setFetchResource(evaFetchResource: IEvaFetchResource?) 26 | 27 | fun setOnResourceClickListener(evaResourceClickListener: OnEvaResourceClickListener?) 28 | //循环播放数 29 | fun setLoop(playLoop: Int) 30 | 31 | //设置起始播放位置 毫秒 32 | //硬解某些机型会有跳帧前几帧解析异常的问题,不建议使用。 33 | fun setStartPoint(startPoint: Long) 34 | 35 | fun supportMask(isSupport: Boolean, isEdgeBlur: Boolean) 36 | 37 | //设置视频帧数和倍率 38 | fun setVideoFps(fps: Int, speed: Float = 1.0f) //speed为倍速 39 | //设置音频倍速 40 | fun setAudioSpeed(speed: Float = 1.0f) 41 | 42 | fun setScaleType(type: ScaleType) 43 | 44 | fun setScaleType(scaleType: IScaleType) 45 | 46 | fun setMute(isMute: Boolean) 47 | //播放文件 48 | fun startPlay(file: File) 49 | 50 | //播放本地文件 51 | fun startPlay(assetManager: AssetManager, assetsPath: String) 52 | 53 | fun startPlay(evaFileContainer: IEvaFileContainer) 54 | // 暂停播放 55 | fun pause() 56 | // 恢复播放 57 | fun resume() 58 | //停止播放 59 | fun stopPlay() 60 | //是否正在运行 61 | fun isRunning(): Boolean 62 | 63 | fun getRealSize(): Pair 64 | 65 | fun updateTextureViewLayout() 66 | //设置背景图 67 | fun setBgImage(bg: Bitmap) 68 | 69 | fun hasBgImage(): Boolean 70 | //是否不透明度mp4 71 | fun setNormalMp4(isNormalMp4: Boolean) 72 | //是否停留在最后一帧 73 | fun setLastFrame(isSetLastFrame: Boolean) 74 | } 75 | -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/view/InnerSurfaceView.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.view 2 | 3 | import android.content.Context 4 | import android.graphics.PixelFormat 5 | import android.util.AttributeSet 6 | import android.view.MotionEvent 7 | import android.view.SurfaceView 8 | import com.yy.yyeva.EvaAnimPlayer 9 | import com.yy.yyeva.util.ELog 10 | 11 | class InnerSurfaceView @JvmOverloads constructor( 12 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 13 | ) : SurfaceView(context, attrs, defStyleAttr) { 14 | val TAG = "InnerSurfaceView" 15 | var playerEva: EvaAnimPlayer? = null 16 | 17 | init { 18 | setBackgroundResource(android.R.color.transparent) 19 | setZOrderOnTop(true) 20 | holder.setFormat(PixelFormat.TRANSLUCENT) 21 | } 22 | 23 | override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { 24 | val isRunning = playerEva?.isRunning() == true 25 | val isDispatch = ev != null && (playerEva?.pluginManager?.onDispatchTouchEvent(ev) == true) 26 | val isHasBg = playerEva?.evaAnimView?.hasBgImage() == true //有背景也是直接拦截掉 27 | ELog.i(TAG, "isRunning playerEva isRunning:$isRunning, isDispatch : $isDispatch, hasBg: $isHasBg") 28 | return if (isRunning && !isDispatch && !isHasBg) super.dispatchTouchEvent(ev) else true 29 | } 30 | } -------------------------------------------------------------------------------- /yyevac/src/main/java/com/yy/yyeva/view/InnerTextureView.kt: -------------------------------------------------------------------------------- 1 | package com.yy.yyeva.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import android.view.TextureView 7 | import com.yy.yyeva.EvaAnimPlayer 8 | import com.yy.yyeva.util.ELog 9 | 10 | class InnerTextureView @JvmOverloads constructor( 11 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 12 | ) : TextureView(context, attrs, defStyleAttr) { 13 | val TAG = "InnerTextureView" 14 | var playerEva: EvaAnimPlayer? = null 15 | 16 | init { 17 | isOpaque = false 18 | } 19 | 20 | override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { 21 | val isRunning = playerEva?.isRunning() == true 22 | val isDispatch = ev != null && (playerEva?.pluginManager?.onDispatchTouchEvent(ev) == true) 23 | val isHasBg = playerEva?.evaAnimView?.hasBgImage() == true //有背景也是直接拦截掉 24 | ELog.i(TAG, "isRunning playerEva isRunning:$isRunning, isDispatch : $isDispatch, hasBg: $isHasBg") 25 | return if (isRunning && !isDispatch && !isHasBg) super.dispatchTouchEvent(ev) else true 26 | } 27 | } -------------------------------------------------------------------------------- /yyevac/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | yyeva 3 | 4 | --------------------------------------------------------------------------------