Use surfaceless context in DummySurface, if available

Issue: #3558

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178604607
This commit is contained in:
andrewlewis 2017-12-11 05:08:01 -08:00 committed by Oliver Woodman
parent ba32d95dc4
commit f8834dacc6
2 changed files with 65 additions and 45 deletions

View File

@ -41,6 +41,8 @@
implementations. implementations.
* CEA-608: Fix handling of row count changes in roll-up mode * CEA-608: Fix handling of row count changes in roll-up mode
([#3513](https://github.com/google/ExoPlayer/issues/3513)). ([#3513](https://github.com/google/ExoPlayer/issues/3513)).
* Use surfaceless context for secure DummySurface, if available
([#3558](https://github.com/google/ExoPlayer/issues/3558)).
* IMA extension: * IMA extension:
* Skip ads before the ad preceding the player's initial seek position * Skip ads before the ad preceding the player's initial seek position
([#3527](https://github.com/google/ExoPlayer/issues/3527)). ([#3527](https://github.com/google/ExoPlayer/issues/3527)).

View File

@ -24,6 +24,7 @@ import static android.opengl.EGL14.EGL_DEPTH_SIZE;
import static android.opengl.EGL14.EGL_GREEN_SIZE; import static android.opengl.EGL14.EGL_GREEN_SIZE;
import static android.opengl.EGL14.EGL_HEIGHT; import static android.opengl.EGL14.EGL_HEIGHT;
import static android.opengl.EGL14.EGL_NONE; import static android.opengl.EGL14.EGL_NONE;
import static android.opengl.EGL14.EGL_NO_SURFACE;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
import static android.opengl.EGL14.EGL_RED_SIZE; import static android.opengl.EGL14.EGL_RED_SIZE;
import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
@ -56,10 +57,13 @@ import android.os.Handler;
import android.os.Handler.Callback; import android.os.Handler.Callback;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Message; import android.os.Message;
import android.support.annotation.IntDef;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL10;
/** /**
@ -70,16 +74,27 @@ public final class DummySurface extends Surface {
private static final String TAG = "DummySurface"; private static final String TAG = "DummySurface";
private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content";
private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
private static boolean secureSupported; @Retention(RetentionPolicy.SOURCE)
private static boolean secureSupportedInitialized; @IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER})
private @interface SecureMode {}
private static final int SECURE_MODE_NONE = 0;
private static final int SECURE_MODE_SURFACELESS_CONTEXT = 1;
private static final int SECURE_MODE_PROTECTED_PBUFFER = 2;
/** /**
* Whether the surface is secure. * Whether the surface is secure.
*/ */
public final boolean secure; public final boolean secure;
private static @SecureMode int secureMode;
private static boolean secureModeInitialized;
private final DummySurfaceThread thread; private final DummySurfaceThread thread;
private boolean threadReleased; private boolean threadReleased;
@ -90,11 +105,11 @@ public final class DummySurface extends Surface {
* @return Whether the device supports secure dummy surfaces. * @return Whether the device supports secure dummy surfaces.
*/ */
public static synchronized boolean isSecureSupported(Context context) { public static synchronized boolean isSecureSupported(Context context) {
if (!secureSupportedInitialized) { if (!secureModeInitialized) {
secureSupported = Util.SDK_INT >= 24 && enableSecureDummySurfaceV24(context); secureMode = Util.SDK_INT < 24 ? SECURE_MODE_NONE : getSecureModeV24(context);
secureSupportedInitialized = true; secureModeInitialized = true;
} }
return secureSupported; return secureMode != SECURE_MODE_NONE;
} }
/** /**
@ -113,7 +128,7 @@ public final class DummySurface extends Surface {
assertApiLevel17OrHigher(); assertApiLevel17OrHigher();
Assertions.checkState(!secure || isSecureSupported(context)); Assertions.checkState(!secure || isSecureSupported(context));
DummySurfaceThread thread = new DummySurfaceThread(); DummySurfaceThread thread = new DummySurfaceThread();
return thread.init(secure); return thread.init(secure ? secureMode : SECURE_MODE_NONE);
} }
private DummySurface(DummySurfaceThread thread, SurfaceTexture surfaceTexture, boolean secure) { private DummySurface(DummySurfaceThread thread, SurfaceTexture surfaceTexture, boolean secure) {
@ -143,33 +158,34 @@ public final class DummySurface extends Surface {
} }
} }
/**
* Returns whether use of secure dummy surfaces should be enabled.
*
* @param context Any {@link Context}.
*/
@TargetApi(24) @TargetApi(24)
private static boolean enableSecureDummySurfaceV24(Context context) { private static @SecureMode int getSecureModeV24(Context context) {
if (Util.SDK_INT < 26 && ("samsung".equals(Util.MANUFACTURER) || "XT1650".equals(Util.MODEL))) { if (Util.SDK_INT < 26 && ("samsung".equals(Util.MANUFACTURER) || "XT1650".equals(Util.MODEL))) {
// Samsung devices running Nougat are known to be broken. See // Samsung devices running Nougat are known to be broken. See
// https://github.com/google/ExoPlayer/issues/3373 and [Internal: b/37197802]. // https://github.com/google/ExoPlayer/issues/3373 and [Internal: b/37197802].
// Moto Z XT1650 is also affected. See // Moto Z XT1650 is also affected. See
// https://github.com/google/ExoPlayer/issues/3215. // https://github.com/google/ExoPlayer/issues/3215.
return false; return SECURE_MODE_NONE;
} }
if (Util.SDK_INT < 26 && !context.getPackageManager().hasSystemFeature( if (Util.SDK_INT < 26 && !context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
// Pre API level 26 devices were not well tested unless they supported VR mode. See // Pre API level 26 devices were not well tested unless they supported VR mode.
// https://github.com/google/ExoPlayer/issues/3215. return SECURE_MODE_NONE;
return false;
} }
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS); String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
if (eglExtensions == null || !eglExtensions.contains("EGL_EXT_protected_content")) { if (eglExtensions == null) {
// EGL_EXT_protected_content is required to enable secure dummy surfaces. return SECURE_MODE_NONE;
return false;
} }
return true; if (!eglExtensions.contains(EXTENSION_PROTECTED_CONTENT)) {
return SECURE_MODE_NONE;
}
// If we can't use surfaceless contexts, we use a protected 1 * 1 pixel buffer surface. This may
// require support for EXT_protected_surface, but in practice it works on some devices that
// don't have that extension. See also https://github.com/google/ExoPlayer/issues/3558.
return eglExtensions.contains(EXTENSION_SURFACELESS_CONTEXT)
? SECURE_MODE_SURFACELESS_CONTEXT
: SECURE_MODE_PROTECTED_PBUFFER;
} }
private static class DummySurfaceThread extends HandlerThread implements OnFrameAvailableListener, private static class DummySurfaceThread extends HandlerThread implements OnFrameAvailableListener,
@ -195,12 +211,12 @@ public final class DummySurface extends Surface {
textureIdHolder = new int[1]; textureIdHolder = new int[1];
} }
public DummySurface init(boolean secure) { public DummySurface init(@SecureMode int secureMode) {
start(); start();
handler = new Handler(getLooper(), this); handler = new Handler(getLooper(), this);
boolean wasInterrupted = false; boolean wasInterrupted = false;
synchronized (this) { synchronized (this) {
handler.obtainMessage(MSG_INIT, secure ? 1 : 0, 0).sendToTarget(); handler.obtainMessage(MSG_INIT, secureMode, 0).sendToTarget();
while (surface == null && initException == null && initError == null) { while (surface == null && initException == null && initError == null) {
try { try {
wait(); wait();
@ -236,7 +252,7 @@ public final class DummySurface extends Surface {
switch (msg.what) { switch (msg.what) {
case MSG_INIT: case MSG_INIT:
try { try {
initInternal(msg.arg1 != 0); initInternal(/* secureMode= */ msg.arg1);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "Failed to initialize dummy surface", e); Log.e(TAG, "Failed to initialize dummy surface", e);
initException = e; initException = e;
@ -266,7 +282,7 @@ public final class DummySurface extends Surface {
} }
} }
private void initInternal(boolean secure) { private void initInternal(@SecureMode int secureMode) {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY); display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
Assertions.checkState(display != null, "eglGetDisplay failed"); Assertions.checkState(display != null, "eglGetDisplay failed");
@ -294,43 +310,45 @@ public final class DummySurface extends Surface {
EGLConfig config = configs[0]; EGLConfig config = configs[0];
int[] glAttributes; int[] glAttributes;
if (secure) { if (secureMode == SECURE_MODE_NONE) {
glAttributes = new int[] { glAttributes = new int[] {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_PROTECTED_CONTENT_EXT, EGL_TRUE,
EGL_NONE}; EGL_NONE};
} else { } else {
glAttributes = new int[] { glAttributes =
EGL_CONTEXT_CLIENT_VERSION, 2, new int[] {
EGL_NONE}; EGL_CONTEXT_CLIENT_VERSION, 2, EGL_PROTECTED_CONTENT_EXT, EGL_TRUE, EGL_NONE
};
} }
context = eglCreateContext(display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes, context = eglCreateContext(display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes,
0); 0);
Assertions.checkState(context != null, "eglCreateContext failed"); Assertions.checkState(context != null, "eglCreateContext failed");
int[] pbufferAttributes; EGLSurface surface;
if (secure) { if (secureMode == SECURE_MODE_SURFACELESS_CONTEXT) {
pbufferAttributes = new int[] { surface = EGL_NO_SURFACE;
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_PROTECTED_CONTENT_EXT, EGL_TRUE,
EGL_NONE};
} else { } else {
pbufferAttributes = new int[] { int[] pbufferAttributes;
EGL_WIDTH, 1, if (secureMode == SECURE_MODE_PROTECTED_PBUFFER) {
EGL_HEIGHT, 1, pbufferAttributes =
EGL_NONE}; new int[] {
EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_PROTECTED_CONTENT_EXT, EGL_TRUE, EGL_NONE
};
} else {
pbufferAttributes = new int[] {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
} }
pbuffer = eglCreatePbufferSurface(display, config, pbufferAttributes, 0); pbuffer = eglCreatePbufferSurface(display, config, pbufferAttributes, 0);
Assertions.checkState(pbuffer != null, "eglCreatePbufferSurface failed"); Assertions.checkState(pbuffer != null, "eglCreatePbufferSurface failed");
surface = pbuffer;
}
boolean eglMadeCurrent = eglMakeCurrent(display, pbuffer, pbuffer, context); boolean eglMadeCurrent = eglMakeCurrent(display, surface, surface, context);
Assertions.checkState(eglMadeCurrent, "eglMakeCurrent failed"); Assertions.checkState(eglMadeCurrent, "eglMakeCurrent failed");
glGenTextures(1, textureIdHolder, 0); glGenTextures(1, textureIdHolder, 0);
surfaceTexture = new SurfaceTexture(textureIdHolder[0]); surfaceTexture = new SurfaceTexture(textureIdHolder[0]);
surfaceTexture.setOnFrameAvailableListener(this); surfaceTexture.setOnFrameAvailableListener(this);
surface = new DummySurface(this, surfaceTexture, secure); this.surface = new DummySurface(this, surfaceTexture, secureMode != SECURE_MODE_NONE);
} }
private void releaseInternal() { private void releaseInternal() {