summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java228
1 files changed, 228 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java
new file mode 100644
index 0000000000..abfb8b0952
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/DummySurface.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 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 org.mozilla.thirdparty.com.google.android.exoplayer2.video;
+
+import static org.mozilla.thirdparty.com.google.android.exoplayer2.util.EGLSurfaceTexture.SECURE_MODE_NONE;
+import static org.mozilla.thirdparty.com.google.android.exoplayer2.util.EGLSurfaceTexture.SECURE_MODE_PROTECTED_PBUFFER;
+import static org.mozilla.thirdparty.com.google.android.exoplayer2.util.EGLSurfaceTexture.SECURE_MODE_SURFACELESS_CONTEXT;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.view.Surface;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.EGLSurfaceTexture;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.EGLSurfaceTexture.SecureMode;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.GlUtil;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Log;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
+
+/** A dummy {@link Surface}. */
+@TargetApi(17)
+public final class DummySurface extends Surface {
+
+ private static final String TAG = "DummySurface";
+
+ /**
+ * Whether the surface is secure.
+ */
+ public final boolean secure;
+
+ private static @SecureMode int secureMode;
+ private static boolean secureModeInitialized;
+
+ private final DummySurfaceThread thread;
+ private boolean threadReleased;
+
+ /**
+ * Returns whether the device supports secure dummy surfaces.
+ *
+ * @param context Any {@link Context}.
+ * @return Whether the device supports secure dummy surfaces.
+ */
+ public static synchronized boolean isSecureSupported(Context context) {
+ if (!secureModeInitialized) {
+ secureMode = getSecureMode(context);
+ secureModeInitialized = true;
+ }
+ return secureMode != SECURE_MODE_NONE;
+ }
+
+ /**
+ * Returns a newly created dummy surface. The surface must be released by calling {@link #release}
+ * when it's no longer required.
+ * <p>
+ * Must only be called if {@link Util#SDK_INT} is 17 or higher.
+ *
+ * @param context Any {@link Context}.
+ * @param secure Whether a secure surface is required. Must only be requested if
+ * {@link #isSecureSupported(Context)} returns {@code true}.
+ * @throws IllegalStateException If a secure surface is requested on a device for which
+ * {@link #isSecureSupported(Context)} returns {@code false}.
+ */
+ public static DummySurface newInstanceV17(Context context, boolean secure) {
+ assertApiLevel17OrHigher();
+ Assertions.checkState(!secure || isSecureSupported(context));
+ DummySurfaceThread thread = new DummySurfaceThread();
+ return thread.init(secure ? secureMode : SECURE_MODE_NONE);
+ }
+
+ private DummySurface(DummySurfaceThread thread, SurfaceTexture surfaceTexture, boolean secure) {
+ super(surfaceTexture);
+ this.thread = thread;
+ this.secure = secure;
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ // The Surface may be released multiple times (explicitly and by Surface.finalize()). The
+ // implementation of super.release() has its own deduplication logic. Below we need to
+ // deduplicate ourselves. Synchronization is required as we don't control the thread on which
+ // Surface.finalize() is called.
+ synchronized (thread) {
+ if (!threadReleased) {
+ thread.release();
+ threadReleased = true;
+ }
+ }
+ }
+
+ private static void assertApiLevel17OrHigher() {
+ if (Util.SDK_INT < 17) {
+ throw new UnsupportedOperationException("Unsupported prior to API level 17");
+ }
+ }
+
+ @SecureMode
+ private static int getSecureMode(Context context) {
+ if (GlUtil.isProtectedContentExtensionSupported(context)) {
+ if (GlUtil.isSurfacelessContextExtensionSupported()) {
+ return SECURE_MODE_SURFACELESS_CONTEXT;
+ } else {
+ // 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 SECURE_MODE_PROTECTED_PBUFFER;
+ }
+ } else {
+ return SECURE_MODE_NONE;
+ }
+ }
+
+ private static class DummySurfaceThread extends HandlerThread implements Callback {
+
+ private static final int MSG_INIT = 1;
+ private static final int MSG_RELEASE = 2;
+
+ private @MonotonicNonNull EGLSurfaceTexture eglSurfaceTexture;
+ private @MonotonicNonNull Handler handler;
+ @Nullable private Error initError;
+ @Nullable private RuntimeException initException;
+ @Nullable private DummySurface surface;
+
+ public DummySurfaceThread() {
+ super("dummySurface");
+ }
+
+ public DummySurface init(@SecureMode int secureMode) {
+ start();
+ handler = new Handler(getLooper(), /* callback= */ this);
+ eglSurfaceTexture = new EGLSurfaceTexture(handler);
+ boolean wasInterrupted = false;
+ synchronized (this) {
+ handler.obtainMessage(MSG_INIT, secureMode, 0).sendToTarget();
+ while (surface == null && initException == null && initError == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ wasInterrupted = true;
+ }
+ }
+ }
+ if (wasInterrupted) {
+ // Restore the interrupted status.
+ Thread.currentThread().interrupt();
+ }
+ if (initException != null) {
+ throw initException;
+ } else if (initError != null) {
+ throw initError;
+ } else {
+ return Assertions.checkNotNull(surface);
+ }
+ }
+
+ public void release() {
+ Assertions.checkNotNull(handler);
+ handler.sendEmptyMessage(MSG_RELEASE);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT:
+ try {
+ initInternal(/* secureMode= */ msg.arg1);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to initialize dummy surface", e);
+ initException = e;
+ } catch (Error e) {
+ Log.e(TAG, "Failed to initialize dummy surface", e);
+ initError = e;
+ } finally {
+ synchronized (this) {
+ notify();
+ }
+ }
+ return true;
+ case MSG_RELEASE:
+ try {
+ releaseInternal();
+ } catch (Throwable e) {
+ Log.e(TAG, "Failed to release dummy surface", e);
+ } finally {
+ quit();
+ }
+ return true;
+ default:
+ return true;
+ }
+ }
+
+ private void initInternal(@SecureMode int secureMode) {
+ Assertions.checkNotNull(eglSurfaceTexture);
+ eglSurfaceTexture.init(secureMode);
+ this.surface =
+ new DummySurface(
+ this, eglSurfaceTexture.getSurfaceTexture(), secureMode != SECURE_MODE_NONE);
+ }
+
+ private void releaseInternal() {
+ Assertions.checkNotNull(eglSurfaceTexture);
+ eglSurfaceTexture.release();
+ }
+
+ }
+
+}