aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2016-08-28 21:53:23 +0200
committerChristian Schneppe <christian@pix-art.de>2016-08-28 21:53:23 +0200
commit084faa3a6277f7270882209468e65dd38f10cdd5 (patch)
treede593d7dd066af3f4a0915f37afb5c3cbff5d996 /src/main/java/de/pixart/messenger/utils/video/OutputSurface.java
parent1aca12fdfdcbb334e279583de0d70d611d22af6a (diff)
parentb3b3475e93a9b08f9e35edbf74673728b560ad3b (diff)
Merge remote-tracking branch 'refs/remotes/origin/video-compression'
Diffstat (limited to 'src/main/java/de/pixart/messenger/utils/video/OutputSurface.java')
-rw-r--r--src/main/java/de/pixart/messenger/utils/video/OutputSurface.java207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java b/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java
new file mode 100644
index 000000000..75ceb46ac
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/utils/video/OutputSurface.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.pixart.messenger.utils.video;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES20;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+@TargetApi(16)
+public class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
+
+ private static final int EGL_OPENGL_ES2_BIT = 4;
+ private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private EGL10 mEGL;
+ private EGLDisplay mEGLDisplay = null;
+ private EGLContext mEGLContext = null;
+ private EGLSurface mEGLSurface = null;
+ private SurfaceTexture mSurfaceTexture;
+ private Surface mSurface;
+ private final Object mFrameSyncObject = new Object();
+ private boolean mFrameAvailable;
+ private TextureRenderer mTextureRender;
+ private int mWidth;
+ private int mHeight;
+ private int rotateRender = 0;
+ private ByteBuffer mPixelBuf;
+
+ public OutputSurface(int width, int height, int rotate) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException();
+ }
+ mWidth = width;
+ mHeight = height;
+ rotateRender = rotate;
+ mPixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
+ mPixelBuf.order(ByteOrder.LITTLE_ENDIAN);
+ eglSetup(width, height);
+ makeCurrent();
+ setup();
+ }
+
+ public OutputSurface() {
+ setup();
+ }
+
+ private void setup() {
+ mTextureRender = new TextureRenderer(rotateRender);
+ mTextureRender.surfaceCreated();
+ mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
+ mSurfaceTexture.setOnFrameAvailableListener(this);
+ mSurface = new Surface(mSurfaceTexture);
+ }
+
+ private void eglSetup(int width, int height) {
+ mEGL = (EGL10) EGLContext.getEGL();
+ mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("unable to get EGL10 display");
+ }
+
+ if (!mEGL.eglInitialize(mEGLDisplay, null)) {
+ mEGLDisplay = null;
+ throw new RuntimeException("unable to initialize EGL10");
+ }
+
+ int[] attribList = {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 8,
+ EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE
+ };
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] numConfigs = new int[1];
+ if (!mEGL.eglChooseConfig(mEGLDisplay, attribList, configs, configs.length, numConfigs)) {
+ throw new RuntimeException("unable to find RGB888+pbuffer EGL config");
+ }
+ int[] attrib_list = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL10.EGL_NONE
+ };
+ mEGLContext = mEGL.eglCreateContext(mEGLDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attrib_list);
+ checkEglError("eglCreateContext");
+ if (mEGLContext == null) {
+ throw new RuntimeException("null context");
+ }
+ int[] surfaceAttribs = {
+ EGL10.EGL_WIDTH, width,
+ EGL10.EGL_HEIGHT, height,
+ EGL10.EGL_NONE
+ };
+ mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs);
+ checkEglError("eglCreatePbufferSurface");
+ if (mEGLSurface == null) {
+ throw new RuntimeException("surface was null");
+ }
+ }
+
+ public void release() {
+ if (mEGL != null) {
+ if (mEGL.eglGetCurrentContext().equals(mEGLContext)) {
+ mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ }
+ mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface);
+ mEGL.eglDestroyContext(mEGLDisplay, mEGLContext);
+ }
+ mSurface.release();
+ mEGLDisplay = null;
+ mEGLContext = null;
+ mEGLSurface = null;
+ mEGL = null;
+ mTextureRender = null;
+ mSurface = null;
+ mSurfaceTexture = null;
+ }
+
+ public void makeCurrent() {
+ if (mEGL == null) {
+ throw new RuntimeException("not configured for makeCurrent");
+ }
+ checkEglError("before makeCurrent");
+ if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ public void changeFragmentShader(String fragmentShader) {
+ mTextureRender.changeFragmentShader(fragmentShader);
+ }
+
+ public void awaitNewImage() {
+ final int TIMEOUT_MS = 5000;
+ synchronized (mFrameSyncObject) {
+ while (!mFrameAvailable) {
+ try {
+ mFrameSyncObject.wait(TIMEOUT_MS);
+ if (!mFrameAvailable) {
+ throw new RuntimeException("Surface frame wait timed out");
+ }
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ }
+ mFrameAvailable = false;
+ }
+ mTextureRender.checkGlError("before updateTexImage");
+ mSurfaceTexture.updateTexImage();
+ }
+
+ public void drawImage(boolean invert) {
+ mTextureRender.drawFrame(mSurfaceTexture, invert);
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture st) {
+ synchronized (mFrameSyncObject) {
+ if (mFrameAvailable) {
+ throw new RuntimeException("mFrameAvailable already set, frame could be dropped");
+ }
+ mFrameAvailable = true;
+ mFrameSyncObject.notifyAll();
+ }
+ }
+
+ public ByteBuffer getFrame() {
+ mPixelBuf.rewind();
+ GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPixelBuf);
+ return mPixelBuf;
+ }
+
+ private void checkEglError(String msg) {
+ if (mEGL.eglGetError() != EGL10.EGL_SUCCESS) {
+ throw new RuntimeException("EGL error encountered (see log)");
+ }
+ }
+}