kopia lustrzana https://github.com/ryukoposting/Signal-Android
188 wiersze
6.3 KiB
Java
188 wiersze
6.3 KiB
Java
/*
|
|
* 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.
|
|
*
|
|
* This file has been modified by Signal.
|
|
*/
|
|
|
|
package org.thoughtcrime.securesms.video.videoconverter;
|
|
|
|
import android.opengl.EGL14;
|
|
import android.opengl.EGLConfig;
|
|
import android.opengl.EGLContext;
|
|
import android.opengl.EGLDisplay;
|
|
import android.opengl.EGLExt;
|
|
import android.opengl.EGLSurface;
|
|
import android.view.Surface;
|
|
|
|
import org.signal.core.util.logging.Log;
|
|
|
|
|
|
/**
|
|
* Holds state associated with a Surface used for MediaCodec encoder input.
|
|
* <p>
|
|
* The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses that
|
|
* to create an EGL window surface. Calls to eglSwapBuffers() cause a frame of data to be sent
|
|
* to the video encoder.
|
|
*/
|
|
final class InputSurface {
|
|
private static final String TAG = "InputSurface";
|
|
private static final boolean VERBOSE = false;
|
|
|
|
private static final int EGL_RECORDABLE_ANDROID = 0x3142;
|
|
private static final int EGL_OPENGL_ES2_BIT = 4;
|
|
|
|
private EGLDisplay mEGLDisplay;
|
|
private EGLContext mEGLContext;
|
|
private EGLSurface mEGLSurface;
|
|
|
|
private Surface mSurface;
|
|
|
|
/**
|
|
* Creates an InputSurface from a Surface.
|
|
*/
|
|
InputSurface(Surface surface) throws TranscodingException {
|
|
if (surface == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
mSurface = surface;
|
|
|
|
eglSetup();
|
|
}
|
|
|
|
/**
|
|
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
|
|
*/
|
|
private void eglSetup() throws TranscodingException {
|
|
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
|
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
|
|
throw new TranscodingException("unable to get EGL14 display");
|
|
}
|
|
int[] version = new int[2];
|
|
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
|
|
mEGLDisplay = null;
|
|
throw new TranscodingException("unable to initialize EGL14");
|
|
}
|
|
|
|
// Configure EGL for pbuffer and OpenGL ES 2.0. We want enough RGB bits
|
|
// to be able to tell if the frame is reasonable.
|
|
int[] attribList = {
|
|
EGL14.EGL_RED_SIZE, 8,
|
|
EGL14.EGL_GREEN_SIZE, 8,
|
|
EGL14.EGL_BLUE_SIZE, 8,
|
|
EGL14.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_RECORDABLE_ANDROID, 1,
|
|
EGL14.EGL_NONE
|
|
};
|
|
EGLConfig[] configs = new EGLConfig[1];
|
|
int[] numConfigs = new int[1];
|
|
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
|
|
numConfigs, 0)) {
|
|
throw new TranscodingException("unable to find RGB888+recordable ES2 EGL config");
|
|
}
|
|
|
|
// Configure context for OpenGL ES 2.0.
|
|
int[] attrib_list = {
|
|
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL14.EGL_NONE
|
|
};
|
|
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
|
|
attrib_list, 0);
|
|
checkEglError("eglCreateContext");
|
|
if (mEGLContext == null) {
|
|
throw new TranscodingException("null context");
|
|
}
|
|
|
|
// Create a window surface, and attach it to the Surface we received.
|
|
int[] surfaceAttribs = {
|
|
EGL14.EGL_NONE
|
|
};
|
|
mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
|
|
surfaceAttribs, 0);
|
|
checkEglError("eglCreateWindowSurface");
|
|
if (mEGLSurface == null) {
|
|
throw new TranscodingException("surface was null");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Discard all resources held by this class, notably the EGL context. Also releases the
|
|
* Surface that was passed to our constructor.
|
|
*/
|
|
public void release() {
|
|
if (EGL14.eglGetCurrentContext().equals(mEGLContext)) {
|
|
// Clear the current context and surface to ensure they are discarded immediately.
|
|
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
|
|
EGL14.EGL_NO_CONTEXT);
|
|
}
|
|
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
|
|
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
|
|
//EGL14.eglTerminate(mEGLDisplay);
|
|
|
|
mSurface.release();
|
|
|
|
// null everything out so future attempts to use this object will cause an NPE
|
|
mEGLDisplay = null;
|
|
mEGLContext = null;
|
|
mEGLSurface = null;
|
|
|
|
mSurface = null;
|
|
}
|
|
|
|
/**
|
|
* Makes our EGL context and surface current.
|
|
*/
|
|
void makeCurrent() throws TranscodingException {
|
|
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
|
|
throw new TranscodingException("eglMakeCurrent failed");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls eglSwapBuffers. Use this to "publish" the current frame.
|
|
*/
|
|
boolean swapBuffers() {
|
|
return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
|
|
}
|
|
|
|
/**
|
|
* Returns the Surface that the MediaCodec receives buffers from.
|
|
*/
|
|
public Surface getSurface() {
|
|
return mSurface;
|
|
}
|
|
|
|
/**
|
|
* Sends the presentation time stamp to EGL. Time is expressed in nanoseconds.
|
|
*/
|
|
void setPresentationTime(long nsecs) {
|
|
EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs);
|
|
}
|
|
|
|
/**
|
|
* Checks for EGL errors.
|
|
*/
|
|
private static void checkEglError(String msg) throws TranscodingException {
|
|
boolean failed = false;
|
|
int error;
|
|
while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
|
|
Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error));
|
|
failed = true;
|
|
}
|
|
if (failed) {
|
|
throw new TranscodingException("EGL error encountered (see log)");
|
|
}
|
|
}
|
|
}
|