├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bilibili │ │ └── ijkvrplayer │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── com │ │ │ └── android │ │ │ │ └── grafika │ │ │ │ └── gles │ │ │ │ ├── Drawable2d.java │ │ │ │ ├── EglCore.java │ │ │ │ ├── EglSurfaceBase.java │ │ │ │ ├── FullFrameRect.java │ │ │ │ ├── GlUtil.java │ │ │ │ ├── OffscreenSurface.java │ │ │ │ ├── Texture2dProgram.java │ │ │ │ └── WindowSurface.java │ │ └── tv │ │ │ └── danmaku │ │ │ └── ijk │ │ │ └── media │ │ │ └── vrplayer │ │ │ ├── GLTextureRender.java │ │ │ ├── IjkVRRender.java │ │ │ ├── Left.java │ │ │ ├── Right.java │ │ │ ├── SideBySideFrameRect.java │ │ │ ├── Square.java │ │ │ └── VRPlayerActivity.java │ └── res │ │ ├── layout │ │ └── activity_vr_player.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── bilibili │ └── ijkvrplayer │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IjkVRPlayer 2 | ==== 3 | 4 | This sample shows the simple way to implement VR video player based on [ijkplayer](https://github.com/Bilibili/ijkplayer). 5 | 6 | 7 | Features 8 | === 9 | 1. Play single video side-by-side. 10 | 2. ~~Project 2d texture onto sphere.~~ (TODO) 11 | 3. ~~Support 360 video.~~(TODO) 12 | 13 | License 14 | === 15 | Codes in package `com.android.grafika.gles` come from [grfika](https://github.com/google/grafika/tree/master/src/com/android/grafika/gles), licensed under Apache License v2. 16 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "tv.danmaku.ijk.media.vrplayer" 9 | minSdkVersion 17 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'tv.danmaku.ijk.media:ijkplayer-java:0.4.5.1' 25 | compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.4.5.1' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bilibili/ijkvrplayer/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.bilibili.ijkvrplayer; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/Drawable2d.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import java.nio.FloatBuffer; 20 | 21 | /** 22 | * Base class for stuff we like to draw. 23 | */ 24 | public class Drawable2d { 25 | private static final int SIZEOF_FLOAT = 4; 26 | 27 | /** 28 | * Simple equilateral triangle (1.0 per side). Centered on (0,0). 29 | */ 30 | private static final float TRIANGLE_COORDS[] = { 31 | 0.0f, 0.577350269f, // 0 top 32 | -0.5f, -0.288675135f, // 1 bottom left 33 | 0.5f, -0.288675135f // 2 bottom right 34 | }; 35 | private static final float TRIANGLE_TEX_COORDS[] = { 36 | 0.5f, 0.0f, // 0 top center 37 | 0.0f, 1.0f, // 1 bottom left 38 | 1.0f, 1.0f, // 2 bottom right 39 | }; 40 | private static final FloatBuffer TRIANGLE_BUF = 41 | GlUtil.createFloatBuffer(TRIANGLE_COORDS); 42 | private static final FloatBuffer TRIANGLE_TEX_BUF = 43 | GlUtil.createFloatBuffer(TRIANGLE_TEX_COORDS); 44 | 45 | /** 46 | * Simple square, specified as a triangle strip. The square is centered on (0,0) and has 47 | * a size of 1x1. 48 | *

49 | * Triangles are 0-1-2 and 2-1-3 (counter-clockwise winding). 50 | */ 51 | private static final float RECTANGLE_COORDS[] = { 52 | -0.5f, -0.5f, // 0 bottom left 53 | 0.5f, -0.5f, // 1 bottom right 54 | -0.5f, 0.5f, // 2 top left 55 | 0.5f, 0.5f, // 3 top right 56 | }; 57 | private static final float RECTANGLE_TEX_COORDS[] = { 58 | 0.0f, 1.0f, // 0 bottom left 59 | 1.0f, 1.0f, // 1 bottom right 60 | 0.0f, 0.0f, // 2 top left 61 | 1.0f, 0.0f // 3 top right 62 | }; 63 | private static final FloatBuffer RECTANGLE_BUF = 64 | GlUtil.createFloatBuffer(RECTANGLE_COORDS); 65 | private static final FloatBuffer RECTANGLE_TEX_BUF = 66 | GlUtil.createFloatBuffer(RECTANGLE_TEX_COORDS); 67 | 68 | /** 69 | * A "full" square, extending from -1 to +1 in both dimensions. When the model/view/projection 70 | * matrix is identity, this will exactly cover the viewport. 71 | *

72 | * The texture coordinates are Y-inverted relative to RECTANGLE. (This seems to work out 73 | * right with external textures from SurfaceTexture.) 74 | */ 75 | private static final float FULL_RECTANGLE_COORDS[] = { 76 | -1.0f, -1.0f, // 0 bottom left 77 | 1.0f, -1.0f, // 1 bottom right 78 | -1.0f, 1.0f, // 2 top left 79 | 1.0f, 1.0f, // 3 top right 80 | }; 81 | private static final float FULL_RECTANGLE_TEX_COORDS[] = { 82 | 0.0f, 0.0f, // 0 bottom left 83 | 1.0f, 0.0f, // 1 bottom right 84 | 0.0f, 1.0f, // 2 top left 85 | 1.0f, 1.0f // 3 top right 86 | }; 87 | private static final FloatBuffer FULL_RECTANGLE_BUF = 88 | GlUtil.createFloatBuffer(FULL_RECTANGLE_COORDS); 89 | private static final FloatBuffer FULL_RECTANGLE_TEX_BUF = 90 | GlUtil.createFloatBuffer(FULL_RECTANGLE_TEX_COORDS); 91 | 92 | 93 | private FloatBuffer mVertexArray; 94 | private FloatBuffer mTexCoordArray; 95 | private int mVertexCount; 96 | private int mCoordsPerVertex; 97 | private int mVertexStride; 98 | private int mTexCoordStride; 99 | private Prefab mPrefab; 100 | 101 | /** 102 | * Enum values for constructor. 103 | */ 104 | public enum Prefab { 105 | TRIANGLE, RECTANGLE, FULL_RECTANGLE 106 | } 107 | 108 | /** 109 | * Prepares a drawable from a "pre-fabricated" shape definition. 110 | *

111 | * Does no EGL/GL operations, so this can be done at any time. 112 | */ 113 | public Drawable2d(Prefab shape) { 114 | switch (shape) { 115 | case TRIANGLE: 116 | mVertexArray = TRIANGLE_BUF; 117 | mTexCoordArray = TRIANGLE_TEX_BUF; 118 | mCoordsPerVertex = 2; 119 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT; 120 | mVertexCount = TRIANGLE_COORDS.length / mCoordsPerVertex; 121 | break; 122 | case RECTANGLE: 123 | mVertexArray = RECTANGLE_BUF; 124 | mTexCoordArray = RECTANGLE_TEX_BUF; 125 | mCoordsPerVertex = 2; 126 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT; 127 | mVertexCount = RECTANGLE_COORDS.length / mCoordsPerVertex; 128 | break; 129 | case FULL_RECTANGLE: 130 | mVertexArray = FULL_RECTANGLE_BUF; 131 | mTexCoordArray = FULL_RECTANGLE_TEX_BUF; 132 | mCoordsPerVertex = 2; 133 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT; 134 | mVertexCount = FULL_RECTANGLE_COORDS.length / mCoordsPerVertex; 135 | break; 136 | default: 137 | throw new RuntimeException("Unknown shape " + shape); 138 | } 139 | mTexCoordStride = 2 * SIZEOF_FLOAT; 140 | mPrefab = shape; 141 | } 142 | 143 | /** 144 | * Returns the array of vertices. 145 | *

146 | * To avoid allocations, this returns internal state. The caller must not modify it. 147 | */ 148 | public FloatBuffer getVertexArray() { 149 | return mVertexArray; 150 | } 151 | 152 | /** 153 | * Returns the array of texture coordinates. 154 | *

155 | * To avoid allocations, this returns internal state. The caller must not modify it. 156 | */ 157 | public FloatBuffer getTexCoordArray() { 158 | return mTexCoordArray; 159 | } 160 | 161 | /** 162 | * Returns the number of vertices stored in the vertex array. 163 | */ 164 | public int getVertexCount() { 165 | return mVertexCount; 166 | } 167 | 168 | /** 169 | * Returns the width, in bytes, of the data for each vertex. 170 | */ 171 | public int getVertexStride() { 172 | return mVertexStride; 173 | } 174 | 175 | /** 176 | * Returns the width, in bytes, of the data for each texture coordinate. 177 | */ 178 | public int getTexCoordStride() { 179 | return mTexCoordStride; 180 | } 181 | 182 | /** 183 | * Returns the number of position coordinates per vertex. This will be 2 or 3. 184 | */ 185 | public int getCoordsPerVertex() { 186 | return mCoordsPerVertex; 187 | } 188 | 189 | @Override 190 | public String toString() { 191 | if (mPrefab != null) { 192 | return "[Drawable2d: " + mPrefab + "]"; 193 | } else { 194 | return "[Drawable2d: ...]"; 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/EglCore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.graphics.SurfaceTexture; 20 | import android.opengl.EGL14; 21 | import android.opengl.EGLConfig; 22 | import android.opengl.EGLContext; 23 | import android.opengl.EGLDisplay; 24 | import android.opengl.EGLExt; 25 | import android.opengl.EGLSurface; 26 | import android.util.Log; 27 | import android.view.Surface; 28 | 29 | /** 30 | * Core EGL state (display, context, config). 31 | *

32 | * The EGLContext must only be attached to one thread at a time. This class is not thread-safe. 33 | */ 34 | public final class EglCore { 35 | private static final String TAG = GlUtil.TAG; 36 | 37 | /** 38 | * Constructor flag: surface must be recordable. This discourages EGL from using a 39 | * pixel format that cannot be converted efficiently to something usable by the video 40 | * encoder. 41 | */ 42 | public static final int FLAG_RECORDABLE = 0x01; 43 | 44 | /** 45 | * Constructor flag: ask for GLES3, fall back to GLES2 if not available. Without this 46 | * flag, GLES2 is used. 47 | */ 48 | public static final int FLAG_TRY_GLES3 = 0x02; 49 | 50 | // Android-specific extension. 51 | private static final int EGL_RECORDABLE_ANDROID = 0x3142; 52 | 53 | private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY; 54 | private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT; 55 | private EGLConfig mEGLConfig = null; 56 | private int mGlVersion = -1; 57 | 58 | 59 | /** 60 | * Prepares EGL display and context. 61 | *

62 | * Equivalent to EglCore(null, 0). 63 | */ 64 | public EglCore() { 65 | this(null, 0); 66 | } 67 | 68 | /** 69 | * Prepares EGL display and context. 70 | *

71 | * @param sharedContext The context to share, or null if sharing is not desired. 72 | * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE. 73 | */ 74 | public EglCore(EGLContext sharedContext, int flags) { 75 | if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 76 | throw new RuntimeException("EGL already set up"); 77 | } 78 | 79 | if (sharedContext == null) { 80 | sharedContext = EGL14.EGL_NO_CONTEXT; 81 | } 82 | 83 | mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 84 | if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 85 | throw new RuntimeException("unable to get EGL14 display"); 86 | } 87 | int[] version = new int[2]; 88 | if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { 89 | mEGLDisplay = null; 90 | throw new RuntimeException("unable to initialize EGL14"); 91 | } 92 | 93 | // Try to get a GLES3 context, if requested. 94 | if ((flags & FLAG_TRY_GLES3) != 0) { 95 | //Log.d(TAG, "Trying GLES 3"); 96 | EGLConfig config = getConfig(flags, 3); 97 | if (config != null) { 98 | int[] attrib3_list = { 99 | EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, 100 | EGL14.EGL_NONE 101 | }; 102 | EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, 103 | attrib3_list, 0); 104 | 105 | if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) { 106 | //Log.d(TAG, "Got GLES 3 config"); 107 | mEGLConfig = config; 108 | mEGLContext = context; 109 | mGlVersion = 3; 110 | } 111 | } 112 | } 113 | if (mEGLContext == EGL14.EGL_NO_CONTEXT) { // GLES 2 only, or GLES 3 attempt failed 114 | //Log.d(TAG, "Trying GLES 2"); 115 | EGLConfig config = getConfig(flags, 2); 116 | if (config == null) { 117 | throw new RuntimeException("Unable to find a suitable EGLConfig"); 118 | } 119 | int[] attrib2_list = { 120 | EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 121 | EGL14.EGL_NONE 122 | }; 123 | EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext, 124 | attrib2_list, 0); 125 | checkEglError("eglCreateContext"); 126 | mEGLConfig = config; 127 | mEGLContext = context; 128 | mGlVersion = 2; 129 | } 130 | 131 | // Confirm with query. 132 | int[] values = new int[1]; 133 | EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, 134 | values, 0); 135 | Log.d(TAG, "EGLContext created, client version " + values[0]); 136 | } 137 | 138 | /** 139 | * Finds a suitable EGLConfig. 140 | * 141 | * @param flags Bit flags from constructor. 142 | * @param version Must be 2 or 3. 143 | */ 144 | private EGLConfig getConfig(int flags, int version) { 145 | int renderableType = EGL14.EGL_OPENGL_ES2_BIT; 146 | if (version >= 3) { 147 | renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR; 148 | } 149 | 150 | // The actual surface is generally RGBA or RGBX, so situationally omitting alpha 151 | // doesn't really help. It can also lead to a huge performance hit on glReadPixels() 152 | // when reading into a GL_RGBA buffer. 153 | int[] attribList = { 154 | EGL14.EGL_RED_SIZE, 8, 155 | EGL14.EGL_GREEN_SIZE, 8, 156 | EGL14.EGL_BLUE_SIZE, 8, 157 | EGL14.EGL_ALPHA_SIZE, 8, 158 | //EGL14.EGL_DEPTH_SIZE, 16, 159 | //EGL14.EGL_STENCIL_SIZE, 8, 160 | EGL14.EGL_RENDERABLE_TYPE, renderableType, 161 | EGL14.EGL_NONE, 0, // placeholder for recordable [@-3] 162 | EGL14.EGL_NONE 163 | }; 164 | if ((flags & FLAG_RECORDABLE) != 0) { 165 | attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID; 166 | attribList[attribList.length - 2] = 1; 167 | } 168 | EGLConfig[] configs = new EGLConfig[1]; 169 | int[] numConfigs = new int[1]; 170 | if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, 171 | numConfigs, 0)) { 172 | Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig"); 173 | return null; 174 | } 175 | return configs[0]; 176 | } 177 | 178 | /** 179 | * Discards all resources held by this class, notably the EGL context. This must be 180 | * called from the thread where the context was created. 181 | *

182 | * On completion, no context will be current. 183 | */ 184 | public void release() { 185 | if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 186 | // Android is unusual in that it uses a reference-counted EGLDisplay. So for 187 | // every eglInitialize() we need an eglTerminate(). 188 | EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 189 | EGL14.EGL_NO_CONTEXT); 190 | EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 191 | EGL14.eglReleaseThread(); 192 | EGL14.eglTerminate(mEGLDisplay); 193 | } 194 | 195 | mEGLDisplay = EGL14.EGL_NO_DISPLAY; 196 | mEGLContext = EGL14.EGL_NO_CONTEXT; 197 | mEGLConfig = null; 198 | } 199 | 200 | @Override 201 | protected void finalize() throws Throwable { 202 | try { 203 | if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 204 | // We're limited here -- finalizers don't run on the thread that holds 205 | // the EGL state, so if a surface or context is still current on another 206 | // thread we can't fully release it here. Exceptions thrown from here 207 | // are quietly discarded. Complain in the log file. 208 | Log.w(TAG, "WARNING: EglCore was not explicitly released -- state may be leaked"); 209 | release(); 210 | } 211 | } finally { 212 | super.finalize(); 213 | } 214 | } 215 | 216 | /** 217 | * Destroys the specified surface. Note the EGLSurface won't actually be destroyed if it's 218 | * still current in a context. 219 | */ 220 | public void releaseSurface(EGLSurface eglSurface) { 221 | EGL14.eglDestroySurface(mEGLDisplay, eglSurface); 222 | } 223 | 224 | /** 225 | * Creates an EGL surface associated with a Surface. 226 | *

227 | * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute. 228 | */ 229 | public EGLSurface createWindowSurface(Object surface) { 230 | if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) { 231 | throw new RuntimeException("invalid surface: " + surface); 232 | } 233 | 234 | // Create a window surface, and attach it to the Surface we received. 235 | int[] surfaceAttribs = { 236 | EGL14.EGL_NONE 237 | }; 238 | EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface, 239 | surfaceAttribs, 0); 240 | checkEglError("eglCreateWindowSurface"); 241 | if (eglSurface == null) { 242 | throw new RuntimeException("surface was null"); 243 | } 244 | return eglSurface; 245 | } 246 | 247 | /** 248 | * Creates an EGL surface associated with an offscreen buffer. 249 | */ 250 | public EGLSurface createOffscreenSurface(int width, int height) { 251 | int[] surfaceAttribs = { 252 | EGL14.EGL_WIDTH, width, 253 | EGL14.EGL_HEIGHT, height, 254 | EGL14.EGL_NONE 255 | }; 256 | EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, 257 | surfaceAttribs, 0); 258 | checkEglError("eglCreatePbufferSurface"); 259 | if (eglSurface == null) { 260 | throw new RuntimeException("surface was null"); 261 | } 262 | return eglSurface; 263 | } 264 | 265 | /** 266 | * Makes our EGL context current, using the supplied surface for both "draw" and "read". 267 | */ 268 | public void makeCurrent(EGLSurface eglSurface) { 269 | if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 270 | // called makeCurrent() before create? 271 | Log.d(TAG, "NOTE: makeCurrent w/o display"); 272 | } 273 | if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) { 274 | throw new RuntimeException("eglMakeCurrent failed"); 275 | } 276 | } 277 | 278 | /** 279 | * Makes our EGL context current, using the supplied "draw" and "read" surfaces. 280 | */ 281 | public void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) { 282 | if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 283 | // called makeCurrent() before create? 284 | Log.d(TAG, "NOTE: makeCurrent w/o display"); 285 | } 286 | if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) { 287 | throw new RuntimeException("eglMakeCurrent(draw,read) failed"); 288 | } 289 | } 290 | 291 | /** 292 | * Makes no context current. 293 | */ 294 | public void makeNothingCurrent() { 295 | if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 296 | EGL14.EGL_NO_CONTEXT)) { 297 | throw new RuntimeException("eglMakeCurrent failed"); 298 | } 299 | } 300 | 301 | /** 302 | * Calls eglSwapBuffers. Use this to "publish" the current frame. 303 | * 304 | * @return false on failure 305 | */ 306 | public boolean swapBuffers(EGLSurface eglSurface) { 307 | return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface); 308 | } 309 | 310 | /** 311 | * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. 312 | */ 313 | public void setPresentationTime(EGLSurface eglSurface, long nsecs) { 314 | EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs); 315 | } 316 | 317 | /** 318 | * Returns true if our context and the specified surface are current. 319 | */ 320 | public boolean isCurrent(EGLSurface eglSurface) { 321 | return mEGLContext.equals(EGL14.eglGetCurrentContext()) && 322 | eglSurface.equals(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW)); 323 | } 324 | 325 | /** 326 | * Performs a simple surface query. 327 | */ 328 | public int querySurface(EGLSurface eglSurface, int what) { 329 | int[] value = new int[1]; 330 | EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0); 331 | return value[0]; 332 | } 333 | 334 | /** 335 | * Queries a string value. 336 | */ 337 | public String queryString(int what) { 338 | return EGL14.eglQueryString(mEGLDisplay, what); 339 | } 340 | 341 | /** 342 | * Returns the GLES version this context is configured for (currently 2 or 3). 343 | */ 344 | public int getGlVersion() { 345 | return mGlVersion; 346 | } 347 | 348 | /** 349 | * Writes the current display, context, and surface to the log. 350 | */ 351 | public static void logCurrent(String msg) { 352 | EGLDisplay display; 353 | EGLContext context; 354 | EGLSurface surface; 355 | 356 | display = EGL14.eglGetCurrentDisplay(); 357 | context = EGL14.eglGetCurrentContext(); 358 | surface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW); 359 | Log.i(TAG, "Current EGL (" + msg + "): display=" + display + ", context=" + context + 360 | ", surface=" + surface); 361 | } 362 | 363 | /** 364 | * Checks for EGL errors. Throws an exception if an error has been raised. 365 | */ 366 | private void checkEglError(String msg) { 367 | int error; 368 | if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 369 | throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/EglSurfaceBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.graphics.Bitmap; 20 | import android.opengl.EGL14; 21 | import android.opengl.EGLSurface; 22 | import android.opengl.GLES20; 23 | import android.util.Log; 24 | 25 | import java.io.BufferedOutputStream; 26 | import java.io.File; 27 | import java.io.FileOutputStream; 28 | import java.io.IOException; 29 | import java.nio.ByteBuffer; 30 | import java.nio.ByteOrder; 31 | 32 | /** 33 | * Common base class for EGL surfaces. 34 | *

35 | * There can be multiple surfaces associated with a single context. 36 | */ 37 | public class EglSurfaceBase { 38 | protected static final String TAG = GlUtil.TAG; 39 | 40 | // EglCore object we're associated with. It may be associated with multiple surfaces. 41 | protected EglCore mEglCore; 42 | 43 | private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE; 44 | private int mWidth = -1; 45 | private int mHeight = -1; 46 | 47 | protected EglSurfaceBase(EglCore eglCore) { 48 | mEglCore = eglCore; 49 | } 50 | 51 | /** 52 | * Creates a window surface. 53 | *

54 | * @param surface May be a Surface or SurfaceTexture. 55 | */ 56 | public void createWindowSurface(Object surface) { 57 | if (mEGLSurface != EGL14.EGL_NO_SURFACE) { 58 | throw new IllegalStateException("surface already created"); 59 | } 60 | mEGLSurface = mEglCore.createWindowSurface(surface); 61 | 62 | // Don't cache width/height here, because the size of the underlying surface can change 63 | // out from under us (see e.g. HardwareScalerActivity). 64 | //mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH); 65 | //mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT); 66 | } 67 | 68 | /** 69 | * Creates an off-screen surface. 70 | */ 71 | public void createOffscreenSurface(int width, int height) { 72 | if (mEGLSurface != EGL14.EGL_NO_SURFACE) { 73 | throw new IllegalStateException("surface already created"); 74 | } 75 | mEGLSurface = mEglCore.createOffscreenSurface(width, height); 76 | mWidth = width; 77 | mHeight = height; 78 | } 79 | 80 | /** 81 | * Returns the surface's width, in pixels. 82 | *

83 | * If this is called on a window surface, and the underlying surface is in the process 84 | * of changing size, we may not see the new size right away (e.g. in the "surfaceChanged" 85 | * callback). The size should match after the next buffer swap. 86 | */ 87 | public int getWidth() { 88 | if (mWidth < 0) { 89 | return mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH); 90 | } else { 91 | return mWidth; 92 | } 93 | } 94 | 95 | /** 96 | * Returns the surface's height, in pixels. 97 | */ 98 | public int getHeight() { 99 | if (mHeight < 0) { 100 | return mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT); 101 | } else { 102 | return mHeight; 103 | } 104 | } 105 | 106 | /** 107 | * Release the EGL surface. 108 | */ 109 | public void releaseEglSurface() { 110 | mEglCore.releaseSurface(mEGLSurface); 111 | mEGLSurface = EGL14.EGL_NO_SURFACE; 112 | mWidth = mHeight = -1; 113 | } 114 | 115 | /** 116 | * Makes our EGL context and surface current. 117 | */ 118 | public void makeCurrent() { 119 | mEglCore.makeCurrent(mEGLSurface); 120 | } 121 | 122 | /** 123 | * Makes our EGL context and surface current for drawing, using the supplied surface 124 | * for reading. 125 | */ 126 | public void makeCurrentReadFrom(EglSurfaceBase readSurface) { 127 | mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface); 128 | } 129 | 130 | /** 131 | * Calls eglSwapBuffers. Use this to "publish" the current frame. 132 | * 133 | * @return false on failure 134 | */ 135 | public boolean swapBuffers() { 136 | boolean result = mEglCore.swapBuffers(mEGLSurface); 137 | if (!result) { 138 | Log.d(TAG, "WARNING: swapBuffers() failed"); 139 | } 140 | return result; 141 | } 142 | 143 | /** 144 | * Sends the presentation time stamp to EGL. 145 | * 146 | * @param nsecs Timestamp, in nanoseconds. 147 | */ 148 | public void setPresentationTime(long nsecs) { 149 | mEglCore.setPresentationTime(mEGLSurface, nsecs); 150 | } 151 | 152 | /** 153 | * Saves the EGL surface to a file. 154 | *

155 | * Expects that this object's EGL surface is current. 156 | */ 157 | public void saveFrame(File file) throws IOException { 158 | if (!mEglCore.isCurrent(mEGLSurface)) { 159 | throw new RuntimeException("Expected EGL context/surface is not current"); 160 | } 161 | 162 | // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA 163 | // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap 164 | // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the 165 | // Bitmap "copy pixels" method wants the same format GL provides. 166 | // 167 | // Ideally we'd have some way to re-use the ByteBuffer, especially if we're calling 168 | // here often. 169 | // 170 | // Making this even more interesting is the upside-down nature of GL, which means 171 | // our output will look upside down relative to what appears on screen if the 172 | // typical GL conventions are used. 173 | 174 | String filename = file.toString(); 175 | 176 | int width = getWidth(); 177 | int height = getHeight(); 178 | ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4); 179 | buf.order(ByteOrder.LITTLE_ENDIAN); 180 | GLES20.glReadPixels(0, 0, width, height, 181 | GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf); 182 | GlUtil.checkGlError("glReadPixels"); 183 | buf.rewind(); 184 | 185 | BufferedOutputStream bos = null; 186 | try { 187 | bos = new BufferedOutputStream(new FileOutputStream(filename)); 188 | Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 189 | bmp.copyPixelsFromBuffer(buf); 190 | bmp.compress(Bitmap.CompressFormat.PNG, 90, bos); 191 | bmp.recycle(); 192 | } finally { 193 | if (bos != null) bos.close(); 194 | } 195 | Log.d(TAG, "Saved " + width + "x" + height + " frame as '" + filename + "'"); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/FullFrameRect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | /** 20 | * This class essentially represents a viewport-sized sprite that will be rendered with 21 | * a texture, usually from an external source like the camera or video decoder. 22 | */ 23 | public class FullFrameRect { 24 | private final Drawable2d mRectDrawable = new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE); 25 | private Texture2dProgram mProgram; 26 | 27 | /** 28 | * Prepares the object. 29 | * 30 | * @param program The program to use. FullFrameRect takes ownership, and will release 31 | * the program when no longer needed. 32 | */ 33 | public FullFrameRect(Texture2dProgram program) { 34 | mProgram = program; 35 | } 36 | 37 | /** 38 | * Releases resources. 39 | *

40 | * This must be called with the appropriate EGL context current (i.e. the one that was 41 | * current when the constructor was called). If we're about to destroy the EGL context, 42 | * there's no value in having the caller make it current just to do this cleanup, so you 43 | * can pass a flag that will tell this function to skip any EGL-context-specific cleanup. 44 | */ 45 | public void release(boolean doEglCleanup) { 46 | if (mProgram != null) { 47 | if (doEglCleanup) { 48 | mProgram.release(); 49 | } 50 | mProgram = null; 51 | } 52 | } 53 | 54 | /** 55 | * Returns the program currently in use. 56 | */ 57 | public Texture2dProgram getProgram() { 58 | return mProgram; 59 | } 60 | 61 | /** 62 | * Changes the program. The previous program will be released. 63 | *

64 | * The appropriate EGL context must be current. 65 | */ 66 | public void changeProgram(Texture2dProgram program) { 67 | mProgram.release(); 68 | mProgram = program; 69 | } 70 | 71 | /** 72 | * Creates a texture object suitable for use with drawFrame(). 73 | */ 74 | public int createTextureObject() { 75 | return mProgram.createTextureObject(); 76 | } 77 | 78 | /** 79 | * Draws a viewport-filling rect, texturing it with the specified texture object. 80 | */ 81 | public void drawFrame(int textureId, float[] texMatrix) { 82 | // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport. 83 | mProgram.draw(GlUtil.IDENTITY_MATRIX, mRectDrawable.getVertexArray(), 0, 84 | mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(), 85 | mRectDrawable.getVertexStride(), 86 | texMatrix, mRectDrawable.getTexCoordArray(), textureId, 87 | mRectDrawable.getTexCoordStride()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/GlUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.opengl.GLES20; 20 | import android.opengl.GLES30; 21 | import android.opengl.Matrix; 22 | import android.util.Log; 23 | 24 | import java.nio.ByteBuffer; 25 | import java.nio.ByteOrder; 26 | import java.nio.FloatBuffer; 27 | 28 | /** 29 | * Some OpenGL utility functions. 30 | */ 31 | public class GlUtil { 32 | public static final String TAG = "Grafika"; 33 | 34 | /** Identity matrix for general use. Don't modify or life will get weird. */ 35 | public static final float[] IDENTITY_MATRIX; 36 | static { 37 | IDENTITY_MATRIX = new float[16]; 38 | Matrix.setIdentityM(IDENTITY_MATRIX, 0); 39 | } 40 | 41 | private static final int SIZEOF_FLOAT = 4; 42 | 43 | 44 | private GlUtil() {} // do not instantiate 45 | 46 | /** 47 | * Creates a new program from the supplied vertex and fragment shaders. 48 | * 49 | * @return A handle to the program, or 0 on failure. 50 | */ 51 | public static int createProgram(String vertexSource, String fragmentSource) { 52 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 53 | if (vertexShader == 0) { 54 | return 0; 55 | } 56 | int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 57 | if (pixelShader == 0) { 58 | return 0; 59 | } 60 | 61 | int program = GLES20.glCreateProgram(); 62 | checkGlError("glCreateProgram"); 63 | if (program == 0) { 64 | Log.e(TAG, "Could not create program"); 65 | } 66 | GLES20.glAttachShader(program, vertexShader); 67 | checkGlError("glAttachShader"); 68 | GLES20.glAttachShader(program, pixelShader); 69 | checkGlError("glAttachShader"); 70 | GLES20.glLinkProgram(program); 71 | int[] linkStatus = new int[1]; 72 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 73 | if (linkStatus[0] != GLES20.GL_TRUE) { 74 | Log.e(TAG, "Could not link program: "); 75 | Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 76 | GLES20.glDeleteProgram(program); 77 | program = 0; 78 | } 79 | return program; 80 | } 81 | 82 | /** 83 | * Compiles the provided shader source. 84 | * 85 | * @return A handle to the shader, or 0 on failure. 86 | */ 87 | public static int loadShader(int shaderType, String source) { 88 | int shader = GLES20.glCreateShader(shaderType); 89 | checkGlError("glCreateShader type=" + shaderType); 90 | GLES20.glShaderSource(shader, source); 91 | GLES20.glCompileShader(shader); 92 | int[] compiled = new int[1]; 93 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 94 | if (compiled[0] == 0) { 95 | Log.e(TAG, "Could not compile shader " + shaderType + ":"); 96 | Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader)); 97 | GLES20.glDeleteShader(shader); 98 | shader = 0; 99 | } 100 | return shader; 101 | } 102 | 103 | /** 104 | * Checks to see if a GLES error has been raised. 105 | */ 106 | public static void checkGlError(String op) { 107 | int error = GLES20.glGetError(); 108 | if (error != GLES20.GL_NO_ERROR) { 109 | String msg = op + ": glError 0x" + Integer.toHexString(error); 110 | Log.e(TAG, msg); 111 | throw new RuntimeException(msg); 112 | } 113 | } 114 | 115 | /** 116 | * Checks to see if the location we obtained is valid. GLES returns -1 if a label 117 | * could not be found, but does not set the GL error. 118 | *

119 | * Throws a RuntimeException if the location is invalid. 120 | */ 121 | public static void checkLocation(int location, String label) { 122 | if (location < 0) { 123 | throw new RuntimeException("Unable to locate '" + label + "' in program"); 124 | } 125 | } 126 | 127 | /** 128 | * Creates a texture from raw data. 129 | * 130 | * @param data Image data, in a "direct" ByteBuffer. 131 | * @param width Texture width, in pixels (not bytes). 132 | * @param height Texture height, in pixels. 133 | * @param format Image data format (use constant appropriate for glTexImage2D(), e.g. GL_RGBA). 134 | * @return Handle to texture. 135 | */ 136 | public static int createImageTexture(ByteBuffer data, int width, int height, int format) { 137 | int[] textureHandles = new int[1]; 138 | int textureHandle; 139 | 140 | GLES20.glGenTextures(1, textureHandles, 0); 141 | textureHandle = textureHandles[0]; 142 | GlUtil.checkGlError("glGenTextures"); 143 | 144 | // Bind the texture handle to the 2D texture target. 145 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); 146 | 147 | // Configure min/mag filtering, i.e. what scaling method do we use if what we're rendering 148 | // is smaller or larger than the source image. 149 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 150 | GLES20.GL_LINEAR); 151 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 152 | GLES20.GL_LINEAR); 153 | GlUtil.checkGlError("loadImageTexture"); 154 | 155 | // Load the data from the buffer into the texture handle. 156 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, /*level*/ 0, format, 157 | width, height, /*border*/ 0, format, GLES20.GL_UNSIGNED_BYTE, data); 158 | GlUtil.checkGlError("loadImageTexture"); 159 | 160 | return textureHandle; 161 | } 162 | 163 | /** 164 | * Allocates a direct float buffer, and populates it with the float array data. 165 | */ 166 | public static FloatBuffer createFloatBuffer(float[] coords) { 167 | // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it. 168 | ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT); 169 | bb.order(ByteOrder.nativeOrder()); 170 | FloatBuffer fb = bb.asFloatBuffer(); 171 | fb.put(coords); 172 | fb.position(0); 173 | return fb; 174 | } 175 | 176 | /** 177 | * Writes GL version info to the log. 178 | */ 179 | public static void logVersionInfo() { 180 | Log.i(TAG, "vendor : " + GLES20.glGetString(GLES20.GL_VENDOR)); 181 | Log.i(TAG, "renderer: " + GLES20.glGetString(GLES20.GL_RENDERER)); 182 | Log.i(TAG, "version : " + GLES20.glGetString(GLES20.GL_VERSION)); 183 | 184 | if (false) { 185 | int[] values = new int[1]; 186 | GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, values, 0); 187 | int majorVersion = values[0]; 188 | GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, values, 0); 189 | int minorVersion = values[0]; 190 | if (GLES30.glGetError() == GLES30.GL_NO_ERROR) { 191 | Log.i(TAG, "iversion: " + majorVersion + "." + minorVersion); 192 | } 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/OffscreenSurface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | /** 20 | * Off-screen EGL surface (pbuffer). 21 | *

22 | * It's good practice to explicitly release() the surface, preferably from a "finally" block. 23 | */ 24 | public class OffscreenSurface extends EglSurfaceBase { 25 | /** 26 | * Creates an off-screen surface with the specified width and height. 27 | */ 28 | public OffscreenSurface(EglCore eglCore, int width, int height) { 29 | super(eglCore); 30 | createOffscreenSurface(width, height); 31 | } 32 | 33 | /** 34 | * Releases any resources associated with the surface. 35 | */ 36 | public void release() { 37 | releaseEglSurface(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/Texture2dProgram.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.opengl.GLES11Ext; 20 | import android.opengl.GLES20; 21 | import android.util.Log; 22 | 23 | import java.nio.FloatBuffer; 24 | 25 | /** 26 | * GL program and supporting functions for textured 2D shapes. 27 | */ 28 | public class Texture2dProgram { 29 | private static final String TAG = GlUtil.TAG; 30 | 31 | public enum ProgramType { 32 | TEXTURE_2D, TEXTURE_EXT, TEXTURE_EXT_BW, TEXTURE_EXT_FILT 33 | } 34 | 35 | // Simple vertex shader, used for all programs. 36 | private static final String VERTEX_SHADER = 37 | "uniform mat4 uMVPMatrix;\n" + 38 | "uniform mat4 uTexMatrix;\n" + 39 | "attribute vec4 aPosition;\n" + 40 | "attribute vec4 aTextureCoord;\n" + 41 | "varying vec2 vTextureCoord;\n" + 42 | "void main() {\n" + 43 | " gl_Position = uMVPMatrix * aPosition;\n" + 44 | " vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" + 45 | "}\n"; 46 | 47 | // Simple fragment shader for use with "normal" 2D textures. 48 | private static final String FRAGMENT_SHADER_2D = 49 | "precision mediump float;\n" + 50 | "varying vec2 vTextureCoord;\n" + 51 | "uniform sampler2D sTexture;\n" + 52 | "void main() {\n" + 53 | " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 54 | "}\n"; 55 | 56 | // Simple fragment shader for use with external 2D textures (e.g. what we get from 57 | // SurfaceTexture). 58 | private static final String FRAGMENT_SHADER_EXT = 59 | "#extension GL_OES_EGL_image_external : require\n" + 60 | "precision mediump float;\n" + 61 | "varying vec2 vTextureCoord;\n" + 62 | "uniform samplerExternalOES sTexture;\n" + 63 | "void main() {\n" + 64 | " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 65 | "}\n"; 66 | 67 | // Fragment shader that converts color to black & white with a simple transformation. 68 | private static final String FRAGMENT_SHADER_EXT_BW = 69 | "#extension GL_OES_EGL_image_external : require\n" + 70 | "precision mediump float;\n" + 71 | "varying vec2 vTextureCoord;\n" + 72 | "uniform samplerExternalOES sTexture;\n" + 73 | "void main() {\n" + 74 | " vec4 tc = texture2D(sTexture, vTextureCoord);\n" + 75 | " float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" + 76 | " gl_FragColor = vec4(color, color, color, 1.0);\n" + 77 | "}\n"; 78 | 79 | // Fragment shader with a convolution filter. The upper-left half will be drawn normally, 80 | // the lower-right half will have the filter applied, and a thin red line will be drawn 81 | // at the border. 82 | // 83 | // This is not optimized for performance. Some things that might make this faster: 84 | // - Remove the conditionals. They're used to present a half & half view with a red 85 | // stripe across the middle, but that's only useful for a demo. 86 | // - Unroll the loop. Ideally the compiler does this for you when it's beneficial. 87 | // - Bake the filter kernel into the shader, instead of passing it through a uniform 88 | // array. That, combined with loop unrolling, should reduce memory accesses. 89 | public static final int KERNEL_SIZE = 9; 90 | private static final String FRAGMENT_SHADER_EXT_FILT = 91 | "#extension GL_OES_EGL_image_external : require\n" + 92 | "#define KERNEL_SIZE " + KERNEL_SIZE + "\n" + 93 | "precision highp float;\n" + 94 | "varying vec2 vTextureCoord;\n" + 95 | "uniform samplerExternalOES sTexture;\n" + 96 | "uniform float uKernel[KERNEL_SIZE];\n" + 97 | "uniform vec2 uTexOffset[KERNEL_SIZE];\n" + 98 | "uniform float uColorAdjust;\n" + 99 | "void main() {\n" + 100 | " int i = 0;\n" + 101 | " vec4 sum = vec4(0.0);\n" + 102 | " if (vTextureCoord.x < vTextureCoord.y - 0.005) {\n" + 103 | " for (i = 0; i < KERNEL_SIZE; i++) {\n" + 104 | " vec4 texc = texture2D(sTexture, vTextureCoord + uTexOffset[i]);\n" + 105 | " sum += texc * uKernel[i];\n" + 106 | " }\n" + 107 | " sum += uColorAdjust;\n" + 108 | " } else if (vTextureCoord.x > vTextureCoord.y + 0.005) {\n" + 109 | " sum = texture2D(sTexture, vTextureCoord);\n" + 110 | " } else {\n" + 111 | " sum.r = 1.0;\n" + 112 | " }\n" + 113 | " gl_FragColor = sum;\n" + 114 | "}\n"; 115 | 116 | private ProgramType mProgramType; 117 | 118 | // Handles to the GL program and various components of it. 119 | private int mProgramHandle; 120 | private int muMVPMatrixLoc; 121 | private int muTexMatrixLoc; 122 | private int muKernelLoc; 123 | private int muTexOffsetLoc; 124 | private int muColorAdjustLoc; 125 | private int maPositionLoc; 126 | private int maTextureCoordLoc; 127 | 128 | private int mTextureTarget; 129 | 130 | private float[] mKernel = new float[KERNEL_SIZE]; 131 | private float[] mTexOffset; 132 | private float mColorAdjust; 133 | 134 | 135 | /** 136 | * Prepares the program in the current EGL context. 137 | */ 138 | public Texture2dProgram(ProgramType programType) { 139 | mProgramType = programType; 140 | 141 | switch (programType) { 142 | case TEXTURE_2D: 143 | mTextureTarget = GLES20.GL_TEXTURE_2D; 144 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_2D); 145 | break; 146 | case TEXTURE_EXT: 147 | mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; 148 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT); 149 | break; 150 | case TEXTURE_EXT_BW: 151 | mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; 152 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT_BW); 153 | break; 154 | case TEXTURE_EXT_FILT: 155 | mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; 156 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT_FILT); 157 | break; 158 | default: 159 | throw new RuntimeException("Unhandled type " + programType); 160 | } 161 | if (mProgramHandle == 0) { 162 | throw new RuntimeException("Unable to create program"); 163 | } 164 | Log.d(TAG, "Created program " + mProgramHandle + " (" + programType + ")"); 165 | 166 | // get locations of attributes and uniforms 167 | 168 | maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition"); 169 | GlUtil.checkLocation(maPositionLoc, "aPosition"); 170 | maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord"); 171 | GlUtil.checkLocation(maTextureCoordLoc, "aTextureCoord"); 172 | muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix"); 173 | GlUtil.checkLocation(muMVPMatrixLoc, "uMVPMatrix"); 174 | muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix"); 175 | GlUtil.checkLocation(muTexMatrixLoc, "uTexMatrix"); 176 | muKernelLoc = GLES20.glGetUniformLocation(mProgramHandle, "uKernel"); 177 | if (muKernelLoc < 0) { 178 | // no kernel in this one 179 | muKernelLoc = -1; 180 | muTexOffsetLoc = -1; 181 | muColorAdjustLoc = -1; 182 | } else { 183 | // has kernel, must also have tex offset and color adj 184 | muTexOffsetLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexOffset"); 185 | GlUtil.checkLocation(muTexOffsetLoc, "uTexOffset"); 186 | muColorAdjustLoc = GLES20.glGetUniformLocation(mProgramHandle, "uColorAdjust"); 187 | GlUtil.checkLocation(muColorAdjustLoc, "uColorAdjust"); 188 | 189 | // initialize default values 190 | setKernel(new float[] {0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f}, 0f); 191 | setTexSize(256, 256); 192 | } 193 | } 194 | 195 | /** 196 | * Releases the program. 197 | *

198 | * The appropriate EGL context must be current (i.e. the one that was used to create 199 | * the program). 200 | */ 201 | public void release() { 202 | Log.d(TAG, "deleting program " + mProgramHandle); 203 | GLES20.glDeleteProgram(mProgramHandle); 204 | mProgramHandle = -1; 205 | } 206 | 207 | /** 208 | * Returns the program type. 209 | */ 210 | public ProgramType getProgramType() { 211 | return mProgramType; 212 | } 213 | 214 | /** 215 | * Creates a texture object suitable for use with this program. 216 | *

217 | * On exit, the texture will be bound. 218 | */ 219 | public int createTextureObject() { 220 | int[] textures = new int[1]; 221 | GLES20.glGenTextures(1, textures, 0); 222 | GlUtil.checkGlError("glGenTextures"); 223 | 224 | int texId = textures[0]; 225 | GLES20.glBindTexture(mTextureTarget, texId); 226 | GlUtil.checkGlError("glBindTexture " + texId); 227 | 228 | GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, 229 | GLES20.GL_NEAREST); 230 | GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, 231 | GLES20.GL_LINEAR); 232 | GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, 233 | GLES20.GL_CLAMP_TO_EDGE); 234 | GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, 235 | GLES20.GL_CLAMP_TO_EDGE); 236 | GlUtil.checkGlError("glTexParameter"); 237 | 238 | return texId; 239 | } 240 | 241 | /** 242 | * Configures the convolution filter values. 243 | * 244 | * @param values Normalized filter values; must be KERNEL_SIZE elements. 245 | */ 246 | public void setKernel(float[] values, float colorAdj) { 247 | if (values.length != KERNEL_SIZE) { 248 | throw new IllegalArgumentException("Kernel size is " + values.length + 249 | " vs. " + KERNEL_SIZE); 250 | } 251 | System.arraycopy(values, 0, mKernel, 0, KERNEL_SIZE); 252 | mColorAdjust = colorAdj; 253 | //Log.d(TAG, "filt kernel: " + Arrays.toString(mKernel) + ", adj=" + colorAdj); 254 | } 255 | 256 | /** 257 | * Sets the size of the texture. This is used to find adjacent texels when filtering. 258 | */ 259 | public void setTexSize(int width, int height) { 260 | float rw = 1.0f / width; 261 | float rh = 1.0f / height; 262 | 263 | // Don't need to create a new array here, but it's syntactically convenient. 264 | mTexOffset = new float[] { 265 | -rw, -rh, 0f, -rh, rw, -rh, 266 | -rw, 0f, 0f, 0f, rw, 0f, 267 | -rw, rh, 0f, rh, rw, rh 268 | }; 269 | //Log.d(TAG, "filt size: " + width + "x" + height + ": " + Arrays.toString(mTexOffset)); 270 | } 271 | 272 | /** 273 | * Issues the draw call. Does the full setup on every call. 274 | * 275 | * @param mvpMatrix The 4x4 projection matrix. 276 | * @param vertexBuffer Buffer with vertex position data. 277 | * @param firstVertex Index of first vertex to use in vertexBuffer. 278 | * @param vertexCount Number of vertices in vertexBuffer. 279 | * @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2). 280 | * @param vertexStride Width, in bytes, of the position data for each vertex (often 281 | * vertexCount * sizeof(float)). 282 | * @param texMatrix A 4x4 transformation matrix for texture coords. (Primarily intended 283 | * for use with SurfaceTexture.) 284 | * @param texBuffer Buffer with vertex texture data. 285 | * @param texStride Width, in bytes, of the texture data for each vertex. 286 | */ 287 | public void draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex, 288 | int vertexCount, int coordsPerVertex, int vertexStride, 289 | float[] texMatrix, FloatBuffer texBuffer, int textureId, int texStride) { 290 | GlUtil.checkGlError("draw start"); 291 | 292 | // Select the program. 293 | GLES20.glUseProgram(mProgramHandle); 294 | GlUtil.checkGlError("glUseProgram"); 295 | 296 | // Set the texture. 297 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 298 | GLES20.glBindTexture(mTextureTarget, textureId); 299 | 300 | // Copy the model / view / projection matrix over. 301 | GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0); 302 | GlUtil.checkGlError("glUniformMatrix4fv"); 303 | 304 | // Copy the texture transformation matrix over. 305 | GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0); 306 | GlUtil.checkGlError("glUniformMatrix4fv"); 307 | 308 | // Enable the "aPosition" vertex attribute. 309 | GLES20.glEnableVertexAttribArray(maPositionLoc); 310 | GlUtil.checkGlError("glEnableVertexAttribArray"); 311 | 312 | // Connect vertexBuffer to "aPosition". 313 | GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex, 314 | GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); 315 | GlUtil.checkGlError("glVertexAttribPointer"); 316 | 317 | // Enable the "aTextureCoord" vertex attribute. 318 | GLES20.glEnableVertexAttribArray(maTextureCoordLoc); 319 | GlUtil.checkGlError("glEnableVertexAttribArray"); 320 | 321 | // Connect texBuffer to "aTextureCoord". 322 | GLES20.glVertexAttribPointer(maTextureCoordLoc, 2, 323 | GLES20.GL_FLOAT, false, texStride, texBuffer); 324 | GlUtil.checkGlError("glVertexAttribPointer"); 325 | 326 | // Populate the convolution kernel, if present. 327 | if (muKernelLoc >= 0) { 328 | GLES20.glUniform1fv(muKernelLoc, KERNEL_SIZE, mKernel, 0); 329 | GLES20.glUniform2fv(muTexOffsetLoc, KERNEL_SIZE, mTexOffset, 0); 330 | GLES20.glUniform1f(muColorAdjustLoc, mColorAdjust); 331 | } 332 | 333 | // Draw the rect. 334 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount); 335 | GlUtil.checkGlError("glDrawArrays"); 336 | 337 | // Done -- disable vertex array, texture, and program. 338 | GLES20.glDisableVertexAttribArray(maPositionLoc); 339 | GLES20.glDisableVertexAttribArray(maTextureCoordLoc); 340 | GLES20.glBindTexture(mTextureTarget, 0); 341 | GLES20.glUseProgram(0); 342 | } 343 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/grafika/gles/WindowSurface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.graphics.SurfaceTexture; 20 | import android.view.Surface; 21 | 22 | /** 23 | * Recordable EGL window surface. 24 | *

25 | * It's good practice to explicitly release() the surface, preferably from a "finally" block. 26 | */ 27 | public class WindowSurface extends EglSurfaceBase { 28 | private Surface mSurface; 29 | private boolean mReleaseSurface; 30 | 31 | /** 32 | * Associates an EGL surface with the native window surface. 33 | *

34 | * Set releaseSurface to true if you want the Surface to be released when release() is 35 | * called. This is convenient, but can interfere with framework classes that expect to 36 | * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the 37 | * surfaceDestroyed() callback won't fire). 38 | */ 39 | public WindowSurface(EglCore eglCore, Surface surface, boolean releaseSurface) { 40 | super(eglCore); 41 | createWindowSurface(surface); 42 | mSurface = surface; 43 | mReleaseSurface = releaseSurface; 44 | } 45 | 46 | /** 47 | * Associates an EGL surface with the SurfaceTexture. 48 | */ 49 | public WindowSurface(EglCore eglCore, SurfaceTexture surfaceTexture) { 50 | super(eglCore); 51 | createWindowSurface(surfaceTexture); 52 | } 53 | 54 | /** 55 | * Releases any resources associated with the EGL surface (and, if configured to do so, 56 | * with the Surface as well). 57 | *

58 | * Does not require that the surface's EGL context be current. 59 | */ 60 | public void release() { 61 | releaseEglSurface(); 62 | if (mSurface != null) { 63 | if (mReleaseSurface) { 64 | mSurface.release(); 65 | } 66 | mSurface = null; 67 | } 68 | } 69 | 70 | /** 71 | * Recreate the EGLSurface, using the new EglBase. The caller should have already 72 | * freed the old EGLSurface with releaseEglSurface(). 73 | *

74 | * This is useful when we want to update the EGLSurface associated with a Surface. 75 | * For example, if we want to share with a different EGLContext, which can only 76 | * be done by tearing down and recreating the context. (That's handled by the caller; 77 | * this just creates a new EGLSurface for the Surface we were handed earlier.) 78 | *

79 | * If the previous EGLSurface isn't fully destroyed, e.g. it's still current on a 80 | * context somewhere, the create call will fail with complaints from the Surface 81 | * about already being connected. 82 | */ 83 | public void recreate(EglCore newEglCore) { 84 | if (mSurface == null) { 85 | throw new RuntimeException("not yet implemented for SurfaceTexture"); 86 | } 87 | mEglCore = newEglCore; // switch to new context 88 | createWindowSurface(mSurface); // create new surface 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/GLTextureRender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package tv.danmaku.ijk.media.vrplayer; 18 | 19 | import android.graphics.SurfaceTexture; 20 | import android.opengl.GLES20; 21 | import android.opengl.GLSurfaceView; 22 | import android.os.Handler; 23 | import android.util.Log; 24 | 25 | import com.android.grafika.gles.FullFrameRect; 26 | import com.android.grafika.gles.GlUtil; 27 | import com.android.grafika.gles.Texture2dProgram; 28 | 29 | import javax.microedition.khronos.egl.EGLConfig; 30 | import javax.microedition.khronos.opengles.GL10; 31 | 32 | 33 | /** 34 | * The GLSurfaceView texture-render implementation 35 | */ 36 | public class GLTextureRender implements GLSurfaceView.Renderer { 37 | 38 | private static final String TAG = "TextureRender"; 39 | private FullFrameRect mRect; 40 | private int mTextureId; 41 | private SurfaceTexture mSurfaceTexture; 42 | private Handler mHandler; 43 | private final float[] mSTMatrix = new float[16]; 44 | public GLTextureRender(Handler handler) { 45 | mHandler = handler; 46 | } 47 | 48 | @Override 49 | public void onSurfaceCreated(GL10 unused, EGLConfig config) { 50 | mRect = onCreateFrameRect(); 51 | mTextureId = mRect.createTextureObject(); 52 | 53 | mSurfaceTexture = new SurfaceTexture(mTextureId); 54 | mHandler.sendEmptyMessage(1); 55 | } 56 | 57 | protected FullFrameRect onCreateFrameRect() { 58 | return new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT)); 59 | } 60 | 61 | @Override 62 | public void onSurfaceChanged(GL10 gl, int width, int height) { 63 | if (BuildConfig.DEBUG) Log.d(TAG, "onSurfaceChanged " + width + "x" + height); 64 | GLES20.glViewport(0, 0, width, height); 65 | GlUtil.checkGlError("glViewport"); 66 | } 67 | 68 | @Override 69 | public void onDrawFrame(GL10 gl) { 70 | if (BuildConfig.DEBUG) { 71 | Log.d(TAG, "onDrawFrame tex=" + mTextureId); 72 | } 73 | // Latch the latest frame. If there isn't anything new, we'll just re-use whatever 74 | // was there before. 75 | mSurfaceTexture.updateTexImage(); 76 | mSurfaceTexture.getTransformMatrix(mSTMatrix); 77 | // Clear dirty rect 78 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 79 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 80 | // Draw the video frame. 81 | mRect.drawFrame(mTextureId, mSTMatrix); 82 | } 83 | 84 | public SurfaceTexture getSurfaceTexture() { 85 | return mSurfaceTexture; 86 | } 87 | 88 | public void requestPause() { 89 | if (mSurfaceTexture != null) { 90 | Log.d(TAG, "Release surface texture"); 91 | mSurfaceTexture.release(); 92 | mSurfaceTexture = null; 93 | } 94 | if (mRect != null) { 95 | mRect.release(false); 96 | mRect = null; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/IjkVRRender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import android.os.Handler; 19 | 20 | import com.android.grafika.gles.FullFrameRect; 21 | import com.android.grafika.gles.Texture2dProgram; 22 | 23 | /** 24 | * Use {@link SideBySideFrameRect} to render video frame. 25 | * @author yrom. 26 | */ 27 | public class IjkVRRender extends GLTextureRender { 28 | public IjkVRRender(Handler handler) { 29 | super(handler); 30 | } 31 | 32 | @Override 33 | protected FullFrameRect onCreateFrameRect() { 34 | return new SideBySideFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/Left.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import android.opengl.Matrix; 19 | 20 | import com.android.grafika.gles.Drawable2d; 21 | import com.android.grafika.gles.Texture2dProgram; 22 | 23 | /** 24 | * @author yrom. 25 | */ 26 | public class Left extends Square { 27 | 28 | private final float[] mModelMatrix = new float[16]; 29 | 30 | public Left(Texture2dProgram program) { 31 | super(program); 32 | Matrix.setIdentityM(mModelMatrix, 0); 33 | Matrix.scaleM(mModelMatrix, 0, 0.5f, 0.5f, 0.5f); 34 | Matrix.translateM(mModelMatrix, 0, -1f, 0, 0); // translation to the left 35 | } 36 | 37 | @Override 38 | protected Drawable2d createDrawable2d() { 39 | // TODO: use the half of 'full' rectangle instead? 40 | return new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE); 41 | } 42 | 43 | @Override 44 | protected float[] getMvpMatrix() { 45 | return mModelMatrix; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/Right.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import android.opengl.Matrix; 19 | 20 | import com.android.grafika.gles.Drawable2d; 21 | import com.android.grafika.gles.Texture2dProgram; 22 | 23 | /** 24 | * @author yrom. 25 | */ 26 | public class Right extends Square { 27 | private final float[] mModelMatrix = new float[16]; 28 | public Right(Texture2dProgram program) { 29 | super(program); 30 | Matrix.setIdentityM(mModelMatrix, 0); 31 | Matrix.scaleM(mModelMatrix, 0, 0.5f, 0.5f, 0.5f); 32 | Matrix.translateM(mModelMatrix, 0, 1f, 0, 0); // translation to the right 33 | } 34 | 35 | @Override 36 | protected Drawable2d createDrawable2d() { 37 | // TODO: use the half of 'full' rectangle instead? 38 | return new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE); 39 | } 40 | 41 | @Override 42 | protected float[] getMvpMatrix() { 43 | return mModelMatrix; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/SideBySideFrameRect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import com.android.grafika.gles.FullFrameRect; 19 | import com.android.grafika.gles.Texture2dProgram; 20 | 21 | /** 22 | * Show side-by-side 'full' frame rect 23 | * @author yrom. 24 | */ 25 | public class SideBySideFrameRect extends FullFrameRect { 26 | private final Left mLeft; 27 | private final Right mRight; 28 | public SideBySideFrameRect(Texture2dProgram program) { 29 | super(program); 30 | mLeft = new Left(program); 31 | mRight = new Right(program); 32 | } 33 | 34 | @Override 35 | public void release(boolean doEglCleanup) { 36 | mLeft.release(false); 37 | mRight.release(false); 38 | super.release(doEglCleanup); 39 | } 40 | 41 | @Override 42 | public void changeProgram(Texture2dProgram program) { 43 | mLeft.changeProgram(program, false); 44 | mRight.changeProgram(program, false); 45 | super.changeProgram(program); 46 | } 47 | 48 | @Override 49 | public void drawFrame(int textureId, float[] texMatrix) { 50 | mLeft.drawFrame(textureId, texMatrix); 51 | mRight.drawFrame(textureId, texMatrix); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/Square.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import com.android.grafika.gles.Drawable2d; 19 | import com.android.grafika.gles.Texture2dProgram; 20 | 21 | /** 22 | * One side in {@link SideBySideFrameRect} 23 | */ 24 | public abstract class Square { 25 | private final Drawable2d mRectDrawable; 26 | private Texture2dProgram mProgram; 27 | 28 | /** 29 | * Prepares the object. 30 | * 31 | * @param program The program to use. FullFrameRect takes ownership, and will release 32 | * the program when no longer needed. 33 | */ 34 | public Square(Texture2dProgram program) { 35 | mProgram = program; 36 | mRectDrawable = createDrawable2d(); 37 | } 38 | 39 | protected abstract Drawable2d createDrawable2d(); 40 | 41 | /** 42 | * Releases resources. 43 | *

44 | * This must be called with the appropriate EGL context current (i.e. the one that was 45 | * current when the constructor was called). If we're about to destroy the EGL context, 46 | * there's no value in having the caller make it current just to do this cleanup, so you 47 | * can pass a flag that will tell this function to skip any EGL-context-specific cleanup. 48 | */ 49 | public void release(boolean doEglCleanup) { 50 | if (mProgram != null) { 51 | if (doEglCleanup) { 52 | mProgram.release(); 53 | } 54 | mProgram = null; 55 | } 56 | } 57 | 58 | /** 59 | * Returns the program currently in use. 60 | */ 61 | public Texture2dProgram getProgram() { 62 | return mProgram; 63 | } 64 | 65 | /** 66 | * Changes the program. The previous program will be released. 67 | *

68 | * The appropriate EGL context must be current. 69 | */ 70 | public void changeProgram(Texture2dProgram program, boolean cleanUp) { 71 | if (mProgram != null && cleanUp) { 72 | mProgram.release(); 73 | } 74 | mProgram = program; 75 | } 76 | 77 | public void drawFrame(int textureId, float[] texMatrix) { 78 | // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport. 79 | mProgram.draw(getMvpMatrix(), mRectDrawable.getVertexArray(), 0, 80 | mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(), 81 | mRectDrawable.getVertexStride(), 82 | texMatrix, mRectDrawable.getTexCoordArray(), textureId, 83 | mRectDrawable.getTexCoordStride()); 84 | } 85 | 86 | protected abstract float[] getMvpMatrix(); 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/tv/danmaku/ijk/media/vrplayer/VRPlayerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Yrom. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package tv.danmaku.ijk.media.vrplayer; 17 | 18 | import android.Manifest; 19 | import android.app.Activity; 20 | import android.content.pm.PackageManager; 21 | import android.graphics.SurfaceTexture; 22 | import android.net.Uri; 23 | import android.opengl.GLSurfaceView; 24 | import android.os.Build; 25 | import android.os.Bundle; 26 | import android.os.Handler; 27 | import android.os.Message; 28 | import android.view.Surface; 29 | import android.widget.Toast; 30 | 31 | import java.io.IOException; 32 | 33 | import tv.danmaku.ijk.media.player.IMediaPlayer; 34 | import tv.danmaku.ijk.media.player.IjkMediaPlayer; 35 | 36 | /** 37 | * @author yrom. 38 | */ 39 | public class VRPlayerActivity extends Activity { 40 | private static final String TAG = "VRPlayer"; 41 | private GLSurfaceView mGLView; 42 | private GLTextureRender mRenderer; 43 | private IjkMediaPlayer mMediaPlayer; 44 | private boolean mSurfaceTextureReady; 45 | private Handler mHandler; 46 | @Override 47 | protected void onCreate(Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | final Uri uri = getIntent().getData(); 50 | if (uri == null) { 51 | Toast.makeText(this, "no media file to open!", Toast.LENGTH_SHORT).show(); 52 | finish(); 53 | return; 54 | } 55 | setContentView(R.layout.activity_vr_player); 56 | mGLView = (GLSurfaceView) findViewById(R.id.surface); 57 | mGLView.setEGLContextClientVersion(2); //GLES 2.0 58 | mHandler = new Handler() { 59 | @Override 60 | public void handleMessage(Message msg) { 61 | if (msg.what == 1) { 62 | prepareMediaPlayerIfReady(); 63 | return; 64 | } else if (msg.what == 2) { 65 | mGLView.requestRender(); 66 | } 67 | super.handleMessage(msg); 68 | } 69 | }; 70 | // TODO: implements your TextureRender 71 | mRenderer = new IjkVRRender(mHandler); 72 | mGLView.setRenderer(mRenderer); 73 | mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 75 | && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 76 | != PackageManager.PERMISSION_GRANTED) { 77 | requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); 78 | } else { 79 | initMediaPlayer(uri); 80 | } 81 | } 82 | 83 | private void prepareMediaPlayerIfReady() { 84 | if (mMediaPlayer != null) { 85 | SurfaceTexture surfaceTexture = mRenderer.getSurfaceTexture(); 86 | surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { 87 | @Override 88 | public void onFrameAvailable(SurfaceTexture surfaceTexture) { 89 | mHandler.sendEmptyMessage(2); 90 | } 91 | }); 92 | mMediaPlayer.setSurface(new Surface(surfaceTexture)); 93 | mMediaPlayer.prepareAsync(); 94 | } else { 95 | mSurfaceTextureReady = true; 96 | } 97 | } 98 | 99 | 100 | private void initMediaPlayer(Uri uri) { 101 | if (mMediaPlayer != null) { 102 | mMediaPlayer.stop(); 103 | mMediaPlayer.reset(); 104 | } else { 105 | mMediaPlayer = new IjkMediaPlayer(); 106 | mMediaPlayer.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() { 107 | @Override 108 | public void onPrepared(IMediaPlayer mp) { 109 | mGLView.setKeepScreenOn(true); 110 | mp.start(); 111 | mp.getVideoWidth(); 112 | } 113 | }); 114 | mMediaPlayer.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() { 115 | @Override 116 | public void onCompletion(IMediaPlayer mp) { 117 | mGLView.setKeepScreenOn(false); 118 | } 119 | }); 120 | } 121 | try { 122 | mMediaPlayer.setDataSource(this, uri); 123 | } catch (IOException e) { 124 | Toast.makeText(this, "cannot open uri:" + uri, Toast.LENGTH_SHORT).show(); 125 | finish(); 126 | return; 127 | } 128 | if (mSurfaceTextureReady) { 129 | prepareMediaPlayerIfReady(); 130 | } 131 | } 132 | 133 | @Override 134 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 135 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 136 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 137 | initMediaPlayer(getIntent().getData()); 138 | } 139 | } 140 | 141 | @Override 142 | protected void onPause() { 143 | super.onPause(); 144 | if (mMediaPlayer != null) { 145 | mMediaPlayer.pause(); 146 | } 147 | mGLView.onPause(); 148 | mGLView.setKeepScreenOn(false); 149 | } 150 | 151 | @Override 152 | protected void onResume() { 153 | super.onResume(); 154 | mGLView.onResume(); 155 | if (mMediaPlayer != null) { 156 | mGLView.setKeepScreenOn(true); 157 | mMediaPlayer.start(); 158 | } 159 | } 160 | 161 | @Override 162 | protected void onStop() { 163 | super.onStop(); 164 | if (mMediaPlayer != null) { 165 | mMediaPlayer.stop(); 166 | mMediaPlayer.release(); 167 | mMediaPlayer = null; 168 | } 169 | mGLView.queueEvent(new Runnable() { 170 | @Override 171 | public void run() { 172 | // Tell the renderer that it's about to be paused so it can clean up. 173 | mRenderer.requestPause(); 174 | } 175 | }); 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_vr_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | IjkVRPlayer 3 | 4 | -------------------------------------------------------------------------------- /app/src/test/java/com/bilibili/ijkvrplayer/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.bilibili.ijkvrplayer; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.5.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yrom/IjkVRPlayer/6eecbda21556ff946dec0638b1c71aa33be5e1e6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------