Bump minSdk to 21 and remove resulting simple dead code
All other AndroidX libraries have already increased their min SDK to 21. This change renames private symbols to remove `V21` suffixes and similar, but doesn't change public or protected symbols with similar names, to avoid needless breakages/churn on users of the library. Some of the dead code removal is more complex, so I've split it out into follow-up changes to make it easier to review. PiperOrigin-RevId: 651776556
This commit is contained in:
parent
5fa9985ce6
commit
6a9ff95bf0
@ -11,6 +11,8 @@
|
|||||||
* Add override for `SimpleBasePlayer.State.Builder.setPlaylist()` to
|
* Add override for `SimpleBasePlayer.State.Builder.setPlaylist()` to
|
||||||
directly specify a `Timeline` and current `Tracks` and `Metadata`
|
directly specify a `Timeline` and current `Tracks` and `Metadata`
|
||||||
instead of building a playlist structure.
|
instead of building a playlist structure.
|
||||||
|
* Increase `minSdk` to 21 (Android Lollipop). This is aligned with all
|
||||||
|
other AndroidX libraries.
|
||||||
* ExoPlayer:
|
* ExoPlayer:
|
||||||
* `MediaCodecRenderer.onProcessedStreamChange()` can now be called for
|
* `MediaCodecRenderer.onProcessedStreamChange()` can now be called for
|
||||||
every media item. Previously it was not called for the first one. Use
|
every media item. Previously it was not called for the first one. Use
|
||||||
|
4
api.txt
4
api.txt
@ -26,7 +26,7 @@ package androidx.media3.common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class AudioAttributes {
|
public final class AudioAttributes {
|
||||||
method @RequiresApi(21) public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21();
|
method public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21();
|
||||||
field public static final androidx.media3.common.AudioAttributes DEFAULT;
|
field public static final androidx.media3.common.AudioAttributes DEFAULT;
|
||||||
field @androidx.media3.common.C.AudioAllowedCapturePolicy public final int allowedCapturePolicy;
|
field @androidx.media3.common.C.AudioAllowedCapturePolicy public final int allowedCapturePolicy;
|
||||||
field @androidx.media3.common.C.AudioContentType public final int contentType;
|
field @androidx.media3.common.C.AudioContentType public final int contentType;
|
||||||
@ -35,7 +35,7 @@ package androidx.media3.common {
|
|||||||
field @androidx.media3.common.C.AudioUsage public final int usage;
|
field @androidx.media3.common.C.AudioUsage public final int usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21) public static final class AudioAttributes.AudioAttributesV21 {
|
public static final class AudioAttributes.AudioAttributesV21 {
|
||||||
field public final android.media.AudioAttributes audioAttributes;
|
field public final android.media.AudioAttributes audioAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
project.ext {
|
project.ext {
|
||||||
releaseVersion = '1.4.0-rc01'
|
releaseVersion = '1.4.0-rc01'
|
||||||
releaseVersionCode = 1_004_000_2_01
|
releaseVersionCode = 1_004_000_2_01
|
||||||
minSdkVersion = 19
|
minSdkVersion = 21
|
||||||
// See https://developer.android.com/training/cars/media/automotive-os#automotive-module
|
// See https://developer.android.com/training/cars/media/automotive-os#automotive-module
|
||||||
automotiveMinSdkVersion = 28
|
automotiveMinSdkVersion = 28
|
||||||
appTargetSdkVersion = 34
|
appTargetSdkVersion = 34
|
||||||
|
@ -63,7 +63,7 @@ public class DemoDownloadService extends DownloadService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Scheduler getScheduler() {
|
protected Scheduler getScheduler() {
|
||||||
return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null;
|
return new PlatformScheduler(this, JOB_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,7 +37,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||||||
public final class AudioAttributes {
|
public final class AudioAttributes {
|
||||||
|
|
||||||
/** A direct wrapper around {@link android.media.AudioAttributes}. */
|
/** A direct wrapper around {@link android.media.AudioAttributes}. */
|
||||||
@RequiresApi(21)
|
|
||||||
public static final class AudioAttributesV21 {
|
public static final class AudioAttributesV21 {
|
||||||
public final android.media.AudioAttributes audioAttributes;
|
public final android.media.AudioAttributes audioAttributes;
|
||||||
|
|
||||||
@ -165,7 +164,6 @@ public final class AudioAttributes {
|
|||||||
* <p>Some fields are ignored if the corresponding {@link android.media.AudioAttributes.Builder}
|
* <p>Some fields are ignored if the corresponding {@link android.media.AudioAttributes.Builder}
|
||||||
* setter is not available on the current API level.
|
* setter is not available on the current API level.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
public AudioAttributesV21 getAudioAttributesV21() {
|
public AudioAttributesV21 getAudioAttributesV21() {
|
||||||
if (audioAttributesV21 == null) {
|
if (audioAttributesV21 == null) {
|
||||||
audioAttributesV21 = new AudioAttributesV21(this);
|
audioAttributesV21 = new AudioAttributesV21(this);
|
||||||
|
@ -32,7 +32,6 @@ import android.media.MediaFormat;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
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 com.google.errorprone.annotations.InlineMe;
|
||||||
@ -1698,7 +1697,6 @@ public final class C {
|
|||||||
replacement = "Util.generateAudioSessionIdV21(context)",
|
replacement = "Util.generateAudioSessionIdV21(context)",
|
||||||
imports = {"androidx.media3.common.util.Util"})
|
imports = {"androidx.media3.common.util.Util"})
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@RequiresApi(21)
|
|
||||||
public static int generateAudioSessionIdV21(Context context) {
|
public static int generateAudioSessionIdV21(Context context) {
|
||||||
return Util.generateAudioSessionIdV21(context);
|
return Util.generateAudioSessionIdV21(context);
|
||||||
}
|
}
|
||||||
|
@ -962,16 +962,14 @@ public final class Util {
|
|||||||
/**
|
/**
|
||||||
* Returns the language tag for a {@link Locale}.
|
* Returns the language tag for a {@link Locale}.
|
||||||
*
|
*
|
||||||
* <p>For API levels ≥ 21, this tag is IETF BCP 47 compliant. Use {@link
|
* <p>This tag is IETF BCP 47 compliant.
|
||||||
* #normalizeLanguageCode(String)} to retrieve a normalized IETF BCP 47 language tag for all API
|
|
||||||
* levels if needed.
|
|
||||||
*
|
*
|
||||||
* @param locale A {@link Locale}.
|
* @param locale A {@link Locale}.
|
||||||
* @return The language tag.
|
* @return The language tag.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static String getLocaleLanguageTag(Locale locale) {
|
public static String getLocaleLanguageTag(Locale locale) {
|
||||||
return SDK_INT >= 21 ? getLocaleLanguageTagV21(locale) : locale.toString();
|
return locale.toLanguageTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2253,7 +2251,6 @@ public final class Util {
|
|||||||
|
|
||||||
/** Creates {@link AudioFormat} with given sampleRate, channelConfig, and encoding. */
|
/** Creates {@link AudioFormat} with given sampleRate, channelConfig, and encoding. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(21)
|
|
||||||
public static AudioFormat getAudioFormat(int sampleRate, int channelConfig, int encoding) {
|
public static AudioFormat getAudioFormat(int sampleRate, int channelConfig, int encoding) {
|
||||||
return new AudioFormat.Builder()
|
return new AudioFormat.Builder()
|
||||||
.setSampleRate(sampleRate)
|
.setSampleRate(sampleRate)
|
||||||
@ -2417,7 +2414,6 @@ public final class Util {
|
|||||||
* @see AudioManager#generateAudioSessionId()
|
* @see AudioManager#generateAudioSessionId()
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(21)
|
|
||||||
public static int generateAudioSessionIdV21(Context context) {
|
public static int generateAudioSessionIdV21(Context context) {
|
||||||
@Nullable
|
@Nullable
|
||||||
AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
||||||
@ -3065,8 +3061,7 @@ public final class Util {
|
|||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static boolean isWear(Context context) {
|
public static boolean isWear(Context context) {
|
||||||
return SDK_INT >= 20
|
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
|
||||||
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3502,9 +3497,7 @@ public final class Util {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static Drawable getDrawable(
|
public static Drawable getDrawable(
|
||||||
Context context, Resources resources, @DrawableRes int drawableRes) {
|
Context context, Resources resources, @DrawableRes int drawableRes) {
|
||||||
return SDK_INT >= 21
|
return resources.getDrawable(drawableRes, context.getTheme());
|
||||||
? Api21.getDrawable(context, resources, drawableRes)
|
|
||||||
: resources.getDrawable(drawableRes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3666,11 +3659,6 @@ public final class Util {
|
|||||||
return split(config.getLocales().toLanguageTags(), ",");
|
return split(config.getLocales().toLanguageTags(), ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static String getLocaleLanguageTagV21(Locale locale) {
|
|
||||||
return locale.toLanguageTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HashMap<String, String> createIsoLanguageReplacementMap() {
|
private static HashMap<String, String> createIsoLanguageReplacementMap() {
|
||||||
String[] iso2Languages = Locale.getISOLanguages();
|
String[] iso2Languages = Locale.getISOLanguages();
|
||||||
HashMap<String, String> replacedLanguages =
|
HashMap<String, String> replacedLanguages =
|
||||||
@ -3890,14 +3878,6 @@ public final class Util {
|
|||||||
0xF3
|
0xF3
|
||||||
};
|
};
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class Api21 {
|
|
||||||
@DoNotInline
|
|
||||||
public static Drawable getDrawable(Context context, Resources resources, @DrawableRes int res) {
|
|
||||||
return resources.getDrawable(res, context.getTheme());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
private static class Api29 {
|
private static class Api29 {
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.NoRouteToHostException;
|
import java.net.NoRouteToHostException;
|
||||||
@ -248,7 +247,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
||||||
private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307;
|
private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307;
|
||||||
private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308;
|
private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308;
|
||||||
private static final long MAX_BYTES_TO_DRAIN = 2048;
|
|
||||||
|
|
||||||
private final boolean allowCrossProtocolRedirects;
|
private final boolean allowCrossProtocolRedirects;
|
||||||
private final boolean crossProtocolRedirectsForceOriginal;
|
private final boolean crossProtocolRedirectsForceOriginal;
|
||||||
@ -482,9 +480,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
try {
|
try {
|
||||||
@Nullable InputStream inputStream = this.inputStream;
|
@Nullable InputStream inputStream = this.inputStream;
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
long bytesRemaining =
|
|
||||||
bytesToRead == C.LENGTH_UNSET ? C.LENGTH_UNSET : bytesToRead - bytesRead;
|
|
||||||
maybeTerminateInputStream(connection, bytesRemaining);
|
|
||||||
try {
|
try {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -784,52 +779,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can
|
|
||||||
* block for a long time if the stream has a lot of data remaining. Call this method before
|
|
||||||
* closing the input stream to make a best effort to cause the input stream to encounter an
|
|
||||||
* unexpected end of input, working around this issue. On other platform API levels, the method
|
|
||||||
* does nothing.
|
|
||||||
*
|
|
||||||
* @param connection The connection whose {@link InputStream} should be terminated.
|
|
||||||
* @param bytesRemaining The number of bytes remaining to be read from the input stream if its
|
|
||||||
* length is known. {@link C#LENGTH_UNSET} otherwise.
|
|
||||||
*/
|
|
||||||
private static void maybeTerminateInputStream(
|
|
||||||
@Nullable HttpURLConnection connection, long bytesRemaining) {
|
|
||||||
if (connection == null || Util.SDK_INT > 20) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
InputStream inputStream = connection.getInputStream();
|
|
||||||
if (bytesRemaining == C.LENGTH_UNSET) {
|
|
||||||
// If the input stream has already ended, do nothing. The socket may be re-used.
|
|
||||||
if (inputStream.read() == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) {
|
|
||||||
// There isn't much data left. Prefer to allow it to drain, which may allow the socket to be
|
|
||||||
// re-used.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String className = inputStream.getClass().getName();
|
|
||||||
if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream".equals(className)
|
|
||||||
|| "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream"
|
|
||||||
.equals(className)) {
|
|
||||||
Class<?> superclass = inputStream.getClass().getSuperclass();
|
|
||||||
Method unexpectedEndOfInput =
|
|
||||||
checkNotNull(superclass).getDeclaredMethod("unexpectedEndOfInput");
|
|
||||||
unexpectedEndOfInput.setAccessible(true);
|
|
||||||
unexpectedEndOfInput.invoke(inputStream);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// If an IOException then the connection didn't ever have an input stream, or it was closed
|
|
||||||
// already. If another type of exception then something went wrong, most likely the device
|
|
||||||
// isn't using okhttp.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Closes the current connection quietly, if there is one. */
|
/** Closes the current connection quietly, if there is one. */
|
||||||
private void closeConnectionQuietly() {
|
private void closeConnectionQuietly() {
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
|
@ -22,14 +22,11 @@ import android.net.Uri;
|
|||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.OsConstants;
|
import android.system.OsConstants;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.DoNotInline;
|
|
||||||
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.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -200,7 +197,8 @@ public final class FileDataSource extends BaseDataSource {
|
|||||||
// different SDK versions.
|
// different SDK versions.
|
||||||
throw new FileDataSourceException(
|
throw new FileDataSourceException(
|
||||||
e,
|
e,
|
||||||
Util.SDK_INT >= 21 && Api21.isPermissionError(e.getCause())
|
e.getCause() instanceof ErrnoException
|
||||||
|
&& ((ErrnoException) e.getCause()).errno == OsConstants.EACCES
|
||||||
? PlaybackException.ERROR_CODE_IO_NO_PERMISSION
|
? PlaybackException.ERROR_CODE_IO_NO_PERMISSION
|
||||||
: PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
|
: PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
@ -209,12 +207,4 @@ public final class FileDataSource extends BaseDataSource {
|
|||||||
throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
|
throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class Api21 {
|
|
||||||
@DoNotInline
|
|
||||||
private static boolean isPermissionError(@Nullable Throwable e) {
|
|
||||||
return e instanceof ErrnoException && ((ErrnoException) e).errno == OsConstants.EACCES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import android.system.ErrnoException;
|
|||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.system.OsConstants;
|
import android.system.OsConstants;
|
||||||
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.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -48,7 +47,6 @@ import java.util.Set;
|
|||||||
* #open(DataSpec)} is not actually used for reading data. Instead, the underlying {@link
|
* #open(DataSpec)} is not actually used for reading data. Instead, the underlying {@link
|
||||||
* FileDescriptor} is used for all read operations.
|
* FileDescriptor} is used for all read operations.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class FileDescriptorDataSource extends BaseDataSource {
|
public class FileDescriptorDataSource extends BaseDataSource {
|
||||||
|
|
||||||
|
@ -748,7 +748,7 @@ public final class MediaMetricsListener
|
|||||||
} else if (cause instanceof DrmSession.DrmSessionException) {
|
} else if (cause instanceof DrmSession.DrmSessionException) {
|
||||||
// Unpack DrmSessionException.
|
// Unpack DrmSessionException.
|
||||||
cause = checkNotNull(cause.getCause());
|
cause = checkNotNull(cause.getCause());
|
||||||
if (Util.SDK_INT >= 21 && cause instanceof MediaDrm.MediaDrmStateException) {
|
if (cause instanceof MediaDrm.MediaDrmStateException) {
|
||||||
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) cause).getDiagnosticInfo();
|
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) cause).getDiagnosticInfo();
|
||||||
int subErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
|
int subErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
|
||||||
int errorCode = getDrmErrorCode(subErrorCode);
|
int errorCode = getDrmErrorCode(subErrorCode);
|
||||||
@ -771,8 +771,7 @@ public final class MediaMetricsListener
|
|||||||
} else if (cause instanceof FileDataSource.FileDataSourceException
|
} else if (cause instanceof FileDataSource.FileDataSourceException
|
||||||
&& cause.getCause() instanceof FileNotFoundException) {
|
&& cause.getCause() instanceof FileNotFoundException) {
|
||||||
@Nullable Throwable notFoundCause = checkNotNull(cause.getCause()).getCause();
|
@Nullable Throwable notFoundCause = checkNotNull(cause.getCause()).getCause();
|
||||||
if (Util.SDK_INT >= 21
|
if (notFoundCause instanceof ErrnoException
|
||||||
&& notFoundCause instanceof ErrnoException
|
|
||||||
&& ((ErrnoException) notFoundCause).errno == OsConstants.EACCES) {
|
&& ((ErrnoException) notFoundCause).errno == OsConstants.EACCES) {
|
||||||
return new ErrorInfo(PlaybackErrorEvent.ERROR_IO_NO_PERMISSION, /* subErrorCode= */ 0);
|
return new ErrorInfo(PlaybackErrorEvent.ERROR_IO_NO_PERMISSION, /* subErrorCode= */ 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,7 +57,7 @@ public final class AudioCapabilitiesReceiver {
|
|||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
@Nullable private final AudioDeviceCallbackV23 audioDeviceCallback;
|
@Nullable private final AudioDeviceCallbackV23 audioDeviceCallback;
|
||||||
@Nullable private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver;
|
private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver;
|
||||||
@Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver;
|
@Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver;
|
||||||
|
|
||||||
@Nullable private AudioCapabilities audioCapabilities;
|
@Nullable private AudioCapabilities audioCapabilities;
|
||||||
@ -105,8 +105,7 @@ public final class AudioCapabilitiesReceiver {
|
|||||||
this.routedDevice = routedDevice;
|
this.routedDevice = routedDevice;
|
||||||
handler = Util.createHandlerForCurrentOrMainLooper();
|
handler = Util.createHandlerForCurrentOrMainLooper();
|
||||||
audioDeviceCallback = Util.SDK_INT >= 23 ? new AudioDeviceCallbackV23() : null;
|
audioDeviceCallback = Util.SDK_INT >= 23 ? new AudioDeviceCallbackV23() : null;
|
||||||
hdmiAudioPlugBroadcastReceiver =
|
hdmiAudioPlugBroadcastReceiver = new HdmiAudioPlugBroadcastReceiver();
|
||||||
Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null;
|
|
||||||
Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri();
|
Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri();
|
||||||
externalSurroundSoundSettingObserver =
|
externalSurroundSoundSettingObserver =
|
||||||
externalSurroundSoundUri != null
|
externalSurroundSoundUri != null
|
||||||
@ -162,16 +161,12 @@ public final class AudioCapabilitiesReceiver {
|
|||||||
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
|
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
|
||||||
Api23.registerAudioDeviceCallback(context, audioDeviceCallback, handler);
|
Api23.registerAudioDeviceCallback(context, audioDeviceCallback, handler);
|
||||||
}
|
}
|
||||||
@Nullable Intent stickyIntent = null;
|
Intent stickyIntent =
|
||||||
if (hdmiAudioPlugBroadcastReceiver != null) {
|
context.registerReceiver(
|
||||||
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
|
hdmiAudioPlugBroadcastReceiver,
|
||||||
stickyIntent =
|
new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG),
|
||||||
context.registerReceiver(
|
/* broadcastPermission= */ null,
|
||||||
hdmiAudioPlugBroadcastReceiver,
|
handler);
|
||||||
intentFilter,
|
|
||||||
/* broadcastPermission= */ null,
|
|
||||||
handler);
|
|
||||||
}
|
|
||||||
audioCapabilities =
|
audioCapabilities =
|
||||||
AudioCapabilities.getCapabilitiesInternal(
|
AudioCapabilities.getCapabilitiesInternal(
|
||||||
context, stickyIntent, audioAttributes, routedDevice);
|
context, stickyIntent, audioAttributes, routedDevice);
|
||||||
@ -190,9 +185,7 @@ public final class AudioCapabilitiesReceiver {
|
|||||||
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
|
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
|
||||||
Api23.unregisterAudioDeviceCallback(context, audioDeviceCallback);
|
Api23.unregisterAudioDeviceCallback(context, audioDeviceCallback);
|
||||||
}
|
}
|
||||||
if (hdmiAudioPlugBroadcastReceiver != null) {
|
context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver);
|
||||||
context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver);
|
|
||||||
}
|
|
||||||
if (externalSurroundSoundSettingObserver != null) {
|
if (externalSurroundSoundSettingObserver != null) {
|
||||||
externalSurroundSoundSettingObserver.unregister();
|
externalSurroundSoundSettingObserver.unregister();
|
||||||
}
|
}
|
||||||
|
@ -244,9 +244,7 @@ public abstract class DecoderAudioRenderer<
|
|||||||
if (formatSupport <= C.FORMAT_UNSUPPORTED_DRM) {
|
if (formatSupport <= C.FORMAT_UNSUPPORTED_DRM) {
|
||||||
return RendererCapabilities.create(formatSupport);
|
return RendererCapabilities.create(formatSupport);
|
||||||
}
|
}
|
||||||
@TunnelingSupport
|
return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED);
|
||||||
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
|
||||||
return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,8 +337,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
/**
|
/**
|
||||||
* Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float
|
* Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float
|
||||||
* output will be used if the input is 32-bit float, and also if the input is high resolution
|
* output will be used if the input is 32-bit float, and also if the input is high resolution
|
||||||
* (24-bit or 32-bit) integer PCM. Float output is supported from API level 21. Audio processing
|
* (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not be
|
||||||
* (for example, speed adjustment) will not be available when float output is in use.
|
* available when float output is in use.
|
||||||
*
|
*
|
||||||
* <p>The default value is {@code false}.
|
* <p>The default value is {@code false}.
|
||||||
*/
|
*/
|
||||||
@ -541,8 +541,6 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
@Nullable private ByteBuffer inputBuffer;
|
@Nullable private ByteBuffer inputBuffer;
|
||||||
private int inputBufferAccessUnitCount;
|
private int inputBufferAccessUnitCount;
|
||||||
@Nullable private ByteBuffer outputBuffer;
|
@Nullable private ByteBuffer outputBuffer;
|
||||||
private byte @MonotonicNonNull [] preV21OutputBuffer;
|
|
||||||
private int preV21OutputBufferOffset;
|
|
||||||
private boolean handledEndOfStream;
|
private boolean handledEndOfStream;
|
||||||
private boolean stoppedAudioTrack;
|
private boolean stoppedAudioTrack;
|
||||||
private boolean handledOffloadOnPresentationEnded;
|
private boolean handledOffloadOnPresentationEnded;
|
||||||
@ -571,7 +569,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
? getCapabilities(context, audioAttributes, /* routedDevice= */ null)
|
? getCapabilities(context, audioAttributes, /* routedDevice= */ null)
|
||||||
: builder.audioCapabilities;
|
: builder.audioCapabilities;
|
||||||
audioProcessorChain = builder.audioProcessorChain;
|
audioProcessorChain = builder.audioProcessorChain;
|
||||||
enableFloatOutput = Util.SDK_INT >= 21 && builder.enableFloatOutput;
|
enableFloatOutput = builder.enableFloatOutput;
|
||||||
preferAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
preferAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
||||||
offloadMode = OFFLOAD_MODE_DISABLED;
|
offloadMode = OFFLOAD_MODE_DISABLED;
|
||||||
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
|
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
|
||||||
@ -700,14 +698,6 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
trimmingAudioProcessor.setTrimFrameCount(
|
trimmingAudioProcessor.setTrimFrameCount(
|
||||||
inputFormat.encoderDelay, inputFormat.encoderPadding);
|
inputFormat.encoderDelay, inputFormat.encoderPadding);
|
||||||
|
|
||||||
if (Util.SDK_INT < 21 && inputFormat.channelCount == 8 && outputChannels == null) {
|
|
||||||
// AudioTrack doesn't support 8 channel output before Android L. Discard the last two (side)
|
|
||||||
// channels to give a 6 channel stream that is supported.
|
|
||||||
outputChannels = new int[6];
|
|
||||||
for (int i = 0; i < outputChannels.length; i++) {
|
|
||||||
outputChannels[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channelMappingAudioProcessor.setChannelMap(outputChannels);
|
channelMappingAudioProcessor.setChannelMap(outputChannels);
|
||||||
|
|
||||||
AudioProcessor.AudioFormat outputFormat = new AudioProcessor.AudioFormat(inputFormat);
|
AudioProcessor.AudioFormat outputFormat = new AudioProcessor.AudioFormat(inputFormat);
|
||||||
@ -1160,32 +1150,10 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
Assertions.checkArgument(outputBuffer == buffer);
|
Assertions.checkArgument(outputBuffer == buffer);
|
||||||
} else {
|
} else {
|
||||||
outputBuffer = buffer;
|
outputBuffer = buffer;
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
int bytesRemaining = buffer.remaining();
|
|
||||||
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
|
|
||||||
preV21OutputBuffer = new byte[bytesRemaining];
|
|
||||||
}
|
|
||||||
int originalPosition = buffer.position();
|
|
||||||
buffer.get(preV21OutputBuffer, 0, bytesRemaining);
|
|
||||||
buffer.position(originalPosition);
|
|
||||||
preV21OutputBufferOffset = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
int bytesRemaining = buffer.remaining();
|
int bytesRemaining = buffer.remaining();
|
||||||
int bytesWrittenOrError = 0; // Error if negative
|
int bytesWrittenOrError = 0; // Error if negative
|
||||||
if (Util.SDK_INT < 21) { // outputMode == OUTPUT_MODE_PCM.
|
if (tunneling) {
|
||||||
// Work out how many bytes we can write without the risk of blocking.
|
|
||||||
int bytesToWrite = audioTrackPositionTracker.getAvailableBufferSize(writtenPcmBytes);
|
|
||||||
if (bytesToWrite > 0) {
|
|
||||||
bytesToWrite = min(bytesRemaining, bytesToWrite);
|
|
||||||
bytesWrittenOrError =
|
|
||||||
audioTrack.write(preV21OutputBuffer, preV21OutputBufferOffset, bytesToWrite);
|
|
||||||
if (bytesWrittenOrError > 0) { // No error
|
|
||||||
preV21OutputBufferOffset += bytesWrittenOrError;
|
|
||||||
buffer.position(buffer.position() + bytesWrittenOrError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tunneling) {
|
|
||||||
Assertions.checkState(avSyncPresentationTimeUs != C.TIME_UNSET);
|
Assertions.checkState(avSyncPresentationTimeUs != C.TIME_UNSET);
|
||||||
if (avSyncPresentationTimeUs == C.TIME_END_OF_SOURCE) {
|
if (avSyncPresentationTimeUs == C.TIME_END_OF_SOURCE) {
|
||||||
// Audio processors during tunneling are required to produce buffers immediately when
|
// Audio processors during tunneling are required to produce buffers immediately when
|
||||||
@ -1196,10 +1164,9 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
lastTunnelingAvSyncPresentationTimeUs = avSyncPresentationTimeUs;
|
lastTunnelingAvSyncPresentationTimeUs = avSyncPresentationTimeUs;
|
||||||
}
|
}
|
||||||
bytesWrittenOrError =
|
bytesWrittenOrError =
|
||||||
writeNonBlockingWithAvSyncV21(
|
writeNonBlockingWithAvSync(audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs);
|
||||||
audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs);
|
|
||||||
} else {
|
} else {
|
||||||
bytesWrittenOrError = writeNonBlockingV21(audioTrack, buffer, bytesRemaining);
|
bytesWrittenOrError = writeNonBlocking(audioTrack, buffer, bytesRemaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
||||||
@ -1402,7 +1369,6 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableTunnelingV21() {
|
public void enableTunnelingV21() {
|
||||||
Assertions.checkState(Util.SDK_INT >= 21);
|
|
||||||
Assertions.checkState(externalAudioSessionIdProvided);
|
Assertions.checkState(externalAudioSessionIdProvided);
|
||||||
if (!tunneling) {
|
if (!tunneling) {
|
||||||
tunneling = true;
|
tunneling = true;
|
||||||
@ -1445,12 +1411,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setVolumeInternal() {
|
private void setVolumeInternal() {
|
||||||
if (!isAudioTrackInitialized()) {
|
if (isAudioTrackInitialized()) {
|
||||||
// Do nothing.
|
audioTrack.setVolume(volume);
|
||||||
} else if (Util.SDK_INT >= 21) {
|
|
||||||
setVolumeInternalV21(audioTrack, volume);
|
|
||||||
} else {
|
|
||||||
setVolumeInternalV3(audioTrack, volume);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1474,14 +1436,6 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
if (isOffloadedPlayback(audioTrack)) {
|
if (isOffloadedPlayback(audioTrack)) {
|
||||||
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) {
|
|
||||||
// Prior to API level 21, audio sessions are not kept alive once there are no components
|
|
||||||
// associated with them. If we generated the session ID internally, the only component
|
|
||||||
// associated with the session is the audio track that's being released, and therefore
|
|
||||||
// the session will not be kept alive. As a result, we need to generate a new session when
|
|
||||||
// we next create an audio track.
|
|
||||||
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
|
|
||||||
}
|
|
||||||
AudioTrackConfig oldAudioTrackConfig = configuration.buildAudioTrackConfig();
|
AudioTrackConfig oldAudioTrackConfig = configuration.buildAudioTrackConfig();
|
||||||
if (pendingConfiguration != null) {
|
if (pendingConfiguration != null) {
|
||||||
configuration = pendingConfiguration;
|
configuration = pendingConfiguration;
|
||||||
@ -1822,13 +1776,11 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
private static int writeNonBlocking(AudioTrack audioTrack, ByteBuffer buffer, int size) {
|
||||||
private static int writeNonBlockingV21(AudioTrack audioTrack, ByteBuffer buffer, int size) {
|
|
||||||
return audioTrack.write(buffer, size, AudioTrack.WRITE_NON_BLOCKING);
|
return audioTrack.write(buffer, size, AudioTrack.WRITE_NON_BLOCKING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
private int writeNonBlockingWithAvSync(
|
||||||
private int writeNonBlockingWithAvSyncV21(
|
|
||||||
AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs) {
|
AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs) {
|
||||||
if (Util.SDK_INT >= 26) {
|
if (Util.SDK_INT >= 26) {
|
||||||
// The underlying platform AudioTrack writes AV sync headers directly.
|
// The underlying platform AudioTrack writes AV sync headers directly.
|
||||||
@ -1858,7 +1810,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int result = writeNonBlockingV21(audioTrack, buffer, size);
|
int result = writeNonBlocking(audioTrack, buffer, size);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
bytesUntilNextAvSync = 0;
|
bytesUntilNextAvSync = 0;
|
||||||
return result;
|
return result;
|
||||||
@ -1867,15 +1819,6 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static void setVolumeInternalV21(AudioTrack audioTrack, float volume) {
|
|
||||||
audioTrack.setVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setVolumeInternalV3(AudioTrack audioTrack, float volume) {
|
|
||||||
audioTrack.setStereoVolume(volume, volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playPendingData() {
|
private void playPendingData() {
|
||||||
if (!stoppedAudioTrack) {
|
if (!stoppedAudioTrack) {
|
||||||
stoppedAudioTrack = true;
|
stoppedAudioTrack = true;
|
||||||
@ -2253,10 +2196,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
private AudioTrack createAudioTrack(AudioAttributes audioAttributes, int audioSessionId) {
|
private AudioTrack createAudioTrack(AudioAttributes audioAttributes, int audioSessionId) {
|
||||||
if (Util.SDK_INT >= 29) {
|
if (Util.SDK_INT >= 29) {
|
||||||
return createAudioTrackV29(audioAttributes, audioSessionId);
|
return createAudioTrackV29(audioAttributes, audioSessionId);
|
||||||
} else if (Util.SDK_INT >= 21) {
|
|
||||||
return createAudioTrackV21(audioAttributes, audioSessionId);
|
|
||||||
} else {
|
} else {
|
||||||
return createAudioTrackV9(audioAttributes, audioSessionId);
|
return createAudioTrackV21(audioAttributes, audioSessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2265,7 +2206,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
AudioFormat audioFormat =
|
AudioFormat audioFormat =
|
||||||
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding);
|
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding);
|
||||||
android.media.AudioAttributes audioTrackAttributes =
|
android.media.AudioAttributes audioTrackAttributes =
|
||||||
getAudioTrackAttributesV21(audioAttributes, tunneling);
|
getAudioTrackAttributes(audioAttributes, tunneling);
|
||||||
return new AudioTrack.Builder()
|
return new AudioTrack.Builder()
|
||||||
.setAudioAttributes(audioTrackAttributes)
|
.setAudioAttributes(audioTrackAttributes)
|
||||||
.setAudioFormat(audioFormat)
|
.setAudioFormat(audioFormat)
|
||||||
@ -2276,59 +2217,28 @@ public final class DefaultAudioSink implements AudioSink {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private AudioTrack createAudioTrackV21(AudioAttributes audioAttributes, int audioSessionId) {
|
private AudioTrack createAudioTrackV21(AudioAttributes audioAttributes, int audioSessionId) {
|
||||||
return new AudioTrack(
|
return new AudioTrack(
|
||||||
getAudioTrackAttributesV21(audioAttributes, tunneling),
|
getAudioTrackAttributes(audioAttributes, tunneling),
|
||||||
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding),
|
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding),
|
||||||
bufferSize,
|
bufferSize,
|
||||||
AudioTrack.MODE_STREAM,
|
AudioTrack.MODE_STREAM,
|
||||||
audioSessionId);
|
audioSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Using deprecated AudioTrack constructor.
|
private static android.media.AudioAttributes getAudioTrackAttributes(
|
||||||
private AudioTrack createAudioTrackV9(AudioAttributes audioAttributes, int audioSessionId) {
|
|
||||||
int streamType = Util.getStreamTypeForAudioUsage(audioAttributes.usage);
|
|
||||||
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
|
|
||||||
return new AudioTrack(
|
|
||||||
streamType,
|
|
||||||
outputSampleRate,
|
|
||||||
outputChannelConfig,
|
|
||||||
outputEncoding,
|
|
||||||
bufferSize,
|
|
||||||
AudioTrack.MODE_STREAM);
|
|
||||||
} else {
|
|
||||||
// Re-attach to the same audio session.
|
|
||||||
return new AudioTrack(
|
|
||||||
streamType,
|
|
||||||
outputSampleRate,
|
|
||||||
outputChannelConfig,
|
|
||||||
outputEncoding,
|
|
||||||
bufferSize,
|
|
||||||
AudioTrack.MODE_STREAM,
|
|
||||||
audioSessionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static android.media.AudioAttributes getAudioTrackAttributesV21(
|
|
||||||
AudioAttributes audioAttributes, boolean tunneling) {
|
AudioAttributes audioAttributes, boolean tunneling) {
|
||||||
if (tunneling) {
|
if (tunneling) {
|
||||||
return getAudioTrackTunnelingAttributesV21();
|
return new android.media.AudioAttributes.Builder()
|
||||||
|
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
|
||||||
|
.setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
|
||||||
|
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
|
||||||
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return audioAttributes.getAudioAttributesV21().audioAttributes;
|
return audioAttributes.getAudioAttributesV21().audioAttributes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static android.media.AudioAttributes getAudioTrackTunnelingAttributesV21() {
|
|
||||||
return new android.media.AudioAttributes.Builder()
|
|
||||||
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
|
|
||||||
.setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
|
|
||||||
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean outputModeIsOffload() {
|
public boolean outputModeIsOffload() {
|
||||||
return outputMode == OUTPUT_MODE_OFFLOAD;
|
return outputMode == OUTPUT_MODE_OFFLOAD;
|
||||||
}
|
}
|
||||||
|
@ -278,8 +278,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
if (!MimeTypes.isAudio(format.sampleMimeType)) {
|
if (!MimeTypes.isAudio(format.sampleMimeType)) {
|
||||||
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
|
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
|
||||||
}
|
}
|
||||||
@TunnelingSupport
|
|
||||||
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
|
||||||
boolean formatHasDrm = format.cryptoType != C.CRYPTO_TYPE_NONE;
|
boolean formatHasDrm = format.cryptoType != C.CRYPTO_TYPE_NONE;
|
||||||
boolean supportsFormatDrm = supportsFormatDrm(format);
|
boolean supportsFormatDrm = supportsFormatDrm(format);
|
||||||
|
|
||||||
@ -291,7 +289,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
audioOffloadSupport = getAudioOffloadSupport(format);
|
audioOffloadSupport = getAudioOffloadSupport(format);
|
||||||
if (audioSink.supportsFormat(format)) {
|
if (audioSink.supportsFormat(format)) {
|
||||||
return RendererCapabilities.create(
|
return RendererCapabilities.create(
|
||||||
C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport, audioOffloadSupport);
|
C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED, audioOffloadSupport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
|
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
|
||||||
@ -346,7 +344,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
return RendererCapabilities.create(
|
return RendererCapabilities.create(
|
||||||
formatSupport,
|
formatSupport,
|
||||||
adaptiveSupport,
|
adaptiveSupport,
|
||||||
tunnelingSupport,
|
TUNNELING_SUPPORTED,
|
||||||
hardwareAccelerationSupport,
|
hardwareAccelerationSupport,
|
||||||
decoderSupport,
|
decoderSupport,
|
||||||
audioOffloadSupport);
|
audioOffloadSupport);
|
||||||
|
@ -77,8 +77,11 @@ public final class DrmUtil {
|
|||||||
*/
|
*/
|
||||||
public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException(
|
public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException(
|
||||||
Throwable exception, @ErrorSource int errorSource) {
|
Throwable exception, @ErrorSource int errorSource) {
|
||||||
if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) {
|
if (exception instanceof MediaDrm.MediaDrmStateException) {
|
||||||
return Api21.mediaDrmStateExceptionToErrorCode(exception);
|
@Nullable
|
||||||
|
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) exception).getDiagnosticInfo();
|
||||||
|
int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
|
||||||
|
return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode);
|
||||||
} 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 (exception instanceof NotProvisionedException
|
} else if (exception instanceof NotProvisionedException
|
||||||
@ -128,26 +131,6 @@ public final class DrmUtil {
|
|||||||
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
|
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal classes.
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class Api21 {
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isMediaDrmStateException(@Nullable Throwable throwable) {
|
|
||||||
return throwable instanceof MediaDrm.MediaDrmStateException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode(
|
|
||||||
Throwable throwable) {
|
|
||||||
@Nullable
|
|
||||||
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo();
|
|
||||||
int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
|
|
||||||
return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
private static final class Api23 {
|
private static final class Api23 {
|
||||||
|
|
||||||
|
@ -53,12 +53,22 @@ public final class FrameworkCryptoConfig implements CryptoConfig {
|
|||||||
@Deprecated public final boolean forceAllowInsecureDecoderComponents;
|
@Deprecated public final boolean forceAllowInsecureDecoderComponents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructs an instance.
|
||||||
|
*
|
||||||
* @param uuid The DRM scheme UUID.
|
* @param uuid The DRM scheme UUID.
|
||||||
* @param sessionId The DRM session id.
|
* @param sessionId The DRM session id.
|
||||||
* @param forceAllowInsecureDecoderComponents Whether to allow use of insecure decoder components
|
*/
|
||||||
* even if the underlying platform says otherwise.
|
@SuppressWarnings("deprecation") // Delegating to deprecated constructor
|
||||||
|
public FrameworkCryptoConfig(UUID uuid, byte[] sessionId) {
|
||||||
|
this(uuid, sessionId, /* forceAllowInsecureDecoderComponents= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link FrameworkCryptoConfig#FrameworkCryptoConfig(UUID, byte[])} instead, and
|
||||||
|
* {@link ExoMediaDrm#requiresSecureDecoder} for the secure decoder handling logic.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation") // Setting deprecated field
|
@SuppressWarnings("deprecation") // Setting deprecated field
|
||||||
|
@Deprecated
|
||||||
public FrameworkCryptoConfig(
|
public FrameworkCryptoConfig(
|
||||||
UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) {
|
UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
@ -308,7 +308,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result && !shouldForceAllowInsecureDecoderComponents();
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -389,17 +389,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public FrameworkCryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException {
|
public FrameworkCryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException {
|
||||||
boolean forceAllowInsecureDecoderComponents = shouldForceAllowInsecureDecoderComponents();
|
return new FrameworkCryptoConfig(adjustUuid(uuid), sessionId);
|
||||||
return new FrameworkCryptoConfig(
|
|
||||||
adjustUuid(uuid), sessionId, forceAllowInsecureDecoderComponents);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work around a bug prior to Lollipop where L1 Widevine forced into L3 mode would still
|
|
||||||
// indicate that it required secure video decoders [Internal ref: b/11428937].
|
|
||||||
private boolean shouldForceAllowInsecureDecoderComponents() {
|
|
||||||
return Util.SDK_INT < 21
|
|
||||||
&& C.WIDEVINE_UUID.equals(uuid)
|
|
||||||
&& "L3".equals(getPropertyString("securityLevel"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@ -252,7 +252,6 @@ public interface MediaCodecAdapter {
|
|||||||
*
|
*
|
||||||
* @see MediaCodec#releaseOutputBuffer(int, long)
|
* @see MediaCodec#releaseOutputBuffer(int, long)
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
void releaseOutputBuffer(int index, long renderTimeStampNs);
|
void releaseOutputBuffer(int index, long renderTimeStampNs);
|
||||||
|
|
||||||
/** Flushes the adapter and the underlying {@link MediaCodec}. */
|
/** Flushes the adapter and the underlying {@link MediaCodec}. */
|
||||||
@ -278,7 +277,6 @@ public interface MediaCodecAdapter {
|
|||||||
* @see MediaCodec.Callback#onOutputBufferAvailable
|
* @see MediaCodec.Callback#onOutputBufferAvailable
|
||||||
* @return Whether listener was successfully registered.
|
* @return Whether listener was successfully registered.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
default boolean registerOnBufferAvailableListener(
|
default boolean registerOnBufferAvailableListener(
|
||||||
MediaCodecAdapter.OnBufferAvailableListener listener) {
|
MediaCodecAdapter.OnBufferAvailableListener listener) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -38,22 +38,16 @@ public class MediaCodecDecoderException extends DecoderException {
|
|||||||
public MediaCodecDecoderException(Throwable cause, @Nullable MediaCodecInfo codecInfo) {
|
public MediaCodecDecoderException(Throwable cause, @Nullable MediaCodecInfo codecInfo) {
|
||||||
super("Decoder failed: " + (codecInfo == null ? null : codecInfo.name), cause);
|
super("Decoder failed: " + (codecInfo == null ? null : codecInfo.name), cause);
|
||||||
this.codecInfo = codecInfo;
|
this.codecInfo = codecInfo;
|
||||||
diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
|
diagnosticInfo =
|
||||||
|
cause instanceof MediaCodec.CodecException
|
||||||
|
? ((MediaCodec.CodecException) cause).getDiagnosticInfo()
|
||||||
|
: null;
|
||||||
errorCode =
|
errorCode =
|
||||||
Util.SDK_INT >= 23
|
Util.SDK_INT >= 23
|
||||||
? getErrorCodeV23(cause)
|
? getErrorCodeV23(cause)
|
||||||
: Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticInfo);
|
: Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
@Nullable
|
|
||||||
private static String getDiagnosticInfoV21(Throwable cause) {
|
|
||||||
if (cause instanceof MediaCodec.CodecException) {
|
|
||||||
return ((MediaCodec.CodecException) cause).getDiagnosticInfo();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
private static int getErrorCodeV23(Throwable cause) {
|
private static int getErrorCodeV23(Throwable cause) {
|
||||||
if (cause instanceof MediaCodec.CodecException) {
|
if (cause instanceof MediaCodec.CodecException) {
|
||||||
|
@ -258,22 +258,12 @@ public final class MediaCodecInfo {
|
|||||||
if (format.width <= 0 || format.height <= 0) {
|
if (format.width <= 0 || format.height <= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 21) {
|
return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate);
|
||||||
return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate);
|
|
||||||
} else {
|
|
||||||
boolean isFormatSupported =
|
|
||||||
format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize();
|
|
||||||
if (!isFormatSupported) {
|
|
||||||
logNoSupport("legacyFrameSize, " + format.width + "x" + format.height);
|
|
||||||
}
|
|
||||||
return isFormatSupported;
|
|
||||||
}
|
|
||||||
} else { // Audio
|
} else { // Audio
|
||||||
return Util.SDK_INT < 21
|
return (format.sampleRate == Format.NO_VALUE
|
||||||
|| ((format.sampleRate == Format.NO_VALUE
|
|| isAudioSampleRateSupportedV21(format.sampleRate))
|
||||||
|| isAudioSampleRateSupportedV21(format.sampleRate))
|
&& (format.channelCount == Format.NO_VALUE
|
||||||
&& (format.channelCount == Format.NO_VALUE
|
|| isAudioChannelCountSupportedV21(format.channelCount));
|
||||||
|| isAudioChannelCountSupportedV21(format.channelCount)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +472,6 @@ public final class MediaCodecInfo {
|
|||||||
* Format#NO_VALUE} or any value less than or equal to 0.
|
* Format#NO_VALUE} or any value less than or equal to 0.
|
||||||
* @return Whether the decoder supports video with the given width, height and frame rate.
|
* @return Whether the decoder supports video with the given width, height and frame rate.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
public boolean isVideoSizeAndRateSupportedV21(int width, int height, double frameRate) {
|
public boolean isVideoSizeAndRateSupportedV21(int width, int height, double frameRate) {
|
||||||
if (capabilities == null) {
|
if (capabilities == null) {
|
||||||
logNoSupport("sizeAndRate.caps");
|
logNoSupport("sizeAndRate.caps");
|
||||||
@ -506,13 +495,13 @@ public final class MediaCodecInfo {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// If COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED then logic falls through
|
// If COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED then logic falls through
|
||||||
// to API 21+ code below.
|
// to code below.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!areSizeAndRateSupportedV21(videoCapabilities, width, height, frameRate)) {
|
if (!areSizeAndRateSupported(videoCapabilities, width, height, frameRate)) {
|
||||||
if (width >= height
|
if (width >= height
|
||||||
|| !needsRotatedVerticalResolutionWorkaround(name)
|
|| !needsRotatedVerticalResolutionWorkaround(name)
|
||||||
|| !areSizeAndRateSupportedV21(videoCapabilities, height, width, frameRate)) {
|
|| !areSizeAndRateSupported(videoCapabilities, height, width, frameRate)) {
|
||||||
logNoSupport("sizeAndRate.support, " + width + "x" + height + "@" + frameRate);
|
logNoSupport("sizeAndRate.support, " + width + "x" + height + "@" + frameRate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -525,8 +514,6 @@ public final class MediaCodecInfo {
|
|||||||
* Returns the smallest video size greater than or equal to a specified size that also satisfies
|
* Returns the smallest video size greater than or equal to a specified size that also satisfies
|
||||||
* the {@link MediaCodec}'s width and height alignment requirements.
|
* the {@link MediaCodec}'s width and height alignment requirements.
|
||||||
*
|
*
|
||||||
* <p>Must not be called if the device SDK version is less than 21.
|
|
||||||
*
|
|
||||||
* @param width Width in pixels.
|
* @param width Width in pixels.
|
||||||
* @param height Height in pixels.
|
* @param height Height in pixels.
|
||||||
* @return The smallest video size greater than or equal to the specified size that also satisfies
|
* @return The smallest video size greater than or equal to the specified size that also satisfies
|
||||||
@ -534,7 +521,6 @@ public final class MediaCodecInfo {
|
|||||||
* codec.
|
* codec.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@RequiresApi(21)
|
|
||||||
public Point alignVideoSizeV21(int width, int height) {
|
public Point alignVideoSizeV21(int width, int height) {
|
||||||
if (capabilities == null) {
|
if (capabilities == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -543,18 +529,15 @@ public final class MediaCodecInfo {
|
|||||||
if (videoCapabilities == null) {
|
if (videoCapabilities == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return alignVideoSizeV21(videoCapabilities, width, height);
|
return alignVideoSize(videoCapabilities, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the decoder supports audio with a given sample rate.
|
* Whether the decoder supports audio with a given sample rate.
|
||||||
*
|
*
|
||||||
* <p>Must not be called if the device SDK version is less than 21.
|
|
||||||
*
|
|
||||||
* @param sampleRate The sample rate in Hz.
|
* @param sampleRate The sample rate in Hz.
|
||||||
* @return Whether the decoder supports audio with the given sample rate.
|
* @return Whether the decoder supports audio with the given sample rate.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
public boolean isAudioSampleRateSupportedV21(int sampleRate) {
|
public boolean isAudioSampleRateSupportedV21(int sampleRate) {
|
||||||
if (capabilities == null) {
|
if (capabilities == null) {
|
||||||
logNoSupport("sampleRate.caps");
|
logNoSupport("sampleRate.caps");
|
||||||
@ -575,12 +558,9 @@ public final class MediaCodecInfo {
|
|||||||
/**
|
/**
|
||||||
* Whether the decoder supports audio with a given channel count.
|
* Whether the decoder supports audio with a given channel count.
|
||||||
*
|
*
|
||||||
* <p>Must not be called if the device SDK version is less than 21.
|
|
||||||
*
|
|
||||||
* @param channelCount The channel count.
|
* @param channelCount The channel count.
|
||||||
* @return Whether the decoder supports audio with the given channel count.
|
* @return Whether the decoder supports audio with the given channel count.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
public boolean isAudioChannelCountSupportedV21(int channelCount) {
|
public boolean isAudioChannelCountSupportedV21(int channelCount) {
|
||||||
if (capabilities == null) {
|
if (capabilities == null) {
|
||||||
logNoSupport("channelCount.caps");
|
logNoSupport("channelCount.caps");
|
||||||
@ -674,28 +654,17 @@ public final class MediaCodecInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTunneling(CodecCapabilities capabilities) {
|
private static boolean isTunneling(CodecCapabilities capabilities) {
|
||||||
return Util.SDK_INT >= 21 && isTunnelingV21(capabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static boolean isTunnelingV21(CodecCapabilities capabilities) {
|
|
||||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback);
|
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSecure(CodecCapabilities capabilities) {
|
private static boolean isSecure(CodecCapabilities capabilities) {
|
||||||
return Util.SDK_INT >= 21 && isSecureV21(capabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static boolean isSecureV21(CodecCapabilities capabilities) {
|
|
||||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
private static boolean areSizeAndRateSupported(
|
||||||
private static boolean areSizeAndRateSupportedV21(
|
|
||||||
VideoCapabilities capabilities, int width, int height, double frameRate) {
|
VideoCapabilities capabilities, int width, int height, double frameRate) {
|
||||||
// Don't ever fail due to alignment. See: https://github.com/google/ExoPlayer/issues/6551.
|
// Don't ever fail due to alignment. See: https://github.com/google/ExoPlayer/issues/6551.
|
||||||
Point alignedSize = alignVideoSizeV21(capabilities, width, height);
|
Point alignedSize = alignVideoSize(capabilities, width, height);
|
||||||
width = alignedSize.x;
|
width = alignedSize.x;
|
||||||
height = alignedSize.y;
|
height = alignedSize.y;
|
||||||
|
|
||||||
@ -712,8 +681,7 @@ public final class MediaCodecInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
private static Point alignVideoSize(VideoCapabilities capabilities, int width, int height) {
|
||||||
private static Point alignVideoSizeV21(VideoCapabilities capabilities, int width, int height) {
|
|
||||||
int widthAlignment = capabilities.getWidthAlignment();
|
int widthAlignment = capabilities.getWidthAlignment();
|
||||||
int heightAlignment = capabilities.getHeightAlignment();
|
int heightAlignment = capabilities.getHeightAlignment();
|
||||||
return new Point(
|
return new Point(
|
||||||
|
@ -166,12 +166,9 @@ public final class MediaCodecUtil {
|
|||||||
if (cachedDecoderInfos != null) {
|
if (cachedDecoderInfos != null) {
|
||||||
return cachedDecoderInfos;
|
return cachedDecoderInfos;
|
||||||
}
|
}
|
||||||
MediaCodecListCompat mediaCodecList =
|
MediaCodecListCompat mediaCodecList = new MediaCodecListCompatV21(secure, tunneling);
|
||||||
Util.SDK_INT >= 21
|
|
||||||
? new MediaCodecListCompatV21(secure, tunneling)
|
|
||||||
: new MediaCodecListCompatV16();
|
|
||||||
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
||||||
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
if (secure && decoderInfos.isEmpty() && Util.SDK_INT <= 23) {
|
||||||
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
||||||
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
||||||
mediaCodecList = new MediaCodecListCompatV16();
|
mediaCodecList = new MediaCodecListCompatV16();
|
||||||
@ -290,9 +287,8 @@ public final class MediaCodecUtil {
|
|||||||
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
|
||||||
result = max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
result = max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
||||||
}
|
}
|
||||||
// We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are
|
// We assume support for at least 480p, which is the level mandated by the Android CDD.
|
||||||
// the levels mandated by the Android CDD.
|
result = max(result, 720 * 480);
|
||||||
result = max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360));
|
|
||||||
}
|
}
|
||||||
maxH264DecodableFrameSize = result;
|
maxH264DecodableFrameSize = result;
|
||||||
}
|
}
|
||||||
@ -562,17 +558,6 @@ public final class MediaCodecUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work around broken audio decoders.
|
|
||||||
if (Util.SDK_INT < 21
|
|
||||||
&& ("CIPAACDecoder".equals(name)
|
|
||||||
|| "CIPMP3Decoder".equals(name)
|
|
||||||
|| "CIPVorbisDecoder".equals(name)
|
|
||||||
|| "CIPAMRNBDecoder".equals(name)
|
|
||||||
|| "AACDecoder".equals(name)
|
|
||||||
|| "MP3Decoder".equals(name))) {
|
|
||||||
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))
|
||||||
@ -588,26 +573,6 @@ public final class MediaCodecUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
|
||||||
if (Util.SDK_INT == 19
|
|
||||||
&& "OMX.SEC.vp8.dec".equals(name)
|
|
||||||
&& "samsung".equals(Util.MANUFACTURER)
|
|
||||||
&& (Util.DEVICE.startsWith("d2")
|
|
||||||
|| Util.DEVICE.startsWith("serrano")
|
|
||||||
|| Util.DEVICE.startsWith("jflte")
|
|
||||||
|| Util.DEVICE.startsWith("santos")
|
|
||||||
|| Util.DEVICE.startsWith("t0"))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VP8 decoder on Samsung Galaxy S4 cannot be queried.
|
|
||||||
if (Util.SDK_INT == 19
|
|
||||||
&& Util.DEVICE.startsWith("jflte")
|
|
||||||
&& "OMX.qcom.video.decoder.vp8".equals(name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MTK AC3 decoder doesn't support decoding JOC streams in 2-D. See [Internal: b/69400041].
|
// MTK AC3 decoder doesn't support decoding JOC streams in 2-D. See [Internal: b/69400041].
|
||||||
if (Util.SDK_INT <= 23
|
if (Util.SDK_INT <= 23
|
||||||
&& MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)
|
&& MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)
|
||||||
@ -663,19 +628,6 @@ public final class MediaCodecUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Util.SDK_INT < 21 && decoderInfos.size() > 1) {
|
|
||||||
String firstCodecName = decoderInfos.get(0).name;
|
|
||||||
if ("OMX.SEC.mp3.dec".equals(firstCodecName)
|
|
||||||
|| "OMX.SEC.MP3.Decoder".equals(firstCodecName)
|
|
||||||
|| "OMX.brcm.audio.mp3.decoder".equals(firstCodecName)) {
|
|
||||||
// Prefer OMX.google codecs over OMX.SEC.mp3.dec, OMX.SEC.MP3.Decoder and
|
|
||||||
// OMX.brcm.audio.mp3.decoder on older devices. See:
|
|
||||||
// https://github.com/google/ExoPlayer/issues/398 and
|
|
||||||
// https://github.com/google/ExoPlayer/issues/4519.
|
|
||||||
sortByScore(decoderInfos, decoderInfo -> decoderInfo.name.startsWith("OMX.google") ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Util.SDK_INT < 32 && decoderInfos.size() > 1) {
|
if (Util.SDK_INT < 32 && decoderInfos.size() > 1) {
|
||||||
String firstCodecName = decoderInfos.get(0).name;
|
String firstCodecName = decoderInfos.get(0).name;
|
||||||
// Prefer anything other than OMX.qti.audio.decoder.flac on older devices. See [Internal
|
// Prefer anything other than OMX.qti.audio.decoder.flac on older devices. See [Internal
|
||||||
@ -1056,7 +1008,6 @@ public final class MediaCodecUtil {
|
|||||||
boolean isFeatureRequired(String feature, String mimeType, CodecCapabilities capabilities);
|
boolean isFeatureRequired(String feature, String mimeType, CodecCapabilities capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
||||||
|
|
||||||
private final int codecKind;
|
private final int codecKind;
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package androidx.media3.exoplayer.mediacodec;
|
package androidx.media3.exoplayer.mediacodec;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Util.castNonNull;
|
|
||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
@ -80,15 +79,9 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final MediaCodec codec;
|
private final MediaCodec codec;
|
||||||
@Nullable private ByteBuffer[] inputByteBuffers;
|
|
||||||
@Nullable private ByteBuffer[] outputByteBuffers;
|
|
||||||
|
|
||||||
private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
|
private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
|
||||||
this.codec = mediaCodec;
|
this.codec = mediaCodec;
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
inputByteBuffers = codec.getInputBuffers();
|
|
||||||
outputByteBuffers = codec.getOutputBuffers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,9 +99,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
int index;
|
int index;
|
||||||
do {
|
do {
|
||||||
index = codec.dequeueOutputBuffer(bufferInfo, 0);
|
index = codec.dequeueOutputBuffer(bufferInfo, 0);
|
||||||
if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED && Util.SDK_INT < 21) {
|
|
||||||
outputByteBuffers = codec.getOutputBuffers();
|
|
||||||
}
|
|
||||||
} while (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED);
|
} while (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED);
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
@ -122,21 +112,13 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public ByteBuffer getInputBuffer(int index) {
|
public ByteBuffer getInputBuffer(int index) {
|
||||||
if (Util.SDK_INT >= 21) {
|
return codec.getInputBuffer(index);
|
||||||
return codec.getInputBuffer(index);
|
|
||||||
} else {
|
|
||||||
return castNonNull(inputByteBuffers)[index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public ByteBuffer getOutputBuffer(int index) {
|
public ByteBuffer getOutputBuffer(int index) {
|
||||||
if (Util.SDK_INT >= 21) {
|
return codec.getOutputBuffer(index);
|
||||||
return codec.getOutputBuffer(index);
|
|
||||||
} else {
|
|
||||||
return castNonNull(outputByteBuffers)[index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,7 +140,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RequiresApi(21)
|
|
||||||
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
|
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
|
||||||
codec.releaseOutputBuffer(index, renderTimeStampNs);
|
codec.releaseOutputBuffer(index, renderTimeStampNs);
|
||||||
}
|
}
|
||||||
@ -170,8 +151,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
inputByteBuffers = null;
|
|
||||||
outputByteBuffers = null;
|
|
||||||
try {
|
try {
|
||||||
if (Util.SDK_INT >= 30 && Util.SDK_INT < 33) {
|
if (Util.SDK_INT >= 30 && Util.SDK_INT < 33) {
|
||||||
// Stopping the codec before releasing it works around a bug on APIs 30, 31 and 32 where
|
// Stopping the codec before releasing it works around a bug on APIs 30, 31 and 32 where
|
||||||
|
@ -25,7 +25,6 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.RequiresPermission;
|
import androidx.annotation.RequiresPermission;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -44,7 +43,6 @@ import androidx.media3.common.util.Util;
|
|||||||
* android:exported="true"/>
|
* android:exported="true"/>
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class PlatformScheduler implements Scheduler {
|
public final class PlatformScheduler implements Scheduler {
|
||||||
|
|
||||||
|
@ -202,9 +202,7 @@ public final class Requirements implements Parcelable {
|
|||||||
private boolean isDeviceIdle(Context context) {
|
private boolean isDeviceIdle(Context context) {
|
||||||
PowerManager powerManager =
|
PowerManager powerManager =
|
||||||
(PowerManager) Assertions.checkNotNull(context.getSystemService(Context.POWER_SERVICE));
|
(PowerManager) Assertions.checkNotNull(context.getSystemService(Context.POWER_SERVICE));
|
||||||
return Util.SDK_INT >= 23
|
return Util.SDK_INT >= 23 ? powerManager.isDeviceIdleMode() : !powerManager.isInteractive();
|
||||||
? powerManager.isDeviceIdleMode()
|
|
||||||
: Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStorageNotLow(Context context) {
|
private boolean isStorageNotLow(Context context) {
|
||||||
|
@ -58,13 +58,10 @@ 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;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
@ -491,7 +488,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
private final VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo;
|
private final VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo;
|
||||||
|
|
||||||
private @MonotonicNonNull VideoFrameProcessor videoFrameProcessor;
|
private @MonotonicNonNull VideoFrameProcessor videoFrameProcessor;
|
||||||
@Nullable private Effect rotationEffect;
|
|
||||||
@Nullable private Format inputFormat;
|
@Nullable private Format inputFormat;
|
||||||
private @InputType int inputType;
|
private @InputType int inputType;
|
||||||
private long inputStreamStartPositionUs;
|
private long inputStreamStartPositionUs;
|
||||||
@ -611,21 +607,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
throw new UnsupportedOperationException("Unsupported input type " + inputType);
|
throw new UnsupportedOperationException("Unsupported input type " + inputType);
|
||||||
}
|
}
|
||||||
videoFrameReleaseControl.setFrameRate(format.frameRate);
|
videoFrameReleaseControl.setFrameRate(format.frameRate);
|
||||||
// MediaCodec applies rotation after API 21.
|
|
||||||
if (inputType == INPUT_TYPE_SURFACE
|
|
||||||
&& Util.SDK_INT < 21
|
|
||||||
&& format.rotationDegrees != Format.NO_VALUE
|
|
||||||
&& format.rotationDegrees != 0) {
|
|
||||||
// We must apply a rotation effect.
|
|
||||||
if (rotationEffect == null
|
|
||||||
|| this.inputFormat == null
|
|
||||||
|| this.inputFormat.rotationDegrees != format.rotationDegrees) {
|
|
||||||
rotationEffect = ScaleAndRotateAccessor.createRotationEffect(format.rotationDegrees);
|
|
||||||
} // Else, the rotation effect matches the previous format's rotation degrees, keep the same
|
|
||||||
// instance.
|
|
||||||
} else {
|
|
||||||
rotationEffect = null;
|
|
||||||
}
|
|
||||||
this.inputType = inputType;
|
this.inputType = inputType;
|
||||||
this.inputFormat = format;
|
this.inputFormat = format;
|
||||||
|
|
||||||
@ -884,9 +865,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Effect> effects = new ArrayList<>();
|
ArrayList<Effect> effects = new ArrayList<>();
|
||||||
if (rotationEffect != null) {
|
|
||||||
effects.add(rotationEffect);
|
|
||||||
}
|
|
||||||
effects.addAll(videoEffects);
|
effects.addAll(videoEffects);
|
||||||
Format inputFormat = checkNotNull(this.inputFormat);
|
Format inputFormat = checkNotNull(this.inputFormat);
|
||||||
checkStateNotNull(videoFrameProcessor)
|
checkStateNotNull(videoFrameProcessor)
|
||||||
@ -1081,42 +1059,4 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
listener);
|
listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ScaleAndRotateAccessor {
|
|
||||||
private static @MonotonicNonNull Constructor<?> scaleAndRotateTransformationBuilderConstructor;
|
|
||||||
private static @MonotonicNonNull Method setRotationMethod;
|
|
||||||
private static @MonotonicNonNull Method buildScaleAndRotateTransformationMethod;
|
|
||||||
|
|
||||||
public static Effect createRotationEffect(float rotationDegrees) {
|
|
||||||
try {
|
|
||||||
prepare();
|
|
||||||
Object builder = scaleAndRotateTransformationBuilderConstructor.newInstance();
|
|
||||||
setRotationMethod.invoke(builder, rotationDegrees);
|
|
||||||
return (Effect) checkNotNull(buildScaleAndRotateTransformationMethod.invoke(builder));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnsuresNonNull({
|
|
||||||
"scaleAndRotateTransformationBuilderConstructor",
|
|
||||||
"setRotationMethod",
|
|
||||||
"buildScaleAndRotateTransformationMethod"
|
|
||||||
})
|
|
||||||
private static void prepare() throws NoSuchMethodException, ClassNotFoundException {
|
|
||||||
if (scaleAndRotateTransformationBuilderConstructor == null
|
|
||||||
|| setRotationMethod == null
|
|
||||||
|| buildScaleAndRotateTransformationMethod == null) {
|
|
||||||
// TODO: b/284964524 - Add LINT and proguard checks for media3.effect reflection.
|
|
||||||
Class<?> scaleAndRotateTransformationBuilderClass =
|
|
||||||
Class.forName("androidx.media3.effect.ScaleAndRotateTransformation$Builder");
|
|
||||||
scaleAndRotateTransformationBuilderConstructor =
|
|
||||||
scaleAndRotateTransformationBuilderClass.getConstructor();
|
|
||||||
setRotationMethod =
|
|
||||||
scaleAndRotateTransformationBuilderClass.getMethod("setRotationDegrees", float.class);
|
|
||||||
buildScaleAndRotateTransformationMethod =
|
|
||||||
scaleAndRotateTransformationBuilderClass.getMethod("build");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package androidx.media3.exoplayer.audio;
|
|||||||
import static androidx.media3.common.C.FORMAT_HANDLED;
|
import static androidx.media3.common.C.FORMAT_HANDLED;
|
||||||
import static androidx.media3.exoplayer.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
|
import static androidx.media3.exoplayer.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
|
||||||
import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_PRIMARY;
|
import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_PRIMARY;
|
||||||
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_NOT_SUPPORTED;
|
|
||||||
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_SUPPORTED;
|
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_SUPPORTED;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
|
||||||
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
||||||
@ -96,17 +95,6 @@ public class DecoderAudioRendererTest {
|
|||||||
audioRenderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT);
|
audioRenderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Config(sdk = 19)
|
|
||||||
@Test
|
|
||||||
public void supportsFormatAtApi19() {
|
|
||||||
assertThat(audioRenderer.supportsFormat(FORMAT))
|
|
||||||
.isEqualTo(
|
|
||||||
ADAPTIVE_NOT_SEAMLESS
|
|
||||||
| TUNNELING_NOT_SUPPORTED
|
|
||||||
| FORMAT_HANDLED
|
|
||||||
| DECODER_SUPPORT_PRIMARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Config(sdk = 21)
|
@Config(sdk = 21)
|
||||||
@Test
|
@Test
|
||||||
public void supportsFormatAtApi21() {
|
public void supportsFormatAtApi21() {
|
||||||
|
@ -315,20 +315,6 @@ public final class DefaultAudioSinkTest {
|
|||||||
.isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING);
|
.isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Config(maxSdk = 20)
|
|
||||||
@Test
|
|
||||||
public void floatPcmNeedsTranscodingIfFloatOutputEnabledBeforeApi21() {
|
|
||||||
defaultAudioSink = new DefaultAudioSink.Builder().setEnableFloatOutput(true).build();
|
|
||||||
Format floatFormat =
|
|
||||||
STEREO_44_1_FORMAT
|
|
||||||
.buildUpon()
|
|
||||||
.setSampleMimeType(MimeTypes.AUDIO_RAW)
|
|
||||||
.setPcmEncoding(C.ENCODING_PCM_FLOAT)
|
|
||||||
.build();
|
|
||||||
assertThat(defaultAudioSink.getFormatSupport(floatFormat))
|
|
||||||
.isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Config(minSdk = 21)
|
@Config(minSdk = 21)
|
||||||
@Test
|
@Test
|
||||||
public void floatOutputSupportedIfFloatOutputEnabledFromApi21() {
|
public void floatOutputSupportedIfFloatOutputEnabledFromApi21() {
|
||||||
|
@ -21,7 +21,6 @@ import android.media.MediaCodec.BufferInfo;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -218,8 +217,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Locale locale =
|
Locale locale = Locale.forLanguageTag(languageTag);
|
||||||
Util.SDK_INT >= 21 ? Locale.forLanguageTag(languageTag) : new Locale(languageTag);
|
|
||||||
|
|
||||||
return locale.getISO3Language().isEmpty() ? languageTag : locale.getISO3Language();
|
return locale.getISO3Language().isEmpty() ? languageTag : locale.getISO3Language();
|
||||||
}
|
}
|
||||||
|
@ -366,7 +366,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.isCommandAvailable(COMMAND_STOP) || Util.SDK_INT < 21) {
|
if (player.isCommandAvailable(COMMAND_STOP)) {
|
||||||
// We must include a cancel intent for pre-L devices.
|
// We must include a cancel intent for pre-L devices.
|
||||||
mediaStyle.setCancelButtonIntent(
|
mediaStyle.setCancelButtonIntent(
|
||||||
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP));
|
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP));
|
||||||
@ -639,9 +639,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static long getPlaybackStartTimeEpochMs(Player player) {
|
private static long getPlaybackStartTimeEpochMs(Player player) {
|
||||||
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
|
if (player.isPlaying()
|
||||||
if (Util.SDK_INT >= 21
|
|
||||||
&& player.isPlaying()
|
|
||||||
&& !player.isPlayingAd()
|
&& !player.isPlayingAd()
|
||||||
&& !player.isCurrentMediaItemDynamic()
|
&& !player.isCurrentMediaItemDynamic()
|
||||||
&& player.getPlaybackParameters().speed == 1f) {
|
&& player.getPlaybackParameters().speed == 1f) {
|
||||||
|
@ -98,13 +98,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
|
|||||||
* <th>{@link ControllerInfo#getPackageName()}<br>for legacy browser</th>
|
* <th>{@link ControllerInfo#getPackageName()}<br>for legacy browser</th>
|
||||||
* <th>{@link ControllerInfo#getUid()}<br>for legacy browser</th></tr>
|
* <th>{@link ControllerInfo#getUid()}<br>for legacy browser</th></tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td>{@code SDK_INT < 21}</td>
|
|
||||||
* <td>Actual package name via {@link Context#getPackageName()}</td>
|
|
||||||
* <td>Actual UID</td>
|
|
||||||
* </tr>
|
|
||||||
* <tr>
|
|
||||||
* <td>
|
* <td>
|
||||||
* {@code 21 <= SDK_INT < 28}<br>
|
* {@code SDK_INT < 28}<br>
|
||||||
* for {@link Callback#onConnect onConnect}<br>
|
* for {@link Callback#onConnect onConnect}<br>
|
||||||
* and {@link Callback#onGetLibraryRoot onGetLibraryRoot}
|
* and {@link Callback#onGetLibraryRoot onGetLibraryRoot}
|
||||||
* </td>
|
* </td>
|
||||||
@ -113,7 +108,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
|
|||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td>
|
* <td>
|
||||||
* {@code 21 <= SDK_INT < 28}<br>
|
* {@code SDK_INT < 28}<br>
|
||||||
* for other {@link Callback callbacks}
|
* for other {@link Callback callbacks}
|
||||||
* </td>
|
* </td>
|
||||||
* <td>{@link ControllerInfo#LEGACY_CONTROLLER_PACKAGE_NAME}</td>
|
* <td>{@link ControllerInfo#LEGACY_CONTROLLER_PACKAGE_NAME}</td>
|
||||||
|
@ -210,14 +210,11 @@ import java.util.concurrent.TimeoutException;
|
|||||||
MediaSession session,
|
MediaSession session,
|
||||||
MediaNotification mediaNotification,
|
MediaNotification mediaNotification,
|
||||||
boolean startInForegroundRequired) {
|
boolean startInForegroundRequired) {
|
||||||
if (Util.SDK_INT >= 21) {
|
// Call Notification.MediaStyle#setMediaSession() indirectly.
|
||||||
// Call Notification.MediaStyle#setMediaSession() indirectly.
|
android.media.session.MediaSession.Token fwkToken =
|
||||||
android.media.session.MediaSession.Token fwkToken =
|
(android.media.session.MediaSession.Token)
|
||||||
(android.media.session.MediaSession.Token)
|
session.getSessionCompat().getSessionToken().getToken();
|
||||||
session.getSessionCompat().getSessionToken().getToken();
|
mediaNotification.notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, fwkToken);
|
||||||
mediaNotification.notification.extras.putParcelable(
|
|
||||||
Notification.EXTRA_MEDIA_SESSION, fwkToken);
|
|
||||||
}
|
|
||||||
this.mediaNotification = mediaNotification;
|
this.mediaNotification = mediaNotification;
|
||||||
if (startInForegroundRequired) {
|
if (startInForegroundRequired) {
|
||||||
startForeground(mediaNotification);
|
startForeground(mediaNotification);
|
||||||
@ -379,9 +376,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
if (Util.SDK_INT >= 24) {
|
if (Util.SDK_INT >= 24) {
|
||||||
Api24.stopForeground(mediaSessionService, removeNotifications);
|
Api24.stopForeground(mediaSessionService, removeNotifications);
|
||||||
} else {
|
} else {
|
||||||
// For pre-L devices, we must call Service.stopForeground(true) anyway as a workaround
|
mediaSessionService.stopForeground(removeNotifications);
|
||||||
// that prevents the media notification from being undismissable.
|
|
||||||
mediaSessionService.stopForeground(removeNotifications || Util.SDK_INT < 21);
|
|
||||||
}
|
}
|
||||||
startedInForeground = false;
|
startedInForeground = false;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import static androidx.media3.session.SessionResult.RESULT_SUCCESS;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -524,25 +523,7 @@ public class MediaSession {
|
|||||||
return interfaceVersion;
|
return interfaceVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the package name, or {@link #LEGACY_CONTROLLER_PACKAGE_NAME} on API ≤ 24. */
|
||||||
* Returns the package name. Can be {@link #LEGACY_CONTROLLER_PACKAGE_NAME} for
|
|
||||||
* interoperability.
|
|
||||||
*
|
|
||||||
* <p>Interoperability: Package name may not be precisely obtained for legacy controller API on
|
|
||||||
* older device. Here are details.
|
|
||||||
*
|
|
||||||
* <table>
|
|
||||||
* <caption>Summary when package name isn't precise</caption>
|
|
||||||
* <tr><th>SDK version when package name isn't precise</th>
|
|
||||||
* <th>{@code ControllerInfo#getPackageName()} for legacy controller</th>
|
|
||||||
* <tr><td>{@code SDK_INT < 21}</td>
|
|
||||||
* <td>Actual package name via {@link PackageManager#getNameForUid} with UID.<br>
|
|
||||||
* It's sufficient for most cases, but doesn't precisely distinguish caller if it
|
|
||||||
* uses shared user ID.</td>
|
|
||||||
* <tr><td>{@code 21 <= SDK_INT < 24}</td>
|
|
||||||
* <td>{@link #LEGACY_CONTROLLER_PACKAGE_NAME}</td>
|
|
||||||
* </table>
|
|
||||||
*/
|
|
||||||
public String getPackageName() {
|
public String getPackageName() {
|
||||||
return remoteUserInfo.getPackageName();
|
return remoteUserInfo.getPackageName();
|
||||||
}
|
}
|
||||||
@ -550,8 +531,8 @@ public class MediaSession {
|
|||||||
/**
|
/**
|
||||||
* Returns the UID of the controller. Can be a negative value for interoperability.
|
* Returns the UID of the controller. Can be a negative value for interoperability.
|
||||||
*
|
*
|
||||||
* <p>Interoperability: If {@code 21 <= SDK_INT < 28}, then UID would be a negative value
|
* <p>Interoperability: If {@code SDK_INT < 28}, then UID would be a negative value because it
|
||||||
* because it cannot be obtained.
|
* cannot be obtained.
|
||||||
*/
|
*/
|
||||||
public int getUid() {
|
public int getUid() {
|
||||||
return remoteUserInfo.getUid();
|
return remoteUserInfo.getUid();
|
||||||
@ -1221,7 +1202,6 @@ public class MediaSession {
|
|||||||
* android.media.session.MediaSession} created internally by this session.
|
* android.media.session.MediaSession} created internally by this session.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnnecessarilyFullyQualified") // Avoiding clash with Media3 token.
|
@SuppressWarnings("UnnecessarilyFullyQualified") // Avoiding clash with Media3 token.
|
||||||
@RequiresApi(21)
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final android.media.session.MediaSession.Token getPlatformToken() {
|
public final android.media.session.MediaSession.Token getPlatformToken() {
|
||||||
return (android.media.session.MediaSession.Token)
|
return (android.media.session.MediaSession.Token)
|
||||||
|
@ -53,11 +53,9 @@ import android.os.SystemClock;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import androidx.annotation.CheckResult;
|
import androidx.annotation.CheckResult;
|
||||||
import androidx.annotation.DoNotInline;
|
|
||||||
import androidx.annotation.FloatRange;
|
import androidx.annotation.FloatRange;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.AudioAttributes;
|
import androidx.media3.common.AudioAttributes;
|
||||||
import androidx.media3.common.DeviceInfo;
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -1213,7 +1211,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
// Double tap detection.
|
// Double tap detection.
|
||||||
int keyCode = keyEvent.getKeyCode();
|
int keyCode = keyEvent.getKeyCode();
|
||||||
boolean isTvApp = Util.SDK_INT >= 21 && Api21.isTvApp(context);
|
boolean isTvApp = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
|
||||||
boolean doubleTapCompleted = false;
|
boolean doubleTapCompleted = false;
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
@ -1910,12 +1908,4 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class Api21 {
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isTvApp(Context context) {
|
|
||||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
|||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
import static androidx.media3.common.util.Util.castNonNull;
|
import static androidx.media3.common.util.Util.castNonNull;
|
||||||
import static androidx.media3.common.util.Util.postOrRun;
|
import static androidx.media3.common.util.Util.postOrRun;
|
||||||
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
|
||||||
import static androidx.media3.session.SessionCommand.COMMAND_CODE_CUSTOM;
|
import static androidx.media3.session.SessionCommand.COMMAND_CODE_CUSTOM;
|
||||||
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
||||||
import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED;
|
import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED;
|
||||||
@ -1226,7 +1225,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
() -> {
|
() -> {
|
||||||
int completedBitmapFutureCount = resultCount.incrementAndGet();
|
int completedBitmapFutureCount = resultCount.incrementAndGet();
|
||||||
if (completedBitmapFutureCount == mediaItemList.size()) {
|
if (completedBitmapFutureCount == mediaItemList.size()) {
|
||||||
handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, timeline, mediaItemList);
|
handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, mediaItemList);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1247,9 +1246,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleBitmapFuturesAllCompletedAndSetQueue(
|
private void handleBitmapFuturesAllCompletedAndSetQueue(
|
||||||
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures,
|
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures, List<MediaItem> mediaItems) {
|
||||||
Timeline timeline,
|
|
||||||
List<MediaItem> mediaItems) {
|
|
||||||
List<QueueItem> queueItemList = new ArrayList<>();
|
List<QueueItem> queueItemList = new ArrayList<>();
|
||||||
for (int i = 0; i < bitmapFutures.size(); i++) {
|
for (int i = 0; i < bitmapFutures.size(); i++) {
|
||||||
@Nullable ListenableFuture<Bitmap> future = bitmapFutures.get(i);
|
@Nullable ListenableFuture<Bitmap> future = bitmapFutures.get(i);
|
||||||
@ -1264,22 +1261,9 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
queueItemList.add(LegacyConversions.convertToQueueItem(mediaItems.get(i), i, bitmap));
|
queueItemList.add(LegacyConversions.convertToQueueItem(mediaItems.get(i), i, bitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Util.SDK_INT < 21) {
|
// Framework MediaSession#setQueue() uses ParceledListSlice,
|
||||||
// In order to avoid TransactionTooLargeException for below API 21, we need to
|
// which means we can safely send long lists.
|
||||||
// cut the list so that it doesn't exceed the binder transaction limit.
|
setQueue(sessionCompat, queueItemList);
|
||||||
List<QueueItem> truncatedList =
|
|
||||||
MediaUtils.truncateListBySize(queueItemList, TRANSACTION_SIZE_LIMIT_IN_BYTES);
|
|
||||||
if (truncatedList.size() != timeline.getWindowCount()) {
|
|
||||||
Log.i(
|
|
||||||
TAG,
|
|
||||||
"Sending " + truncatedList.size() + " items out of " + timeline.getWindowCount());
|
|
||||||
}
|
|
||||||
setQueue(sessionCompat, truncatedList);
|
|
||||||
} else {
|
|
||||||
// Framework MediaSession#setQueue() uses ParceledListSlice,
|
|
||||||
// which means we can safely send long lists.
|
|
||||||
setQueue(sessionCompat, queueItemList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -300,8 +300,7 @@ public final class SessionToken {
|
|||||||
|
|
||||||
private static MediaSessionCompat.Token createCompatToken(
|
private static MediaSessionCompat.Token createCompatToken(
|
||||||
Parcelable platformOrLegacyCompatToken) {
|
Parcelable platformOrLegacyCompatToken) {
|
||||||
if (Util.SDK_INT >= 21
|
if (platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) {
|
||||||
&& platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) {
|
|
||||||
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
|
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
|
||||||
}
|
}
|
||||||
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.
|
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.
|
||||||
|
@ -666,7 +666,7 @@ public final class DashStreamingTest {
|
|||||||
MediaCodecUtil.getDecoderInfo(
|
MediaCodecUtil.getDecoderInfo(
|
||||||
MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false);
|
MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false);
|
||||||
assertThat(decoderInfo).isNotNull();
|
assertThat(decoderInfo).isNotNull();
|
||||||
assertThat(Util.SDK_INT < 21 || decoderInfo.adaptive).isTrue();
|
assertThat(decoderInfo.adaptive).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -17,13 +17,11 @@ package androidx.media3.test.exoplayer.playback.gts;
|
|||||||
|
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
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.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
@ -244,8 +242,6 @@ import java.util.ArrayList;
|
|||||||
super.renderOutputBuffer(codec, index, presentationTimeUs);
|
super.renderOutputBuffer(codec, index, presentationTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("UseSdkSuppress") // Not a test class or method.
|
|
||||||
@RequiresApi(21)
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderOutputBufferV21(
|
protected void renderOutputBufferV21(
|
||||||
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {
|
||||||
|
@ -115,9 +115,7 @@ public class EnumerateDecodersTest {
|
|||||||
boolean isAudio = MimeTypes.isAudio(requestedMimeType);
|
boolean isAudio = MimeTypes.isAudio(requestedMimeType);
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
result.append("[requestedMimeType=").append(requestedMimeType);
|
result.append("[requestedMimeType=").append(requestedMimeType);
|
||||||
if (Util.SDK_INT >= 21) {
|
result.append(", mimeType=").append(codecCapabilities.getMimeType());
|
||||||
result.append(", mimeType=").append(codecCapabilities.getMimeType());
|
|
||||||
}
|
|
||||||
result.append(", profileLevels=");
|
result.append(", profileLevels=");
|
||||||
appendProfileLevels(codecCapabilities.profileLevels, result);
|
appendProfileLevels(codecCapabilities.profileLevels, result);
|
||||||
if (Util.SDK_INT >= 23) {
|
if (Util.SDK_INT >= 23) {
|
||||||
@ -125,23 +123,19 @@ public class EnumerateDecodersTest {
|
|||||||
.append(", maxSupportedInstances=")
|
.append(", maxSupportedInstances=")
|
||||||
.append(codecCapabilities.getMaxSupportedInstances());
|
.append(codecCapabilities.getMaxSupportedInstances());
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 21) {
|
if (isVideo) {
|
||||||
if (isVideo) {
|
result.append(", videoCapabilities=");
|
||||||
result.append(", videoCapabilities=");
|
appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result);
|
||||||
appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result);
|
result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats));
|
||||||
result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats));
|
} else if (isAudio) {
|
||||||
} else if (isAudio) {
|
result.append(", audioCapabilities=");
|
||||||
result.append(", audioCapabilities=");
|
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
|
||||||
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isVideo
|
if (isVideo
|
||||||
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
|
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
|
||||||
result.append(", FEATURE_AdaptivePlayback");
|
result.append(", FEATURE_AdaptivePlayback");
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 21
|
if (isVideo && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) {
|
||||||
&& isVideo
|
|
||||||
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) {
|
|
||||||
result.append(", FEATURE_SecurePlayback");
|
result.append(", FEATURE_SecurePlayback");
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 26
|
if (Util.SDK_INT >= 26
|
||||||
@ -149,8 +143,7 @@ public class EnumerateDecodersTest {
|
|||||||
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_PartialFrame)) {
|
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_PartialFrame)) {
|
||||||
result.append(", FEATURE_PartialFrame");
|
result.append(", FEATURE_PartialFrame");
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 21
|
if ((isVideo || isAudio)
|
||||||
&& (isVideo || isAudio)
|
|
||||||
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) {
|
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) {
|
||||||
result.append(", FEATURE_TunneledPlayback");
|
result.append(", FEATURE_TunneledPlayback");
|
||||||
}
|
}
|
||||||
|
@ -610,7 +610,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
session.setPlayer(playerConfigToUpdate);
|
session.setPlayer(playerConfigToUpdate);
|
||||||
|
|
||||||
// In API 21 and 22, onAudioInfoChanged is not called when playback is changed to local.
|
// In API 21 and 22, onAudioInfoChanged is not called when playback is changed to local.
|
||||||
if (Util.SDK_INT == 21 || Util.SDK_INT == 22) {
|
if (Util.SDK_INT <= 22) {
|
||||||
PollingCheck.waitFor(
|
PollingCheck.waitFor(
|
||||||
TIMEOUT_MS,
|
TIMEOUT_MS,
|
||||||
() -> {
|
() -> {
|
||||||
@ -657,21 +657,13 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
|
|
||||||
session.setPlayer(playerConfig);
|
session.setPlayer(playerConfig);
|
||||||
|
|
||||||
// In API 21+, onAudioInfoChanged() is not called when playbackType is not changed.
|
PollingCheck.waitFor(
|
||||||
if (Util.SDK_INT >= 21) {
|
TIMEOUT_MS,
|
||||||
PollingCheck.waitFor(
|
() -> {
|
||||||
TIMEOUT_MS,
|
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
||||||
() -> {
|
return info.getPlaybackType() == legacyPlaybackType
|
||||||
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
&& info.getAudioAttributes().getLegacyStreamType() == legacyStream;
|
||||||
return info.getPlaybackType() == legacyPlaybackType
|
});
|
||||||
&& info.getAudioAttributes().getLegacyStreamType() == legacyStream;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
|
||||||
assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackType);
|
|
||||||
assertThat(info.getAudioAttributes().getLegacyStreamType()).isEqualTo(legacyStream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -709,23 +701,14 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
|
|
||||||
session.setPlayer(playerConfigToUpdate);
|
session.setPlayer(playerConfigToUpdate);
|
||||||
|
|
||||||
// In API 21+, onAudioInfoChanged() is not called when playbackType is not changed.
|
PollingCheck.waitFor(
|
||||||
if (Util.SDK_INT >= 21) {
|
TIMEOUT_MS,
|
||||||
PollingCheck.waitFor(
|
() -> {
|
||||||
TIMEOUT_MS,
|
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
||||||
() -> {
|
return info.getPlaybackType() == legacyPlaybackTypeToUpdate
|
||||||
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
&& info.getMaxVolume() == deviceInfoToUpdate.maxVolume
|
||||||
return info.getPlaybackType() == legacyPlaybackTypeToUpdate
|
&& info.getCurrentVolume() == deviceVolumeToUpdate;
|
||||||
&& info.getMaxVolume() == deviceInfoToUpdate.maxVolume
|
});
|
||||||
&& info.getCurrentVolume() == deviceVolumeToUpdate;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
|
||||||
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
|
|
||||||
assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackTypeToUpdate);
|
|
||||||
assertThat(info.getMaxVolume()).isEqualTo(deviceInfoToUpdate.maxVolume);
|
|
||||||
assertThat(info.getCurrentVolume()).isEqualTo(deviceVolumeToUpdate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1555,15 +1538,8 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||||||
assertThat(latch.await(LONG_TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(latch.await(LONG_TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
List<QueueItem> queueFromParam = queueRef.get();
|
List<QueueItem> queueFromParam = queueRef.get();
|
||||||
List<QueueItem> queueFromGetter = controllerCompat.getQueue();
|
List<QueueItem> queueFromGetter = controllerCompat.getQueue();
|
||||||
if (Util.SDK_INT >= 21) {
|
assertThat(queueFromParam).hasSize(listSize);
|
||||||
assertThat(queueFromParam).hasSize(listSize);
|
assertThat(queueFromGetter).hasSize(listSize);
|
||||||
assertThat(queueFromGetter).hasSize(listSize);
|
|
||||||
} else {
|
|
||||||
// Below API 21, only the initial part of the playlist is sent to the
|
|
||||||
// MediaControllerCompat when the list is too long.
|
|
||||||
assertThat(queueFromParam.size() < listSize).isTrue();
|
|
||||||
assertThat(queueFromGetter).hasSize(queueFromParam.size());
|
|
||||||
}
|
|
||||||
for (int i = 0; i < queueFromParam.size(); i++) {
|
for (int i = 0; i < queueFromParam.size(); i++) {
|
||||||
assertThat(queueFromParam.get(i).getDescription().getMediaId())
|
assertThat(queueFromParam.get(i).getDescription().getMediaId())
|
||||||
.isEqualTo(TestUtils.getMediaIdInFakeTimeline(i));
|
.isEqualTo(TestUtils.getMediaIdInFakeTimeline(i));
|
||||||
|
@ -347,7 +347,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
|
|||||||
// We need to trigger MediaControllerCompat.Callback.onAudioInfoChanged in order to raise the
|
// We need to trigger MediaControllerCompat.Callback.onAudioInfoChanged in order to raise the
|
||||||
// onAudioAttributesChanged() callback. In API 21 and 22, onAudioInfoChanged is not called when
|
// onAudioAttributesChanged() callback. In API 21 and 22, onAudioInfoChanged is not called when
|
||||||
// playback is changed to local.
|
// playback is changed to local.
|
||||||
assumeTrue(Util.SDK_INT != 21 && Util.SDK_INT != 22);
|
assumeTrue(Util.SDK_INT > 22);
|
||||||
|
|
||||||
session.setPlaybackToRemote(
|
session.setPlaybackToRemote(
|
||||||
/* volumeControl= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE,
|
/* volumeControl= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE,
|
||||||
|
@ -49,7 +49,6 @@ import androidx.media3.common.Player.PositionInfo;
|
|||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.Timeline.Window;
|
import androidx.media3.common.Timeline.Window;
|
||||||
import androidx.media3.common.util.BitmapLoader;
|
import androidx.media3.common.util.BitmapLoader;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.datasource.DataSourceBitmapLoader;
|
import androidx.media3.datasource.DataSourceBitmapLoader;
|
||||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@ -200,24 +199,6 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest {
|
|||||||
MediaItem mediaItem = timelineRef.get().getWindow(i, new Window()).mediaItem;
|
MediaItem mediaItem = timelineRef.get().getWindow(i, new Window()).mediaItem;
|
||||||
MediaItem expectedMediaItem =
|
MediaItem expectedMediaItem =
|
||||||
(i == testMediaItemIndex) ? testCurrentMediaItem : testMediaItems.get(i);
|
(i == testMediaItemIndex) ? testCurrentMediaItem : testMediaItems.get(i);
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
// Bitmap conversion and back gives not exactly the same byte array below API 21
|
|
||||||
MediaMetadata mediaMetadata =
|
|
||||||
mediaItem
|
|
||||||
.mediaMetadata
|
|
||||||
.buildUpon()
|
|
||||||
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
|
|
||||||
.build();
|
|
||||||
MediaMetadata expectedMediaMetadata =
|
|
||||||
expectedMediaItem
|
|
||||||
.mediaMetadata
|
|
||||||
.buildUpon()
|
|
||||||
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
|
|
||||||
.build();
|
|
||||||
mediaItem = mediaItem.buildUpon().setMediaMetadata(mediaMetadata).build();
|
|
||||||
expectedMediaItem =
|
|
||||||
expectedMediaItem.buildUpon().setMediaMetadata(expectedMediaMetadata).build();
|
|
||||||
}
|
|
||||||
assertThat(mediaItem).isEqualTo(expectedMediaItem);
|
assertThat(mediaItem).isEqualTo(expectedMediaItem);
|
||||||
}
|
}
|
||||||
assertThat(timelineChangeReasonRef.get()).isEqualTo(TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
assertThat(timelineChangeReasonRef.get()).isEqualTo(TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||||
|
@ -27,7 +27,6 @@ import android.media.session.PlaybackState;
|
|||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.Player.State;
|
import androidx.media3.common.Player.State;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
import androidx.media3.test.session.common.MainLooperTestRule;
|
||||||
import androidx.media3.test.session.common.TestHandler;
|
import androidx.media3.test.session.common.TestHandler;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@ -37,7 +36,6 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -57,9 +55,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context = ApplicationProvider.getApplicationContext();
|
context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
HandlerThread handlerThread = new HandlerThread(TAG);
|
HandlerThread handlerThread = new HandlerThread(TAG);
|
||||||
@ -79,7 +74,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
|
|||||||
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
|
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
|
||||||
@Test
|
@Test
|
||||||
public void createController() throws Exception {
|
public void createController() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession.
|
|
||||||
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
|
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
|
||||||
fwkSession.setActive(true);
|
fwkSession.setActive(true);
|
||||||
fwkSession.setFlags(
|
fwkSession.setFlags(
|
||||||
@ -100,7 +94,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
|
|||||||
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
|
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
|
||||||
@Test
|
@Test
|
||||||
public void onPlaybackStateChanged_isNotifiedByFwkSessionChanges() throws Exception {
|
public void onPlaybackStateChanged_isNotifiedByFwkSessionChanges() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession.
|
|
||||||
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
|
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
|
||||||
fwkSession.setActive(true);
|
fwkSession.setActive(true);
|
||||||
fwkSession.setFlags(
|
fwkSession.setFlags(
|
||||||
|
@ -571,11 +571,8 @@ public class MediaControllerWithMediaSessionCompatTest {
|
|||||||
assertThat(TextUtils.equals(metadata.subtitle, testSubtitle)).isTrue();
|
assertThat(TextUtils.equals(metadata.subtitle, testSubtitle)).isTrue();
|
||||||
assertThat(TextUtils.equals(metadata.description, testDescription)).isTrue();
|
assertThat(TextUtils.equals(metadata.description, testDescription)).isTrue();
|
||||||
assertThat(metadata.artworkUri).isEqualTo(testIconUri);
|
assertThat(metadata.artworkUri).isEqualTo(testIconUri);
|
||||||
if (Util.SDK_INT >= 21) {
|
assertThat(metadata.artworkData).isEqualTo(testArtworkData);
|
||||||
// Bitmap conversion and back gives not exactly the same byte array below API 21
|
if (Util.SDK_INT >= 23) {
|
||||||
assertThat(metadata.artworkData).isEqualTo(testArtworkData);
|
|
||||||
}
|
|
||||||
if (Util.SDK_INT < 21 || Util.SDK_INT >= 23) {
|
|
||||||
// TODO(b/199055952): Test mediaUri for all API levels once the bug is fixed.
|
// TODO(b/199055952): Test mediaUri for all API levels once the bug is fixed.
|
||||||
assertThat(mediaItem.requestMetadata.mediaUri).isEqualTo(testMediaUri);
|
assertThat(mediaItem.requestMetadata.mediaUri).isEqualTo(testMediaUri);
|
||||||
}
|
}
|
||||||
@ -909,19 +906,6 @@ public class MediaControllerWithMediaSessionCompatTest {
|
|||||||
threadTestRule.getHandler().postAndSync(controller::getMediaMetadata);
|
threadTestRule.getHandler().postAndSync(controller::getMediaMetadata);
|
||||||
|
|
||||||
assertThat(mediaMetadata.artworkData).isNotNull();
|
assertThat(mediaMetadata.artworkData).isNotNull();
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
// Bitmap conversion and back gives not exactly the same byte array below API 21
|
|
||||||
mediaMetadata =
|
|
||||||
mediaMetadata
|
|
||||||
.buildUpon()
|
|
||||||
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
|
|
||||||
.build();
|
|
||||||
testMediaMetadata =
|
|
||||||
testMediaMetadata
|
|
||||||
.buildUpon()
|
|
||||||
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
assertThat(mediaMetadata.artworkData).isEqualTo(testMediaMetadata.artworkData);
|
assertThat(mediaMetadata.artworkData).isEqualTo(testMediaMetadata.artworkData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1573,7 +1557,7 @@ public class MediaControllerWithMediaSessionCompatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setPlaybackToLocal_notifiesDeviceInfoAndVolume() throws Exception {
|
public void setPlaybackToLocal_notifiesDeviceInfoAndVolume() throws Exception {
|
||||||
if (Util.SDK_INT == 21 || Util.SDK_INT == 22) {
|
if (Util.SDK_INT <= 22) {
|
||||||
// In API 21 and 22, onAudioInfoChanged is not called.
|
// In API 21 and 22, onAudioInfoChanged is not called.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
private static final String TEST_URI = "http://test.test";
|
private static final String TEST_URI = "http://test.test";
|
||||||
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
|
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
|
||||||
(Util.SDK_INT < 21 || Util.SDK_INT >= 24) ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
|
Util.SDK_INT >= 24 ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
|
||||||
|
|
||||||
@ClassRule public static MainLooperTestRule mainLooperTestRule = new MainLooperTestRule();
|
@ClassRule public static MainLooperTestRule mainLooperTestRule = new MainLooperTestRule();
|
||||||
|
|
||||||
@ -1200,7 +1200,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setVolumeWithLocalVolume() throws Exception {
|
public void setVolumeWithLocalVolume() throws Exception {
|
||||||
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
|
if (audioManager.isVolumeFixed()) {
|
||||||
// This test is not eligible for this device.
|
// This test is not eligible for this device.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1250,7 +1250,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjustVolumeWithLocalVolume() throws Exception {
|
public void adjustVolumeWithLocalVolume() throws Exception {
|
||||||
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
|
if (audioManager.isVolumeFixed()) {
|
||||||
// This test is not eligible for this device.
|
// This test is not eligible for this device.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -541,7 +541,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setDeviceVolume_forLocalPlayback_setsStreamVolume() throws Exception {
|
public void setDeviceVolume_forLocalPlayback_setsStreamVolume() throws Exception {
|
||||||
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
|
if (audioManager.isVolumeFixed()) {
|
||||||
// This test is not eligible for this device.
|
// This test is not eligible for this device.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -570,7 +570,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void increaseDeviceVolume_forLocalPlayback_increasesStreamVolume() throws Exception {
|
public void increaseDeviceVolume_forLocalPlayback_increasesStreamVolume() throws Exception {
|
||||||
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
|
if (audioManager.isVolumeFixed()) {
|
||||||
// This test is not eligible for this device.
|
// This test is not eligible for this device.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -600,7 +600,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decreaseDeviceVolume_forLocalPlayback_decreasesStreamVolume() throws Exception {
|
public void decreaseDeviceVolume_forLocalPlayback_decreasesStreamVolume() throws Exception {
|
||||||
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
|
if (audioManager.isVolumeFixed()) {
|
||||||
// This test is not eligible for this device.
|
// This test is not eligible for this device.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@ -79,9 +78,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
handler = threadTestRule.getHandler();
|
handler = threadTestRule.getHandler();
|
||||||
@ -128,9 +124,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
if (mediaPlayer != null) {
|
if (mediaPlayer != null) {
|
||||||
@ -143,7 +136,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playKeyEvent() throws Exception {
|
public void playKeyEvent() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
||||||
@ -151,7 +143,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pauseKeyEvent() throws Exception {
|
public void pauseKeyEvent() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
|
||||||
@ -159,7 +150,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nextKeyEvent() throws Exception {
|
public void nextKeyEvent() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
|
||||||
@ -167,7 +157,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void previousKeyEvent() throws Exception {
|
public void previousKeyEvent() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
|
||||||
@ -177,7 +166,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
public void
|
public void
|
||||||
fastForwardKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
|
fastForwardKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
MediaController controller = connectMediaNotificationController();
|
MediaController controller = connectMediaNotificationController();
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, /* doubleTap= */ false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, /* doubleTap= */ false);
|
||||||
|
|
||||||
@ -204,8 +192,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
public void
|
public void
|
||||||
fastForwardKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
|
fastForwardKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_FORWARD, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_FORWARD, TIMEOUT_MS);
|
||||||
@ -220,7 +206,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
@Test
|
@Test
|
||||||
public void rewindKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
|
public void rewindKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
MediaController controller = connectMediaNotificationController();
|
MediaController controller = connectMediaNotificationController();
|
||||||
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
|
||||||
@ -246,8 +231,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
public void
|
public void
|
||||||
rewindKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
|
rewindKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_BACK, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_BACK, TIMEOUT_MS);
|
||||||
@ -261,7 +244,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void stopKeyEvent() throws Exception {
|
public void stopKeyEvent() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false);
|
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
|
||||||
@ -320,7 +302,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playPauseKeyEvent_playing_pause() throws Exception {
|
public void playPauseKeyEvent_playing_pause() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
player.playWhenReady = true;
|
player.playWhenReady = true;
|
||||||
@ -334,7 +315,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playPauseKeyEvent_doubleTapOnPlayPause_seekNext() throws Exception {
|
public void playPauseKeyEvent_doubleTapOnPlayPause_seekNext() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
player.playWhenReady = true;
|
player.playWhenReady = true;
|
||||||
@ -348,7 +328,6 @@ public class MediaSessionKeyEventTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playPauseKeyEvent_doubleTapOnHeadsetHook_seekNext() throws Exception {
|
public void playPauseKeyEvent_doubleTapOnHeadsetHook_seekNext() throws Exception {
|
||||||
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
player.playWhenReady = true;
|
player.playWhenReady = true;
|
||||||
@ -390,10 +369,8 @@ public class MediaSessionKeyEventTest {
|
|||||||
return SUPPORT_APP_PACKAGE_NAME;
|
return SUPPORT_APP_PACKAGE_NAME;
|
||||||
}
|
}
|
||||||
// Legacy controllers
|
// Legacy controllers
|
||||||
if (Util.SDK_INT < 21 || Util.SDK_INT >= 28) {
|
if (Util.SDK_INT >= 28) {
|
||||||
// Above API 28: package of the app using AudioManager.
|
// Above API 28: package of the app using AudioManager.
|
||||||
// Below 21: package of the owner of the session. Note: This is specific to this test setup
|
|
||||||
// where `ApplicationProvider.getContext().packageName == SUPPORT_APP_PACKAGE_NAME`.
|
|
||||||
return SUPPORT_APP_PACKAGE_NAME;
|
return SUPPORT_APP_PACKAGE_NAME;
|
||||||
} else if (Util.SDK_INT >= 24) {
|
} else if (Util.SDK_INT >= 24) {
|
||||||
// API 24 - 27: KeyEvent from system service has the package name "android".
|
// API 24 - 27: KeyEvent from system service has the package name "android".
|
||||||
|
@ -1014,8 +1014,7 @@ public class MediaSessionTest {
|
|||||||
* <p>Calling this method should only be required to test legacy behaviour.
|
* <p>Calling this method should only be required to test legacy behaviour.
|
||||||
*/
|
*/
|
||||||
private static String getControllerCallerPackageName(ControllerInfo controllerInfo) {
|
private static String getControllerCallerPackageName(ControllerInfo controllerInfo) {
|
||||||
return (Util.SDK_INT < 21
|
return (Util.SDK_INT > 23
|
||||||
|| Util.SDK_INT > 23
|
|
||||||
|| controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION)
|
|| controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION)
|
||||||
? ApplicationProvider.getApplicationContext().getPackageName()
|
? ApplicationProvider.getApplicationContext().getPackageName()
|
||||||
: MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
|
: MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
|
||||||
|
@ -20,7 +20,6 @@ import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_SE
|
|||||||
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
|
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
|
||||||
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -28,7 +27,6 @@ import android.os.Bundle;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import androidx.media3.common.MediaLibraryInfo;
|
import androidx.media3.common.MediaLibraryInfo;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
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;
|
||||||
import androidx.media3.test.session.common.TestUtils;
|
import androidx.media3.test.session.common.TestUtils;
|
||||||
@ -117,8 +115,6 @@ public class SessionTokenTest {
|
|||||||
@Test
|
@Test
|
||||||
public void createSessionToken_withPlatformTokenFromMedia1Session_returnsTokenForLegacySession()
|
public void createSessionToken_withPlatformTokenFromMedia1Session_returnsTokenForLegacySession()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
assumeTrue(Util.SDK_INT >= 21);
|
|
||||||
|
|
||||||
MediaSessionCompat sessionCompat =
|
MediaSessionCompat sessionCompat =
|
||||||
sessionTestRule.ensureReleaseAfterTest(
|
sessionTestRule.ensureReleaseAfterTest(
|
||||||
new MediaSessionCompat(context, "createSessionToken_withLegacyToken"));
|
new MediaSessionCompat(context, "createSessionToken_withLegacyToken"));
|
||||||
|
@ -29,7 +29,6 @@ import android.system.ErrnoException;
|
|||||||
import android.system.OsConstants;
|
import android.system.OsConstants;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -148,8 +147,7 @@ public final class AssetContentProvider extends ContentProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBrokenPipe(IOException e) {
|
private static boolean isBrokenPipe(IOException e) {
|
||||||
return Util.SDK_INT >= 21
|
return e.getCause() instanceof ErrnoException
|
||||||
&& e.getCause() instanceof ErrnoException
|
|
||||||
&& ((ErrnoException) e.getCause()).errno == OsConstants.EPIPE;
|
&& ((ErrnoException) e.getCause()).errno == OsConstants.EPIPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
dequeuedOutputBuffers.delete(index);
|
dequeuedOutputBuffers.delete(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
|
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
|
||||||
MediaCodec.BufferInfo bufferInfo = checkNotNull(dequeuedOutputBuffers.get(index));
|
MediaCodec.BufferInfo bufferInfo = checkNotNull(dequeuedOutputBuffers.get(index));
|
||||||
|
@ -204,9 +204,6 @@ public final class DecodeOneFrameUtil {
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String getSupportedDecoderName(MediaFormat format) {
|
private static String getSupportedDecoderName(MediaFormat format) {
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
throw new UnsupportedOperationException("Unable to detect decoder support under API 21.");
|
|
||||||
}
|
|
||||||
// TODO(b/266923205): De-duplicate logic from EncoderUtil.java#findCodecForFormat().
|
// TODO(b/266923205): De-duplicate logic from EncoderUtil.java#findCodecForFormat().
|
||||||
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||||
// Format must not include KEY_FRAME_RATE on API21.
|
// Format must not include KEY_FRAME_RATE on API21.
|
||||||
|
@ -24,7 +24,6 @@ import android.content.Context;
|
|||||||
import android.media.Image;
|
import android.media.Image;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -43,7 +42,6 @@ import java.nio.ByteBuffer;
|
|||||||
* channel (Y') because the {@linkplain MediaCodec decoder} decodes to luma.
|
* channel (Y') because the {@linkplain MediaCodec decoder} decodes to luma.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(21)
|
|
||||||
public final class SsimHelper {
|
public final class SsimHelper {
|
||||||
|
|
||||||
/** The default comparison interval. */
|
/** The default comparison interval. */
|
||||||
|
@ -32,7 +32,6 @@ import android.media.MediaFormat;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
@ -55,7 +54,6 @@ import java.util.List;
|
|||||||
* ImageReader} for use in CPU test utility functions.
|
* ImageReader} for use in CPU test utility functions.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RequiresApi(21)
|
|
||||||
public final class VideoDecodingWrapper implements AutoCloseable {
|
public final class VideoDecodingWrapper implements AutoCloseable {
|
||||||
|
|
||||||
private static final String TAG = "VideoDecodingWrapper";
|
private static final String TAG = "VideoDecodingWrapper";
|
||||||
|
@ -25,7 +25,6 @@ import android.media.MediaFormat;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
|
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
@ -77,10 +76,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void before() throws Throwable {
|
protected void before() throws Throwable {
|
||||||
if (Util.SDK_INT == 19) {
|
|
||||||
// Codec config not supported with Robolectric on API == 19. Skip rule set up step.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
configureCodecs(supportedMimeTypes);
|
configureCodecs(supportedMimeTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,10 +83,6 @@ 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) {
|
|
||||||
// Codec config not supported with Robolectric on API == 19. Skip rule tear down step.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShadowMediaCodecList.reset();
|
ShadowMediaCodecList.reset();
|
||||||
ShadowMediaCodec.clearCodecs();
|
ShadowMediaCodec.clearCodecs();
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ import android.view.accessibility.CaptioningManager;
|
|||||||
import android.view.accessibility.CaptioningManager.CaptionStyle;
|
import android.view.accessibility.CaptioningManager.CaptionStyle;
|
||||||
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.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.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@ -116,17 +114,13 @@ public final class CaptionStyleCompat {
|
|||||||
*/
|
*/
|
||||||
public static CaptionStyleCompat createFromCaptionStyle(
|
public static CaptionStyleCompat createFromCaptionStyle(
|
||||||
CaptioningManager.CaptionStyle captionStyle) {
|
CaptioningManager.CaptionStyle captionStyle) {
|
||||||
if (Util.SDK_INT >= 21) {
|
return new CaptionStyleCompat(
|
||||||
return createFromCaptionStyleV21(captionStyle);
|
captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor,
|
||||||
} else {
|
captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor,
|
||||||
return new CaptionStyleCompat(
|
captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor,
|
||||||
captionStyle.foregroundColor,
|
captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType,
|
||||||
captionStyle.backgroundColor,
|
captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor,
|
||||||
Color.TRANSPARENT,
|
captionStyle.getTypeface());
|
||||||
captionStyle.edgeType,
|
|
||||||
captionStyle.edgeColor,
|
|
||||||
captionStyle.getTypeface());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,17 +145,4 @@ public final class CaptionStyleCompat {
|
|||||||
this.edgeColor = edgeColor;
|
this.edgeColor = edgeColor;
|
||||||
this.typeface = typeface;
|
this.typeface = typeface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
@SuppressWarnings("ResourceType")
|
|
||||||
private static CaptionStyleCompat createFromCaptionStyleV21(
|
|
||||||
CaptioningManager.CaptionStyle captionStyle) {
|
|
||||||
return new CaptionStyleCompat(
|
|
||||||
captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor,
|
|
||||||
captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor,
|
|
||||||
captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor,
|
|
||||||
captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType,
|
|
||||||
captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor,
|
|
||||||
captionStyle.getTypeface());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -759,13 +759,8 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||||||
if (duration <= 0) {
|
if (duration <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT >= 21) {
|
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
|
||||||
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
|
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
|
||||||
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
|
|
||||||
} else {
|
|
||||||
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
|
|
||||||
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,8 +115,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider {
|
|||||||
if (TextUtils.isEmpty(language) || C.LANGUAGE_UNDETERMINED.equals(language)) {
|
if (TextUtils.isEmpty(language) || C.LANGUAGE_UNDETERMINED.equals(language)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
Locale languageLocale =
|
Locale languageLocale = Locale.forLanguageTag(language);
|
||||||
Util.SDK_INT >= 21 ? Locale.forLanguageTag(language) : new Locale(language);
|
|
||||||
Locale displayLocale = Util.getDefaultDisplayLocale();
|
Locale displayLocale = Util.getDefaultDisplayLocale();
|
||||||
String languageName = languageLocale.getDisplayName(displayLocale);
|
String languageName = languageLocale.getDisplayName(displayLocale);
|
||||||
if (TextUtils.isEmpty(languageName)) {
|
if (TextUtils.isEmpty(languageName)) {
|
||||||
|
@ -47,9 +47,7 @@ import android.view.accessibility.AccessibilityEvent;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.DoNotInline;
|
|
||||||
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.MediaLibraryInfo;
|
import androidx.media3.common.MediaLibraryInfo;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
@ -857,19 +855,15 @@ public class LegacyPlayerControlView extends FrameLayout {
|
|||||||
boolean requestPlayPauseAccessibilityFocus = false;
|
boolean requestPlayPauseAccessibilityFocus = false;
|
||||||
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, showPlayButtonIfSuppressed);
|
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, showPlayButtonIfSuppressed);
|
||||||
if (playButton != null) {
|
if (playButton != null) {
|
||||||
requestPlayPauseFocus |= !shouldShowPlayButton && playButton.isFocused();
|
requestPlayPauseFocus = !shouldShowPlayButton && playButton.isFocused();
|
||||||
requestPlayPauseAccessibilityFocus |=
|
requestPlayPauseAccessibilityFocus =
|
||||||
Util.SDK_INT < 21
|
(!shouldShowPlayButton && playButton.isAccessibilityFocused());
|
||||||
? requestPlayPauseFocus
|
|
||||||
: (!shouldShowPlayButton && Api21.isAccessibilityFocused(playButton));
|
|
||||||
playButton.setVisibility(shouldShowPlayButton ? VISIBLE : GONE);
|
playButton.setVisibility(shouldShowPlayButton ? VISIBLE : GONE);
|
||||||
}
|
}
|
||||||
if (pauseButton != null) {
|
if (pauseButton != null) {
|
||||||
requestPlayPauseFocus |= shouldShowPlayButton && pauseButton.isFocused();
|
requestPlayPauseFocus |= shouldShowPlayButton && pauseButton.isFocused();
|
||||||
requestPlayPauseAccessibilityFocus |=
|
requestPlayPauseAccessibilityFocus |=
|
||||||
Util.SDK_INT < 21
|
shouldShowPlayButton && pauseButton.isAccessibilityFocused();
|
||||||
? requestPlayPauseFocus
|
|
||||||
: (shouldShowPlayButton && Api21.isAccessibilityFocused(pauseButton));
|
|
||||||
pauseButton.setVisibility(shouldShowPlayButton ? GONE : VISIBLE);
|
pauseButton.setVisibility(shouldShowPlayButton ? GONE : VISIBLE);
|
||||||
}
|
}
|
||||||
if (requestPlayPauseFocus) {
|
if (requestPlayPauseFocus) {
|
||||||
@ -1357,12 +1351,4 @@ public class LegacyPlayerControlView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class Api21 {
|
|
||||||
@DoNotInline
|
|
||||||
public static boolean isAccessibilityFocused(View view) {
|
|
||||||
return view.isAccessibilityFocused();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ import androidx.annotation.DrawableRes;
|
|||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.IntRange;
|
import androidx.annotation.IntRange;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
|
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
@ -1013,11 +1012,10 @@ public class PlayerNotificationManager {
|
|||||||
* @deprecated Use {@link #setMediaSessionToken(MediaSession.Token)} and pass in {@code
|
* @deprecated Use {@link #setMediaSessionToken(MediaSession.Token)} and pass in {@code
|
||||||
* (MediaSession.Token) compatToken.getToken()}.
|
* (MediaSession.Token) compatToken.getToken()}.
|
||||||
*/
|
*/
|
||||||
|
// TODO: b/333355694 - Remove the dependency on androidx.media when this method is removed.
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public final void setMediaSessionToken(MediaSessionCompat.Token compatToken) {
|
public final void setMediaSessionToken(MediaSessionCompat.Token compatToken) {
|
||||||
if (Util.SDK_INT >= 21) {
|
setMediaSessionToken((MediaSession.Token) compatToken.getToken());
|
||||||
setMediaSessionToken((MediaSession.Token) compatToken.getToken());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1028,7 +1026,6 @@ public class PlayerNotificationManager {
|
|||||||
*
|
*
|
||||||
* @param token The {@link MediaSession.Token}.
|
* @param token The {@link MediaSession.Token}.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(21)
|
|
||||||
public final void setMediaSessionToken(MediaSession.Token token) {
|
public final void setMediaSessionToken(MediaSession.Token token) {
|
||||||
if (!Util.areEqual(this.mediaSessionToken, token)) {
|
if (!Util.areEqual(this.mediaSessionToken, token)) {
|
||||||
mediaSessionToken = token;
|
mediaSessionToken = token;
|
||||||
@ -1247,8 +1244,7 @@ public class PlayerNotificationManager {
|
|||||||
* Creates the notification given the current player state.
|
* Creates the notification given the current player state.
|
||||||
*
|
*
|
||||||
* @param player The player for which state to build a notification.
|
* @param player The player for which state to build a notification.
|
||||||
* @param builder The builder used to build the last notification, or {@code null}. Re-using the
|
* @param builder The builder used to build the last notification, or {@code null}.
|
||||||
* builder when possible can prevent notification flicker when {@code Util#SDK_INT} < 21.
|
|
||||||
* @param ongoing Whether the notification should be ongoing.
|
* @param ongoing Whether the notification should be ongoing.
|
||||||
* @param largeIcon The large icon to be used.
|
* @param largeIcon The large icon to be used.
|
||||||
* @return The {@link NotificationCompat.Builder} on which to call {@link
|
* @return The {@link NotificationCompat.Builder} on which to call {@link
|
||||||
@ -1291,17 +1287,7 @@ public class PlayerNotificationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int[] actionIndicesForCompactView = getActionIndicesForCompactView(actionNames, player);
|
int[] actionIndicesForCompactView = getActionIndicesForCompactView(actionNames, player);
|
||||||
if (Util.SDK_INT >= 21) {
|
builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView));
|
||||||
builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView));
|
|
||||||
} else {
|
|
||||||
// TODO: b/333355694 - Remove dependency on androidx.media once this logic is gone.
|
|
||||||
androidx.media.app.NotificationCompat.MediaStyle mediaStyle =
|
|
||||||
new androidx.media.app.NotificationCompat.MediaStyle();
|
|
||||||
mediaStyle.setShowActionsInCompactView(actionIndicesForCompactView);
|
|
||||||
mediaStyle.setShowCancelButton(!ongoing);
|
|
||||||
mediaStyle.setCancelButtonIntent(dismissPendingIntent);
|
|
||||||
builder.setStyle(mediaStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set intent which is sent if the user selects 'clear all'
|
// Set intent which is sent if the user selects 'clear all'
|
||||||
builder.setDeleteIntent(dismissPendingIntent);
|
builder.setDeleteIntent(dismissPendingIntent);
|
||||||
@ -1317,9 +1303,7 @@ public class PlayerNotificationManager {
|
|||||||
.setPriority(priority)
|
.setPriority(priority)
|
||||||
.setDefaults(defaults);
|
.setDefaults(defaults);
|
||||||
|
|
||||||
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
|
if (useChronometer
|
||||||
if (Util.SDK_INT >= 21
|
|
||||||
&& useChronometer
|
|
||||||
&& player.isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)
|
&& player.isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)
|
||||||
&& player.isPlaying()
|
&& player.isPlaying()
|
||||||
&& !player.isPlayingAd()
|
&& !player.isPlayingAd()
|
||||||
@ -1628,7 +1612,6 @@ public class PlayerNotificationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static final class MediaStyle extends androidx.core.app.NotificationCompat.Style {
|
private static final class MediaStyle extends androidx.core.app.NotificationCompat.Style {
|
||||||
|
|
||||||
private final int[] actionsToShowInCompact;
|
private final int[] actionsToShowInCompact;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user