public class SurfaceTextureRenderer extends Object
A renderer class that manages the GL state, and can draw a frame into a set of output
{@link Surface}s.
Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final int
EGL_RECORDABLE_ANDROID
private static final int
GL_MATRIX_SIZE
private static final int
VERTEX_POS_SIZE
private static final int
VERTEX_UV_SIZE
private static final int
EGL_COLOR_BITLENGTH
private static final int
GLES_VERSION
private static final int
PBUFFER_PIXEL_BYTES
private static final int
FLIP_TYPE_NONE
private static final int
FLIP_TYPE_HORIZONTAL
private static final int
FLIP_TYPE_VERTICAL
private static final int
FLIP_TYPE_BOTH
private android.opengl.EGLDisplay
mEGLDisplay
private android.opengl.EGLContext
mEGLContext
private android.opengl.EGLConfig
mConfigs
private List
mSurfaces
private List
mConversionSurfaces
private ByteBuffer
mPBufferPixels
private volatile android.graphics.SurfaceTexture
mSurfaceTexture
private static final int
FLOAT_SIZE_BYTES
private static final int
TRIANGLE_VERTICES_DATA_STRIDE_BYTES
private static final int
TRIANGLE_VERTICES_DATA_POS_OFFSET
private static final int
TRIANGLE_VERTICES_DATA_UV_OFFSET
private static final float[]
sHorizontalFlipTriangleVertices
private static final float[]
sVerticalFlipTriangleVertices
private static final float[]
sBothFlipTriangleVertices
private static final float[]
sRegularTriangleVertices
private FloatBuffer
mRegularTriangleVertices
private FloatBuffer
mHorizontalFlipTriangleVertices
private FloatBuffer
mVerticalFlipTriangleVertices
private FloatBuffer
mBothFlipTriangleVertices
private final int
mFacing
private static final String
VERTEX_SHADER
As used in this file, this vertex shader maps a unit square to the view, and
tells the fragment shader to interpolate over it. Each surface pixel position
is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with
s and t in the inclusive range [0, 1], and the matrix from
{@link SurfaceTexture#getTransformMatrix(float[])} is used to map this
coordinate to a texture location.
private static final String
FRAGMENT_SHADER
This fragment shader simply draws the color in the 2D texture at
the location from the {@code VERTEX_SHADER}.
Draw the current buffer in the {@link SurfaceTexture} returned from
{@link #getSurfaceTexture()} into the set of target {@link Surface}s
in the next request from the given {@link CaptureCollector}, or drop
the frame if none is available.
Any {@link Surface}s targeted must be a subset of the {@link Surface}s
set in the last {@link #configureSurfaces(java.util.Collection)} call.
param
targetCollector the surfaces to draw to.
if ((mSurfaces == null || mSurfaces.size() == 0)
&& (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
return;
}
boolean doTiming = targetCollector.hasPendingPreviewCaptures();
checkGlError("before updateTexImage");
if (doTiming) {
beginGlTiming();
}
mSurfaceTexture.updateTexImage();
long timestamp = mSurfaceTexture.getTimestamp();
Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp);
// No preview request queued, drop frame.
if (captureHolder == null) {
if (DEBUG) {
Log.d(TAG, "Dropping preview frame.");
}
if (doTiming) {
endGlTiming();
}
return;
}
RequestHolder request = captureHolder.first;
Collection<Surface> targetSurfaces = request.getHolderTargets();
if (doTiming) {
addGlTimestamp(timestamp);
}
List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
for (EGLSurfaceHolder holder : mSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
try{
LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
holder.height);
makeCurrent(holder.eglSurface);
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
drawFrame(mSurfaceTexture, holder.width, holder.height,
(mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);
swapBuffers(holder.eglSurface);
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
}
}
}
for (EGLSurfaceHolder holder : mConversionSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
// glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
drawFrame(mSurfaceTexture, holder.width, holder.height,
(mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
mPBufferPixels.clear();
GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
checkGlError("glReadPixels");
try {
int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
holder.height);
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
holder.width, holder.height, format);
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
}
}
}
targetCollector.previewProduced();
if (doTiming) {
endGlTiming();
}
private void
dumpGlTiming()
Save a measurement dump to disk, in
{@code /sdcard/CameraLegacy/durations_
private void
endGlTiming()
if (mPerfMeasurer == null) return;
mPerfMeasurer.stopTimer();
public void
flush()
Drop all current GL operations on the floor.
// TODO: implement flush
Log.e(TAG, "Flush not yet implemented.");