Add SurfaceCapturer base class, and provide the first implementation

Use PixelCopy API for the first SurfaceCapturer implementation. This supports
devices from API 24+.

Github: #3609.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=197732711
This commit is contained in:
hoangtc 2018-05-23 08:53:08 -07:00 committed by Oliver Woodman
parent a98d8fedfa
commit 0cb34dcd1c
5 changed files with 127 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,84 @@
/*
* Copyright (C) 2018 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 com.google.android.exoplayer2.surfacecapturer;
import android.graphics.Bitmap;
import android.view.Surface;
/**
* A surface capturer, which captures image drawn into its surface as bitmaps.
*
* <p>It constructs a {@link Surface}, which can be used as the output surface for an image producer
* to draw images to. As images are being drawn into this surface, this capturer will capture these
* images, and return them via {@link Callback}. The output images will have a fixed frame size of
* (width, height), and any image drawn into the surface will be stretched to fit this frame size.
*/
public abstract class SurfaceCapturer {
/** The callback to be notified of the image capturing result. */
public interface Callback {
/**
* Called when the surface capturer has been able to capture its surface into a {@link Bitmap}.
* This will happen whenever the producer updates the image on the wrapped surface.
*/
void onSurfaceCaptured(Bitmap bitmap);
/** Called when the surface capturer couldn't capture its surface due to an error. */
void onSurfaceCaptureError(Exception e);
}
/** The callback to be notified of the image capturing result. */
private final Callback callback;
/** The width of the output images. */
private final int outputWidth;
/** The height of the output images. */
private final int outputHeight;
/**
* Constructs a new instance.
*
* @param callback See {@link #callback}.
* @param outputWidth See {@link #outputWidth}.
* @param outputHeight See {@link #outputHeight}.
*/
protected SurfaceCapturer(Callback callback, int outputWidth, int outputHeight) {
this.callback = callback;
this.outputWidth = outputWidth;
this.outputHeight = outputHeight;
}
/** Returns the callback to be notified of the image capturing result. */
protected Callback getCallback() {
return callback;
}
/** Returns the width of the output images. */
public int getOutputWidth() {
return outputWidth;
}
/** Returns the height of the output images. */
public int getOutputHeight() {
return outputHeight;
}
/** Returns a {@link Surface} that image producers (camera, video codec etc...) can draw to. */
public abstract Surface getSurface();
/** Releases all kept resources. This instance cannot be used after this call. */
public abstract void release();
}

View file

@ -33,6 +33,12 @@ import java.lang.annotation.RetentionPolicy;
@TargetApi(17)
public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener, Runnable {
/** Listener to be called when the texture image on {@link SurfaceTexture} has been updated. */
public interface TextureImageListener {
/** Called when the {@link SurfaceTexture} receives a new frame from its image producer. */
void onFrameAvailable();
}
/** Secure mode to be used by the EGL surface and context. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER})
@ -45,6 +51,9 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
/** Creating a secure surface backed by a pixel buffer. */
public static final int SECURE_MODE_PROTECTED_PBUFFER = 2;
private static final int EGL_SURFACE_WIDTH = 1;
private static final int EGL_SURFACE_HEIGHT = 1;
private static final int[] EGL_CONFIG_ATTRIBUTES =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
@ -69,6 +78,7 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
private final Handler handler;
private final int[] textureIdHolder;
private final @Nullable TextureImageListener callback;
private @Nullable EGLDisplay display;
private @Nullable EGLContext context;
@ -82,7 +92,21 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
* looper.
*/
public EGLSurfaceTexture(Handler handler) {
this(handler, /* callback= */ null);
}
/**
* @param handler The {@link Handler} that will be used to call {@link
* SurfaceTexture#updateTexImage()} to update images on the {@link SurfaceTexture}. Note that
* {@link #init(int)} has to be called on the same looper thread as the looper of the {@link
* Handler}.
* @param callback The {@link TextureImageListener} to be called when the texture image on {@link
* SurfaceTexture} has been updated. This callback will be called on the same handler thread
* as the {@code handler}.
*/
public EGLSurfaceTexture(Handler handler, @Nullable TextureImageListener callback) {
this.handler = handler;
this.callback = callback;
textureIdHolder = new int[1];
}
@ -142,8 +166,20 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
@Override
public void run() {
// Run on the provided handler thread when a new image frame is available.
dispatchOnFrameAvailable();
if (texture != null) {
texture.updateTexImage();
try {
texture.updateTexImage();
} catch (RuntimeException e) {
// Ignore
}
}
}
private void dispatchOnFrameAvailable() {
if (callback != null) {
callback.onFrameAvailable();
}
}
@ -220,9 +256,9 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
pbufferAttributes =
new int[] {
EGL14.EGL_WIDTH,
1,
EGL_SURFACE_WIDTH,
EGL14.EGL_HEIGHT,
1,
EGL_SURFACE_HEIGHT,
EGL_PROTECTED_CONTENT_EXT,
EGL14.EGL_TRUE,
EGL14.EGL_NONE
@ -230,8 +266,10 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
} else {
pbufferAttributes =
new int[] {
EGL14.EGL_WIDTH, 1,
EGL14.EGL_HEIGHT, 1,
EGL14.EGL_WIDTH,
EGL_SURFACE_WIDTH,
EGL14.EGL_HEIGHT,
EGL_SURFACE_HEIGHT,
EGL14.EGL_NONE
};
}