diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
index 374e70f689..98d1113eda 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
@@ -69,6 +69,7 @@ import android.security.NetworkSecurityPolicy;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import android.view.Display;
import android.view.SurfaceView;
@@ -516,6 +517,16 @@ public final class Util {
return false;
}
+ /**
+ * Tests whether a {@link SparseArray} contains a given {@code key}.
+ *
+ *
This implements {@code SparseArray#contains} for lower API versions.
+ */
+ @UnstableApi
+ public static boolean containsKey(SparseArray sparseArray, int key) {
+ return sparseArray.indexOfKey(key) >= 0;
+ }
+
/**
* Removes an indexed range from a List.
*
diff --git a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java
index 058492ed8d..8edab8770b 100644
--- a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java
+++ b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java
@@ -20,6 +20,7 @@ package androidx.media3.effect;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
+import static androidx.media3.common.util.Util.containsKey;
import android.content.Context;
import android.util.SparseArray;
@@ -147,7 +148,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public void switchToInput(@VideoFrameProcessor.InputType int newInputType) {
checkStateNotNull(downstreamShaderProgram);
- checkState(inputs.indexOfKey(newInputType) >= 0, "Input type not registered: " + newInputType);
+ checkState(containsKey(inputs, newInputType), "Input type not registered: " + newInputType);
for (int i = 0; i < inputs.size(); i++) {
@VideoFrameProcessor.InputType int inputType = inputs.keyAt(i);
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioMixerImpl.java b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioMixerImpl.java
index 8a27712906..8bee6e9172 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioMixerImpl.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioMixerImpl.java
@@ -18,7 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.audio.AudioProcessor.EMPTY_BUFFER;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState;
-import static androidx.media3.common.util.Assertions.checkStateNotNull;
+import static androidx.media3.common.util.Util.containsKey;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -153,7 +153,7 @@ import java.nio.ByteOrder;
public boolean hasSource(int sourceId) {
checkStateIsConfigured();
- return sources.get(sourceId) != null;
+ return containsKey(sources, sourceId);
}
@Override
@@ -295,7 +295,8 @@ import java.nio.ByteOrder;
}
private SourceInfo getSourceById(int sourceId) {
- return checkStateNotNull(sources.get(sourceId), "Source not found.");
+ checkState(containsKey(sources, sourceId), "Source not found.");
+ return sources.get(sourceId);
}
/** A buffer holding partially-mixed audio within an interval. */
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java
index ec3b2a18e3..8ebf121719 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java
@@ -19,6 +19,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
+import static androidx.media3.common.util.Util.containsKey;
import static java.lang.Math.max;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -174,9 +175,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO,
"Unsupported track format: " + sampleMimeType);
- // SparseArray.get() returns null by default if the value is not found.
checkState(
- trackTypeToInfo.get(trackType) == null, "There is already a track of type " + trackType);
+ !containsKey(trackTypeToInfo, trackType), "There is already a track of type " + trackType);
ensureMuxerInitialized();
@@ -218,10 +218,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
public boolean writeSample(
@C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
throws Muxer.MuxerException {
- @Nullable TrackInfo trackInfo = trackTypeToInfo.get(trackType);
- // SparseArray.get() returns null by default if the value is not found.
- checkArgument(
- trackInfo != null, "Could not write sample because there is no track of type " + trackType);
+ checkArgument(containsKey(trackTypeToInfo, trackType));
+ TrackInfo trackInfo = trackTypeToInfo.get(trackType);
boolean canWriteSample = canWriteSample(trackType, presentationTimeUs);
if (trackType == C.TRACK_TYPE_VIDEO) {
DebugTraceUtil.logEvent(
@@ -262,12 +260,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* @param trackType The {@link C.TrackType}.
*/
public void endTrack(@C.TrackType int trackType) {
- @Nullable TrackInfo trackInfo = trackTypeToInfo.get(trackType);
- if (trackInfo == null) {
- // SparseArray.get() returns null by default if the value is not found.
+ if (!containsKey(trackTypeToInfo, trackType)) {
return;
}
+ TrackInfo trackInfo = trackTypeToInfo.get(trackType);
maxEndedTrackTimeUs = max(maxEndedTrackTimeUs, trackInfo.timeUs);
listener.onTrackEnded(
trackType, trackInfo.format, trackInfo.getAverageBitrate(), trackInfo.sampleCount);
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java
index 9dbf2b1233..696686a51a 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java
@@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState;
+import static androidx.media3.common.util.Util.containsKey;
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_DECODED;
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_ENCODED;
import static androidx.media3.transformer.Composition.HDR_MODE_KEEP_HDR;
@@ -792,7 +793,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public Format getAssetLoaderInputFormat(int sequenceIndex, @C.TrackType int trackType) {
SparseArray trackTypeToFirstAssetLoaderInputFormat =
sequencesMetadata.get(sequenceIndex).trackTypeToFirstAssetLoaderInputFormat;
- checkState(contains(trackTypeToFirstAssetLoaderInputFormat, trackType));
+ checkState(containsKey(trackTypeToFirstAssetLoaderInputFormat, trackType));
return trackTypeToFirstAssetLoaderInputFormat.get(trackType);
}
@@ -833,7 +834,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@C.TrackType int trackType = getProcessedTrackType(assetLoaderInputFormat.sampleMimeType);
SparseArray trackTypeToFirstAssetLoaderInputFormat =
sequencesMetadata.get(sequenceIndex).trackTypeToFirstAssetLoaderInputFormat;
- checkState(!contains(trackTypeToFirstAssetLoaderInputFormat, trackType));
+ checkState(!containsKey(trackTypeToFirstAssetLoaderInputFormat, trackType));
trackTypeToFirstAssetLoaderInputFormat.put(trackType, assetLoaderInputFormat);
}
@@ -841,7 +842,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* Returns the index of the primary sequence for a given {@link C.TrackType trackType}.
*
* A primary sequence for a {@link C.TrackType trackType} is defined as the lowest indexed
- * sequence that contains a track of the given {@code trackType}.
+ * sequence that containsKey a track of the given {@code trackType}.
*/
public int getIndexForPrimarySequence(@C.TrackType int trackType) {
checkState(
@@ -850,7 +851,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
for (int i = 0; i < sequencesMetadata.size(); i++) {
SparseArray trackTypeToFirstAssetLoaderInputFormat =
sequencesMetadata.get(i).trackTypeToFirstAssetLoaderInputFormat;
- if (contains(trackTypeToFirstAssetLoaderInputFormat, trackType)) {
+ if (containsKey(trackTypeToFirstAssetLoaderInputFormat, trackType)) {
return i;
}
}
@@ -881,7 +882,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public void registerGraphInput(@C.TrackType int trackType) {
int numberOfGraphInputForTrackType = 1;
- if (contains(trackTypeToNumberOfRegisteredGraphInput, trackType)) {
+ if (containsKey(trackTypeToNumberOfRegisteredGraphInput, trackType)) {
numberOfGraphInputForTrackType += trackTypeToNumberOfRegisteredGraphInput.get(trackType);
}
trackTypeToNumberOfRegisteredGraphInput.put(trackType, numberOfGraphInputForTrackType);
@@ -894,7 +895,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public boolean hasAssociatedAllTracksWithGraphInput(@C.TrackType int trackType) {
int numberOfTracksForTrackType = 0;
for (int i = 0; i < sequencesMetadata.size(); i++) {
- if (contains(sequencesMetadata.get(i).trackTypeToFirstAssetLoaderInputFormat, trackType)) {
+ if (containsKey(
+ sequencesMetadata.get(i).trackTypeToFirstAssetLoaderInputFormat, trackType)) {
numberOfTracksForTrackType++;
}
}
@@ -908,10 +910,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
for (int i = 0; i < sequencesMetadata.size(); i++) {
SparseArray trackTypeToFirstAssetLoaderInputFormat =
sequencesMetadata.get(i).trackTypeToFirstAssetLoaderInputFormat;
- if (contains(trackTypeToFirstAssetLoaderInputFormat, C.TRACK_TYPE_AUDIO)) {
+ if (containsKey(trackTypeToFirstAssetLoaderInputFormat, C.TRACK_TYPE_AUDIO)) {
outputHasAudio = true;
}
- if (contains(trackTypeToFirstAssetLoaderInputFormat, C.TRACK_TYPE_VIDEO)) {
+ if (containsKey(trackTypeToFirstAssetLoaderInputFormat, C.TRACK_TYPE_VIDEO)) {
outputHasVideo = true;
}
}
@@ -921,14 +923,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Registers a {@link SampleExporter} for the given {@link C.TrackType trackType}. */
public void registerSampleExporter(int trackType, SampleExporter sampleExporter) {
checkState(
- !contains(trackTypeToSampleExporter, trackType),
+ !containsKey(trackTypeToSampleExporter, trackType),
"Exactly one SampleExporter can be added for each track type.");
trackTypeToSampleExporter.put(trackType, sampleExporter);
}
/** Sets whether a track should be transcoded. */
public void setShouldTranscode(@C.TrackType int trackType, boolean shouldTranscode) {
- if (contains(trackTypeToShouldTranscode, trackType)) {
+ if (containsKey(trackTypeToShouldTranscode, trackType)) {
checkState(shouldTranscode == trackTypeToShouldTranscode.get(trackType));
return;
}
@@ -937,7 +939,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Returns whether a track should be transcoded. */
public boolean shouldTranscode(@C.TrackType int trackType) {
- checkState(contains(trackTypeToShouldTranscode, trackType));
+ checkState(containsKey(trackTypeToShouldTranscode, trackType));
return trackTypeToShouldTranscode.get(trackType);
}
@@ -962,10 +964,5 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
requiredTrackCount = C.LENGTH_UNSET;
}
}
-
- /** Implements {@code SparseArray#contains} for lower API versions. */
- private static boolean contains(SparseArray sparseArray, int key) {
- return sparseArray.get(key) != null;
- }
}
}