Merge pull request #983 from MGaetan89:min_api_19
PiperOrigin-RevId: 607319987
This commit is contained in:
commit
fb7438378d
2
api.txt
2
api.txt
@ -1370,7 +1370,7 @@ package androidx.media3.exoplayer.analytics {
|
|||||||
|
|
||||||
package androidx.media3.exoplayer.drm {
|
package androidx.media3.exoplayer.drm {
|
||||||
|
|
||||||
@RequiresApi(18) public final class FrameworkMediaDrm {
|
public final class FrameworkMediaDrm {
|
||||||
method public static boolean isCryptoSchemeSupported(java.util.UUID);
|
method public static boolean isCryptoSchemeSupported(java.util.UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ public final class MainActivity extends Activity {
|
|||||||
? Assertions.checkNotNull(intent.getData())
|
? Assertions.checkNotNull(intent.getData())
|
||||||
: Uri.parse(DEFAULT_MEDIA_URI);
|
: Uri.parse(DEFAULT_MEDIA_URI);
|
||||||
DrmSessionManager drmSessionManager;
|
DrmSessionManager drmSessionManager;
|
||||||
if (Util.SDK_INT >= 18 && intent.hasExtra(DRM_SCHEME_EXTRA)) {
|
if (intent.hasExtra(DRM_SCHEME_EXTRA)) {
|
||||||
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
||||||
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
||||||
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
||||||
|
@ -26,7 +26,6 @@ import android.os.Looper;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DrmInitData;
|
import androidx.media3.common.DrmInitData;
|
||||||
@ -202,12 +201,7 @@ public class DownloadTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The content is DRM protected. We need to acquire an offline license.
|
// The content is DRM protected. We need to acquire an offline license.
|
||||||
if (Util.SDK_INT < 18) {
|
|
||||||
Toast.makeText(context, R.string.error_drm_unsupported_before_api_18, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
Log.e(TAG, "Downloading DRM protected content is not supported on API versions below 18");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO(internal b/163107948): Support cases where DrmInitData are not in the manifest.
|
// TODO(internal b/163107948): Support cases where DrmInitData are not in the manifest.
|
||||||
if (!hasNonNullWidevineSchemaData(format.drmInitData)) {
|
if (!hasNonNullWidevineSchemaData(format.drmInitData)) {
|
||||||
Toast.makeText(context, R.string.download_start_error_offline_license, Toast.LENGTH_LONG)
|
Toast.makeText(context, R.string.download_start_error_offline_license, Toast.LENGTH_LONG)
|
||||||
@ -362,7 +356,6 @@ public class DownloadTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Downloads a Widevine offline license in a background thread. */
|
/** Downloads a Widevine offline license in a background thread. */
|
||||||
@RequiresApi(18)
|
|
||||||
private static final class WidevineOfflineLicenseFetchTask {
|
private static final class WidevineOfflineLicenseFetchTask {
|
||||||
|
|
||||||
private final Format format;
|
private final Format format;
|
||||||
|
@ -361,11 +361,7 @@ public class PlayerActivity extends AppCompatActivity
|
|||||||
|
|
||||||
MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
|
MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
|
||||||
if (drmConfiguration != null) {
|
if (drmConfiguration != null) {
|
||||||
if (Build.VERSION.SDK_INT < 18) {
|
if (!FrameworkMediaDrm.isCryptoSchemeSupported(drmConfiguration.scheme)) {
|
||||||
showToast(R.string.error_drm_unsupported_before_api_18);
|
|
||||||
finish();
|
|
||||||
return Collections.emptyList();
|
|
||||||
} else if (!FrameworkMediaDrm.isCryptoSchemeSupported(drmConfiguration.scheme)) {
|
|
||||||
showToast(R.string.error_drm_unsupported_scheme);
|
showToast(R.string.error_drm_unsupported_scheme);
|
||||||
finish();
|
finish();
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
<string name="error_generic">Playback failed</string>
|
<string name="error_generic">Playback failed</string>
|
||||||
|
|
||||||
<string name="error_drm_unsupported_before_api_18">DRM content not supported on API levels below 18</string>
|
|
||||||
|
|
||||||
<string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
|
<string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
|
||||||
|
|
||||||
<string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
|
<string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
|
||||||
|
@ -21,7 +21,6 @@ import android.opengl.EGLContext;
|
|||||||
import android.opengl.EGLDisplay;
|
import android.opengl.EGLDisplay;
|
||||||
import android.opengl.EGLSurface;
|
import android.opengl.EGLSurface;
|
||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.GlUtil.GlException;
|
import androidx.media3.common.util.GlUtil.GlException;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
@ -39,7 +38,6 @@ public interface GlObjectsProvider {
|
|||||||
* @param configAttributes The attributes to configure EGL with.
|
* @param configAttributes The attributes to configure EGL with.
|
||||||
* @throws GlException If an error occurs during creation.
|
* @throws GlException If an error occurs during creation.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
EGLContext createEglContext(
|
EGLContext createEglContext(
|
||||||
EGLDisplay eglDisplay, @IntRange(from = 2, to = 3) int openGlVersion, int[] configAttributes)
|
EGLDisplay eglDisplay, @IntRange(from = 2, to = 3) int openGlVersion, int[] configAttributes)
|
||||||
throws GlException;
|
throws GlException;
|
||||||
@ -54,7 +52,6 @@ public interface GlObjectsProvider {
|
|||||||
* @param isEncoderInputSurface Whether the {@code surface} is the input surface of an encoder.
|
* @param isEncoderInputSurface Whether the {@code surface} is the input surface of an encoder.
|
||||||
* @throws GlException If an error occurs during creation.
|
* @throws GlException If an error occurs during creation.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
EGLSurface createEglSurface(
|
EGLSurface createEglSurface(
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
Object surface,
|
Object surface,
|
||||||
@ -72,7 +69,6 @@ public interface GlObjectsProvider {
|
|||||||
* surface.
|
* surface.
|
||||||
* @throws GlException If an error occurs during creation.
|
* @throws GlException If an error occurs during creation.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
EGLSurface createFocusedPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay)
|
EGLSurface createFocusedPlaceholderEglSurface(EGLContext eglContext, EGLDisplay eglDisplay)
|
||||||
throws GlException;
|
throws GlException;
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import android.util.Pair;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.BundleCollectionUtil;
|
import androidx.media3.common.util.BundleCollectionUtil;
|
||||||
import androidx.media3.common.util.BundleUtil;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
@ -1453,8 +1452,8 @@ public abstract class Timeline implements Bundleable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
BundleUtil.putBinder(bundle, FIELD_WINDOWS, new BundleListRetriever(windowBundles));
|
bundle.putBinder(FIELD_WINDOWS, new BundleListRetriever(windowBundles));
|
||||||
BundleUtil.putBinder(bundle, FIELD_PERIODS, new BundleListRetriever(periodBundles));
|
bundle.putBinder(FIELD_PERIODS, new BundleListRetriever(periodBundles));
|
||||||
bundle.putIntArray(FIELD_SHUFFLED_WINDOW_INDICES, shuffledWindowIndices);
|
bundle.putIntArray(FIELD_SHUFFLED_WINDOW_INDICES, shuffledWindowIndices);
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
@ -1503,9 +1502,9 @@ public abstract class Timeline implements Bundleable {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static Timeline fromBundle(Bundle bundle) {
|
public static Timeline fromBundle(Bundle bundle) {
|
||||||
ImmutableList<Window> windows =
|
ImmutableList<Window> windows =
|
||||||
fromBundleListRetriever(Window::fromBundle, BundleUtil.getBinder(bundle, FIELD_WINDOWS));
|
fromBundleListRetriever(Window::fromBundle, bundle.getBinder(FIELD_WINDOWS));
|
||||||
ImmutableList<Period> periods =
|
ImmutableList<Period> periods =
|
||||||
fromBundleListRetriever(Period::fromBundle, BundleUtil.getBinder(bundle, FIELD_PERIODS));
|
fromBundleListRetriever(Period::fromBundle, bundle.getBinder(FIELD_PERIODS));
|
||||||
@Nullable int[] shuffledWindowIndices = bundle.getIntArray(FIELD_SHUFFLED_WINDOW_INDICES);
|
@Nullable int[] shuffledWindowIndices = bundle.getIntArray(FIELD_SHUFFLED_WINDOW_INDICES);
|
||||||
return new RemotableTimeline(
|
return new RemotableTimeline(
|
||||||
windows,
|
windows,
|
||||||
|
@ -28,7 +28,6 @@ import android.os.Looper;
|
|||||||
import android.view.accessibility.CaptioningManager;
|
import android.view.accessibility.CaptioningManager;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.BundleCollectionUtil;
|
import androidx.media3.common.util.BundleCollectionUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -623,7 +622,7 @@ public class TrackSelectionParameters implements Bundleable {
|
|||||||
* Sets the preferred language and role flags for text tracks based on the accessibility
|
* Sets the preferred language and role flags for text tracks based on the accessibility
|
||||||
* settings of {@link CaptioningManager}.
|
* settings of {@link CaptioningManager}.
|
||||||
*
|
*
|
||||||
* <p>Does nothing for API levels < 19 or when the {@link CaptioningManager} is disabled.
|
* <p>Does nothing when the {@link CaptioningManager} is disabled.
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @return This builder.
|
* @return This builder.
|
||||||
@ -631,8 +630,20 @@ public class TrackSelectionParameters implements Bundleable {
|
|||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Builder setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings(
|
public Builder setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettings(
|
||||||
Context context) {
|
Context context) {
|
||||||
if (Util.SDK_INT >= 19) {
|
if (Util.SDK_INT < 23 && Looper.myLooper() == null) {
|
||||||
setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettingsV19(context);
|
// Android platform bug (pre-Marshmallow) that causes RuntimeExceptions when
|
||||||
|
// CaptioningService is instantiated from a non-Looper thread. See [internal: b/143779904].
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
CaptioningManager captioningManager =
|
||||||
|
(CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
|
||||||
|
if (captioningManager == null || !captioningManager.isEnabled()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
preferredTextRoleFlags = C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND;
|
||||||
|
Locale preferredLocale = captioningManager.getLocale();
|
||||||
|
if (preferredLocale != null) {
|
||||||
|
preferredTextLanguages = ImmutableList.of(Util.getLocaleLanguageTag(preferredLocale));
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -832,26 +843,6 @@ public class TrackSelectionParameters implements Bundleable {
|
|||||||
return new TrackSelectionParameters(this);
|
return new TrackSelectionParameters(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
private void setPreferredTextLanguageAndRoleFlagsToCaptioningManagerSettingsV19(
|
|
||||||
Context context) {
|
|
||||||
if (Util.SDK_INT < 23 && Looper.myLooper() == null) {
|
|
||||||
// Android platform bug (pre-Marshmallow) that causes RuntimeExceptions when
|
|
||||||
// CaptioningService is instantiated from a non-Looper thread. See [internal: b/143779904].
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CaptioningManager captioningManager =
|
|
||||||
(CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
|
|
||||||
if (captioningManager == null || !captioningManager.isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
preferredTextRoleFlags = C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND;
|
|
||||||
Locale preferredLocale = captioningManager.getLocale();
|
|
||||||
if (preferredLocale != null) {
|
|
||||||
preferredTextLanguages = ImmutableList.of(Util.getLocaleLanguageTag(preferredLocale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImmutableList<String> normalizeLanguageCodes(String[] preferredTextLanguages) {
|
private static ImmutableList<String> normalizeLanguageCodes(String[] preferredTextLanguages) {
|
||||||
ImmutableList.Builder<String> listBuilder = ImmutableList.builder();
|
ImmutableList.Builder<String> listBuilder = ImmutableList.builder();
|
||||||
for (String language : checkNotNull(preferredTextLanguages)) {
|
for (String language : checkNotNull(preferredTextLanguages)) {
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021 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 androidx.media3.common.util;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/** Utilities for {@link Bundle}. */
|
|
||||||
@UnstableApi
|
|
||||||
public final class BundleUtil {
|
|
||||||
|
|
||||||
private static final String TAG = "BundleUtil";
|
|
||||||
|
|
||||||
@Nullable private static Method getIBinderMethod;
|
|
||||||
@Nullable private static Method putIBinderMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an {@link IBinder} inside a {@link Bundle} for all Android versions.
|
|
||||||
*
|
|
||||||
* @param bundle The bundle to get the {@link IBinder}.
|
|
||||||
* @param key The key to use while getting the {@link IBinder}.
|
|
||||||
* @return The {@link IBinder} that was obtained.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static IBinder getBinder(Bundle bundle, @Nullable String key) {
|
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
return bundle.getBinder(key);
|
|
||||||
} else {
|
|
||||||
return getBinderByReflection(bundle, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Puts an {@link IBinder} inside a {@link Bundle} for all Android versions.
|
|
||||||
*
|
|
||||||
* @param bundle The bundle to insert the {@link IBinder}.
|
|
||||||
* @param key The key to use while putting the {@link IBinder}.
|
|
||||||
* @param binder The {@link IBinder} to put.
|
|
||||||
*/
|
|
||||||
public static void putBinder(Bundle bundle, @Nullable String key, @Nullable IBinder binder) {
|
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
bundle.putBinder(key, binder);
|
|
||||||
} else {
|
|
||||||
putBinderByReflection(bundle, key, binder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method.invoke may take null "key".
|
|
||||||
@SuppressWarnings("nullness:argument")
|
|
||||||
@Nullable
|
|
||||||
private static IBinder getBinderByReflection(Bundle bundle, @Nullable String key) {
|
|
||||||
@Nullable Method getIBinder = getIBinderMethod;
|
|
||||||
if (getIBinder == null) {
|
|
||||||
try {
|
|
||||||
getIBinderMethod = Bundle.class.getMethod("getIBinder", String.class);
|
|
||||||
getIBinderMethod.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Log.i(TAG, "Failed to retrieve getIBinder method", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
getIBinder = getIBinderMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return (IBinder) getIBinder.invoke(bundle, key);
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
|
|
||||||
Log.i(TAG, "Failed to invoke getIBinder via reflection", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method.invoke may take null "key" and "binder".
|
|
||||||
@SuppressWarnings("nullness:argument")
|
|
||||||
private static void putBinderByReflection(
|
|
||||||
Bundle bundle, @Nullable String key, @Nullable IBinder binder) {
|
|
||||||
@Nullable Method putIBinder = putIBinderMethod;
|
|
||||||
if (putIBinder == null) {
|
|
||||||
try {
|
|
||||||
putIBinderMethod = Bundle.class.getMethod("putIBinder", String.class, IBinder.class);
|
|
||||||
putIBinderMethod.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
Log.i(TAG, "Failed to retrieve putIBinder method", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
putIBinder = putIBinderMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
putIBinder.invoke(bundle, key, binder);
|
|
||||||
} catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
|
|
||||||
Log.i(TAG, "Failed to invoke putIBinder via reflection", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BundleUtil() {}
|
|
||||||
}
|
|
@ -27,14 +27,12 @@ import android.opengl.GLES20;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/** Generates a {@link SurfaceTexture} using EGL/GLES functions. */
|
/** Generates a {@link SurfaceTexture} using EGL/GLES functions. */
|
||||||
@RequiresApi(17)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener, Runnable {
|
public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener, Runnable {
|
||||||
|
|
||||||
@ -150,10 +148,7 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
|
|||||||
if (context != null) {
|
if (context != null) {
|
||||||
EGL14.eglDestroyContext(display, context);
|
EGL14.eglDestroyContext(display, context);
|
||||||
}
|
}
|
||||||
// EGL14.eglReleaseThread could crash before Android K (see [internal: b/11327779]).
|
|
||||||
if (Util.SDK_INT >= 19) {
|
|
||||||
EGL14.eglReleaseThread();
|
EGL14.eglReleaseThread();
|
||||||
}
|
|
||||||
if (display != null && !display.equals(EGL14.EGL_NO_DISPLAY)) {
|
if (display != null && !display.equals(EGL14.EGL_NO_DISPLAY)) {
|
||||||
// Android is unusual in that it uses a reference-counted EGLDisplay. So for
|
// Android is unusual in that it uses a reference-counted EGLDisplay. So for
|
||||||
// every eglInitialize() we need an eglTerminate().
|
// every eglInitialize() we need an eglTerminate().
|
||||||
|
@ -34,10 +34,8 @@ import android.opengl.GLES20;
|
|||||||
import android.opengl.GLES30;
|
import android.opengl.GLES30;
|
||||||
import android.opengl.GLUtils;
|
import android.opengl.GLUtils;
|
||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
import androidx.annotation.DoNotInline;
|
|
||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@ -181,7 +179,7 @@ public final class GlUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Api17.isExtensionSupported(EXTENSION_PROTECTED_CONTENT);
|
return isExtensionSupported(EXTENSION_PROTECTED_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,7 +190,7 @@ public final class GlUtil {
|
|||||||
* EGLContext)}.
|
* EGLContext)}.
|
||||||
*/
|
*/
|
||||||
public static boolean isSurfacelessContextExtensionSupported() {
|
public static boolean isSurfacelessContextExtensionSupported() {
|
||||||
return Util.SDK_INT >= 17 && Api17.isExtensionSupported(EXTENSION_SURFACELESS_CONTEXT);
|
return isExtensionSupported(EXTENSION_SURFACELESS_CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,11 +200,8 @@ public final class GlUtil {
|
|||||||
* for HDR input.
|
* for HDR input.
|
||||||
*/
|
*/
|
||||||
public static boolean isYuvTargetExtensionSupported() {
|
public static boolean isYuvTargetExtensionSupported() {
|
||||||
if (Util.SDK_INT < 17) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@Nullable String glExtensions;
|
@Nullable String glExtensions;
|
||||||
if (Util.areEqual(Api17.getCurrentContext(), EGL14.EGL_NO_CONTEXT)) {
|
if (Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT)) {
|
||||||
// Create a placeholder context and make it current to allow calling GLES20.glGetString().
|
// Create a placeholder context and make it current to allow calling GLES20.glGetString().
|
||||||
try {
|
try {
|
||||||
EGLDisplay eglDisplay = getDefaultEglDisplay();
|
EGLDisplay eglDisplay = getDefaultEglDisplay();
|
||||||
@ -226,13 +221,23 @@ public final class GlUtil {
|
|||||||
|
|
||||||
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_PQ} is supported. */
|
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_PQ} is supported. */
|
||||||
public static boolean isBt2020PqExtensionSupported() {
|
public static boolean isBt2020PqExtensionSupported() {
|
||||||
return Util.SDK_INT >= 17 && Api17.isExtensionSupported(EXTENSION_COLORSPACE_BT2020_PQ);
|
return isExtensionSupported(EXTENSION_COLORSPACE_BT2020_PQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an initialized default {@link EGLDisplay}. */
|
/** Returns an initialized default {@link EGLDisplay}. */
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLDisplay getDefaultEglDisplay() throws GlException {
|
public static EGLDisplay getDefaultEglDisplay() throws GlException {
|
||||||
return Api17.getDefaultEglDisplay();
|
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
|
checkGlException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
|
||||||
|
checkGlException(
|
||||||
|
EGL14.eglInitialize(
|
||||||
|
eglDisplay,
|
||||||
|
/* unusedMajor */ new int[1],
|
||||||
|
/* majorOffset= */ 0,
|
||||||
|
/* unusedMinor */ new int[1],
|
||||||
|
/* minorOffset= */ 0),
|
||||||
|
"Error in eglInitialize.");
|
||||||
|
checkGlError();
|
||||||
|
return eglDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,7 +248,6 @@ public final class GlUtil {
|
|||||||
*
|
*
|
||||||
* @param eglDisplay The {@link EGLDisplay} to create an {@link EGLContext} for.
|
* @param eglDisplay The {@link EGLDisplay} to create an {@link EGLContext} for.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLContext createEglContext(EGLDisplay eglDisplay) throws GlException {
|
public static EGLContext createEglContext(EGLDisplay eglDisplay) throws GlException {
|
||||||
return createEglContext(
|
return createEglContext(
|
||||||
EGL14.EGL_NO_CONTEXT, eglDisplay, /* openGlVersion= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888);
|
EGL14.EGL_NO_CONTEXT, eglDisplay, /* openGlVersion= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888);
|
||||||
@ -259,7 +263,6 @@ public final class GlUtil {
|
|||||||
* @param configAttributes The attributes to configure EGL with. Accepts either {@link
|
* @param configAttributes The attributes to configure EGL with. Accepts either {@link
|
||||||
* #EGL_CONFIG_ATTRIBUTES_RGBA_1010102}, or {@link #EGL_CONFIG_ATTRIBUTES_RGBA_8888}.
|
* #EGL_CONFIG_ATTRIBUTES_RGBA_1010102}, or {@link #EGL_CONFIG_ATTRIBUTES_RGBA_8888}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLContext createEglContext(
|
public static EGLContext createEglContext(
|
||||||
EGLContext sharedContext,
|
EGLContext sharedContext,
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
@ -270,7 +273,23 @@ public final class GlUtil {
|
|||||||
Arrays.equals(configAttributes, EGL_CONFIG_ATTRIBUTES_RGBA_8888)
|
Arrays.equals(configAttributes, EGL_CONFIG_ATTRIBUTES_RGBA_8888)
|
||||||
|| Arrays.equals(configAttributes, EGL_CONFIG_ATTRIBUTES_RGBA_1010102));
|
|| Arrays.equals(configAttributes, EGL_CONFIG_ATTRIBUTES_RGBA_1010102));
|
||||||
checkArgument(openGlVersion == 2 || openGlVersion == 3);
|
checkArgument(openGlVersion == 2 || openGlVersion == 3);
|
||||||
return Api17.createEglContext(sharedContext, eglDisplay, openGlVersion, configAttributes);
|
int[] contextAttributes = {EGL_CONTEXT_CLIENT_VERSION, openGlVersion, EGL14.EGL_NONE};
|
||||||
|
EGLContext eglContext =
|
||||||
|
EGL14.eglCreateContext(
|
||||||
|
eglDisplay,
|
||||||
|
getEglConfig(eglDisplay, configAttributes),
|
||||||
|
sharedContext,
|
||||||
|
contextAttributes,
|
||||||
|
/* offset= */ 0);
|
||||||
|
if (eglContext == null) {
|
||||||
|
EGL14.eglTerminate(eglDisplay);
|
||||||
|
throw new GlException(
|
||||||
|
"eglCreateContext() failed to create a valid context. The device may not support EGL"
|
||||||
|
+ " version "
|
||||||
|
+ openGlVersion);
|
||||||
|
}
|
||||||
|
checkGlError();
|
||||||
|
return eglContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,7 +304,6 @@ public final class GlUtil {
|
|||||||
* C#COLOR_TRANSFER_SDR}, {@link C#COLOR_TRANSFER_HLG} and {@link C#COLOR_TRANSFER_ST2084}.
|
* C#COLOR_TRANSFER_SDR}, {@link C#COLOR_TRANSFER_HLG} and {@link C#COLOR_TRANSFER_ST2084}.
|
||||||
* @param isEncoderInputSurface Whether the {@code surface} is the input surface of an encoder.
|
* @param isEncoderInputSurface Whether the {@code surface} is the input surface of an encoder.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLSurface createEglSurface(
|
public static EGLSurface createEglSurface(
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
Object surface,
|
Object surface,
|
||||||
@ -316,7 +334,15 @@ public final class GlUtil {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unsupported color transfer: " + colorTransfer);
|
throw new IllegalArgumentException("Unsupported color transfer: " + colorTransfer);
|
||||||
}
|
}
|
||||||
return Api17.createEglSurface(eglDisplay, surface, configAttributes, windowAttributes);
|
EGLSurface eglSurface =
|
||||||
|
EGL14.eglCreateWindowSurface(
|
||||||
|
eglDisplay,
|
||||||
|
getEglConfig(eglDisplay, configAttributes),
|
||||||
|
surface,
|
||||||
|
windowAttributes,
|
||||||
|
/* offset= */ 0);
|
||||||
|
checkEglException("Error creating a new EGL surface");
|
||||||
|
return eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,7 +354,6 @@ public final class GlUtil {
|
|||||||
* @param configAttributes EGL configuration attributes. Valid arguments include {@link
|
* @param configAttributes EGL configuration attributes. Valid arguments include {@link
|
||||||
* #EGL_CONFIG_ATTRIBUTES_RGBA_8888} and {@link #EGL_CONFIG_ATTRIBUTES_RGBA_1010102}.
|
* #EGL_CONFIG_ATTRIBUTES_RGBA_8888} and {@link #EGL_CONFIG_ATTRIBUTES_RGBA_1010102}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
private static EGLSurface createPbufferSurface(
|
private static EGLSurface createPbufferSurface(
|
||||||
EGLDisplay eglDisplay, int width, int height, int[] configAttributes) throws GlException {
|
EGLDisplay eglDisplay, int width, int height, int[] configAttributes) throws GlException {
|
||||||
int[] pbufferAttributes =
|
int[] pbufferAttributes =
|
||||||
@ -337,7 +362,14 @@ public final class GlUtil {
|
|||||||
EGL14.EGL_HEIGHT, height,
|
EGL14.EGL_HEIGHT, height,
|
||||||
EGL14.EGL_NONE
|
EGL14.EGL_NONE
|
||||||
};
|
};
|
||||||
return Api17.createEglPbufferSurface(eglDisplay, configAttributes, pbufferAttributes);
|
EGLSurface eglSurface =
|
||||||
|
EGL14.eglCreatePbufferSurface(
|
||||||
|
eglDisplay,
|
||||||
|
getEglConfig(eglDisplay, configAttributes),
|
||||||
|
pbufferAttributes,
|
||||||
|
/* offset= */ 0);
|
||||||
|
checkEglException("Error creating a new EGL Pbuffer surface");
|
||||||
|
return eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -350,7 +382,6 @@ public final class GlUtil {
|
|||||||
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
|
||||||
* @return {@link EGL14#EGL_NO_SURFACE} if supported and a 1x1 pixel buffer surface otherwise.
|
* @return {@link EGL14#EGL_NO_SURFACE} if supported and a 1x1 pixel buffer surface otherwise.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLSurface createFocusedPlaceholderEglSurface(
|
public static EGLSurface createFocusedPlaceholderEglSurface(
|
||||||
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
||||||
// EGL_CONFIG_ATTRIBUTES_RGBA_1010102 could be used for HDR input, but EGL14.EGL_NO_SURFACE
|
// EGL_CONFIG_ATTRIBUTES_RGBA_1010102 could be used for HDR input, but EGL14.EGL_NO_SURFACE
|
||||||
@ -372,9 +403,16 @@ public final class GlUtil {
|
|||||||
* <p>Returns {@code 0} if no {@link EGLContext} {@linkplain #createFocusedPlaceholderEglSurface
|
* <p>Returns {@code 0} if no {@link EGLContext} {@linkplain #createFocusedPlaceholderEglSurface
|
||||||
* is focused}.
|
* is focused}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static long getContextMajorVersion() throws GlException {
|
public static long getContextMajorVersion() throws GlException {
|
||||||
return Api17.getContextMajorVersion();
|
int[] currentEglContextVersion = new int[1];
|
||||||
|
EGL14.eglQueryContext(
|
||||||
|
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
|
||||||
|
EGL14.eglGetCurrentContext(),
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION,
|
||||||
|
currentEglContextVersion,
|
||||||
|
/* offset= */ 0);
|
||||||
|
checkGlError();
|
||||||
|
return currentEglContextVersion[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -384,10 +422,20 @@ public final class GlUtil {
|
|||||||
* #createFocusedPlaceholderEglSurface is focused}, or the focused {@link EGLContext} version is
|
* #createFocusedPlaceholderEglSurface is focused}, or the focused {@link EGLContext} version is
|
||||||
* less than 3.0.
|
* less than 3.0.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static long createGlSyncFence() throws GlException {
|
public static long createGlSyncFence() throws GlException {
|
||||||
// If the context is an OpenGL 3.0 context, we must be running API 18 or later.
|
if (getContextMajorVersion() >= 3) {
|
||||||
return Api17.getContextMajorVersion() >= 3 ? Api18.createSyncFence() : 0;
|
long syncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, /* flags= */ 0);
|
||||||
|
checkGlError();
|
||||||
|
// Due to specifics of OpenGL, it might happen that the fence creation command is not yet
|
||||||
|
// sent into the GPU command queue, which can cause other threads to wait infinitely if
|
||||||
|
// the glSyncWait/glClientSyncWait command went into the GPU earlier. Hence, we have to
|
||||||
|
// call glFlush to ensure that glFenceSync is inside of the GPU command queue.
|
||||||
|
GLES20.glFlush();
|
||||||
|
checkGlError();
|
||||||
|
return syncObject;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,22 +444,13 @@ public final class GlUtil {
|
|||||||
* <p>The {@code syncObject} must not be used after deletion.
|
* <p>The {@code syncObject} must not be used after deletion.
|
||||||
*/
|
*/
|
||||||
public static void deleteSyncObject(long syncObject) throws GlException {
|
public static void deleteSyncObject(long syncObject) throws GlException {
|
||||||
// If the sync object is set, we must be running API 18 or later.
|
deleteSyncObjectQuietly(syncObject);
|
||||||
if (Util.SDK_INT >= 18) {
|
checkGlError();
|
||||||
Api18.deleteSyncObject(syncObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Releases the GL sync object if set, suppressing any error. */
|
/** Releases the GL sync object if set, suppressing any error. */
|
||||||
public static void deleteSyncObjectQuietly(long syncObject) {
|
public static void deleteSyncObjectQuietly(long syncObject) {
|
||||||
if (Util.SDK_INT >= 18) {
|
GLES30.glDeleteSync(syncObject);
|
||||||
try {
|
|
||||||
// glDeleteSync ignores a 0-valued sync object.
|
|
||||||
Api18.deleteSyncObject(syncObject);
|
|
||||||
} catch (GlException unused) {
|
|
||||||
// Suppress exceptions.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -424,15 +463,14 @@ public final class GlUtil {
|
|||||||
// Fallback to using glFinish for synchronization when fence creation failed.
|
// Fallback to using glFinish for synchronization when fence creation failed.
|
||||||
GLES20.glFinish();
|
GLES20.glFinish();
|
||||||
} else {
|
} else {
|
||||||
// If the sync object is set, we must be running API 18 or later.
|
GLES30.glWaitSync(syncObject, /* flags= */ 0, GLES30.GL_TIMEOUT_IGNORED);
|
||||||
Api18.waitSync(syncObject);
|
checkGlError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the current {@link EGLContext context}. */
|
/** Gets the current {@link EGLContext context}. */
|
||||||
@RequiresApi(17)
|
|
||||||
public static EGLContext getCurrentContext() {
|
public static EGLContext getCurrentContext() {
|
||||||
return Api17.getCurrentContext();
|
return EGL14.eglGetCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -506,19 +544,16 @@ public final class GlUtil {
|
|||||||
* Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
|
* Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
|
||||||
* {@code height} pixels.
|
* {@code height} pixels.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static void focusEglSurface(
|
public static void focusEglSurface(
|
||||||
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height)
|
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height)
|
||||||
throws GlException {
|
throws GlException {
|
||||||
Api17.focusRenderTarget(
|
focusRenderTarget(eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
|
||||||
eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the specified {@code framebuffer} the render target, using a viewport of {@code width} by
|
* Makes the specified {@code framebuffer} the render target, using a viewport of {@code width} by
|
||||||
* {@code height} pixels.
|
* {@code height} pixels.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static void focusFramebuffer(
|
public static void focusFramebuffer(
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
EGLContext eglContext,
|
EGLContext eglContext,
|
||||||
@ -527,7 +562,7 @@ public final class GlUtil {
|
|||||||
int width,
|
int width,
|
||||||
int height)
|
int height)
|
||||||
throws GlException {
|
throws GlException {
|
||||||
Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
|
focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -614,7 +649,6 @@ public final class GlUtil {
|
|||||||
// TODO(b/227624622): Implement a pixel test that confirms 16f has less posterization.
|
// TODO(b/227624622): Implement a pixel test that confirms 16f has less posterization.
|
||||||
// TODO - b/309459038: Consider renaming the method, as the created textures are uninitialized.
|
// TODO - b/309459038: Consider renaming the method, as the created textures are uninitialized.
|
||||||
if (useHighPrecisionColorComponents) {
|
if (useHighPrecisionColorComponents) {
|
||||||
checkState(Util.SDK_INT >= 18, "GLES30 extensions are not supported below API 18.");
|
|
||||||
return createTextureUninitialized(width, height, GLES30.GL_RGBA16F, GLES30.GL_HALF_FLOAT);
|
return createTextureUninitialized(width, height, GLES30.GL_RGBA16F, GLES30.GL_HALF_FLOAT);
|
||||||
}
|
}
|
||||||
return createTextureUninitialized(width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
|
return createTextureUninitialized(width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
|
||||||
@ -721,20 +755,39 @@ public final class GlUtil {
|
|||||||
* <p>This is a no-op if called on already-destroyed {@link EGLDisplay} and {@link EGLContext}
|
* <p>This is a no-op if called on already-destroyed {@link EGLDisplay} and {@link EGLContext}
|
||||||
* instances.
|
* instances.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static void destroyEglContext(
|
public static void destroyEglContext(
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
||||||
Api17.destroyEglContext(eglDisplay, eglContext);
|
if (eglDisplay == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EGL14.eglMakeCurrent(
|
||||||
|
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||||
|
checkEglException("Error releasing context");
|
||||||
|
if (eglContext != null) {
|
||||||
|
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||||
|
checkEglException("Error destroying context");
|
||||||
|
}
|
||||||
|
EGL14.eglReleaseThread();
|
||||||
|
checkEglException("Error releasing thread");
|
||||||
|
EGL14.eglTerminate(eglDisplay);
|
||||||
|
checkEglException("Error terminating display");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys the {@link EGLSurface} identified by the provided {@link EGLDisplay} and {@link
|
* Destroys the {@link EGLSurface} identified by the provided {@link EGLDisplay} and {@link
|
||||||
* EGLSurface}.
|
* EGLSurface}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static void destroyEglSurface(
|
public static void destroyEglSurface(
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLSurface eglSurface) throws GlException {
|
@Nullable EGLDisplay eglDisplay, @Nullable EGLSurface eglSurface) throws GlException {
|
||||||
Api17.destroyEglSurface(eglDisplay, eglSurface);
|
if (eglDisplay == null || eglSurface == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW) == EGL_NO_SURFACE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGL14.eglDestroySurface(eglDisplay, eglSurface);
|
||||||
|
checkEglException("Error destroying surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deletes a framebuffer, or silently ignores the method call if {@code fboId} is unused. */
|
/** Deletes a framebuffer, or silently ignores the method call if {@code fboId} is unused. */
|
||||||
@ -760,55 +813,6 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private static final class Api17 {
|
|
||||||
private Api17() {}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static EGLDisplay getDefaultEglDisplay() throws GlException {
|
|
||||||
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
|
||||||
checkGlException(!eglDisplay.equals(EGL14.EGL_NO_DISPLAY), "No EGL display.");
|
|
||||||
checkGlException(
|
|
||||||
EGL14.eglInitialize(
|
|
||||||
eglDisplay,
|
|
||||||
/* unusedMajor */ new int[1],
|
|
||||||
/* majorOffset= */ 0,
|
|
||||||
/* unusedMinor */ new int[1],
|
|
||||||
/* minorOffset= */ 0),
|
|
||||||
"Error in eglInitialize.");
|
|
||||||
checkGlError();
|
|
||||||
return eglDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static EGLContext createEglContext(
|
|
||||||
EGLContext sharedContext, EGLDisplay eglDisplay, int version, int[] configAttributes)
|
|
||||||
throws GlException {
|
|
||||||
int[] contextAttributes = {EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE};
|
|
||||||
EGLContext eglContext =
|
|
||||||
EGL14.eglCreateContext(
|
|
||||||
eglDisplay,
|
|
||||||
getEglConfig(eglDisplay, configAttributes),
|
|
||||||
sharedContext,
|
|
||||||
contextAttributes,
|
|
||||||
/* offset= */ 0);
|
|
||||||
if (eglContext == null) {
|
|
||||||
EGL14.eglTerminate(eglDisplay);
|
|
||||||
throw new GlException(
|
|
||||||
"eglCreateContext() failed to create a valid context. The device may not support EGL"
|
|
||||||
+ " version "
|
|
||||||
+ version);
|
|
||||||
}
|
|
||||||
checkGlError();
|
|
||||||
return eglContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static EGLContext getCurrentContext() {
|
|
||||||
return EGL14.eglGetCurrentContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes)
|
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes)
|
||||||
throws GlException {
|
throws GlException {
|
||||||
EGLConfig[] eglConfigs = new EGLConfig[1];
|
EGLConfig[] eglConfigs = new EGLConfig[1];
|
||||||
@ -826,56 +830,13 @@ public final class GlUtil {
|
|||||||
return eglConfigs[0];
|
return eglConfigs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
private static boolean isExtensionSupported(String extensionName) {
|
||||||
public static boolean isExtensionSupported(String extensionName) {
|
|
||||||
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
||||||
return eglExtensions != null && eglExtensions.contains(extensionName);
|
return eglExtensions != null && eglExtensions.contains(extensionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
private static void focusRenderTarget(
|
||||||
public static int getContextMajorVersion() throws GlException {
|
|
||||||
int[] currentEglContextVersion = new int[1];
|
|
||||||
EGL14.eglQueryContext(
|
|
||||||
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
|
|
||||||
EGL14.eglGetCurrentContext(),
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION,
|
|
||||||
currentEglContextVersion,
|
|
||||||
/* offset= */ 0);
|
|
||||||
checkGlError();
|
|
||||||
return currentEglContextVersion[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static EGLSurface createEglSurface(
|
|
||||||
EGLDisplay eglDisplay, Object surface, int[] configAttributes, int[] windowAttributes)
|
|
||||||
throws GlException {
|
|
||||||
EGLSurface eglSurface =
|
|
||||||
EGL14.eglCreateWindowSurface(
|
|
||||||
eglDisplay,
|
|
||||||
getEglConfig(eglDisplay, configAttributes),
|
|
||||||
surface,
|
|
||||||
windowAttributes,
|
|
||||||
/* offset= */ 0);
|
|
||||||
checkEglException("Error creating a new EGL surface");
|
|
||||||
return eglSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static EGLSurface createEglPbufferSurface(
|
|
||||||
EGLDisplay eglDisplay, int[] configAttributes, int[] pbufferAttributes) throws GlException {
|
|
||||||
EGLSurface eglSurface =
|
|
||||||
EGL14.eglCreatePbufferSurface(
|
|
||||||
eglDisplay,
|
|
||||||
getEglConfig(eglDisplay, configAttributes),
|
|
||||||
pbufferAttributes,
|
|
||||||
/* offset= */ 0);
|
|
||||||
checkEglException("Error creating a new EGL Pbuffer surface");
|
|
||||||
return eglSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void focusRenderTarget(
|
|
||||||
EGLDisplay eglDisplay,
|
EGLDisplay eglDisplay,
|
||||||
EGLContext eglContext,
|
EGLContext eglContext,
|
||||||
EGLSurface eglSurface,
|
EGLSurface eglSurface,
|
||||||
@ -888,75 +849,10 @@ public final class GlUtil {
|
|||||||
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
private static void checkEglException(String errorMessage) throws GlException {
|
||||||
public static void destroyEglContext(
|
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
|
||||||
if (eglDisplay == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
EGL14.eglMakeCurrent(
|
|
||||||
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
|
||||||
checkEglException("Error releasing context");
|
|
||||||
if (eglContext != null) {
|
|
||||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
|
||||||
checkEglException("Error destroying context");
|
|
||||||
}
|
|
||||||
EGL14.eglReleaseThread();
|
|
||||||
checkEglException("Error releasing thread");
|
|
||||||
EGL14.eglTerminate(eglDisplay);
|
|
||||||
checkEglException("Error terminating display");
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void destroyEglSurface(
|
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLSurface eglSurface) throws GlException {
|
|
||||||
if (eglDisplay == null || eglSurface == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW) == EGL_NO_SURFACE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGL14.eglDestroySurface(eglDisplay, eglSurface);
|
|
||||||
checkEglException("Error destroying surface");
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void checkEglException(String errorMessage) throws GlException {
|
|
||||||
int error = EGL14.eglGetError();
|
int error = EGL14.eglGetError();
|
||||||
if (error != EGL14.EGL_SUCCESS) {
|
if (error != EGL14.EGL_SUCCESS) {
|
||||||
throw new GlException(errorMessage + ", error code: 0x" + Integer.toHexString(error));
|
throw new GlException(errorMessage + ", error code: 0x" + Integer.toHexString(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
private static final class Api18 {
|
|
||||||
private Api18() {}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static long createSyncFence() throws GlException {
|
|
||||||
long syncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, /* flags= */ 0);
|
|
||||||
checkGlError();
|
|
||||||
// Due to specifics of OpenGL, it might happen that the fence creation command is not yet
|
|
||||||
// sent into the GPU command queue, which can cause other threads to wait infinitely if
|
|
||||||
// the glSyncWait/glClientSyncWait command went into the GPU earlier. Hence, we have to
|
|
||||||
// call glFlush to ensure that glFenceSync is inside of the GPU command queue.
|
|
||||||
GLES20.glFlush();
|
|
||||||
checkGlError();
|
|
||||||
return syncObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void deleteSyncObject(long syncObject) throws GlException {
|
|
||||||
GLES30.glDeleteSync(syncObject);
|
|
||||||
checkGlError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void waitSync(long syncObject) throws GlException {
|
|
||||||
GLES30.glWaitSync(syncObject, /* flags= */ 0, GLES30.GL_TIMEOUT_IGNORED);
|
|
||||||
checkGlError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.common.util;
|
package androidx.media3.common.util;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
import android.os.Trace;
|
||||||
import androidx.media3.common.MediaLibraryInfo;
|
import androidx.media3.common.MediaLibraryInfo;
|
||||||
|
|
||||||
/** Calls through to {@link android.os.Trace} methods on supported API levels. */
|
/**
|
||||||
|
* Calls through to {@link Trace} methods if {@link MediaLibraryInfo#TRACE_ENABLED} is {@code true}.
|
||||||
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class TraceUtil {
|
public final class TraceUtil {
|
||||||
|
|
||||||
@ -27,34 +29,24 @@ public final class TraceUtil {
|
|||||||
/**
|
/**
|
||||||
* Writes a trace message to indicate that a given section of code has begun.
|
* Writes a trace message to indicate that a given section of code has begun.
|
||||||
*
|
*
|
||||||
* @see android.os.Trace#beginSection(String)
|
* @see Trace#beginSection(String)
|
||||||
* @param sectionName The name of the code section to appear in the trace. This may be at most 127
|
* @param sectionName The name of the code section to appear in the trace. This may be at most 127
|
||||||
* Unicode code units long.
|
* Unicode code units long.
|
||||||
*/
|
*/
|
||||||
public static void beginSection(String sectionName) {
|
public static void beginSection(String sectionName) {
|
||||||
if (MediaLibraryInfo.TRACE_ENABLED && Util.SDK_INT >= 18) {
|
if (MediaLibraryInfo.TRACE_ENABLED) {
|
||||||
beginSectionV18(sectionName);
|
Trace.beginSection(sectionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a trace message to indicate that a given section of code has ended.
|
* Writes a trace message to indicate that a given section of code has ended.
|
||||||
*
|
*
|
||||||
* @see android.os.Trace#endSection()
|
* @see Trace#endSection()
|
||||||
*/
|
*/
|
||||||
public static void endSection() {
|
public static void endSection() {
|
||||||
if (MediaLibraryInfo.TRACE_ENABLED && Util.SDK_INT >= 18) {
|
if (MediaLibraryInfo.TRACE_ENABLED) {
|
||||||
endSectionV18();
|
Trace.endSection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
private static void beginSectionV18(String sectionName) {
|
|
||||||
android.os.Trace.beginSection(sectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
private static void endSectionV18() {
|
|
||||||
android.os.Trace.endSection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1560,7 +1560,6 @@ public final class Util {
|
|||||||
* @throws NoSuchElementException If the array is empty.
|
* @throws NoSuchElementException If the array is empty.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(18)
|
|
||||||
public static long minValue(SparseLongArray sparseLongArray) {
|
public static long minValue(SparseLongArray sparseLongArray) {
|
||||||
if (sparseLongArray.size() == 0) {
|
if (sparseLongArray.size() == 0) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
@ -1580,7 +1579,6 @@ public final class Util {
|
|||||||
* @throws NoSuchElementException If the array is empty.
|
* @throws NoSuchElementException If the array is empty.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(18)
|
|
||||||
public static long maxValue(SparseLongArray sparseLongArray) {
|
public static long maxValue(SparseLongArray sparseLongArray) {
|
||||||
if (sparseLongArray.size() == 0) {
|
if (sparseLongArray.size() == 0) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
@ -3134,16 +3132,13 @@ public final class Util {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static Point getCurrentDisplayModeSize(Context context) {
|
public static Point getCurrentDisplayModeSize(Context context) {
|
||||||
@Nullable Display defaultDisplay = null;
|
@Nullable Display defaultDisplay = null;
|
||||||
if (SDK_INT >= 17) {
|
|
||||||
@Nullable
|
@Nullable
|
||||||
DisplayManager displayManager =
|
DisplayManager displayManager =
|
||||||
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||||
// We don't expect displayManager to ever be null, so this check is just precautionary.
|
// We don't expect displayManager to ever be null, so this check is just precautionary.
|
||||||
// Consider removing it when the library minSdkVersion is increased to 17 or higher.
|
|
||||||
if (displayManager != null) {
|
if (displayManager != null) {
|
||||||
defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
|
defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (defaultDisplay == null) {
|
if (defaultDisplay == null) {
|
||||||
WindowManager windowManager =
|
WindowManager windowManager =
|
||||||
checkNotNull((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
|
checkNotNull((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
|
||||||
@ -3214,10 +3209,8 @@ public final class Util {
|
|||||||
Point displaySize = new Point();
|
Point displaySize = new Point();
|
||||||
if (SDK_INT >= 23) {
|
if (SDK_INT >= 23) {
|
||||||
getDisplaySizeV23(display, displaySize);
|
getDisplaySizeV23(display, displaySize);
|
||||||
} else if (SDK_INT >= 17) {
|
|
||||||
getDisplaySizeV17(display, displaySize);
|
|
||||||
} else {
|
} else {
|
||||||
getDisplaySizeV16(display, displaySize);
|
display.getRealSize(displaySize);
|
||||||
}
|
}
|
||||||
return displaySize;
|
return displaySize;
|
||||||
}
|
}
|
||||||
@ -3703,15 +3696,6 @@ public final class Util {
|
|||||||
outSize.y = mode.getPhysicalHeight();
|
outSize.y = mode.getPhysicalHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private static void getDisplaySizeV17(Display display, Point outSize) {
|
|
||||||
display.getRealSize(outSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getDisplaySizeV16(Display display, Point outSize) {
|
|
||||||
display.getSize(outSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] getSystemLocales() {
|
private static String[] getSystemLocales() {
|
||||||
Configuration config = Resources.getSystem().getConfiguration();
|
Configuration config = Resources.getSystem().getConfiguration();
|
||||||
return SDK_INT >= 24
|
return SDK_INT >= 24
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021 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 androidx.media3.common.util;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/** Unit tests for {@link BundleUtil}. */
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class BundleUtilTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getPutBinder() {
|
|
||||||
String key = "key";
|
|
||||||
IBinder binder = new Binder();
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
|
|
||||||
BundleUtil.putBinder(bundle, key, binder);
|
|
||||||
IBinder returnedBinder = BundleUtil.getBinder(bundle, key);
|
|
||||||
|
|
||||||
assertThat(returnedBinder).isSameInstanceAs(binder);
|
|
||||||
}
|
|
||||||
}
|
|
@ -822,7 +822,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
*/
|
*/
|
||||||
private static void maybeTerminateInputStream(
|
private static void maybeTerminateInputStream(
|
||||||
@Nullable HttpURLConnection connection, long bytesRemaining) {
|
@Nullable HttpURLConnection connection, long bytesRemaining) {
|
||||||
if (connection == null || Util.SDK_INT < 19 || Util.SDK_INT > 20) {
|
if (connection == null || Util.SDK_INT > 20) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import static androidx.media3.common.util.Assertions.checkState;
|
|||||||
import static androidx.media3.common.util.Util.castNonNull;
|
import static androidx.media3.common.util.Util.castNonNull;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
@ -347,19 +346,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return cachedContent;
|
return cachedContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("GetInstance") // Suppress warning about specifying "BC" as an explicit provider.
|
|
||||||
private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
|
|
||||||
// Workaround for https://issuetracker.google.com/issues/36976726
|
|
||||||
if (Util.SDK_INT == 18) {
|
|
||||||
try {
|
|
||||||
return Cipher.getInstance("AES/CBC/PKCS5PADDING", "BC");
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Cipher.getInstance("AES/CBC/PKCS5PADDING");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an id which isn't used in the given array. If the maximum id in the array is smaller
|
* Returns an id which isn't used in the given array. If the maximum id in the array is smaller
|
||||||
* than {@link java.lang.Integer#MAX_VALUE} it just returns the next bigger integer. Otherwise it
|
* than {@link java.lang.Integer#MAX_VALUE} it just returns the next bigger integer. Otherwise it
|
||||||
@ -526,7 +512,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
if (secretKey != null) {
|
if (secretKey != null) {
|
||||||
Assertions.checkArgument(secretKey.length == 16);
|
Assertions.checkArgument(secretKey.length == 16);
|
||||||
try {
|
try {
|
||||||
cipher = getCipher();
|
cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
|
||||||
secretKeySpec = new SecretKeySpec(secretKey, "AES");
|
secretKeySpec = new SecretKeySpec(secretKey, "AES");
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||||
throw new IllegalStateException(e); // Should never happen.
|
throw new IllegalStateException(e); // Should never happen.
|
||||||
|
@ -24,7 +24,6 @@ import static androidx.media3.test.utils.BitmapPixelTestUtil.createArgb8888Bitma
|
|||||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
||||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -103,8 +102,6 @@ public class EffectPlaybackTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void exoplayerEffectsPreviewTest_ensuresFirstFrameRendered() throws Exception {
|
public void exoplayerEffectsPreviewTest_ensuresFirstFrameRendered() throws Exception {
|
||||||
assumeTrue(Util.SDK_INT >= 18);
|
|
||||||
|
|
||||||
String testId =
|
String testId =
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
"exoplayerEffectsPreviewTest_withPlayWhenReady[%b]_ensuresFirstFrameRendered",
|
"exoplayerEffectsPreviewTest_withPlayWhenReady[%b]_ensuresFirstFrameRendered",
|
||||||
|
@ -374,11 +374,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// audio recording or speech recognition'.
|
// audio recording or speech recognition'.
|
||||||
// Assistant is considered as both recording and notifying developer
|
// Assistant is considered as both recording and notifying developer
|
||||||
case C.USAGE_ASSISTANT:
|
case C.USAGE_ASSISTANT:
|
||||||
if (Util.SDK_INT >= 19) {
|
|
||||||
return AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
|
return AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
|
||||||
} else {
|
|
||||||
return AUDIOFOCUS_GAIN_TRANSIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special usages:
|
// Special usages:
|
||||||
case C.USAGE_ASSISTANCE_ACCESSIBILITY:
|
case C.USAGE_ASSISTANCE_ACCESSIBILITY:
|
||||||
|
@ -1586,7 +1586,6 @@ public interface ExoPlayer extends Player {
|
|||||||
*
|
*
|
||||||
* @param videoEffects The {@link List} of {@linkplain Effect video effects} to apply.
|
* @param videoEffects The {@link List} of {@linkplain Effect video effects} to apply.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(18)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
void setVideoEffects(List<Effect> videoEffects);
|
void setVideoEffects(List<Effect> videoEffects);
|
||||||
|
|
||||||
|
@ -754,10 +754,10 @@ public final class MediaMetricsListener
|
|||||||
return new ErrorInfo(errorCode, subErrorCode);
|
return new ErrorInfo(errorCode, subErrorCode);
|
||||||
} else if (Util.SDK_INT >= 23 && cause instanceof MediaDrmResetException) {
|
} else if (Util.SDK_INT >= 23 && cause instanceof MediaDrmResetException) {
|
||||||
return new ErrorInfo(PlaybackErrorEvent.ERROR_DRM_SYSTEM_ERROR, /* subErrorCode= */ 0);
|
return new ErrorInfo(PlaybackErrorEvent.ERROR_DRM_SYSTEM_ERROR, /* subErrorCode= */ 0);
|
||||||
} else if (Util.SDK_INT >= 18 && cause instanceof NotProvisionedException) {
|
} else if (cause instanceof NotProvisionedException) {
|
||||||
return new ErrorInfo(
|
return new ErrorInfo(
|
||||||
PlaybackErrorEvent.ERROR_DRM_PROVISIONING_FAILED, /* subErrorCode= */ 0);
|
PlaybackErrorEvent.ERROR_DRM_PROVISIONING_FAILED, /* subErrorCode= */ 0);
|
||||||
} else if (Util.SDK_INT >= 18 && cause instanceof DeniedByServerException) {
|
} else if (cause instanceof DeniedByServerException) {
|
||||||
return new ErrorInfo(PlaybackErrorEvent.ERROR_DRM_DEVICE_REVOKED, /* subErrorCode= */ 0);
|
return new ErrorInfo(PlaybackErrorEvent.ERROR_DRM_DEVICE_REVOKED, /* subErrorCode= */ 0);
|
||||||
} else if (cause instanceof UnsupportedDrmException) {
|
} else if (cause instanceof UnsupportedDrmException) {
|
||||||
return new ErrorInfo(
|
return new ErrorInfo(
|
||||||
@ -810,7 +810,7 @@ public final class MediaMetricsListener
|
|||||||
} else if (cause instanceof AudioSink.WriteException) {
|
} else if (cause instanceof AudioSink.WriteException) {
|
||||||
int subErrorCode = ((AudioSink.WriteException) cause).errorCode;
|
int subErrorCode = ((AudioSink.WriteException) cause).errorCode;
|
||||||
return new ErrorInfo(PlaybackErrorEvent.ERROR_AUDIO_TRACK_WRITE_FAILED, subErrorCode);
|
return new ErrorInfo(PlaybackErrorEvent.ERROR_AUDIO_TRACK_WRITE_FAILED, subErrorCode);
|
||||||
} else if (Util.SDK_INT >= 16 && cause instanceof MediaCodec.CryptoException) {
|
} else if (cause instanceof MediaCodec.CryptoException) {
|
||||||
int subErrorCode = ((MediaCodec.CryptoException) cause).getErrorCode();
|
int subErrorCode = ((MediaCodec.CryptoException) cause).getErrorCode();
|
||||||
int errorCode = getDrmErrorCode(subErrorCode);
|
int errorCode = getDrmErrorCode(subErrorCode);
|
||||||
return new ErrorInfo(errorCode, subErrorCode);
|
return new ErrorInfo(errorCode, subErrorCode);
|
||||||
|
@ -354,8 +354,7 @@ public final class AudioCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean deviceMaySetExternalSurroundSoundGlobalSetting() {
|
private static boolean deviceMaySetExternalSurroundSoundGlobalSetting() {
|
||||||
return Util.SDK_INT >= 17
|
return "Amazon".equals(Util.MANUFACTURER) || "Xiaomi".equals(Util.MANUFACTURER);
|
||||||
&& ("Amazon".equals(Util.MANUFACTURER) || "Xiaomi".equals(Util.MANUFACTURER));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getChannelConfigForPassthrough(int channelCount) {
|
private static int getChannelConfigForPassthrough(int channelCount) {
|
||||||
|
@ -17,14 +17,11 @@ package androidx.media3.exoplayer.audio;
|
|||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.media.AudioTimestamp;
|
import android.media.AudioTimestamp;
|
||||||
import android.media.AudioTrack;
|
import android.media.AudioTrack;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@ -94,7 +91,7 @@ import java.lang.annotation.Target;
|
|||||||
*/
|
*/
|
||||||
private static final int INITIALIZING_DURATION_US = 500_000;
|
private static final int INITIALIZING_DURATION_US = 500_000;
|
||||||
|
|
||||||
@Nullable private final AudioTimestampV19 audioTimestamp;
|
@Nullable private final AudioTimestampWrapper audioTimestamp;
|
||||||
|
|
||||||
private @State int state;
|
private @State int state;
|
||||||
private long initializeSystemTimeUs;
|
private long initializeSystemTimeUs;
|
||||||
@ -105,16 +102,11 @@ import java.lang.annotation.Target;
|
|||||||
/**
|
/**
|
||||||
* Creates a new audio timestamp poller.
|
* Creates a new audio timestamp poller.
|
||||||
*
|
*
|
||||||
* @param audioTrack The audio track that will provide timestamps, if the platform supports it.
|
* @param audioTrack The audio track that will provide timestamps.
|
||||||
*/
|
*/
|
||||||
public AudioTimestampPoller(AudioTrack audioTrack) {
|
public AudioTimestampPoller(AudioTrack audioTrack) {
|
||||||
if (Util.SDK_INT >= 19) {
|
audioTimestamp = new AudioTimestampWrapper(audioTrack);
|
||||||
audioTimestamp = new AudioTimestampV19(audioTrack);
|
|
||||||
reset();
|
reset();
|
||||||
} else {
|
|
||||||
audioTimestamp = null;
|
|
||||||
updateState(STATE_NO_TIMESTAMP);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +119,6 @@ import java.lang.annotation.Target;
|
|||||||
* @param systemTimeUs The current system time, in microseconds.
|
* @param systemTimeUs The current system time, in microseconds.
|
||||||
* @return Whether the timestamp was updated.
|
* @return Whether the timestamp was updated.
|
||||||
*/
|
*/
|
||||||
@TargetApi(19) // audioTimestamp will be null if Util.SDK_INT < 19.
|
|
||||||
public boolean maybePollTimestamp(long systemTimeUs) {
|
public boolean maybePollTimestamp(long systemTimeUs) {
|
||||||
if (audioTimestamp == null || (systemTimeUs - lastTimestampSampleTimeUs) < sampleIntervalUs) {
|
if (audioTimestamp == null || (systemTimeUs - lastTimestampSampleTimeUs) < sampleIntervalUs) {
|
||||||
return false;
|
return false;
|
||||||
@ -233,7 +224,6 @@ import java.lang.annotation.Target;
|
|||||||
* If {@link #maybePollTimestamp(long)} or {@link #hasTimestamp()} returned {@code true}, returns
|
* If {@link #maybePollTimestamp(long)} or {@link #hasTimestamp()} returned {@code true}, returns
|
||||||
* the system time at which the latest timestamp was sampled, in microseconds.
|
* the system time at which the latest timestamp was sampled, in microseconds.
|
||||||
*/
|
*/
|
||||||
@TargetApi(19) // audioTimestamp will be null if Util.SDK_INT < 19.
|
|
||||||
public long getTimestampSystemTimeUs() {
|
public long getTimestampSystemTimeUs() {
|
||||||
return audioTimestamp != null ? audioTimestamp.getTimestampSystemTimeUs() : C.TIME_UNSET;
|
return audioTimestamp != null ? audioTimestamp.getTimestampSystemTimeUs() : C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
@ -242,7 +232,6 @@ import java.lang.annotation.Target;
|
|||||||
* If {@link #maybePollTimestamp(long)} or {@link #hasTimestamp()} returned {@code true}, returns
|
* If {@link #maybePollTimestamp(long)} or {@link #hasTimestamp()} returned {@code true}, returns
|
||||||
* the latest timestamp's position in frames.
|
* the latest timestamp's position in frames.
|
||||||
*/
|
*/
|
||||||
@TargetApi(19) // audioTimestamp will be null if Util.SDK_INT < 19.
|
|
||||||
public long getTimestampPositionFrames() {
|
public long getTimestampPositionFrames() {
|
||||||
return audioTimestamp != null ? audioTimestamp.getTimestampPositionFrames() : C.INDEX_UNSET;
|
return audioTimestamp != null ? audioTimestamp.getTimestampPositionFrames() : C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
@ -272,8 +261,7 @@ import java.lang.annotation.Target;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(19)
|
private static final class AudioTimestampWrapper {
|
||||||
private static final class AudioTimestampV19 {
|
|
||||||
|
|
||||||
private final AudioTrack audioTrack;
|
private final AudioTrack audioTrack;
|
||||||
private final AudioTimestamp audioTimestamp;
|
private final AudioTimestamp audioTimestamp;
|
||||||
@ -287,7 +275,7 @@ import java.lang.annotation.Target;
|
|||||||
*
|
*
|
||||||
* @param audioTrack The audio track that will provide timestamps.
|
* @param audioTrack The audio track that will provide timestamps.
|
||||||
*/
|
*/
|
||||||
public AudioTimestampV19(AudioTrack audioTrack) {
|
public AudioTimestampWrapper(AudioTrack audioTrack) {
|
||||||
this.audioTrack = audioTrack;
|
this.audioTrack = audioTrack;
|
||||||
audioTimestamp = new AudioTimestamp();
|
audioTimestamp = new AudioTimestamp();
|
||||||
}
|
}
|
||||||
|
@ -224,13 +224,11 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
public AudioTrackPositionTracker(Listener listener) {
|
public AudioTrackPositionTracker(Listener listener) {
|
||||||
this.listener = checkNotNull(listener);
|
this.listener = checkNotNull(listener);
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
try {
|
try {
|
||||||
getLatencyMethod = AudioTrack.class.getMethod("getLatency", (Class<?>[]) null);
|
getLatencyMethod = AudioTrack.class.getMethod("getLatency", (Class<?>[]) null);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
// There's no guarantee this method exists. Do nothing.
|
// There's no guarantee this method exists. Do nothing.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
|
playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
|
||||||
clock = Clock.DEFAULT;
|
clock = Clock.DEFAULT;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import android.os.SystemClock;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DrmInitData.SchemeData;
|
import androidx.media3.common.DrmInitData.SchemeData;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
@ -57,7 +56,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
/** A {@link DrmSession} that supports playbacks using {@link ExoMediaDrm}. */
|
/** A {@link DrmSession} that supports playbacks using {@link ExoMediaDrm}. */
|
||||||
@RequiresApi(18)
|
|
||||||
/* package */ class DefaultDrmSession implements DrmSession {
|
/* package */ class DefaultDrmSession implements DrmSession {
|
||||||
|
|
||||||
/** Thrown when an unexpected exception or error is thrown during provisioning or key requests. */
|
/** Thrown when an unexpected exception or error is thrown during provisioning or key requests. */
|
||||||
|
@ -29,7 +29,6 @@ import android.os.Message;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DrmInitData;
|
import androidx.media3.common.DrmInitData;
|
||||||
import androidx.media3.common.DrmInitData.SchemeData;
|
import androidx.media3.common.DrmInitData.SchemeData;
|
||||||
@ -68,7 +67,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
* <p>This implementation supports pre-acquisition of sessions using {@link
|
* <p>This implementation supports pre-acquisition of sessions using {@link
|
||||||
* #preacquireSession(DrmSessionEventListener.EventDispatcher, Format)}.
|
* #preacquireSession(DrmSessionEventListener.EventDispatcher, Format)}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(18)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class DefaultDrmSessionManager implements DrmSessionManager {
|
public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||||
|
|
||||||
@ -657,11 +655,8 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean acquisitionFailedIndicatingResourceShortage(DrmSession session) {
|
private static boolean acquisitionFailedIndicatingResourceShortage(DrmSession session) {
|
||||||
// ResourceBusyException is only available at API 19, so on earlier versions we
|
|
||||||
// assume any error indicates resource shortage (ensuring we retry).
|
|
||||||
return session.getState() == DrmSession.STATE_ERROR
|
return session.getState() == DrmSession.STATE_ERROR
|
||||||
&& (Util.SDK_INT < 19
|
&& checkNotNull(session.getError()).getCause() instanceof ResourceBusyException;
|
||||||
|| checkNotNull(session.getError()).getCause() instanceof ResourceBusyException);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,6 @@ import static androidx.media3.exoplayer.drm.DefaultDrmSessionManager.MODE_PLAYBA
|
|||||||
|
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -75,7 +74,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
|
|||||||
checkNotNull(mediaItem.localConfiguration);
|
checkNotNull(mediaItem.localConfiguration);
|
||||||
@Nullable
|
@Nullable
|
||||||
MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
|
MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
|
||||||
if (drmConfiguration == null || Util.SDK_INT < 18) {
|
if (drmConfiguration == null) {
|
||||||
return DrmSessionManager.DRM_UNSUPPORTED;
|
return DrmSessionManager.DRM_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +87,6 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
|
private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
|
||||||
DataSource.Factory dataSourceFactory =
|
DataSource.Factory dataSourceFactory =
|
||||||
drmHttpDataSourceFactory != null
|
drmHttpDataSourceFactory != null
|
||||||
|
@ -80,13 +80,13 @@ public final class DrmUtil {
|
|||||||
return Api21.mediaDrmStateExceptionToErrorCode(exception);
|
return Api21.mediaDrmStateExceptionToErrorCode(exception);
|
||||||
} else if (Util.SDK_INT >= 23 && Api23.isMediaDrmResetException(exception)) {
|
} else if (Util.SDK_INT >= 23 && Api23.isMediaDrmResetException(exception)) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR;
|
return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR;
|
||||||
} else if (Util.SDK_INT >= 18 && Api18.isNotProvisionedException(exception)) {
|
} else if (exception instanceof NotProvisionedException) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED;
|
return PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED;
|
||||||
} else if (Util.SDK_INT >= 18 && Api18.isDeniedByServerException(exception)) {
|
} else if (exception instanceof DeniedByServerException) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED;
|
return PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED;
|
||||||
} else if (exception instanceof UnsupportedDrmException) {
|
} else if (exception instanceof UnsupportedDrmException) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_SCHEME_UNSUPPORTED;
|
return PlaybackException.ERROR_CODE_DRM_SCHEME_UNSUPPORTED;
|
||||||
} else if (Util.SDK_INT >= 18 && Api18.isMissingSchemeDataException(exception)) {
|
} else if (exception instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_CONTENT_ERROR;
|
return PlaybackException.ERROR_CODE_DRM_CONTENT_ERROR;
|
||||||
} else if (exception instanceof KeysExpiredException) {
|
} else if (exception instanceof KeysExpiredException) {
|
||||||
return PlaybackException.ERROR_CODE_DRM_LICENSE_EXPIRED;
|
return PlaybackException.ERROR_CODE_DRM_LICENSE_EXPIRED;
|
||||||
@ -106,25 +106,6 @@ public final class DrmUtil {
|
|||||||
|
|
||||||
// Internal classes.
|
// Internal classes.
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
private static final class Api18 {
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isNotProvisionedException(@Nullable Throwable throwable) {
|
|
||||||
return throwable instanceof NotProvisionedException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isDeniedByServerException(@Nullable Throwable throwable) {
|
|
||||||
return throwable instanceof DeniedByServerException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isMissingSchemeDataException(@Nullable Throwable throwable) {
|
|
||||||
return throwable instanceof DefaultDrmSessionManager.MissingSchemeDataException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
private static final class Api21 {
|
private static final class Api21 {
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ package androidx.media3.exoplayer.drm;
|
|||||||
import android.media.MediaDrmException;
|
import android.media.MediaDrmException;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.DrmInitData;
|
import androidx.media3.common.DrmInitData;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -29,7 +28,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/** An {@link ExoMediaDrm} that does not support any protection schemes. */
|
/** An {@link ExoMediaDrm} that does not support any protection schemes. */
|
||||||
@RequiresApi(18)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class DummyExoMediaDrm implements ExoMediaDrm {
|
public final class DummyExoMediaDrm implements ExoMediaDrm {
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/** An {@link ExoMediaDrm} implementation that wraps the framework {@link MediaDrm}. */
|
/** An {@link ExoMediaDrm} implementation that wraps the framework {@link MediaDrm}. */
|
||||||
@RequiresApi(18)
|
|
||||||
public final class FrameworkMediaDrm implements ExoMediaDrm {
|
public final class FrameworkMediaDrm implements ExoMediaDrm {
|
||||||
|
|
||||||
private static final String TAG = "FrameworkMediaDrm";
|
private static final String TAG = "FrameworkMediaDrm";
|
||||||
|
@ -22,7 +22,6 @@ import android.os.HandlerThread;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.DrmInitData;
|
import androidx.media3.common.DrmInitData;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
@ -38,7 +37,6 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
/** Helper class to download, renew and release offline licenses. */
|
/** Helper class to download, renew and release offline licenses. */
|
||||||
@RequiresApi(18)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class OfflineLicenseHelper {
|
public final class OfflineLicenseHelper {
|
||||||
|
|
||||||
|
@ -250,7 +250,6 @@ public interface MediaCodecAdapter {
|
|||||||
*
|
*
|
||||||
* @see MediaCodec#setParameters(Bundle)
|
* @see MediaCodec#setParameters(Bundle)
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
void setParameters(Bundle params);
|
void setParameters(Bundle params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,6 @@ package androidx.media3.exoplayer.mediacodec;
|
|||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.decoder.CryptoInfo;
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,7 +56,6 @@ import androidx.media3.decoder.CryptoInfo;
|
|||||||
*
|
*
|
||||||
* @see MediaCodec#setParameters(Bundle)
|
* @see MediaCodec#setParameters(Bundle)
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
void setParameters(Bundle parameters);
|
void setParameters(Bundle parameters);
|
||||||
|
|
||||||
/** Flushes the instance. */
|
/** Flushes the instance. */
|
||||||
|
@ -695,11 +695,6 @@ public final class MediaCodecInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAdaptive(CodecCapabilities capabilities) {
|
private static boolean isAdaptive(CodecCapabilities capabilities) {
|
||||||
return Util.SDK_INT >= 19 && isAdaptiveV19(capabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
private static boolean isAdaptiveV19(CodecCapabilities capabilities) {
|
|
||||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
|
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1217,8 +1217,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
|
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
|
||||||
codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName);
|
codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName);
|
||||||
codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecName);
|
codecNeedsEosBufferTimestampWorkaround = codecNeedsEosBufferTimestampWorkaround(codecName);
|
||||||
codecNeedsMonoChannelCountWorkaround =
|
codecNeedsMonoChannelCountWorkaround = false;
|
||||||
codecNeedsMonoChannelCountWorkaround(codecName, checkNotNull(codecInputFormat));
|
|
||||||
codecNeedsEosPropagation =
|
codecNeedsEosPropagation =
|
||||||
codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation();
|
codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation();
|
||||||
if (checkNotNull(codec).needsReconfiguration()) {
|
if (checkNotNull(codec).needsReconfiguration()) {
|
||||||
@ -2499,12 +2498,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
* @return True if the decoder is known to fail when flushed.
|
* @return True if the decoder is known to fail when flushed.
|
||||||
*/
|
*/
|
||||||
private static boolean codecNeedsFlushWorkaround(String name) {
|
private static boolean codecNeedsFlushWorkaround(String name) {
|
||||||
return Util.SDK_INT < 18
|
return Util.SDK_INT == 19
|
||||||
|| (Util.SDK_INT == 18
|
|
||||||
&& ("OMX.SEC.avc.dec".equals(name) || "OMX.SEC.avc.dec.secure".equals(name)))
|
|
||||||
|| (Util.SDK_INT == 19
|
|
||||||
&& Util.MODEL.startsWith("SM-G800")
|
&& Util.MODEL.startsWith("SM-G800")
|
||||||
&& ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name)));
|
&& ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2589,7 +2585,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
private static boolean codecNeedsEosPropagationWorkaround(MediaCodecInfo codecInfo) {
|
private static boolean codecNeedsEosPropagationWorkaround(MediaCodecInfo codecInfo) {
|
||||||
String name = codecInfo.name;
|
String name = codecInfo.name;
|
||||||
return (Util.SDK_INT <= 25 && "OMX.rk.video_decoder.avc".equals(name))
|
return (Util.SDK_INT <= 25 && "OMX.rk.video_decoder.avc".equals(name))
|
||||||
|| (Util.SDK_INT <= 17 && "OMX.allwinner.video.decoder.avc".equals(name))
|
|
||||||
|| (Util.SDK_INT <= 29
|
|| (Util.SDK_INT <= 29
|
||||||
&& ("OMX.broadcom.video_decoder.tunnel".equals(name)
|
&& ("OMX.broadcom.video_decoder.tunnel".equals(name)
|
||||||
|| "OMX.broadcom.video_decoder.tunnel.secure".equals(name)
|
|| "OMX.broadcom.video_decoder.tunnel.secure".equals(name)
|
||||||
@ -2615,7 +2610,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
*/
|
*/
|
||||||
private static boolean codecNeedsEosFlushWorkaround(String name) {
|
private static boolean codecNeedsEosFlushWorkaround(String name) {
|
||||||
return (Util.SDK_INT <= 23 && "OMX.google.vorbis.decoder".equals(name))
|
return (Util.SDK_INT <= 23 && "OMX.google.vorbis.decoder".equals(name))
|
||||||
|| (Util.SDK_INT <= 19
|
|| (Util.SDK_INT == 19
|
||||||
&& ("hb2000".equals(Util.DEVICE) || "stvm8".equals(Util.DEVICE))
|
&& ("hb2000".equals(Util.DEVICE) || "stvm8".equals(Util.DEVICE))
|
||||||
&& ("OMX.amlogic.avc.decoder.awesome".equals(name)
|
&& ("OMX.amlogic.avc.decoder.awesome".equals(name)
|
||||||
|| "OMX.amlogic.avc.decoder.awesome.secure".equals(name)));
|
|| "OMX.amlogic.avc.decoder.awesome.secure".equals(name)));
|
||||||
@ -2654,26 +2649,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
return Util.SDK_INT == 21 && "OMX.google.aac.decoder".equals(name);
|
return Util.SDK_INT == 21 && "OMX.google.aac.decoder".equals(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the decoder is known to set the number of audio channels in the output {@link
|
|
||||||
* Format} to 2 for the given input {@link Format}, whilst only actually outputting a single
|
|
||||||
* channel.
|
|
||||||
*
|
|
||||||
* <p>If true is returned then we explicitly override the number of channels in the output {@link
|
|
||||||
* Format}, setting it to 1.
|
|
||||||
*
|
|
||||||
* @param name The decoder name.
|
|
||||||
* @param format The input {@link Format}.
|
|
||||||
* @return True if the decoder is known to set the number of audio channels in the output {@link
|
|
||||||
* Format} to 2 for the given input {@link Format}, whilst only actually outputting a single
|
|
||||||
* channel. False otherwise.
|
|
||||||
*/
|
|
||||||
private static boolean codecNeedsMonoChannelCountWorkaround(String name, Format format) {
|
|
||||||
return Util.SDK_INT <= 18
|
|
||||||
&& format.channelCount == 1
|
|
||||||
&& "OMX.MTK.AUDIO.DECODER.MP3".equals(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class OutputStreamInfo {
|
private static final class OutputStreamInfo {
|
||||||
|
|
||||||
public static final OutputStreamInfo UNSET =
|
public static final OutputStreamInfo UNSET =
|
||||||
|
@ -543,44 +543,6 @@ public final class MediaCodecUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work around https://github.com/google/ExoPlayer/issues/1528 and
|
|
||||||
// https://github.com/google/ExoPlayer/issues/3171.
|
|
||||||
if (Util.SDK_INT < 18
|
|
||||||
&& "OMX.MTK.AUDIO.DECODER.AAC".equals(name)
|
|
||||||
&& ("a70".equals(Util.DEVICE)
|
|
||||||
|| ("Xiaomi".equals(Util.MANUFACTURER) && Util.DEVICE.startsWith("HM")))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work around an issue where querying/creating a particular MP3 decoder on some devices on
|
|
||||||
// platform API version 16 fails.
|
|
||||||
if (Util.SDK_INT == 16
|
|
||||||
&& "OMX.qcom.audio.decoder.mp3".equals(name)
|
|
||||||
&& ("dlxu".equals(Util.DEVICE) // HTC Butterfly
|
|
||||||
|| "protou".equals(Util.DEVICE) // HTC Desire X
|
|
||||||
|| "ville".equals(Util.DEVICE) // HTC One S
|
|
||||||
|| "villeplus".equals(Util.DEVICE)
|
|
||||||
|| "villec2".equals(Util.DEVICE)
|
|
||||||
|| Util.DEVICE.startsWith("gee") // LGE Optimus G
|
|
||||||
|| "C6602".equals(Util.DEVICE) // Sony Xperia Z
|
|
||||||
|| "C6603".equals(Util.DEVICE)
|
|
||||||
|| "C6606".equals(Util.DEVICE)
|
|
||||||
|| "C6616".equals(Util.DEVICE)
|
|
||||||
|| "L36h".equals(Util.DEVICE)
|
|
||||||
|| "SO-02E".equals(Util.DEVICE))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work around an issue where large timestamps are not propagated correctly.
|
|
||||||
if (Util.SDK_INT == 16
|
|
||||||
&& "OMX.qcom.audio.decoder.aac".equals(name)
|
|
||||||
&& ("C1504".equals(Util.DEVICE) // Sony Xperia E
|
|
||||||
|| "C1505".equals(Util.DEVICE)
|
|
||||||
|| "C1604".equals(Util.DEVICE) // Sony Xperia E dual
|
|
||||||
|| "C1605".equals(Util.DEVICE))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work around https://github.com/google/ExoPlayer/issues/3249.
|
// Work around https://github.com/google/ExoPlayer/issues/3249.
|
||||||
if (Util.SDK_INT < 24
|
if (Util.SDK_INT < 24
|
||||||
&& ("OMX.SEC.aac.dec".equals(name) || "OMX.Exynos.AAC.Decoder".equals(name))
|
&& ("OMX.SEC.aac.dec".equals(name) || "OMX.Exynos.AAC.Decoder".equals(name))
|
||||||
@ -598,7 +560,7 @@ public final class MediaCodecUtil {
|
|||||||
|
|
||||||
// Work around https://github.com/google/ExoPlayer/issues/548.
|
// Work around https://github.com/google/ExoPlayer/issues/548.
|
||||||
// VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video.
|
// VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video.
|
||||||
if (Util.SDK_INT <= 19
|
if (Util.SDK_INT == 19
|
||||||
&& "OMX.SEC.vp8.dec".equals(name)
|
&& "OMX.SEC.vp8.dec".equals(name)
|
||||||
&& "samsung".equals(Util.MANUFACTURER)
|
&& "samsung".equals(Util.MANUFACTURER)
|
||||||
&& (Util.DEVICE.startsWith("d2")
|
&& (Util.DEVICE.startsWith("d2")
|
||||||
@ -610,7 +572,7 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VP8 decoder on Samsung Galaxy S4 cannot be queried.
|
// VP8 decoder on Samsung Galaxy S4 cannot be queried.
|
||||||
if (Util.SDK_INT <= 19
|
if (Util.SDK_INT == 19
|
||||||
&& Util.DEVICE.startsWith("jflte")
|
&& Util.DEVICE.startsWith("jflte")
|
||||||
&& "OMX.qcom.video.decoder.vp8".equals(name)) {
|
&& "OMX.qcom.video.decoder.vp8".equals(name)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -192,7 +192,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RequiresApi(19)
|
|
||||||
public void setParameters(Bundle params) {
|
public void setParameters(Bundle params) {
|
||||||
codec.setParameters(params);
|
codec.setParameters(params);
|
||||||
}
|
}
|
||||||
|
@ -715,7 +715,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(17) // Needed for placeholderSurface usage, as it is always null on API level 16.
|
|
||||||
@Override
|
@Override
|
||||||
protected void onReset() {
|
protected void onReset() {
|
||||||
try {
|
try {
|
||||||
@ -804,7 +803,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
} else {
|
} else {
|
||||||
MediaCodecInfo codecInfo = getCodecInfo();
|
MediaCodecInfo codecInfo = getCodecInfo();
|
||||||
if (codecInfo != null && shouldUsePlaceholderSurface(codecInfo)) {
|
if (codecInfo != null && shouldUsePlaceholderSurface(codecInfo)) {
|
||||||
placeholderSurface = PlaceholderSurface.newInstanceV17(context, codecInfo.secure);
|
placeholderSurface = PlaceholderSurface.newInstance(context, codecInfo.secure);
|
||||||
displaySurface = placeholderSurface;
|
displaySurface = placeholderSurface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -866,7 +865,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
return tunneling && Util.SDK_INT < 23;
|
return tunneling && Util.SDK_INT < 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(17) // Needed for placeHolderSurface usage, as it is always null on API level 16.
|
|
||||||
@Override
|
@Override
|
||||||
protected MediaCodecAdapter.Configuration getMediaCodecConfiguration(
|
protected MediaCodecAdapter.Configuration getMediaCodecConfiguration(
|
||||||
MediaCodecInfo codecInfo,
|
MediaCodecInfo codecInfo,
|
||||||
@ -892,7 +890,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
if (placeholderSurface == null) {
|
if (placeholderSurface == null) {
|
||||||
placeholderSurface = PlaceholderSurface.newInstanceV17(context, codecInfo.secure);
|
placeholderSurface = PlaceholderSurface.newInstance(context, codecInfo.secure);
|
||||||
}
|
}
|
||||||
displaySurface = placeholderSurface;
|
displaySurface = placeholderSurface;
|
||||||
}
|
}
|
||||||
@ -1682,7 +1680,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
&& (!codecInfo.secure || PlaceholderSurface.isSecureSupported(context));
|
&& (!codecInfo.secure || PlaceholderSurface.isSecureSupported(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private void releasePlaceholderSurface() {
|
private void releasePlaceholderSurface() {
|
||||||
if (displaySurface == placeholderSurface) {
|
if (displaySurface == placeholderSurface) {
|
||||||
displaySurface = null;
|
displaySurface = null;
|
||||||
|
@ -26,7 +26,6 @@ import android.os.HandlerThread;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.EGLSurfaceTexture;
|
import androidx.media3.common.util.EGLSurfaceTexture;
|
||||||
import androidx.media3.common.util.EGLSurfaceTexture.SecureMode;
|
import androidx.media3.common.util.EGLSurfaceTexture.SecureMode;
|
||||||
@ -34,10 +33,10 @@ import androidx.media3.common.util.GlUtil;
|
|||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import com.google.errorprone.annotations.InlineMe;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** A placeholder {@link Surface}. */
|
/** A placeholder {@link Surface}. */
|
||||||
@RequiresApi(17)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class PlaceholderSurface extends Surface {
|
public final class PlaceholderSurface extends Surface {
|
||||||
|
|
||||||
@ -66,6 +65,17 @@ public final class PlaceholderSurface extends Surface {
|
|||||||
return secureMode != SECURE_MODE_NONE;
|
return secureMode != SECURE_MODE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #newInstance(Context, boolean)} instead.
|
||||||
|
*/
|
||||||
|
@InlineMe(
|
||||||
|
replacement = "PlaceholderSurface.newInstance(context, secure)",
|
||||||
|
imports = "androidx.media3.exoplayer.video.PlaceholderSurface")
|
||||||
|
@Deprecated
|
||||||
|
public static PlaceholderSurface newInstanceV17(Context context, boolean secure) {
|
||||||
|
return newInstance(context, secure);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a newly created placeholder surface. The surface must be released by calling {@link
|
* Returns a newly created placeholder surface. The surface must be released by calling {@link
|
||||||
* #release} when it's no longer required.
|
* #release} when it's no longer required.
|
||||||
@ -78,7 +88,7 @@ public final class PlaceholderSurface extends Surface {
|
|||||||
* @throws IllegalStateException If a secure surface is requested on a device for which {@link
|
* @throws IllegalStateException If a secure surface is requested on a device for which {@link
|
||||||
* #isSecureSupported(Context)} returns {@code false}.
|
* #isSecureSupported(Context)} returns {@code false}.
|
||||||
*/
|
*/
|
||||||
public static PlaceholderSurface newInstanceV17(Context context, boolean secure) {
|
public static PlaceholderSurface newInstance(Context context, boolean secure) {
|
||||||
Assertions.checkState(!secure || isSecureSupported(context));
|
Assertions.checkState(!secure || isSecureSupported(context));
|
||||||
PlaceholderSurfaceThread thread = new PlaceholderSurfaceThread();
|
PlaceholderSurfaceThread thread = new PlaceholderSurfaceThread();
|
||||||
return thread.init(secure ? secureMode : SECURE_MODE_NONE);
|
return thread.init(secure ? secureMode : SECURE_MODE_NONE);
|
||||||
|
@ -26,7 +26,6 @@ import android.view.Choreographer;
|
|||||||
import android.view.Choreographer.FrameCallback;
|
import android.view.Choreographer.FrameCallback;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.WindowManager;
|
|
||||||
import androidx.annotation.DoNotInline;
|
import androidx.annotation.DoNotInline;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
@ -161,7 +160,7 @@ public final class VideoFrameReleaseHelper {
|
|||||||
resetAdjustment();
|
resetAdjustment();
|
||||||
if (displayHelper != null) {
|
if (displayHelper != null) {
|
||||||
checkNotNull(vsyncSampler).addObserver();
|
checkNotNull(vsyncSampler).addObserver();
|
||||||
displayHelper.register(this::updateDefaultDisplayRefreshRateParams);
|
displayHelper.register();
|
||||||
}
|
}
|
||||||
updateSurfacePlaybackFrameRate(/* forceUpdate= */ false);
|
updateSurfacePlaybackFrameRate(/* forceUpdate= */ false);
|
||||||
}
|
}
|
||||||
@ -172,7 +171,7 @@ public final class VideoFrameReleaseHelper {
|
|||||||
* @param surface The new {@link Surface}, or {@code null} if the renderer does not have one.
|
* @param surface The new {@link Surface}, or {@code null} if the renderer does not have one.
|
||||||
*/
|
*/
|
||||||
public void onSurfaceChanged(@Nullable Surface surface) {
|
public void onSurfaceChanged(@Nullable Surface surface) {
|
||||||
if (Util.SDK_INT >= 17 && Api17.isPlaceholderSurface(surface)) {
|
if (surface instanceof PlaceholderSurface) {
|
||||||
// We don't care about dummy surfaces for release timing, since they're not visible.
|
// We don't care about dummy surfaces for release timing, since they're not visible.
|
||||||
surface = null;
|
surface = null;
|
||||||
}
|
}
|
||||||
@ -418,18 +417,13 @@ public final class VideoFrameReleaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static DisplayHelper maybeBuildDisplayHelper(@Nullable Context context) {
|
private DisplayHelper maybeBuildDisplayHelper(@Nullable Context context) {
|
||||||
@Nullable DisplayHelper displayHelper = null;
|
if (context == null) {
|
||||||
if (context != null) {
|
return null;
|
||||||
context = context.getApplicationContext();
|
|
||||||
if (Util.SDK_INT >= 17) {
|
|
||||||
displayHelper = DisplayHelperV17.maybeBuildNewInstance(context);
|
|
||||||
}
|
}
|
||||||
if (displayHelper == null) {
|
DisplayManager displayManager =
|
||||||
displayHelper = DisplayHelperV16.maybeBuildNewInstance(context);
|
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||||
}
|
return displayManager != null ? new DisplayHelper(displayManager) : null;
|
||||||
}
|
|
||||||
return displayHelper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested classes.
|
// Nested classes.
|
||||||
@ -450,92 +444,27 @@ public final class VideoFrameReleaseHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper for listening to changes to the default display. */
|
private final class DisplayHelper implements DisplayManager.DisplayListener {
|
||||||
private interface DisplayHelper {
|
|
||||||
|
|
||||||
/** Listener for changes to the default display. */
|
|
||||||
interface Listener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the default display changes.
|
|
||||||
*
|
|
||||||
* @param defaultDisplay The default display, or {@code null} if a corresponding {@link
|
|
||||||
* Display} object could not be obtained.
|
|
||||||
*/
|
|
||||||
void onDefaultDisplayChanged(@Nullable Display defaultDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables the helper, invoking {@link Listener#onDefaultDisplayChanged(Display)} to pass the
|
|
||||||
* initial default display.
|
|
||||||
*/
|
|
||||||
void register(Listener listener);
|
|
||||||
|
|
||||||
/** Disables the helper. */
|
|
||||||
void unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class DisplayHelperV16 implements DisplayHelper {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static DisplayHelper maybeBuildNewInstance(Context context) {
|
|
||||||
WindowManager windowManager =
|
|
||||||
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
||||||
return windowManager != null ? new DisplayHelperV16(windowManager) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final WindowManager windowManager;
|
|
||||||
|
|
||||||
private DisplayHelperV16(WindowManager windowManager) {
|
|
||||||
this.windowManager = windowManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void register(Listener listener) {
|
|
||||||
listener.onDefaultDisplayChanged(windowManager.getDefaultDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregister() {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private static final class DisplayHelperV17
|
|
||||||
implements DisplayHelper, DisplayManager.DisplayListener {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static DisplayHelper maybeBuildNewInstance(Context context) {
|
|
||||||
DisplayManager displayManager =
|
|
||||||
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
|
||||||
return displayManager != null ? new DisplayHelperV17(displayManager) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DisplayManager displayManager;
|
private final DisplayManager displayManager;
|
||||||
@Nullable private Listener listener;
|
|
||||||
|
|
||||||
private DisplayHelperV17(DisplayManager displayManager) {
|
public DisplayHelper(DisplayManager displayManager) {
|
||||||
this.displayManager = displayManager;
|
this.displayManager = displayManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void register() {
|
||||||
public void register(Listener listener) {
|
|
||||||
this.listener = listener;
|
|
||||||
displayManager.registerDisplayListener(this, Util.createHandlerForCurrentLooper());
|
displayManager.registerDisplayListener(this, Util.createHandlerForCurrentLooper());
|
||||||
listener.onDefaultDisplayChanged(getDefaultDisplay());
|
updateDefaultDisplayRefreshRateParams(getDefaultDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregister() {
|
public void unregister() {
|
||||||
displayManager.unregisterDisplayListener(this);
|
displayManager.unregisterDisplayListener(this);
|
||||||
listener = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayChanged(int displayId) {
|
public void onDisplayChanged(int displayId) {
|
||||||
if (listener != null && displayId == Display.DEFAULT_DISPLAY) {
|
if (displayId == Display.DEFAULT_DISPLAY) {
|
||||||
listener.onDefaultDisplayChanged(getDefaultDisplay());
|
updateDefaultDisplayRefreshRateParams(getDefaultDisplay());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,13 +582,4 @@ public final class VideoFrameReleaseHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private static final class Api17 {
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isPlaceholderSurface(@Nullable Surface surface) {
|
|
||||||
return surface instanceof PlaceholderSurface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import javax.microedition.khronos.egl.EGLConfig;
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
@ -103,14 +102,12 @@ public final class SphericalGLSurfaceView extends GLSurfaceView {
|
|||||||
// Configure sensors and touch.
|
// Configure sensors and touch.
|
||||||
sensorManager =
|
sensorManager =
|
||||||
(SensorManager) Assertions.checkNotNull(context.getSystemService(Context.SENSOR_SERVICE));
|
(SensorManager) Assertions.checkNotNull(context.getSystemService(Context.SENSOR_SERVICE));
|
||||||
@Nullable Sensor orientationSensor = null;
|
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
// TYPE_GAME_ROTATION_VECTOR is the easiest sensor since it handles all the complex math for
|
// TYPE_GAME_ROTATION_VECTOR is the easiest sensor since it handles all the complex math for
|
||||||
// fusion. It's used instead of TYPE_ROTATION_VECTOR since the latter uses the magnetometer on
|
// fusion. It's used instead of TYPE_ROTATION_VECTOR since the latter uses the magnetometer on
|
||||||
// devices. When used indoors, the magnetometer can take some time to settle depending on the
|
// devices. When used indoors, the magnetometer can take some time to settle depending on the
|
||||||
// device and amount of metal in the environment.
|
// device and amount of metal in the environment.
|
||||||
orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
|
@Nullable
|
||||||
}
|
Sensor orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
|
||||||
if (orientationSensor == null) {
|
if (orientationSensor == null) {
|
||||||
orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
orientationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import androidx.core.app.BundleCompat;
|
|||||||
import androidx.media3.common.Bundleable;
|
import androidx.media3.common.Bundleable;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.util.BundleCollectionUtil;
|
import androidx.media3.common.util.BundleCollectionUtil;
|
||||||
import androidx.media3.common.util.BundleUtil;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -139,7 +138,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public Bundle toBundleInProcess() {
|
public Bundle toBundleInProcess() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
BundleUtil.putBinder(bundle, FIELD_IN_PROCESS_BINDER, new InProcessBinder());
|
bundle.putBinder(FIELD_IN_PROCESS_BINDER, new InProcessBinder());
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +151,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/** Restores a {@code ConnectionState} from a {@link Bundle}. */
|
/** Restores a {@code ConnectionState} from a {@link Bundle}. */
|
||||||
public static ConnectionState fromBundle(Bundle bundle) {
|
public static ConnectionState fromBundle(Bundle bundle) {
|
||||||
@Nullable IBinder inProcessBinder = BundleUtil.getBinder(bundle, FIELD_IN_PROCESS_BINDER);
|
@Nullable IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
|
||||||
if (inProcessBinder instanceof InProcessBinder) {
|
if (inProcessBinder instanceof InProcessBinder) {
|
||||||
return ((InProcessBinder) inProcessBinder).getConnectionState();
|
return ((InProcessBinder) inProcessBinder).getConnectionState();
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,6 @@ import androidx.media3.common.Tracks;
|
|||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.text.CueGroup;
|
import androidx.media3.common.text.CueGroup;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.BundleUtil;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
@ -901,7 +900,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||||||
*/
|
*/
|
||||||
public Bundle toBundleInProcess() {
|
public Bundle toBundleInProcess() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
BundleUtil.putBinder(bundle, FIELD_IN_PROCESS_BINDER, new InProcessBinder());
|
bundle.putBinder(FIELD_IN_PROCESS_BINDER, new InProcessBinder());
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,7 +1024,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||||||
|
|
||||||
/** Restores a {@code PlayerInfo} from a {@link Bundle}. */
|
/** Restores a {@code PlayerInfo} from a {@link Bundle}. */
|
||||||
public static PlayerInfo fromBundle(Bundle bundle) {
|
public static PlayerInfo fromBundle(Bundle bundle) {
|
||||||
@Nullable IBinder inProcessBinder = BundleUtil.getBinder(bundle, FIELD_IN_PROCESS_BINDER);
|
@Nullable IBinder inProcessBinder = bundle.getBinder(FIELD_IN_PROCESS_BINDER);
|
||||||
if (inProcessBinder instanceof InProcessBinder) {
|
if (inProcessBinder instanceof InProcessBinder) {
|
||||||
return ((InProcessBinder) inProcessBinder).getPlayerInfo();
|
return ((InProcessBinder) inProcessBinder).getPlayerInfo();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import android.media.MediaDrm;
|
|||||||
import android.media.UnsupportedSchemeException;
|
import android.media.UnsupportedSchemeException;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.Size;
|
import androidx.annotation.Size;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
@ -106,23 +105,18 @@ import java.util.List;
|
|||||||
|
|
||||||
@SuppressWarnings("ResourceType")
|
@SuppressWarnings("ResourceType")
|
||||||
public static boolean isL1WidevineAvailable(String mimeType) {
|
public static boolean isL1WidevineAvailable(String mimeType) {
|
||||||
if (Util.SDK_INT >= 18) {
|
try (MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID)) {
|
||||||
try {
|
|
||||||
// Force L3 if secure decoder is not available.
|
// Force L3 if secure decoder is not available.
|
||||||
if (MediaCodecUtil.getDecoderInfo(mimeType, /* secure= */ true, /* tunneling= */ false)
|
if (MediaCodecUtil.getDecoderInfo(mimeType, /* secure= */ true, /* tunneling= */ false)
|
||||||
== null) {
|
== null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MediaDrm mediaDrm = MediaDrmBuilder.build();
|
|
||||||
String securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
|
String securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
|
||||||
mediaDrm.release();
|
|
||||||
return WIDEVINE_SECURITY_LEVEL_1.equals(securityProperty);
|
return WIDEVINE_SECURITY_LEVEL_1.equals(securityProperty);
|
||||||
} catch (MediaCodecUtil.DecoderQueryException e) {
|
} catch (UnsupportedSchemeException | MediaCodecUtil.DecoderQueryException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DashTestRunner(@Size(max = 23) String tag, HostActivity activity) {
|
public DashTestRunner(@Size(max = 23) String tag, HostActivity activity) {
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
@ -507,20 +501,4 @@ import java.util.List;
|
|||||||
return RendererCapabilities.getFormatSupport(formatSupport) == C.FORMAT_HANDLED;
|
return RendererCapabilities.getFormatSupport(formatSupport) == C.FORMAT_HANDLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@code MediaDrm} object. The encapsulation ensures that the tests can be executed
|
|
||||||
* for API level < 18.
|
|
||||||
*/
|
|
||||||
@RequiresApi(18)
|
|
||||||
private static final class MediaDrmBuilder {
|
|
||||||
|
|
||||||
public static MediaDrm build() {
|
|
||||||
try {
|
|
||||||
return new MediaDrm(WIDEVINE_UUID);
|
|
||||||
} catch (UnsupportedSchemeException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -135,8 +135,7 @@ public class EnumerateDecodersTest {
|
|||||||
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
|
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 19
|
if (isVideo
|
||||||
&& isVideo
|
|
||||||
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
|
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
|
||||||
result.append(", FEATURE_AdaptivePlayback");
|
result.append(", FEATURE_AdaptivePlayback");
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import static androidx.media3.common.C.WIDEVINE_UUID;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.media.MediaDrm;
|
import android.media.MediaDrm;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
|
|
||||||
/** Utility methods for GTS tests. */
|
/** Utility methods for GTS tests. */
|
||||||
public final class GtsTestUtil {
|
public final class GtsTestUtil {
|
||||||
@ -30,10 +29,6 @@ public final class GtsTestUtil {
|
|||||||
|
|
||||||
/** Returns true if the device doesn't support Widevine and this is permitted. */
|
/** Returns true if the device doesn't support Widevine and this is permitted. */
|
||||||
public static boolean shouldSkipWidevineTest(Context context) {
|
public static boolean shouldSkipWidevineTest(Context context) {
|
||||||
if (Util.SDK_INT < 18) {
|
|
||||||
// MediaDrm isn't present until API 18
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (isGmsInstalled(context)) {
|
if (isGmsInstalled(context)) {
|
||||||
// GMS devices are required to support Widevine.
|
// GMS devices are required to support Widevine.
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package androidx.media3.test.session.common;
|
package androidx.media3.test.session.common;
|
||||||
|
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
|
|
||||||
/** TestRule for providing a handler and an executor for {@link HandlerThread}. */
|
/** TestRule for providing a handler and an executor for {@link HandlerThread}. */
|
||||||
@ -40,11 +39,7 @@ public final class HandlerThreadTestRule extends ExternalResource {
|
|||||||
@Override
|
@Override
|
||||||
protected void after() {
|
protected void after() {
|
||||||
try {
|
try {
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
handler.getLooper().quitSafely();
|
handler.getLooper().quitSafely();
|
||||||
} else {
|
|
||||||
handler.getLooper().quit();
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ import androidx.media3.common.TrackSelectionOverride;
|
|||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.Tracks;
|
import androidx.media3.common.Tracks;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.test.session.R;
|
import androidx.media3.test.session.R;
|
||||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
import androidx.media3.test.session.common.MainLooperTestRule;
|
||||||
@ -163,13 +162,10 @@ public class MediaControllerTest {
|
|||||||
MediaController controller = controllerTestRule.createController(session.getToken());
|
MediaController controller = controllerTestRule.createController(session.getToken());
|
||||||
PendingIntent sessionActivity = controller.getSessionActivity();
|
PendingIntent sessionActivity = controller.getSessionActivity();
|
||||||
assertThat(sessionActivity).isNotNull();
|
assertThat(sessionActivity).isNotNull();
|
||||||
if (Util.SDK_INT >= 17) {
|
|
||||||
// PendingIntent#getCreatorPackage() is added in API 17.
|
|
||||||
assertThat(sessionActivity.getCreatorPackage()).isEqualTo(SUPPORT_APP_PACKAGE_NAME);
|
assertThat(sessionActivity.getCreatorPackage()).isEqualTo(SUPPORT_APP_PACKAGE_NAME);
|
||||||
|
|
||||||
// TODO: Add getPid/getUid in MediaControllerProviderService and compare them.
|
// TODO: Add getPid/getUid in MediaControllerProviderService and compare them.
|
||||||
// assertThat(sessionActivity.getCreatorUid()).isEqualTo(remoteSession.getUid());
|
// assertThat(sessionActivity.getCreatorUid()).isEqualTo(remoteSession.getUid());
|
||||||
}
|
|
||||||
session.cleanUp();
|
session.cleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,11 +71,7 @@ public class MediaControllerWithFrameworkMediaSessionTest {
|
|||||||
@After
|
@After
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
handler.getLooper().quitSafely();
|
handler.getLooper().quitSafely();
|
||||||
} else {
|
|
||||||
handler.getLooper().quit();
|
|
||||||
}
|
|
||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,11 +371,8 @@ public class MediaControllerWithMediaSessionCompatTest {
|
|||||||
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
MediaController controller = controllerTestRule.createController(session.getSessionToken());
|
||||||
PendingIntent sessionActivityOut = controller.getSessionActivity();
|
PendingIntent sessionActivityOut = controller.getSessionActivity();
|
||||||
assertThat(sessionActivityOut).isNotNull();
|
assertThat(sessionActivityOut).isNotNull();
|
||||||
if (Util.SDK_INT >= 17) {
|
|
||||||
// PendingIntent#getCreatorPackage() is added in API 17.
|
|
||||||
assertThat(sessionActivityOut.getCreatorPackage()).isEqualTo(context.getPackageName());
|
assertThat(sessionActivityOut.getCreatorPackage()).isEqualTo(context.getPackageName());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setRepeatMode_updatesAndNotifiesRepeatMode() throws Exception {
|
public void setRepeatMode_updatesAndNotifiesRepeatMode() throws Exception {
|
||||||
|
@ -379,11 +379,7 @@ public class MediaSessionTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
testThread.quitSafely();
|
testThread.quitSafely();
|
||||||
} else {
|
|
||||||
testThread.quit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,11 +186,7 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
expectedParams = null;
|
expectedParams = null;
|
||||||
}
|
}
|
||||||
TestServiceRegistry.getInstance().cleanUp();
|
TestServiceRegistry.getInstance().cleanUp();
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
handlerThread.quitSafely();
|
handlerThread.quitSafely();
|
||||||
} else {
|
|
||||||
handlerThread.quit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,7 +23,6 @@ import android.os.IBinder;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -91,11 +90,7 @@ public class MockMediaSessionService extends MediaSessionService {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
TestServiceRegistry.getInstance().cleanUp();
|
TestServiceRegistry.getInstance().cleanUp();
|
||||||
if (Util.SDK_INT >= 18) {
|
|
||||||
handlerThread.quitSafely();
|
handlerThread.quitSafely();
|
||||||
} else {
|
|
||||||
handlerThread.quit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package androidx.media3.test.utils;
|
package androidx.media3.test.utils;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.NullableType;
|
import androidx.media3.common.util.NullableType;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -39,7 +38,6 @@ import org.junit.runners.model.Statement;
|
|||||||
* <p>Includes special handling for {@link AssertionError} to ensure that test failures are
|
* <p>Includes special handling for {@link AssertionError} to ensure that test failures are
|
||||||
* correctly distinguished from test errors (all other errors/exceptions).
|
* correctly distinguished from test errors (all other errors/exceptions).
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class AdditionalFailureInfo implements TestRule {
|
public final class AdditionalFailureInfo implements TestRule {
|
||||||
|
|
||||||
|
@ -145,7 +145,6 @@ public class BitmapPixelTestUtil {
|
|||||||
* @return A {@link Bitmap}.
|
* @return A {@link Bitmap}.
|
||||||
* @throws IOException If the bitmap can't be read.
|
* @throws IOException If the bitmap can't be read.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19) // BitmapFactory.Options#inPremultiplied.
|
|
||||||
public static Bitmap readBitmapUnpremultipliedAlpha(String assetString) throws IOException {
|
public static Bitmap readBitmapUnpremultipliedAlpha(String assetString) throws IOException {
|
||||||
Bitmap bitmap;
|
Bitmap bitmap;
|
||||||
try (InputStream inputStream = getApplicationContext().getAssets().open(assetString)) {
|
try (InputStream inputStream = getApplicationContext().getAssets().open(assetString)) {
|
||||||
@ -161,7 +160,6 @@ public class BitmapPixelTestUtil {
|
|||||||
* Returns a bitmap with the same information as the provided alpha/red/green/blue 8-bits per
|
* Returns a bitmap with the same information as the provided alpha/red/green/blue 8-bits per
|
||||||
* component image.
|
* component image.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
public static Bitmap createArgb8888BitmapFromRgba8888Image(Image image) {
|
public static Bitmap createArgb8888BitmapFromRgba8888Image(Image image) {
|
||||||
int width = image.getWidth();
|
int width = image.getWidth();
|
||||||
int height = image.getHeight();
|
int height = image.getHeight();
|
||||||
@ -186,7 +184,6 @@ public class BitmapPixelTestUtil {
|
|||||||
/**
|
/**
|
||||||
* Returns a grayscale bitmap from the Luma channel in the {@link ImageFormat#YUV_420_888} image.
|
* Returns a grayscale bitmap from the Luma channel in the {@link ImageFormat#YUV_420_888} image.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
public static Bitmap createGrayscaleArgb8888BitmapFromYuv420888Image(Image image) {
|
public static Bitmap createGrayscaleArgb8888BitmapFromYuv420888Image(Image image) {
|
||||||
int width = image.getWidth();
|
int width = image.getWidth();
|
||||||
int height = image.getHeight();
|
int height = image.getHeight();
|
||||||
@ -423,7 +420,6 @@ public class BitmapPixelTestUtil {
|
|||||||
// createUnpremultipliedArgb8888BitmapFromFocusedGlFramebuffer back to
|
// createUnpremultipliedArgb8888BitmapFromFocusedGlFramebuffer back to
|
||||||
// createArgb8888BitmapFromFocusedGlFramebuffer. Also, apply
|
// createArgb8888BitmapFromFocusedGlFramebuffer. Also, apply
|
||||||
// setPremultiplied(false) to createBitmapFromFocusedGlFrameBuffer.
|
// setPremultiplied(false) to createBitmapFromFocusedGlFrameBuffer.
|
||||||
@RequiresApi(17) // #flipBitmapVertically.
|
|
||||||
public static Bitmap createArgb8888BitmapFromFocusedGlFramebuffer(int width, int height)
|
public static Bitmap createArgb8888BitmapFromFocusedGlFramebuffer(int width, int height)
|
||||||
throws GlUtil.GlException {
|
throws GlUtil.GlException {
|
||||||
return createBitmapFromFocusedGlFrameBuffer(
|
return createBitmapFromFocusedGlFrameBuffer(
|
||||||
@ -440,7 +436,6 @@ public class BitmapPixelTestUtil {
|
|||||||
* @param height The height of the pixel rectangle to read.
|
* @param height The height of the pixel rectangle to read.
|
||||||
* @return A {@link Bitmap} with the framebuffer's values.
|
* @return A {@link Bitmap} with the framebuffer's values.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19) // Bitmap#setPremultiplied.
|
|
||||||
public static Bitmap createUnpremultipliedArgb8888BitmapFromFocusedGlFramebuffer(
|
public static Bitmap createUnpremultipliedArgb8888BitmapFromFocusedGlFramebuffer(
|
||||||
int width, int height) throws GlUtil.GlException {
|
int width, int height) throws GlUtil.GlException {
|
||||||
Bitmap bitmap =
|
Bitmap bitmap =
|
||||||
@ -471,7 +466,6 @@ public class BitmapPixelTestUtil {
|
|||||||
width, height, /* pixelSize= */ 8, GLES30.GL_HALF_FLOAT, Bitmap.Config.RGBA_F16);
|
width, height, /* pixelSize= */ 8, GLES30.GL_HALF_FLOAT, Bitmap.Config.RGBA_F16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17) // #flipBitmapVertically.
|
|
||||||
private static Bitmap createBitmapFromFocusedGlFrameBuffer(
|
private static Bitmap createBitmapFromFocusedGlFrameBuffer(
|
||||||
int width, int height, int pixelSize, int glReadPixelsFormat, Bitmap.Config bitmapConfig)
|
int width, int height, int pixelSize, int glReadPixelsFormat, Bitmap.Config bitmapConfig)
|
||||||
throws GlUtil.GlException {
|
throws GlUtil.GlException {
|
||||||
@ -494,14 +488,12 @@ public class BitmapPixelTestUtil {
|
|||||||
* @param bitmap A {@link Bitmap}.
|
* @param bitmap A {@link Bitmap}.
|
||||||
* @return The identifier of the newly created texture.
|
* @return The identifier of the newly created texture.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17) // #flipBitmapVertically.
|
|
||||||
public static int createGlTextureFromBitmap(Bitmap bitmap) throws GlUtil.GlException {
|
public static int createGlTextureFromBitmap(Bitmap bitmap) throws GlUtil.GlException {
|
||||||
// Put the flipped bitmap in the OpenGL texture as the bitmap's positive y-axis points down
|
// Put the flipped bitmap in the OpenGL texture as the bitmap's positive y-axis points down
|
||||||
// while OpenGL's positive y-axis points up.
|
// while OpenGL's positive y-axis points up.
|
||||||
return GlUtil.createTexture(flipBitmapVertically(bitmap));
|
return GlUtil.createTexture(flipBitmapVertically(bitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17) // Bitmap#isPremultiplied.
|
|
||||||
public static Bitmap flipBitmapVertically(Bitmap bitmap) {
|
public static Bitmap flipBitmapVertically(Bitmap bitmap) {
|
||||||
boolean wasPremultiplied = bitmap.isPremultiplied();
|
boolean wasPremultiplied = bitmap.isPremultiplied();
|
||||||
if (!wasPremultiplied) {
|
if (!wasPremultiplied) {
|
||||||
|
@ -174,7 +174,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
constructedAdapters = new ArrayList<>();
|
constructedAdapters = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(18)
|
|
||||||
@Override
|
@Override
|
||||||
public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException {
|
public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException {
|
||||||
CapturingMediaCodecAdapter adapter =
|
CapturingMediaCodecAdapter adapter =
|
||||||
@ -343,7 +342,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
delegate.setOutputSurface(surface);
|
delegate.setOutputSurface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
@Override
|
@Override
|
||||||
public void setParameters(Bundle params) {
|
public void setParameters(Bundle params) {
|
||||||
delegate.setParameters(params);
|
delegate.setParameters(params);
|
||||||
|
@ -31,7 +31,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -68,7 +67,6 @@ import org.mockito.Mockito;
|
|||||||
* not required) to also annotate this {@link Ignore @Ignore} so that JUnit correctly reports the
|
* not required) to also annotate this {@link Ignore @Ignore} so that JUnit correctly reports the
|
||||||
* test as skipped/ignored instead of passing.
|
* test as skipped/ignored instead of passing.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public abstract class DataSourceContractTest {
|
public abstract class DataSourceContractTest {
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ package androidx.media3.test.utils;
|
|||||||
import static androidx.media3.test.utils.WebServerDispatcher.getRequestPath;
|
import static androidx.media3.test.utils.WebServerDispatcher.getRequestPath;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
import androidx.media3.datasource.HttpDataSource;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -32,7 +31,6 @@ import org.junit.Rule;
|
|||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
|
|
||||||
/** A JUnit {@link Rule} that creates test resources for {@link HttpDataSource} contract tests. */
|
/** A JUnit {@link Rule} that creates test resources for {@link HttpDataSource} contract tests. */
|
||||||
@RequiresApi(19)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class HttpDataSourceTestEnv extends ExternalResource {
|
public class HttpDataSourceTestEnv extends ExternalResource {
|
||||||
private static int seed = 0;
|
private static int seed = 0;
|
||||||
|
@ -23,7 +23,6 @@ import android.graphics.Bitmap;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.GlTextureInfo;
|
import androidx.media3.common.GlTextureInfo;
|
||||||
import androidx.media3.common.VideoFrameProcessingException;
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
@ -93,7 +92,6 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
|||||||
// createArgb8888BitmapFromFocusedGlFramebuffer, so that TextureBitmapReader always reads bitmaps
|
// createArgb8888BitmapFromFocusedGlFramebuffer, so that TextureBitmapReader always reads bitmaps
|
||||||
// as unpremultiplied alpha. Then, remove this method (as we'll already be using premultiplied
|
// as unpremultiplied alpha. Then, remove this method (as we'll already be using premultiplied
|
||||||
// alpha).
|
// alpha).
|
||||||
@RequiresApi(17) // BitmapPixelTestUtil#createArgb8888BitmapFromFocusedGlFramebuffer.
|
|
||||||
public void readBitmap(GlTextureInfo outputTexture, long presentationTimeUs)
|
public void readBitmap(GlTextureInfo outputTexture, long presentationTimeUs)
|
||||||
throws VideoFrameProcessingException {
|
throws VideoFrameProcessingException {
|
||||||
try {
|
try {
|
||||||
@ -114,7 +112,6 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
|||||||
* <p>The read result can be fetched by calling {@link #getBitmapAtPresentationTimeUs} or {@link
|
* <p>The read result can be fetched by calling {@link #getBitmapAtPresentationTimeUs} or {@link
|
||||||
* #getBitmap}.
|
* #getBitmap}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19) // BitmapPixelTestUtil#createArgb8888BitmapFromFocusedGlFramebuffer.
|
|
||||||
public void readBitmapUnpremultipliedAlpha(GlTextureInfo outputTexture, long presentationTimeUs)
|
public void readBitmapUnpremultipliedAlpha(GlTextureInfo outputTexture, long presentationTimeUs)
|
||||||
throws VideoFrameProcessingException {
|
throws VideoFrameProcessingException {
|
||||||
checkState(!useHighPrecisionColorComponents);
|
checkState(!useHighPrecisionColorComponents);
|
||||||
@ -130,7 +127,6 @@ public final class TextureBitmapReader implements VideoFrameProcessorTestRunner.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17) // BitmapPixelTestUtil#createArgb8888BitmapFromFocusedGlFramebuffer.
|
|
||||||
private static Bitmap createBitmapFromCurrentGlFrameBuffer(
|
private static Bitmap createBitmapFromCurrentGlFrameBuffer(
|
||||||
int width, int height, boolean useHighPrecisionColorComponents) throws GlUtil.GlException {
|
int width, int height, boolean useHighPrecisionColorComponents) throws GlUtil.GlException {
|
||||||
if (!useHighPrecisionColorComponents) {
|
if (!useHighPrecisionColorComponents) {
|
||||||
|
@ -35,7 +35,6 @@ import android.media.MediaFormat;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.ColorInfo;
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.common.DebugViewProvider;
|
import androidx.media3.common.DebugViewProvider;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
@ -61,7 +60,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/** A test runner for {@link VideoFrameProcessor} tests. */
|
/** A test runner for {@link VideoFrameProcessor} tests. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(19)
|
|
||||||
public final class VideoFrameProcessorTestRunner {
|
public final class VideoFrameProcessorTestRunner {
|
||||||
|
|
||||||
/** A builder for {@link VideoFrameProcessorTestRunner} instances. */
|
/** A builder for {@link VideoFrameProcessorTestRunner} instances. */
|
||||||
|
@ -77,8 +77,8 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void before() throws Throwable {
|
protected void before() throws Throwable {
|
||||||
if (Util.SDK_INT <= 19) {
|
if (Util.SDK_INT == 19) {
|
||||||
// Codec config not supported with Robolectric on API <= 19. Skip rule set up step.
|
// Codec config not supported with Robolectric on API == 19. Skip rule set up step.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
configureCodecs(supportedMimeTypes);
|
configureCodecs(supportedMimeTypes);
|
||||||
@ -88,8 +88,8 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
protected void after() {
|
protected void after() {
|
||||||
supportedMimeTypes.clear();
|
supportedMimeTypes.clear();
|
||||||
MediaCodecUtil.clearDecoderInfoCache();
|
MediaCodecUtil.clearDecoderInfoCache();
|
||||||
if (Util.SDK_INT <= 19) {
|
if (Util.SDK_INT == 19) {
|
||||||
// Codec config not supported with Robolectric on API <= 19. Skip rule tear down step.
|
// Codec config not supported with Robolectric on API == 19. Skip rule tear down step.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShadowMediaCodecList.reset();
|
ShadowMediaCodecList.reset();
|
||||||
|
@ -114,15 +114,18 @@ public final class CaptionStyleCompat {
|
|||||||
* @param captionStyle A {@link CaptionStyle}.
|
* @param captionStyle A {@link CaptionStyle}.
|
||||||
* @return The equivalent {@link CaptionStyleCompat}.
|
* @return The equivalent {@link CaptionStyleCompat}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(19)
|
|
||||||
public static CaptionStyleCompat createFromCaptionStyle(
|
public static CaptionStyleCompat createFromCaptionStyle(
|
||||||
CaptioningManager.CaptionStyle captionStyle) {
|
CaptioningManager.CaptionStyle captionStyle) {
|
||||||
if (Util.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
return createFromCaptionStyleV21(captionStyle);
|
return createFromCaptionStyleV21(captionStyle);
|
||||||
} else {
|
} else {
|
||||||
// Note - Any caller must be on at least API level 19 or greater (because CaptionStyle did
|
return new CaptionStyleCompat(
|
||||||
// not exist in earlier API levels).
|
captionStyle.foregroundColor,
|
||||||
return createFromCaptionStyleV19(captionStyle);
|
captionStyle.backgroundColor,
|
||||||
|
Color.TRANSPARENT,
|
||||||
|
captionStyle.edgeType,
|
||||||
|
captionStyle.edgeColor,
|
||||||
|
captionStyle.getTypeface());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,19 +152,6 @@ public final class CaptionStyleCompat {
|
|||||||
this.typeface = typeface;
|
this.typeface = typeface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
@SuppressWarnings("ResourceType")
|
|
||||||
private static CaptionStyleCompat createFromCaptionStyleV19(
|
|
||||||
CaptioningManager.CaptionStyle captionStyle) {
|
|
||||||
return new CaptionStyleCompat(
|
|
||||||
captionStyle.foregroundColor,
|
|
||||||
captionStyle.backgroundColor,
|
|
||||||
Color.TRANSPARENT,
|
|
||||||
captionStyle.edgeType,
|
|
||||||
captionStyle.edgeColor,
|
|
||||||
captionStyle.getTypeface());
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
@SuppressWarnings("ResourceType")
|
@SuppressWarnings("ResourceType")
|
||||||
private static CaptionStyleCompat createFromCaptionStyleV21(
|
private static CaptionStyleCompat createFromCaptionStyleV21(
|
||||||
|
@ -33,7 +33,6 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
@ -319,7 +318,7 @@ public final class SubtitleView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private float getUserCaptionFontScale() {
|
private float getUserCaptionFontScale() {
|
||||||
if (Util.SDK_INT < 19 || isInEditMode()) {
|
if (isInEditMode()) {
|
||||||
return 1f;
|
return 1f;
|
||||||
}
|
}
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -331,7 +330,7 @@ public final class SubtitleView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CaptionStyleCompat getUserCaptionStyle() {
|
private CaptionStyleCompat getUserCaptionStyle() {
|
||||||
if (Util.SDK_INT < 19 || isInEditMode()) {
|
if (isInEditMode()) {
|
||||||
return CaptionStyleCompat.DEFAULT;
|
return CaptionStyleCompat.DEFAULT;
|
||||||
}
|
}
|
||||||
@Nullable
|
@Nullable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user