From 6070b200ae412ee86a02359939cb8df8a255cf7c Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 11 Jan 2022 16:03:40 +0000 Subject: [PATCH 001/251] Add error code and exception type for muxing failures. Exceptions thrown by MediaMuxer are converted MuxerExceptions and later to TransformationExceptions with ERROR_CODE_MUXING_FAILED. PiperOrigin-RevId: 421033721 --- .../media3/transformer/FrameworkMuxer.java | 45 +++++++++++++++---- .../androidx/media3/transformer/Muxer.java | 27 +++++++++-- .../media3/transformer/MuxerWrapper.java | 12 +++-- .../transformer/TransformationException.java | 5 +++ .../media3/transformer/Transformer.java | 25 +++++++---- .../transformer/TransformerBaseRenderer.java | 6 ++- .../media3/transformer/TestMuxer.java | 7 +-- 7 files changed, 99 insertions(+), 28 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java index 57ca4989ab..b3aed9b064 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java @@ -127,7 +127,7 @@ import java.nio.ByteBuffer; } @Override - public int addTrack(Format format) { + public int addTrack(Format format) throws MuxerException { String sampleMimeType = checkNotNull(format.sampleMimeType); MediaFormat mediaFormat; if (MimeTypes.isAudio(sampleMimeType)) { @@ -137,29 +137,56 @@ import java.nio.ByteBuffer; } else { mediaFormat = MediaFormat.createVideoFormat(castNonNull(sampleMimeType), format.width, format.height); - mediaMuxer.setOrientationHint(format.rotationDegrees); + try { + mediaMuxer.setOrientationHint(format.rotationDegrees); + } catch (RuntimeException e) { + throw new MuxerException( + "Failed to set orientation hint with rotationDegrees=" + format.rotationDegrees, e); + } } MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); - return mediaMuxer.addTrack(mediaFormat); + int trackIndex; + try { + trackIndex = mediaMuxer.addTrack(mediaFormat); + } catch (RuntimeException e) { + throw new MuxerException("Failed to add track with format=" + format, e); + } + return trackIndex; } @SuppressLint("WrongConstant") // C.BUFFER_FLAG_KEY_FRAME equals MediaCodec.BUFFER_FLAG_KEY_FRAME. @Override public void writeSampleData( - int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) + throws MuxerException { if (!isStarted) { isStarted = true; - mediaMuxer.start(); + try { + mediaMuxer.start(); + } catch (RuntimeException e) { + throw new MuxerException("Failed to start the muxer", e); + } } int offset = data.position(); int size = data.limit() - offset; int flags = isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0; bufferInfo.set(offset, size, presentationTimeUs, flags); - mediaMuxer.writeSampleData(trackIndex, data, bufferInfo); + try { + mediaMuxer.writeSampleData(trackIndex, data, bufferInfo); + } catch (RuntimeException e) { + throw new MuxerException( + "Failed to write sample for trackIndex=" + + trackIndex + + ", presentationTimeUs=" + + presentationTimeUs + + ", size=" + + size, + e); + } } @Override - public void release(boolean forCancellation) { + public void release(boolean forCancellation) throws MuxerException { if (!isStarted) { mediaMuxer.release(); return; @@ -168,7 +195,7 @@ import java.nio.ByteBuffer; isStarted = false; try { mediaMuxer.stop(); - } catch (IllegalStateException e) { + } catch (RuntimeException e) { if (SDK_INT < 30) { // Set the muxer state to stopped even if mediaMuxer.stop() failed so that // mediaMuxer.release() doesn't attempt to stop the muxer and therefore doesn't throw the @@ -187,7 +214,7 @@ import java.nio.ByteBuffer; } // It doesn't matter that stopping the muxer throws if the transformation is being cancelled. if (!forCancellation) { - throw e; + throw new MuxerException("Failed to stop the muxer", e); } } finally { mediaMuxer.release(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java index e831bd0727..a6bfa5db7d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java @@ -36,6 +36,19 @@ import java.nio.ByteBuffer; */ /* package */ interface Muxer { + /** Thrown when a muxing failure occurs. */ + /* package */ final class MuxerException extends Exception { + /** + * Creates an instance. + * + * @param message See {@link #getMessage()}. + * @param cause See {@link #getCause()}. + */ + public MuxerException(String message, Throwable cause) { + super(message, cause); + } + } + /** Factory for muxers. */ interface Factory { /** @@ -83,8 +96,11 @@ import java.nio.ByteBuffer; /** * Adds a track with the specified format, and returns its index (to be passed in subsequent calls * to {@link #writeSampleData(int, ByteBuffer, boolean, long)}). + * + * @param format The {@link Format} of the track. + * @throws MuxerException If the muxer encounters a problem while adding the track. */ - int addTrack(Format format); + int addTrack(Format format) throws MuxerException; /** * Writes the specified sample. @@ -93,15 +109,18 @@ import java.nio.ByteBuffer; * @param data Buffer containing the sample data to write to the container. * @param isKeyFrame Whether the sample is a key frame. * @param presentationTimeUs The presentation time of the sample in microseconds. + * @throws MuxerException If the muxer fails to start or an error occurs while writing the sample. */ - void writeSampleData( - int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs); + void writeSampleData(int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) + throws MuxerException; /** * Releases any resources associated with muxing. * * @param forCancellation Whether the reason for releasing the resources is the transformation * cancellation. + * @throws MuxerException If the muxer fails to stop or release resources and {@code + * forCancellation} is false. */ - void release(boolean forCancellation); + void release(boolean forCancellation) throws MuxerException; } 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 d9899e40a9..5f62af713c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java @@ -103,8 +103,10 @@ import java.nio.ByteBuffer; * @param format The {@link Format} to be added. * @throws IllegalStateException If the format is unsupported or if there is already a track * format of the same type (audio or video). + * @throws Muxer.MuxerException If the underlying muxer encounters a problem while adding the + * track. */ - public void addTrackFormat(Format format) { + public void addTrackFormat(Format format) throws Muxer.MuxerException { checkState(trackCount > 0, "All tracks should be registered before the formats are added."); checkState(trackFormatCount < trackCount, "All track formats have already been added."); @Nullable String sampleMimeType = format.sampleMimeType; @@ -138,9 +140,11 @@ import java.nio.ByteBuffer; * good interleaving. * @throws IllegalStateException If the muxer doesn't have any {@link #endTrack(int) non-ended} * track of the given track type. + * @throws Muxer.MuxerException If the underlying muxer fails to write the sample. */ public boolean writeSample( - @C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { + @C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) + throws Muxer.MuxerException { int trackIndex = trackTypeToIndex.get(trackType, /* valueIfKeyNotFound= */ C.INDEX_UNSET); checkState( trackIndex != C.INDEX_UNSET, @@ -174,8 +178,10 @@ import java.nio.ByteBuffer; * * @param forCancellation Whether the reason for releasing the resources is the transformation * cancellation. + * @throws Muxer.MuxerException If the underlying muxer fails to stop and to release resources and + * {@code forCancellation} is false. */ - public void release(boolean forCancellation) { + public void release(boolean forCancellation) throws Muxer.MuxerException { isReady = false; muxer.release(forCancellation); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index cf57953bc3..d980b5a8da 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -73,6 +73,8 @@ public final class TransformationException extends Exception { ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED, ERROR_CODE_GL_INIT_FAILED, ERROR_CODE_GL_PROCESSING_FAILED, + ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED, + ERROR_CODE_MUXING_FAILED, }) public @interface ErrorCode {} @@ -164,6 +166,8 @@ public final class TransformationException extends Exception { * TransformationRequest.Builder#setVideoMimeType(String)} to transcode to a supported MIME type. */ public static final int ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED = 6001; + /** Caused by a failure while muxing media samples. */ + public static final int ERROR_CODE_MUXING_FAILED = 6002; private static final ImmutableBiMap NAME_TO_ERROR_CODE = new ImmutableBiMap.Builder() @@ -188,6 +192,7 @@ public final class TransformationException extends Exception { .put( "ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED", ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED) + .put("ERROR_CODE_MUXING_FAILED", ERROR_CODE_MUXING_FAILED) .buildOrThrow(); /** Returns the {@code errorCode} for a given name. */ diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 6e7ee9a9fc..917a8b07b4 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -672,7 +672,11 @@ public final class Transformer { * @throws IllegalStateException If this method is called from the wrong thread. */ public void cancel() { - releaseResources(/* forCancellation= */ true); + try { + releaseResources(/* forCancellation= */ true); + } catch (TransformationException impossible) { + throw new IllegalStateException(impossible); + } } /** @@ -681,17 +685,22 @@ public final class Transformer { * @param forCancellation Whether the reason for releasing the resources is the transformation * cancellation. * @throws IllegalStateException If this method is called from the wrong thread. - * @throws IllegalStateException If the muxer is in the wrong state and {@code forCancellation} is - * false. + * @throws TransformationException If the muxer is in the wrong state and {@code forCancellation} + * is false. */ - private void releaseResources(boolean forCancellation) { + private void releaseResources(boolean forCancellation) throws TransformationException { verifyApplicationThread(); if (player != null) { player.release(); player = null; } if (muxerWrapper != null) { - muxerWrapper.release(forCancellation); + try { + muxerWrapper.release(forCancellation); + } catch (Muxer.MuxerException e) { + throw TransformationException.createForMuxer( + e, TransformationException.ERROR_CODE_MUXING_FAILED); + } muxerWrapper = null; } progressState = PROGRESS_STATE_NO_TRANSFORMATION; @@ -826,9 +835,9 @@ public final class Transformer { @Nullable TransformationException resourceReleaseException = null; try { releaseResources(/* forCancellation= */ false); - } catch (IllegalStateException e) { - // TODO(internal b/209469847): Use a more specific error code when the IllegalStateException - // is caused by the muxer. + } catch (TransformationException e) { + resourceReleaseException = e; + } catch (RuntimeException e) { resourceReleaseException = TransformationException.createForUnexpected(e); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index 945f392244..b72b9fc641 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -97,6 +97,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {} } catch (TransformationException e) { throw wrapTransformationException(e); + } catch (Muxer.MuxerException e) { + throw wrapTransformationException( + TransformationException.createForMuxer( + e, TransformationException.ERROR_CODE_MUXING_FAILED)); } } @@ -145,7 +149,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @return Whether it may be possible to write more data immediately by calling this method again. */ @RequiresNonNull("samplePipeline") - private boolean feedMuxerFromPipeline() { + private boolean feedMuxerFromPipeline() throws Muxer.MuxerException { if (!muxerWrapperTrackAdded) { @Nullable Format samplePipelineOutputFormat = samplePipeline.getOutputFormat(); if (samplePipelineOutputFormat == null) { diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java index a346d4bba0..db28cf8f3c 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java @@ -45,7 +45,7 @@ public final class TestMuxer implements Muxer, Dumper.Dumpable { // Muxer implementation. @Override - public int addTrack(Format format) { + public int addTrack(Format format) throws MuxerException { int trackIndex = muxer.addTrack(format); dumpables.add(new DumpableFormat(format, trackIndex)); return trackIndex; @@ -53,13 +53,14 @@ public final class TestMuxer implements Muxer, Dumper.Dumpable { @Override public void writeSampleData( - int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) { + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) + throws MuxerException { dumpables.add(new DumpableSample(trackIndex, data, isKeyFrame, presentationTimeUs)); muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); } @Override - public void release(boolean forCancellation) { + public void release(boolean forCancellation) throws MuxerException { dumpables.add(dumper -> dumper.add("released", true)); muxer.release(forCancellation); } From 152e986c0921fcabcfc77c4b27453d609ec10cba Mon Sep 17 00:00:00 2001 From: christosts Date: Tue, 11 Jan 2022 16:18:41 +0000 Subject: [PATCH 002/251] Fix typo PiperOrigin-RevId: 421036800 --- demos/session/src/main/assets/catalog.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/session/src/main/assets/catalog.json b/demos/session/src/main/assets/catalog.json index f28f525314..9a20485f1c 100644 --- a/demos/session/src/main/assets/catalog.json +++ b/demos/session/src/main/assets/catalog.json @@ -2,7 +2,7 @@ "media": [ { "id": "video_01", - "title": "Future Scenerio", + "title": "Future Scenario", "album": "Mango Open Movie project", "artist": "Blender Foundation", "genre": "Video", @@ -488,4 +488,4 @@ "site": "https://www.youtube.com/audiolibrary/music" } ] -} \ No newline at end of file +} From 657e8768becded013280ddb3bf60b6d0e11a09ce Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 11 Jan 2022 16:35:08 +0000 Subject: [PATCH 003/251] Update Muxer exception javadoc to match MuxerWrapper. PiperOrigin-RevId: 421039869 --- .../src/main/java/androidx/media3/transformer/Muxer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java index a6bfa5db7d..df8138f837 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Muxer.java @@ -109,7 +109,7 @@ import java.nio.ByteBuffer; * @param data Buffer containing the sample data to write to the container. * @param isKeyFrame Whether the sample is a key frame. * @param presentationTimeUs The presentation time of the sample in microseconds. - * @throws MuxerException If the muxer fails to start or an error occurs while writing the sample. + * @throws MuxerException If the muxer fails to write the sample. */ void writeSampleData(int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) throws MuxerException; From b0ae7c04d5b9805d0f3cbe026432edf4761cccbb Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 11 Jan 2022 16:42:12 +0000 Subject: [PATCH 004/251] Remove MediaCodecAdapter dependency from Transformer. Codec and its factories can use MediaCodec directly as for API >= 21, the SynchronousMediaCodecAdapter methods used in Codec just correspond to a single MediaCodec call each so there is no reason to have another wrapping layer. PiperOrigin-RevId: 421041177 --- .../androidx/media3/transformer/Codec.java | 47 +++--- .../transformer/DefaultCodecFactory.java | 149 +++++++++--------- 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java index f19cb91b64..575df2a55e 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java @@ -29,18 +29,17 @@ import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; import androidx.media3.decoder.DecoderInputBuffer; -import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter; import com.google.common.collect.ImmutableList; import java.nio.ByteBuffer; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * A wrapper around {@link MediaCodecAdapter}. + * A wrapper around {@link MediaCodec}. * - *

Provides a layer of abstraction for callers that need to interact with {@link MediaCodec} - * through {@link MediaCodecAdapter}. This is done by simplifying the calls needed to queue and - * dequeue buffers, removing the need to track buffer indices and codec events. + *

Provides a layer of abstraction for callers that need to interact with {@link MediaCodec}. + * This is done by simplifying the calls needed to queue and dequeue buffers, removing the need to + * track buffer indices and codec events. */ @UnstableApi public final class Codec { @@ -66,11 +65,12 @@ public final class Codec { * * @param format The {@link Format} (of the input data) used to determine the underlying {@link * MediaCodec} and its configuration values. - * @param surface The {@link Surface} to which the decoder output is rendered. + * @param outputSurface The {@link Surface} to which the decoder output is rendered. * @return A configured and started decoder wrapper. * @throws TransformationException If the underlying codec cannot be created. */ - Codec createForVideoDecoding(Format format, Surface surface) throws TransformationException; + Codec createForVideoDecoding(Format format, Surface outputSurface) + throws TransformationException; } /** A factory for {@link Codec encoder} instances. */ @@ -108,7 +108,8 @@ public final class Codec { private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT; private final BufferInfo outputBufferInfo; - private final MediaCodecAdapter mediaCodecAdapter; + private final MediaCodec mediaCodec; + @Nullable private final Surface inputSurface; private @MonotonicNonNull Format outputFormat; @Nullable private ByteBuffer outputBuffer; @@ -118,9 +119,10 @@ public final class Codec { private boolean inputStreamEnded; private boolean outputStreamEnded; - /** Creates a {@code Codec} from a configured and started {@link MediaCodecAdapter}. */ - public Codec(MediaCodecAdapter mediaCodecAdapter) { - this.mediaCodecAdapter = mediaCodecAdapter; + /** Creates a {@code Codec} from a configured and started {@link MediaCodec}. */ + public Codec(MediaCodec mediaCodec, @Nullable Surface inputSurface) { + this.mediaCodec = mediaCodec; + this.inputSurface = inputSurface; outputBufferInfo = new BufferInfo(); inputBufferIndex = C.INDEX_UNSET; outputBufferIndex = C.INDEX_UNSET; @@ -129,7 +131,7 @@ public final class Codec { /** Returns the input {@link Surface}, or null if the input is not a surface. */ @Nullable public Surface getInputSurface() { - return mediaCodecAdapter.getInputSurface(); + return inputSurface; } /** @@ -144,11 +146,11 @@ public final class Codec { return false; } if (inputBufferIndex < 0) { - inputBufferIndex = mediaCodecAdapter.dequeueInputBufferIndex(); + inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0); if (inputBufferIndex < 0) { return false; } - inputBuffer.data = mediaCodecAdapter.getInputBuffer(inputBufferIndex); + inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex); inputBuffer.clear(); } checkNotNull(inputBuffer.data); @@ -174,13 +176,13 @@ public final class Codec { inputStreamEnded = true; flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; } - mediaCodecAdapter.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); + mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); inputBufferIndex = C.INDEX_UNSET; inputBuffer.data = null; } public void signalEndOfInputStream() { - mediaCodecAdapter.signalEndOfInputStream(); + mediaCodec.signalEndOfInputStream(); } /** Returns the current output format, if available. */ @@ -224,7 +226,7 @@ public final class Codec { */ public void releaseOutputBuffer(boolean render) { outputBuffer = null; - mediaCodecAdapter.releaseOutputBuffer(outputBufferIndex, render); + mediaCodec.releaseOutputBuffer(outputBufferIndex, render); outputBufferIndex = C.INDEX_UNSET; } @@ -236,7 +238,10 @@ public final class Codec { /** Releases the underlying codec. */ public void release() { outputBuffer = null; - mediaCodecAdapter.release(); + if (inputSurface != null) { + inputSurface.release(); + } + mediaCodec.release(); } /** @@ -249,7 +254,7 @@ public final class Codec { return false; } - outputBuffer = checkNotNull(mediaCodecAdapter.getOutputBuffer(outputBufferIndex)); + outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex)); outputBuffer.position(outputBufferInfo.offset); outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); return true; @@ -267,10 +272,10 @@ public final class Codec { return false; } - outputBufferIndex = mediaCodecAdapter.dequeueOutputBufferIndex(outputBufferInfo); + outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0); if (outputBufferIndex < 0) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - outputFormat = getFormat(mediaCodecAdapter.getOutputFormat()); + outputFormat = getFormat(mediaCodec.getOutputFormat()); } return false; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java index 9529475134..2e290c8f22 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java @@ -25,29 +25,17 @@ import android.media.MediaCodec; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaFormat; import android.view.Surface; +import androidx.annotation.Nullable; import androidx.media3.common.Format; import androidx.media3.common.util.MediaFormatUtil; -import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter; -import androidx.media3.exoplayer.mediacodec.MediaCodecInfo; -import androidx.media3.exoplayer.mediacodec.SynchronousMediaCodecAdapter; +import androidx.media3.common.util.TraceUtil; import java.io.IOException; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** A default {@link Codec.DecoderFactory} and {@link Codec.EncoderFactory}. */ /* package */ final class DefaultCodecFactory implements Codec.DecoderFactory, Codec.EncoderFactory { - private static final MediaCodecInfo PLACEHOLDER_MEDIA_CODEC_INFO = - MediaCodecInfo.newInstance( - /* name= */ "name-placeholder", - /* mimeType= */ "mime-type-placeholder", - /* codecMimeType= */ "mime-type-placeholder", - /* capabilities= */ null, - /* hardwareAccelerated= */ false, - /* softwareOnly= */ false, - /* vendor= */ false, - /* forceDisableAdaptive= */ false, - /* forceSecure= */ false); - @Override public Codec createForAudioDecoding(Format format) throws TransformationException { MediaFormat mediaFormat = @@ -57,22 +45,17 @@ import java.io.IOException; mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); - MediaCodecAdapter adapter; - try { - adapter = - new MediaCodecFactory() - .createAdapter( - MediaCodecAdapter.Configuration.createForAudioDecoding( - PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format, /* crypto= */ null)); - } catch (Exception e) { - throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ true); - } - return new Codec(adapter); + return createCodec( + format, + mediaFormat, + /* isVideo= */ false, + /* isDecoder= */ true, + /* outputSurface= */ null); } @Override @SuppressLint("InlinedApi") - public Codec createForVideoDecoding(Format format, Surface surface) + public Codec createForVideoDecoding(Format format, Surface outputSurface) throws TransformationException { MediaFormat mediaFormat = MediaFormat.createVideoFormat( @@ -87,21 +70,8 @@ import java.io.IOException; mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0); } - MediaCodecAdapter adapter; - try { - adapter = - new MediaCodecFactory() - .createAdapter( - MediaCodecAdapter.Configuration.createForVideoDecoding( - PLACEHOLDER_MEDIA_CODEC_INFO, - mediaFormat, - format, - surface, - /* crypto= */ null)); - } catch (Exception e) { - throw createTransformationException(e, format, /* isVideo= */ true, /* isDecoder= */ true); - } - return new Codec(adapter); + return createCodec( + format, mediaFormat, /* isVideo= */ true, /* isDecoder= */ true, outputSurface); } @Override @@ -111,17 +81,12 @@ import java.io.IOException; checkNotNull(format.sampleMimeType), format.sampleRate, format.channelCount); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); - MediaCodecAdapter adapter; - try { - adapter = - new MediaCodecFactory() - .createAdapter( - MediaCodecAdapter.Configuration.createForAudioEncoding( - PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format)); - } catch (Exception e) { - throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ false); - } - return new Codec(adapter); + return createCodec( + format, + mediaFormat, + /* isVideo= */ false, + /* isDecoder= */ false, + /* outputSurface= */ null); } @Override @@ -141,36 +106,70 @@ import java.io.IOException; mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 413_000); - MediaCodecAdapter adapter; - try { - adapter = - new MediaCodecFactory() - .createAdapter( - MediaCodecAdapter.Configuration.createForVideoEncoding( - PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format)); - } catch (Exception e) { - throw createTransformationException(e, format, /* isVideo= */ true, /* isDecoder= */ false); - } - return new Codec(adapter); + return createCodec( + format, + mediaFormat, + /* isVideo= */ true, + /* isDecoder= */ false, + /* outputSurface= */ null); } - private static final class MediaCodecFactory extends SynchronousMediaCodecAdapter.Factory { - @Override - protected MediaCodec createCodec(MediaCodecAdapter.Configuration configuration) - throws IOException { - String sampleMimeType = - checkNotNull(configuration.mediaFormat.getString(MediaFormat.KEY_MIME)); - boolean isDecoder = (configuration.flags & MediaCodec.CONFIGURE_FLAG_ENCODE) == 0; - return isDecoder - ? MediaCodec.createDecoderByType(checkNotNull(sampleMimeType)) - : MediaCodec.createEncoderByType(checkNotNull(sampleMimeType)); + @RequiresNonNull("#1.sampleMimeType") + private static Codec createCodec( + Format format, + MediaFormat mediaFormat, + boolean isVideo, + boolean isDecoder, + @Nullable Surface outputSurface) + throws TransformationException { + @Nullable MediaCodec mediaCodec = null; + @Nullable Surface inputSurface = null; + try { + mediaCodec = + isDecoder + ? MediaCodec.createDecoderByType(format.sampleMimeType) + : MediaCodec.createEncoderByType(format.sampleMimeType); + configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface); + if (isVideo && !isDecoder) { + inputSurface = mediaCodec.createInputSurface(); + } + startCodec(mediaCodec); + } catch (Exception e) { + if (inputSurface != null) { + inputSurface.release(); + } + if (mediaCodec != null) { + mediaCodec.release(); + } + throw createTransformationException(e, format, isVideo, isDecoder); } + return new Codec(mediaCodec, inputSurface); + } + + private static void configureCodec( + MediaCodec codec, + MediaFormat mediaFormat, + boolean isDecoder, + @Nullable Surface outputSurface) { + TraceUtil.beginSection("configureCodec"); + codec.configure( + mediaFormat, + outputSurface, + /* crypto= */ null, + isDecoder ? 0 : MediaCodec.CONFIGURE_FLAG_ENCODE); + TraceUtil.endSection(); + } + + private static void startCodec(MediaCodec codec) { + TraceUtil.beginSection("startCodec"); + codec.start(); + TraceUtil.endSection(); } private static TransformationException createTransformationException( Exception cause, Format format, boolean isVideo, boolean isDecoder) { String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); - if (cause instanceof IOException) { + if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { return TransformationException.createForCodec( cause, componentName, From 725b861f54b2a7decd8e112bef7025cf47ea54a7 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 11 Jan 2022 17:13:33 +0000 Subject: [PATCH 005/251] Allow multiple Transformer listeners to be registered. Multiple listeners can be added to Transformer and its builder. All or specific listeners can also be removed. PiperOrigin-RevId: 421047650 --- .../media3/common/util/ListenerSet.java | 20 +++ .../transformer/mh/AndroidTestUtil.java | 2 +- .../media3/transformer/Transformer.java | 121 +++++++++++++++--- .../media3/transformer/TransformerTest.java | 74 +++++++++++ .../transformer/TransformerTestRunner.java | 5 +- 5 files changed, 198 insertions(+), 24 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java b/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java index a05d1a82d7..077141b05c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/ListenerSet.java @@ -118,6 +118,21 @@ public final class ListenerSet { */ @CheckResult public ListenerSet copy(Looper looper, IterationFinishedEvent iterationFinishedEvent) { + return copy(looper, clock, iterationFinishedEvent); + } + + /** + * Copies the listener set. + * + * @param looper The new {@link Looper} for the copied listener set. + * @param clock The new {@link Clock} for the copied listener set. + * @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events + * sent during one {@link Looper} message queue iteration were handled by the listeners. + * @return The copied listener set. + */ + @CheckResult + public ListenerSet copy( + Looper looper, Clock clock, IterationFinishedEvent iterationFinishedEvent) { return new ListenerSet<>(listeners, looper, clock, iterationFinishedEvent); } @@ -152,6 +167,11 @@ public final class ListenerSet { } } + /** Removes all listeners from the set. */ + public void clear() { + listeners.clear(); + } + /** Returns the number of added listeners. */ public int size() { return listeners.size(); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java index 7ecd8805ac..5f7eea3f7c 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java @@ -72,7 +72,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; Transformer testTransformer = transformer .buildUpon() - .setListener( + .addListener( new Transformer.Listener() { @Override public void onTransformationCompleted(MediaItem inputMediaItem) { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 917a8b07b4..dd7d46a9c9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -43,6 +43,7 @@ import androidx.media3.common.Player; import androidx.media3.common.Timeline; import androidx.media3.common.TracksInfo; import androidx.media3.common.util.Clock; +import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.DefaultLoadControl; @@ -100,7 +101,7 @@ public final class Transformer { private boolean removeVideo; private String containerMimeType; private TransformationRequest transformationRequest; - private Transformer.Listener listener; + private ListenerSet listeners; private DebugViewProvider debugViewProvider; private Looper looper; private Clock clock; @@ -110,9 +111,9 @@ public final class Transformer { @Deprecated public Builder() { muxerFactory = new FrameworkMuxer.Factory(); - listener = new Listener() {}; looper = Util.getCurrentOrMainLooper(); clock = Clock.DEFAULT; + listeners = new ListenerSet<>(looper, clock, (listener, flags) -> {}); encoderFactory = Codec.EncoderFactory.DEFAULT; debugViewProvider = DebugViewProvider.NONE; containerMimeType = MimeTypes.VIDEO_MP4; @@ -127,9 +128,9 @@ public final class Transformer { public Builder(Context context) { this.context = context.getApplicationContext(); muxerFactory = new FrameworkMuxer.Factory(); - listener = new Listener() {}; looper = Util.getCurrentOrMainLooper(); clock = Clock.DEFAULT; + listeners = new ListenerSet<>(looper, clock, (listener, flags) -> {}); encoderFactory = Codec.EncoderFactory.DEFAULT; debugViewProvider = DebugViewProvider.NONE; containerMimeType = MimeTypes.VIDEO_MP4; @@ -145,7 +146,7 @@ public final class Transformer { this.removeVideo = transformer.removeVideo; this.containerMimeType = transformer.containerMimeType; this.transformationRequest = transformer.transformationRequest; - this.listener = transformer.listener; + this.listeners = transformer.listeners; this.looper = transformer.looper; this.encoderFactory = transformer.encoderFactory; this.debugViewProvider = transformer.debugViewProvider; @@ -267,15 +268,51 @@ public final class Transformer { } /** - * Sets the {@link Transformer.Listener} to listen to the transformation events. + * @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link + * #removeAllListeners()} instead. + */ + @Deprecated + public Builder setListener(Transformer.Listener listener) { + this.listeners.clear(); + this.listeners.add(listener); + return this; + } + + /** + * Adds a {@link Transformer.Listener} to listen to the transformation events. * - *

This is equivalent to {@link Transformer#setListener(Listener)}. + *

This is equivalent to {@link Transformer#addListener(Listener)}. * * @param listener A {@link Transformer.Listener}. * @return This builder. */ - public Builder setListener(Transformer.Listener listener) { - this.listener = listener; + public Builder addListener(Transformer.Listener listener) { + this.listeners.add(listener); + return this; + } + + /** + * Removes a {@link Transformer.Listener}. + * + *

This is equivalent to {@link Transformer#removeListener(Listener)}. + * + * @param listener A {@link Transformer.Listener}. + * @return This builder. + */ + public Builder removeListener(Transformer.Listener listener) { + this.listeners.remove(listener); + return this; + } + + /** + * Removes all {@link Transformer.Listener listeners}. + * + *

This is equivalent to {@link Transformer#removeAllListeners()}. + * + * @return This builder. + */ + public Builder removeAllListeners() { + this.listeners.clear(); return this; } @@ -290,6 +327,7 @@ public final class Transformer { */ public Builder setLooper(Looper looper) { this.looper = looper; + this.listeners = listeners.copy(looper, (listener, flags) -> {}); return this; } @@ -330,6 +368,7 @@ public final class Transformer { @VisibleForTesting /* package */ Builder setClock(Clock clock) { this.clock = clock; + this.listeners = listeners.copy(looper, clock, (listener, flags) -> {}); return this; } @@ -383,7 +422,7 @@ public final class Transformer { removeVideo, containerMimeType, transformationRequest, - listener, + listeners, looper, clock, encoderFactory, @@ -482,8 +521,8 @@ public final class Transformer { private final Codec.EncoderFactory encoderFactory; private final Codec.DecoderFactory decoderFactory; private final Transformer.DebugViewProvider debugViewProvider; + private final ListenerSet listeners; - private Transformer.Listener listener; @Nullable private MuxerWrapper muxerWrapper; @Nullable private ExoPlayer player; @ProgressState private int progressState; @@ -496,7 +535,7 @@ public final class Transformer { boolean removeVideo, String containerMimeType, TransformationRequest transformationRequest, - Transformer.Listener listener, + ListenerSet listeners, Looper looper, Clock clock, Codec.EncoderFactory encoderFactory, @@ -510,7 +549,7 @@ public final class Transformer { this.removeVideo = removeVideo; this.containerMimeType = containerMimeType; this.transformationRequest = transformationRequest; - this.listener = listener; + this.listeners = listeners; this.looper = looper; this.clock = clock; this.encoderFactory = encoderFactory; @@ -525,20 +564,52 @@ public final class Transformer { } /** - * Sets the {@link Transformer.Listener} to listen to the transformation events. + * @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link + * #removeAllListeners()} instead. + */ + @Deprecated + public void setListener(Transformer.Listener listener) { + verifyApplicationThread(); + this.listeners.clear(); + this.listeners.add(listener); + } + + /** + * Adds a {@link Transformer.Listener} to listen to the transformation events. * * @param listener A {@link Transformer.Listener}. * @throws IllegalStateException If this method is called from the wrong thread. */ - public void setListener(Transformer.Listener listener) { + public void addListener(Transformer.Listener listener) { verifyApplicationThread(); - this.listener = listener; + this.listeners.add(listener); + } + + /** + * Removes a {@link Transformer.Listener}. + * + * @param listener A {@link Transformer.Listener}. + * @throws IllegalStateException If this method is called from the wrong thread. + */ + public void removeListener(Transformer.Listener listener) { + verifyApplicationThread(); + this.listeners.remove(listener); + } + + /** + * Removes all {@link Transformer.Listener listeners}. + * + * @throws IllegalStateException If this method is called from the wrong thread. + */ + public void removeAllListeners() { + verifyApplicationThread(); + this.listeners.clear(); } /** * Starts an asynchronous operation to transform the given {@link MediaItem}. * - *

The transformation state is notified through the {@link Builder#setListener(Listener) + *

The transformation state is notified through the {@link Builder#addListener(Listener) * listener}. * *

Concurrent transformations on the same Transformer object are not allowed. @@ -561,7 +632,7 @@ public final class Transformer { /** * Starts an asynchronous operation to transform the given {@link MediaItem}. * - *

The transformation state is notified through the {@link Builder#setListener(Listener) + *

The transformation state is notified through the {@link Builder#addListener(Listener) * listener}. * *

Concurrent transformations on the same Transformer object are not allowed. @@ -842,16 +913,26 @@ public final class Transformer { } if (exception == null && resourceReleaseException == null) { - listener.onTransformationCompleted(mediaItem); + // TODO(b/213341814): Add event flags for Transformer events. + listeners.queueEvent( + /* eventFlag= */ C.INDEX_UNSET, + listener -> listener.onTransformationCompleted(mediaItem)); + listeners.flushEvents(); return; } if (exception != null) { - listener.onTransformationError(mediaItem, exception); + listeners.queueEvent( + /* eventFlag= */ C.INDEX_UNSET, + listener -> listener.onTransformationError(mediaItem, exception)); } if (resourceReleaseException != null) { - listener.onTransformationError(mediaItem, resourceReleaseException); + TransformationException finalResourceReleaseException = resourceReleaseException; + listeners.queueEvent( + /* eventFlag= */ C.INDEX_UNSET, + listener -> listener.onTransformationError(mediaItem, finalResourceReleaseException)); } + listeners.flushEvents(); } } } diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTest.java index 4e3a5139ea..bc7fe5a9cb 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTest.java @@ -22,6 +22,9 @@ import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE import static androidx.media3.transformer.Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.content.Context; import android.media.MediaCrypto; @@ -248,6 +251,77 @@ public final class TransformerTest { context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo")); } + @Test + public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception { + Transformer.Listener mockListener1 = mock(Transformer.Listener.class); + Transformer.Listener mockListener2 = mock(Transformer.Listener.class); + Transformer.Listener mockListener3 = mock(Transformer.Listener.class); + Transformer transformer = + new Transformer.Builder(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .addListener(mockListener1) + .addListener(mockListener2) + .addListener(mockListener3) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); + + transformer.startTransformation(mediaItem, outputPath); + TransformerTestRunner.runUntilCompleted(transformer); + + verify(mockListener1, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener2, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener3, times(1)).onTransformationCompleted(mediaItem); + } + + @Test + public void startTransformation_withMultipleListeners_callsEachOnError() throws Exception { + Transformer.Listener mockListener1 = mock(Transformer.Listener.class); + Transformer.Listener mockListener2 = mock(Transformer.Listener.class); + Transformer.Listener mockListener3 = mock(Transformer.Listener.class); + Transformer transformer = + new Transformer.Builder(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .addListener(mockListener1) + .addListener(mockListener2) + .addListener(mockListener3) + .build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER); + + transformer.startTransformation(mediaItem, outputPath); + TransformationException exception = TransformerTestRunner.runUntilError(transformer); + + verify(mockListener1, times(1)).onTransformationError(mediaItem, exception); + verify(mockListener2, times(1)).onTransformationError(mediaItem, exception); + verify(mockListener3, times(1)).onTransformationError(mediaItem, exception); + } + + @Test + public void startTransformation_afterBuildUponWithListenerRemoved_onlyCallsRemainingListeners() + throws Exception { + Transformer.Listener mockListener1 = mock(Transformer.Listener.class); + Transformer.Listener mockListener2 = mock(Transformer.Listener.class); + Transformer.Listener mockListener3 = mock(Transformer.Listener.class); + Transformer transformer1 = + new Transformer.Builder(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .addListener(mockListener1) + .addListener(mockListener2) + .addListener(mockListener3) + .build(); + Transformer transformer2 = transformer1.buildUpon().removeListener(mockListener2).build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); + + transformer2.startTransformation(mediaItem, outputPath); + TransformerTestRunner.runUntilCompleted(transformer2); + + verify(mockListener1, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener2, times(0)).onTransformationCompleted(mediaItem); + verify(mockListener3, times(1)).onTransformationCompleted(mediaItem); + } + @Test public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception { Transformer transformer = diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java index 748f4c477d..a739428697 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java @@ -69,7 +69,7 @@ public final class TransformerTestRunner { private static TransformationException runUntilListenerCalled(Transformer transformer) throws TimeoutException { TransformationResult transformationResult = new TransformationResult(); - Transformer.Listener listener = + transformer.addListener( new Transformer.Listener() { @Override public void onTransformationCompleted(MediaItem inputMediaItem) { @@ -81,8 +81,7 @@ public final class TransformerTestRunner { MediaItem inputMediaItem, TransformationException exception) { transformationResult.exception = exception; } - }; - transformer.setListener(listener); + }); runLooperUntil( transformer.getApplicationLooper(), () -> transformationResult.isCompleted || transformationResult.exception != null); From 63a32e85c564db3748bfe6afa335d2c1b24ab32d Mon Sep 17 00:00:00 2001 From: christosts Date: Wed, 12 Jan 2022 11:45:24 +0000 Subject: [PATCH 006/251] Session demo: update playlist add/remove icons PiperOrigin-RevId: 421256149 --- .../baseline_playlist_add_white_48.png | Bin 0 -> 114 bytes .../baseline_playlist_remove_white_48.png | Bin 0 -> 251 bytes .../baseline_playlist_add_white_48.png | Bin 0 -> 109 bytes .../baseline_playlist_remove_white_48.png | Bin 0 -> 191 bytes .../baseline_playlist_add_white_48.png | Bin 0 -> 113 bytes .../baseline_playlist_remove_white_48.png | Bin 0 -> 298 bytes .../baseline_playlist_add_white_48.png | Bin 0 -> 121 bytes .../baseline_playlist_remove_white_48.png | Bin 0 -> 383 bytes .../baseline_playlist_add_white_48.png | Bin 0 -> 126 bytes .../baseline_playlist_remove_white_48.png | Bin 0 -> 455 bytes .../src/main/res/layout/playable_items.xml | 2 +- .../src/main/res/layout/playlist_items.xml | 2 +- 12 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 demos/session/src/main/res/drawable-hdpi/baseline_playlist_add_white_48.png create mode 100644 demos/session/src/main/res/drawable-hdpi/baseline_playlist_remove_white_48.png create mode 100644 demos/session/src/main/res/drawable-mdpi/baseline_playlist_add_white_48.png create mode 100644 demos/session/src/main/res/drawable-mdpi/baseline_playlist_remove_white_48.png create mode 100644 demos/session/src/main/res/drawable-xhdpi/baseline_playlist_add_white_48.png create mode 100644 demos/session/src/main/res/drawable-xhdpi/baseline_playlist_remove_white_48.png create mode 100644 demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_add_white_48.png create mode 100644 demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_remove_white_48.png create mode 100644 demos/session/src/main/res/drawable-xxxhdpi/baseline_playlist_add_white_48.png create mode 100644 demos/session/src/main/res/drawable-xxxhdpi/baseline_playlist_remove_white_48.png diff --git a/demos/session/src/main/res/drawable-hdpi/baseline_playlist_add_white_48.png b/demos/session/src/main/res/drawable-hdpi/baseline_playlist_add_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..91ed929422e97b400a23f34a427589b2bd413d61 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^9w5vJBp7O^^}Pa8OeH~n!3+##lh0ZJdD@;Xjv*C{ z$r6(m{QLj^Kl_Q~z`XbJMiB-MR@_;qcu8c%9=%Kt{XUhcUl+Xh z)IKQ}xz}yCu>9j0ONwQhOD27uzGeRB$t7*clgaC?tA&^HX8u6{1-oD!MV!Z literal 0 HcmV?d00001 diff --git a/demos/session/src/main/res/drawable-mdpi/baseline_playlist_remove_white_48.png b/demos/session/src/main/res/drawable-mdpi/baseline_playlist_remove_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ad59bf2b54f252d2f6ae1030149324f5097293 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DHcuDFkch)?r+9N6P~c(Zo_A53 zYu?49CCXd0MCupn-P38_$Cz9c>%>tg!K2XF>yW{xBC!0tW9cQq4`*xr59A6w=y1>y ztq>Bh7rEOspT$m`Z~Df*BafCZDwc^0Yi%978G? zlNE$}nEw4&a7gC+`}gzwX)-JeY}@5?R5>n8Yu-@uqPR_-fuY=f!%pLy@6H?O1eK%u>!E{-7;jBlqL6=ez(ao)(*l>3os0aMuKcCHQoq8*x_8ytLl z_v`7M@sm02CM$3#RMsglIsR$qV42^$Z$p^9GncLU1nDzIDl4z_Iof|xm1L~u#$m3eT-ZYCG|RENW!(HlZh45O%sjqU^ellQmOuX%U$r~hNl{`2+s!s`_tcWae!di2Z+e4#X1B2@EY>Keh) c|A8#rlGl8smH6NK0zJau>FVdQ&MBb@0IdFUuK)l5 literal 0 HcmV?d00001 diff --git a/demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_add_white_48.png b/demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_add_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..c07417c910cc5980084298fe2adb913d2220d961 GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^6F`^|NHCnYy)O!+m`Z~Df*BafCZDwc@{BxP978G? zlO?PYM0%Kh{}=d>c!RU|N(IZJhng)6ha*nNJxZVPynXJ4f{rbL3lo{$7#TL|?49u4 Rc7GtqOix!omvv4FO#rB*CaC}b literal 0 HcmV?d00001 diff --git a/demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_remove_white_48.png b/demos/session/src/main/res/drawable-xxhdpi/baseline_playlist_remove_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..f78a05f28e85d38df5ef46ca6fe2b2a7cb1761a1 GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^6F``S8Ays|{O<-)0X`wFK>9xh@Hn5#0+e7c3GxeO z_^-NO`pK=0-~Y{LU|kc-Jsgthq%tK6ckz3c)u4GK%HOY2npH}1Hx zC+|n3L2Ldmp2f4CEN>NX;!tcc=;`QCVp96W?Q~+VSfGnwtAJXAbZL3+*`4#*T%SK) zD|z~tjPP5zD$~WU3#5eRs`^KTy^Iv7oM5xkd)g5t{$DDU6I3^zXohk>E%BeAD(WJB za`LWyZ%!}L1FHUf&1K(}t)Uaww<;VEa~628`tDB6%;*WKg#zl9GA^DnM?AkO_&rka zTcoh;db0v&(~LO2q>C;6Zt>q*=Fk5(!FU@-&MnE~95Ok<{ZSTw)MMOh9IrJWn_SV6 zP_ahyP_x+K#qpeSZjy5Sl5#g*e-LhZ=+1li{2GBn{*}xUzo%zP&Ajv*7+?&Zu6{1- HoD!M<<-4L^ literal 0 HcmV?d00001 diff --git a/demos/session/src/main/res/drawable-xxxhdpi/baseline_playlist_add_white_48.png b/demos/session/src/main/res/drawable-xxxhdpi/baseline_playlist_add_white_48.png new file mode 100644 index 0000000000000000000000000000000000000000..b5cf8c2b04ff1217186898d3ce5b4a4882fef5ee GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^2SAt+NHA0_4_pPLm`Z~Df*BafCZDwc^2|M5978G? z-(FtG%V5C6eDK}=l2fw|8E9XUtUEL<4+6T!Hj|4B&A-mjx)nUJ~RN z%<$iFGuPL>Torw63=E7>o-U3d6^w7L8uBtQa2#>ad2nBc%Om0M9my9rn1EUueC?mG zv48->0V7s0{lIFDU|UiuyAN|j&x&-$-~_H^jCqT=LN+^u2Ap9$%-q@Jcx6+AWtSYws24NZ7&Y1!DzvTeI5j_J`75^kliL}eyfkXwz{9}9bU-18nSq0W<23^V6T=}h zh6aX)0uBZR2B>g5SXlAHTt|i@Uu-K@HFmSMam~(+aVG9F?DuWYuLjV(q$q>rq(8+K?(S+fz!*5pxRwf1~<_n4p zQ#cr8nH;8m$Uff3$-s~x(Dt*uw=Kq|x3l-xLCbkQ_P_pbfBL`T diff --git a/demos/session/src/main/res/layout/playlist_items.xml b/demos/session/src/main/res/layout/playlist_items.xml index 88255c6600..e631d20b7a 100644 --- a/demos/session/src/main/res/layout/playlist_items.xml +++ b/demos/session/src/main/res/layout/playlist_items.xml @@ -35,7 +35,7 @@ android:layout_width="50dp" android:layout_height="match_parent" android:id="@+id/delete_button" - android:background="@android:drawable/ic_menu_close_clear_cancel" + android:background="@drawable/baseline_playlist_remove_white_48" /> From aab4872fc2da153cfe3fa1bf2e97339e9de9077a Mon Sep 17 00:00:00 2001 From: samrobinson Date: Wed, 12 Jan 2022 14:05:49 +0000 Subject: [PATCH 007/251] Add a Builder for TransformationResult. PiperOrigin-RevId: 421278099 --- .../transformer/mh/AndroidTestUtil.java | 55 +++++++++++++++---- .../RepeatedTranscodeTransformationTest.java | 48 ++++++++-------- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java index 5f7eea3f7c..1abe43e5a7 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java @@ -43,12 +43,45 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; /** Information about the result of successfully running a transformer. */ public static final class TransformationResult { - public final String testId; - public final long outputSizeBytes; - private TransformationResult(String testId, long outputSizeBytes) { + /** A builder for {@link TransformationResult} instances. */ + public static final class Builder { + private final String testId; + @Nullable private Long fileSizeBytes; + + public Builder(String testId) { + this.testId = testId; + } + + public Builder setFileSizeBytes(long fileSizeBytes) { + this.fileSizeBytes = fileSizeBytes; + return this; + } + + public TransformationResult build() { + return new TransformationResult(testId, fileSizeBytes); + } + } + + public final String testId; + @Nullable public final Long fileSizeBytes; + + private TransformationResult(String testId, @Nullable Long fileSizeBytes) { this.testId = testId; - this.outputSizeBytes = outputSizeBytes; + this.fileSizeBytes = fileSizeBytes; + } + + /** + * Returns all the analysis data from the test. + * + *

If a value was not generated, it will not be part of the return value. + */ + public String getFormattedAnalysis() { + String analysis = "test=" + testId; + if (fileSizeBytes != null) { + analysis += ", fileSizeBytes=" + fileSizeBytes; + } + return analysis; } } @@ -108,9 +141,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; if (exception != null) { throw exception; } - long outputSizeBytes = outputVideoFile.length(); - TransformationResult result = new TransformationResult(testId, outputSizeBytes); + TransformationResult result = + new TransformationResult.Builder(testId).setFileSizeBytes(outputVideoFile.length()).build(); + writeTransformationResultToFile(context, result); return result; } @@ -121,16 +155,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; createExternalCacheFile(context, /* fileName= */ result.testId + "-result.txt"); try (FileWriter fileWriter = new FileWriter(analysisFile)) { String fileContents = - "test=" - + result.testId + result.getFormattedAnalysis() + + ", deviceFingerprint=" + + Build.FINGERPRINT + ", deviceBrand=" + Build.MANUFACTURER + ", deviceModel=" + Build.MODEL + ", sdkVersion=" - + Build.VERSION.SDK_INT - + ", outputSizeBytes=" - + result.outputSizeBytes; + + Build.VERSION.SDK_INT; fileWriter.write(fileContents); } } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java index be2162a7e5..ae1da3bb29 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -21,19 +21,19 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.graphics.Matrix; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Assertions; import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.util.HashSet; import java.util.Set; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; /** Tests repeated transcoding operations (as a stress test and to help reproduce flakiness). */ @RunWith(AndroidJUnit4.class) -@Ignore("Internal - b/206917996") +// @Ignore("Internal - b/206917996") public final class RepeatedTranscodeTransformationTest { private static final int TRANSCODE_COUNT = 10; @@ -55,14 +55,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - differentOutputSizesBytes.add( + AndroidTestUtil.TransformationResult result = runTransformer( - context, - /* testId= */ "repeatedTranscode_givesConsistentLengthOutput", - transformer, - AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, - /* timeoutSeconds= */ 120) - .outputSizeBytes); + context, + /* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, + transformer, + AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, + /* timeoutSeconds= */ 120); + differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); } assertWithMessage( @@ -89,14 +89,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - differentOutputSizesBytes.add( + AndroidTestUtil.TransformationResult result = runTransformer( - context, - /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput", - transformer, - AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, - /* timeoutSeconds= */ 120) - .outputSizeBytes); + context, + /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, + transformer, + AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, + /* timeoutSeconds= */ 120); + differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); } assertWithMessage( @@ -108,7 +108,7 @@ public final class RepeatedTranscodeTransformationTest { @Test public void repeatedTranscodeNoVideo_givesConsistentLengthOutput() throws Exception { Context context = ApplicationProvider.getApplicationContext(); - Transformer transcodingTransformer = + Transformer transformer = new Transformer.Builder(context) .setRemoveVideo(true) .setTransformationRequest( @@ -120,14 +120,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - differentOutputSizesBytes.add( + AndroidTestUtil.TransformationResult result = runTransformer( - context, - /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput", - transcodingTransformer, - AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, - /* timeoutSeconds= */ 120) - .outputSizeBytes); + context, + /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i, + transformer, + AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, + /* timeoutSeconds= */ 120); + differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); } assertWithMessage( From 9a4ad0558611296b1978fc01ffc5a0cb4499124d Mon Sep 17 00:00:00 2001 From: samrobinson Date: Wed, 12 Jan 2022 16:24:42 +0000 Subject: [PATCH 008/251] Uncomment line. Accidentally commented out the Ignore annotation. PiperOrigin-RevId: 421304369 --- .../transformer/mh/RepeatedTranscodeTransformationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java index ae1da3bb29..aec9918141 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -28,12 +28,13 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.util.HashSet; import java.util.Set; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; /** Tests repeated transcoding operations (as a stress test and to help reproduce flakiness). */ @RunWith(AndroidJUnit4.class) -// @Ignore("Internal - b/206917996") +@Ignore("Internal - b/206917996") public final class RepeatedTranscodeTransformationTest { private static final int TRANSCODE_COUNT = 10; From 7756d9823d82df5fb16a4cfb712a821fa3f79eec Mon Sep 17 00:00:00 2001 From: jaewan Date: Thu, 13 Jan 2022 00:33:26 +0000 Subject: [PATCH 009/251] Fix NPE in MediaController's constructor MediaController tries to release itself when the binder to the session becomes unavailable. However, if such thing happens while connecting in the constructor, it causes NPE when accessing MediaControllerImpl which is only initialized after it's connected. This fixes random failures in existing tests. PiperOrigin-RevId: 421423381 --- .../androidx/media3/session/MediaController.java | 3 +++ .../media3/session/MediaControllerImplBase.java | 16 +++++++++++++--- .../session/MediaControllerImplLegacy.java | 4 +++- .../session/MediaSessionAndControllerTest.java | 8 +++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaController.java b/libraries/session/src/main/java/androidx/media3/session/MediaController.java index 2cdbba2276..53d7b912c1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java @@ -374,6 +374,7 @@ public class MediaController implements Player { @Initialized MediaController thisRef = this; impl = thisRef.createImpl(context, thisRef, token, connectionHints); + impl.connect(); } /* package */ MediaControllerImpl createImpl( @@ -1823,6 +1824,8 @@ public class MediaController implements Player { interface MediaControllerImpl { + void connect(); + void addListener(Player.Listener listener); void removeListener(Player.Listener listener); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 47d07b71e2..c2fd9f81a9 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -166,6 +166,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; private final Context context; private final SessionToken token; + private final Bundle connectionHints; private final IBinder.DeathRecipient deathRecipient; private final SurfaceCallback surfaceCallback; private final ListenerSet listeners; @@ -211,12 +212,24 @@ import org.checkerframework.checker.nullness.qual.NonNull; sequencedFutureManager = new SequencedFutureManager(); controllerStub = new MediaControllerStub(this); this.token = token; + this.connectionHints = connectionHints; deathRecipient = () -> MediaControllerImplBase.this.instance.runOnApplicationLooper( MediaControllerImplBase.this.instance::release); surfaceCallback = new SurfaceCallback(); + serviceConnection = + (this.token.getType() == TYPE_SESSION) + ? null + : new SessionServiceConnection(connectionHints); + flushCommandQueueHandler = new FlushCommandQueueHandler(instance.getApplicationLooper()); + lastReturnedContentPositionMs = C.TIME_UNSET; + lastSetPlayWhenReadyCalledTimeMs = C.TIME_UNSET; + } + + @Override + public void connect() { boolean connectionRequested; if (this.token.getType() == TYPE_SESSION) { // Session @@ -229,9 +242,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; if (!connectionRequested) { this.instance.runOnApplicationLooper(MediaControllerImplBase.this.instance::release); } - flushCommandQueueHandler = new FlushCommandQueueHandler(instance.getApplicationLooper()); - lastReturnedContentPositionMs = C.TIME_UNSET; - lastSetPlayWhenReadyCalledTimeMs = C.TIME_UNSET; } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java index a88f438b28..aec3f925f5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -155,9 +155,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; this.instance = instance; controllerCompatCallback = new ControllerCompatCallback(); this.token = token; + } + @Override + public void connect() { if (this.token.getType() == SessionToken.TYPE_SESSION) { - browserCompat = null; connectToSession((MediaSessionCompat.Token) checkStateNotNull(this.token.getBinder())); } else { connectToService(); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionAndControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionAndControllerTest.java index 6b04d16644..0ff9c10b0a 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionAndControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionAndControllerTest.java @@ -38,6 +38,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -117,11 +118,15 @@ public class MediaSessionAndControllerTest { Handler mainHandler = new Handler(Looper.getMainLooper()); Executor mainExecutor = (runnable) -> Util.postOrRun(mainHandler, runnable); for (int i = 0; i < 100; i++) { - int idx = i; MediaSession session = sessionTestRule.ensureReleaseAfterTest( new MediaSession.Builder(context, player).setId(TAG).build()); CountDownLatch latch = new CountDownLatch(1); + // Keep the instance in AtomicReference to prevent GC. + // Otherwise ListenableFuture and corresponding controllers can be GCed and disconnected + // callback can be ignored. + AtomicReference> controllerFutureRef = + new AtomicReference<>(); mainHandler.post( () -> { ListenableFuture controllerFuture = @@ -134,6 +139,7 @@ public class MediaSessionAndControllerTest { } }) .buildAsync(); + controllerFutureRef.set(controllerFuture); controllerFuture.addListener( () -> { try { From 6caaf58d404497a54011bd6d346278133ff2a3c8 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 13 Jan 2022 09:53:57 +0000 Subject: [PATCH 010/251] Update test relying on network type detection to run on all API levels. This ensures we test the API level specific logic, in particular around 5G-NSA detection. Robolectric has a remaining bug that it doesn't support listening to service state changes. Hence, we need to ignore some tests on these API levels still until this is fixed. PiperOrigin-RevId: 421505951 --- .../upstream/DefaultBandwidthMeterTest.java | 147 ++++++++++++++++-- 1 file changed, 137 insertions(+), 10 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java index c084b330b9..450222a71f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java @@ -25,9 +25,11 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.Uri; +import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import androidx.media3.common.C; import androidx.media3.common.util.NetworkTypeObserver; +import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.test.utils.FakeClock; @@ -42,9 +44,11 @@ import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowNetworkInfo; +import org.robolectric.shadows.ShadowTelephonyManager; /** Unit test for {@link DefaultBandwidthMeter}. */ @RunWith(AndroidJUnit4.class) +@Config(sdk = Config.ALL_SDKS) // Test all SDKs because network detection logic changed over time. public final class DefaultBandwidthMeterTest { private static final int SIMULATED_TRANSFER_COUNT = 100; @@ -58,8 +62,6 @@ public final class DefaultBandwidthMeterTest { private NetworkInfo networkInfo2g; private NetworkInfo networkInfo3g; private NetworkInfo networkInfo4g; - // TODO: Add tests covering 5G-NSA networks. Not testable right now because we need to set the - // TelephonyDisplayInfo on API 31, which isn't available for Robolectric yet. private NetworkInfo networkInfo5gSa; private NetworkInfo networkInfoEthernet; @@ -185,9 +187,15 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimateEthernet).isGreaterThan(initialEstimate3g); } - @Config(sdk = 28) // TODO(b/190021699): Fix 4G tests to work on newer API levels @Test public void defaultInitialBitrateEstimate_for4G_isGreaterThanEstimateFor2G() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + setActiveNetworkInfo(networkInfo4g); DefaultBandwidthMeter bandwidthMeter4g = new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); @@ -201,9 +209,15 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimate4g).isGreaterThan(initialEstimate2g); } - @Config(sdk = 28) // TODO(b/190021699): Fix 4G tests to work on newer API levels @Test public void defaultInitialBitrateEstimate_for4G_isGreaterThanEstimateFor3G() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + setActiveNetworkInfo(networkInfo4g); DefaultBandwidthMeter bandwidthMeter4g = new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); @@ -233,18 +247,42 @@ public final class DefaultBandwidthMeterTest { } @Test - public void defaultInitialBitrateEstimate_for5gSa_isGreaterThanEstimateFor4g() { + @Config(minSdk = 29) // 5G detection support was added in API 29. + public void defaultInitialBitrateEstimate_for5gNsa_isGreaterThanEstimateFor4g() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + setActiveNetworkInfo(networkInfo4g); DefaultBandwidthMeter bandwidthMeter4g = new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); long initialEstimate4g = bandwidthMeter4g.getBitrateEstimate(); + setActiveNetworkInfo(networkInfo4g, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA); + DefaultBandwidthMeter bandwidthMeter5gNsa = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); + long initialEstimate5gNsa = bandwidthMeter5gNsa.getBitrateEstimate(); + + assertThat(initialEstimate5gNsa).isGreaterThan(initialEstimate4g); + } + + @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. + public void defaultInitialBitrateEstimate_for5gSa_isGreaterThanEstimateFor3g() { + setActiveNetworkInfo(networkInfo3g); + DefaultBandwidthMeter bandwidthMeter3g = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); + long initialEstimate3g = bandwidthMeter3g.getBitrateEstimate(); + setActiveNetworkInfo(networkInfo5gSa); DefaultBandwidthMeter bandwidthMeter5gSa = new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); long initialEstimate5gSa = bandwidthMeter5gSa.getBitrateEstimate(); - assertThat(initialEstimate5gSa).isGreaterThan(initialEstimate4g); + assertThat(initialEstimate5gSa).isGreaterThan(initialEstimate3g); } @Test @@ -326,10 +364,16 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); } - @Config(sdk = 28) // TODO(b/190021699): Fix 4G tests to work on newer API levels @Test public void defaultInitialBitrateEstimate_for4g_forFastCountry_isGreaterThanEstimateForSlowCountry() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + setActiveNetworkInfo(networkInfo4g); setNetworkCountryIso(FAST_COUNTRY_ISO); DefaultBandwidthMeter bandwidthMeterFast = @@ -345,6 +389,32 @@ public final class DefaultBandwidthMeterTest { } @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. + public void + defaultInitialBitrateEstimate_for5gNsa_forFastCountry_isGreaterThanEstimateForSlowCountry() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + + setActiveNetworkInfo(networkInfo4g, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA); + setNetworkCountryIso(FAST_COUNTRY_ISO); + DefaultBandwidthMeter bandwidthMeterFast = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); + long initialEstimateFast = bandwidthMeterFast.getBitrateEstimate(); + + setNetworkCountryIso(SLOW_COUNTRY_ISO); + DefaultBandwidthMeter bandwidthMeterSlow = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()).build(); + long initialEstimateSlow = bandwidthMeterSlow.getBitrateEstimate(); + + assertThat(initialEstimateFast).isGreaterThan(initialEstimateSlow); + } + + @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. public void defaultInitialBitrateEstimate_for5gSa_forFastCountry_isGreaterThanEstimateForSlowCountry() { setActiveNetworkInfo(networkInfo5gSa); @@ -486,9 +556,15 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimate).isNotEqualTo(123456789); } - @Config(sdk = 28) // TODO(b/190021699): Fix 4G tests to work on newer API levels @Test public void initialBitrateEstimateOverwrite_for4G_whileConnectedTo4G_setsInitialEstimate() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + setActiveNetworkInfo(networkInfo4g); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) @@ -499,7 +575,6 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimate).isEqualTo(123456789); } - @Config(sdk = 28) // TODO(b/190021699): Fix 4G tests to work on newer API levels @Test public void initialBitrateEstimateOverwrite_for4G_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { @@ -514,6 +589,41 @@ public final class DefaultBandwidthMeterTest { } @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. + public void initialBitrateEstimateOverwrite_for5gNsa_whileConnectedTo5gNsa_setsInitialEstimate() { + if (Util.SDK_INT == 29 || Util.SDK_INT == 30) { + // Robolectric doesn't support listening to service state changes, which we need on APIs 29 + // and 30 to run this test successfully. + // TODO(b/190021699): Update once Robolectric released support for this. + return; + } + + setActiveNetworkInfo(networkInfo4g, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA); + DefaultBandwidthMeter bandwidthMeter = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) + .setInitialBitrateEstimate(C.NETWORK_TYPE_5G_NSA, 123456789) + .build(); + long initialEstimate = bandwidthMeter.getBitrateEstimate(); + + assertThat(initialEstimate).isEqualTo(123456789); + } + + @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. + public void + initialBitrateEstimateOverwrite_for5gNsa_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { + setActiveNetworkInfo(networkInfo4g); + DefaultBandwidthMeter bandwidthMeter = + new DefaultBandwidthMeter.Builder(ApplicationProvider.getApplicationContext()) + .setInitialBitrateEstimate(C.NETWORK_TYPE_5G_NSA, 123456789) + .build(); + long initialEstimate = bandwidthMeter.getBitrateEstimate(); + + assertThat(initialEstimate).isNotEqualTo(123456789); + } + + @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. public void initialBitrateEstimateOverwrite_for5gSa_whileConnectedTo5gSa_setsInitialEstimate() { setActiveNetworkInfo(networkInfo5gSa); DefaultBandwidthMeter bandwidthMeter = @@ -526,6 +636,7 @@ public final class DefaultBandwidthMeterTest { } @Test + @Config(minSdk = 29) // 5G detection support was added in API 29. public void initialBitrateEstimateOverwrite_for5gSa_whileConnectedToOtherNetwork_doesNotSetInitialEstimate() { setActiveNetworkInfo(networkInfoWifi); @@ -638,11 +749,27 @@ public final class DefaultBandwidthMeterTest { assertThat(initialEstimateWithoutBuilder).isLessThan(50_000_000L); } - @SuppressWarnings("StickyBroadcast") private void setActiveNetworkInfo(NetworkInfo networkInfo) { + setActiveNetworkInfo(networkInfo, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); + } + + @SuppressWarnings("StickyBroadcast") + private void setActiveNetworkInfo(NetworkInfo networkInfo, int networkTypeOverride) { + // Set network info in ConnectivityManager and TelephonyDisplayInfo in TelephonyManager. Shadows.shadowOf(connectivityManager).setActiveNetworkInfo(networkInfo); + if (Util.SDK_INT >= 31) { + Object displayInfo = + ShadowTelephonyManager.createTelephonyDisplayInfo( + networkInfo.getType(), networkTypeOverride); + Shadows.shadowOf(telephonyManager).setTelephonyDisplayInfo(displayInfo); + } + // Create a sticky broadcast for the connectivity action because Roboletric isn't replying with + // the current network state if a receiver for this intent is registered. ApplicationProvider.getApplicationContext() .sendStickyBroadcast(new Intent(ConnectivityManager.CONNECTIVITY_ACTION)); + // Trigger initialization of static network type observer. + NetworkTypeObserver.getInstance(ApplicationProvider.getApplicationContext()); + // Wait until all pending messages are handled and the network initialization is done. ShadowLooper.idleMainLooper(); } From b12918d1e6b92763c0a7b870e598d260a052361f Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 13 Jan 2022 10:38:43 +0000 Subject: [PATCH 011/251] Disable live speed adjustment where it has no benefit Live speed adjustment is used for all live playback at the moment, but has no user visible effect if the media is not played with low latency. To avoid unnecessary adjustment during playback without benefit, this change restricts the live speed adjustment to cases where either the user requested a speed value in the MediaItem or the media specifically defined a low-latency stream. Issue: google/ExoPlayer#9329 PiperOrigin-RevId: 421514283 --- .../DefaultLivePlaybackSpeedControl.java | 4 + .../DefaultLivePlaybackSpeedControlTest.java | 68 +++++++++------ .../exoplayer/dash/DashMediaSource.java | 14 ++- .../exoplayer/dash/DashMediaSourceTest.java | 75 ++++++++++++++-- .../media3/exoplayer/hls/HlsMediaSource.java | 21 +++-- .../exoplayer/hls/HlsMediaSourceTest.java | 85 ++++++++++++++++++- 6 files changed, 221 insertions(+), 46 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControl.java index b8f84a7743..0de033fc9d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControl.java @@ -311,6 +311,10 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC liveConfiguration.maxPlaybackSpeed != C.RATE_UNSET ? liveConfiguration.maxPlaybackSpeed : fallbackMaxPlaybackSpeed; + if (minPlaybackSpeed == 1f && maxPlaybackSpeed == 1f) { + // Don't bother calculating adjustments if it's not possible to change the speed. + mediaConfigurationTargetLiveOffsetUs = C.TIME_UNSET; + } maybeResetTargetLiveOffsetUs(); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControlTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControlTest.java index 930a9356a7..7ee65951a6 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControlTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/DefaultLivePlaybackSpeedControlTest.java @@ -49,8 +49,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(42_000); @@ -66,8 +66,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(4321) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(400_000); @@ -83,8 +83,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(3) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); assertThat(defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs()).isEqualTo(5_000); @@ -101,8 +101,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); @@ -122,8 +122,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); @@ -143,8 +143,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); @@ -164,6 +164,22 @@ public class DefaultLivePlaybackSpeedControlTest { assertThat(targetLiveOffsetUs).isEqualTo(C.TIME_UNSET); } + @Test + public void getTargetLiveOffsetUs_withUnitSpeed_returnsUnset() { + DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl = + new DefaultLivePlaybackSpeedControl.Builder().build(); + defaultLivePlaybackSpeedControl.setLiveConfiguration( + new LiveConfiguration.Builder() + .setTargetOffsetMs(42) + .setMinPlaybackSpeed(1f) + .setMaxPlaybackSpeed(1f) + .build()); + + long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); + + assertThat(targetLiveOffsetUs).isEqualTo(C.TIME_UNSET); + } + @Test public void getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideWithTimeUnset_returnsMediaLiveOffset() { @@ -175,8 +191,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); defaultLivePlaybackSpeedControl.setTargetLiveOffsetOverrideUs(C.TIME_UNSET); @@ -196,8 +212,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); @@ -219,8 +235,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); List targetOffsetsUs = new ArrayList<>(); @@ -245,8 +261,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); defaultLivePlaybackSpeedControl.notifyRebuffer(); @@ -267,8 +283,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); defaultLivePlaybackSpeedControl.notifyRebuffer(); @@ -290,8 +306,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); long targetLiveOffsetBeforeUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs(); @@ -322,8 +338,8 @@ public class DefaultLivePlaybackSpeedControlTest { .setTargetOffsetMs(42) .setMinOffsetMs(5) .setMaxOffsetMs(400) - .setMinPlaybackSpeed(1f) - .setMaxPlaybackSpeed(1f) + .setMinPlaybackSpeed(0.95f) + .setMaxPlaybackSpeed(1.05f) .build()); defaultLivePlaybackSpeedControl.notifyRebuffer(); diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java index 3e151d60d5..038b4191ef 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java @@ -802,7 +802,7 @@ public final class DashMediaSource extends BaseMediaSource { nowUnixTimeUs - Util.msToUs(manifest.availabilityStartTimeMs) - windowStartTimeInManifestUs; - updateMediaItemLiveConfiguration(nowInWindowUs, windowDurationUs); + updateLiveConfiguration(nowInWindowUs, windowDurationUs); windowStartUnixTimeMs = manifest.availabilityStartTimeMs + Util.usToMs(windowStartTimeInManifestUs); windowDefaultPositionUs = nowInWindowUs - Util.msToUs(liveConfiguration.targetOffsetMs); @@ -861,7 +861,7 @@ public final class DashMediaSource extends BaseMediaSource { } } - private void updateMediaItemLiveConfiguration(long nowInWindowUs, long windowDurationUs) { + private void updateLiveConfiguration(long nowInWindowUs, long windowDurationUs) { // Default maximum offset: start of window. long maxPossibleLiveOffsetMs = usToMs(nowInWindowUs); long maxLiveOffsetMs = maxPossibleLiveOffsetMs; @@ -936,6 +936,16 @@ public final class DashMediaSource extends BaseMediaSource { } else if (manifest.serviceDescription != null) { maxPlaybackSpeed = manifest.serviceDescription.maxPlaybackSpeed; } + if (minPlaybackSpeed == C.RATE_UNSET + && maxPlaybackSpeed == C.RATE_UNSET + && (manifest.serviceDescription == null + || manifest.serviceDescription.targetOffsetMs == C.TIME_UNSET)) { + // Force unit speed (instead of automatic adjustment with fallback speeds) if there are no + // specific speed limits defined by the media item or the manifest, and the manifest contains + // no low-latency target offset either. + minPlaybackSpeed = 1f; + maxPlaybackSpeed = 1f; + } liveConfiguration = new MediaItem.LiveConfiguration.Builder() .setTargetOffsetMs(targetOffsetMs) diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaSourceTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaSourceTest.java index 20ba0560ee..4cca2aee2e 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaSourceTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaSourceTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.fail; import android.net.Uri; import androidx.media3.common.C; import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaItem.LiveConfiguration; import androidx.media3.common.ParserException; import androidx.media3.common.Timeline; import androidx.media3.common.Timeline.Window; @@ -140,7 +141,7 @@ public final class DashMediaSourceTest { } @Test - public void prepare_withoutLiveConfiguration_withoutMediaItemLiveProperties_usesDefaultFallback() + public void prepare_withoutLiveConfiguration_withoutMediaItemLiveConfiguration_usesUnitSpeed() throws InterruptedException { DashMediaSource mediaSource = new DashMediaSource.Factory( @@ -154,18 +155,71 @@ public final class DashMediaSourceTest { .isEqualTo(DashMediaSource.DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS); assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L); - assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); - assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(1f); + assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(1f); } @Test - public void prepare_withoutLiveConfiguration_withoutMediaItemLiveProperties_usesFallback() + public void prepare_withoutLiveConfiguration_withOnlyMediaItemTargetOffset_usesUnitSpeed() throws InterruptedException { + DashMediaSource mediaSource = + new DashMediaSource.Factory( + () -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION)) + .createMediaSource( + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setLiveConfiguration( + new LiveConfiguration.Builder().setTargetOffsetMs(10_000L).build()) + .build()); + + MediaItem.LiveConfiguration liveConfiguration = + prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration; + + assertThat(liveConfiguration.targetOffsetMs).isEqualTo(10_000L); + assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L); + assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L); + assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(1f); + assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(1f); + } + + @Test + public void prepare_withoutLiveConfiguration_withMediaItemSpeedLimits_usesDefaultFallbackValues() + throws InterruptedException { + DashMediaSource mediaSource = + new DashMediaSource.Factory( + () -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION)) + .createMediaSource( + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setLiveConfiguration( + new LiveConfiguration.Builder().setMinPlaybackSpeed(0.95f).build()) + .build()); + + MediaItem.LiveConfiguration liveConfiguration = + prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration; + + assertThat(liveConfiguration.targetOffsetMs) + .isEqualTo(DashMediaSource.DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS); + assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L); + assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L); + assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(0.95f); + assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); + } + + @Test + public void + prepare_withoutLiveConfiguration_withoutMediaItemTargetOffset_usesDefinedFallbackTargetOffset() + throws InterruptedException { DashMediaSource mediaSource = new DashMediaSource.Factory( () -> createSampleMpdDataSource(SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION)) .setFallbackTargetLiveOffsetMs(1234L) - .createMediaSource(MediaItem.fromUri(Uri.EMPTY)); + .createMediaSource( + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setLiveConfiguration( + new LiveConfiguration.Builder().setMinPlaybackSpeed(0.95f).build()) + .build()); MediaItem.LiveConfiguration liveConfiguration = prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration; @@ -173,7 +227,7 @@ public final class DashMediaSourceTest { assertThat(liveConfiguration.targetOffsetMs).isEqualTo(1234L); assertThat(liveConfiguration.minOffsetMs).isEqualTo(0L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L); - assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(0.95f); assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); } @@ -213,7 +267,12 @@ public final class DashMediaSourceTest { createSampleMpdDataSource( SAMPLE_MPD_LIVE_WITH_SUGGESTED_PRESENTATION_DELAY_2S_MIN_BUFFER_TIME_500MS)) .setFallbackTargetLiveOffsetMs(1234L) - .createMediaSource(MediaItem.fromUri(Uri.EMPTY)); + .createMediaSource( + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setLiveConfiguration( + new LiveConfiguration.Builder().setMaxPlaybackSpeed(1.05f).build()) + .build()); MediaItem.LiveConfiguration liveConfiguration = prepareAndWaitForTimelineRefresh(mediaSource).liveConfiguration; @@ -222,7 +281,7 @@ public final class DashMediaSourceTest { assertThat(liveConfiguration.minOffsetMs).isEqualTo(500L); assertThat(liveConfiguration.maxOffsetMs).isEqualTo(58_000L); assertThat(liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); - assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(liveConfiguration.maxPlaybackSpeed).isEqualTo(1.05f); } @Test diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java index e0bd7029fb..7610aa8286 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java @@ -25,6 +25,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.media3.common.C; import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaItem.LiveConfiguration; import androidx.media3.common.MediaLibraryInfo; import androidx.media3.common.StreamKey; import androidx.media3.common.util.UnstableApi; @@ -476,7 +477,7 @@ public final class HlsMediaSource extends BaseMediaSource targetLiveOffsetUs = Util.constrainValue( targetLiveOffsetUs, liveEdgeOffsetUs, playlist.durationUs + liveEdgeOffsetUs); - maybeUpdateLiveConfiguration(targetLiveOffsetUs); + updateLiveConfiguration(playlist, targetLiveOffsetUs); long windowDefaultStartPositionUs = getLiveWindowDefaultStartPositionUs(playlist, liveEdgeOffsetUs); boolean suppressPositionProjection = @@ -566,12 +567,18 @@ public final class HlsMediaSource extends BaseMediaSource return segment.relativeStartTimeUs; } - private void maybeUpdateLiveConfiguration(long targetLiveOffsetUs) { - long targetLiveOffsetMs = Util.usToMs(targetLiveOffsetUs); - if (targetLiveOffsetMs != liveConfiguration.targetOffsetMs) { - liveConfiguration = - liveConfiguration.buildUpon().setTargetOffsetMs(targetLiveOffsetMs).build(); - } + private void updateLiveConfiguration(HlsMediaPlaylist playlist, long targetLiveOffsetUs) { + boolean disableSpeedAdjustment = + mediaItem.liveConfiguration.minPlaybackSpeed == C.RATE_UNSET + && mediaItem.liveConfiguration.maxPlaybackSpeed == C.RATE_UNSET + && playlist.serverControl.holdBackUs == C.TIME_UNSET + && playlist.serverControl.partHoldBackUs == C.TIME_UNSET; + liveConfiguration = + new LiveConfiguration.Builder() + .setTargetOffsetMs(Util.usToMs(targetLiveOffsetUs)) + .setMinPlaybackSpeed(disableSpeedAdjustment ? 1f : liveConfiguration.minPlaybackSpeed) + .setMaxPlaybackSpeed(disableSpeedAdjustment ? 1f : liveConfiguration.maxPlaybackSpeed) + .build(); } /** diff --git a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsMediaSourceTest.java b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsMediaSourceTest.java index 01e73a87f0..7e3f637fa3 100644 --- a/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsMediaSourceTest.java +++ b/libraries/exoplayer_hls/src/test/java/androidx/media3/exoplayer/hls/HlsMediaSourceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; import android.os.SystemClock; +import androidx.media3.common.C; import androidx.media3.common.MediaItem; import androidx.media3.common.ParserException; import androidx.media3.common.Timeline; @@ -77,6 +78,8 @@ public class HlsMediaSourceTest { // The target live offset is picked from target duration (3 * 4 = 12 seconds) and then expressed // in relation to the live edge (12 + 1 seconds). assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000); + assertThat(window.liveConfiguration.minPlaybackSpeed).isEqualTo(1f); + assertThat(window.liveConfiguration.maxPlaybackSpeed).isEqualTo(1f); assertThat(window.defaultPositionUs).isEqualTo(4000000); } @@ -100,7 +103,7 @@ public class HlsMediaSourceTest { + "#EXTINF:4.00000,\n" + "fileSequence3.ts\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK=12"; - // The playlist finishes 1 second before the the current time, therefore there's a live edge + // The playlist finishes 1 second before the current time, therefore there's a live edge // offset of 1 second. SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:17.0+00:00")); HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist); @@ -113,6 +116,8 @@ public class HlsMediaSourceTest { // The target live offset is picked from hold back and then expressed in relation to the live // edge (+1 seconds). assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000); + assertThat(window.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(window.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(window.defaultPositionUs).isEqualTo(4000000); } @@ -139,7 +144,7 @@ public class HlsMediaSourceTest { + "#EXTINF:4.00000,\n" + "fileSequence3.ts\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK=12,PART-HOLD-BACK=3"; - // The playlist finishes 1 second before the the current time. + // The playlist finishes 1 second before the current time. SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:17.0+00:00")); HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist); MediaItem mediaItem = MediaItem.fromUri(playlistUri); @@ -151,6 +156,8 @@ public class HlsMediaSourceTest { // The target live offset is picked from hold back and then expressed in relation to the live // edge (+1 seconds). assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(13000); + assertThat(window.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(window.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(window.defaultPositionUs).isEqualTo(4000000); } @@ -182,6 +189,8 @@ public class HlsMediaSourceTest { // The target live offset is picked from part hold back and then expressed in relation to the // live edge (+1 seconds). assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(4000); + assertThat(window.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET); + assertThat(window.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET); assertThat(window.defaultPositionUs).isEqualTo(0); } @@ -395,7 +404,7 @@ public class HlsMediaSourceTest { + "#EXTINF:4.00000,\n" + "fileSequence0.ts\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK=12,PART-HOLD-BACK=3"; - // The playlist finishes 1 second before the the current time. This should not affect the target + // The playlist finishes 1 second before the current time. This should not affect the target // live offset set in the media item. SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:05.0+00:00")); HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist); @@ -415,6 +424,76 @@ public class HlsMediaSourceTest { assertThat(window.defaultPositionUs).isEqualTo(0); } + @Test + public void loadLivePlaylist_noHoldBackInPlaylistAndNoPlaybackSpeedInMediaItem_usesUnitSpeed() + throws TimeoutException, ParserException { + String playlistUri = "fake://foo.bar/media0/playlist.m3u8"; + // The playlist has no hold back defined. + String playlist = + "#EXTM3U\n" + + "#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.0+00:00\n" + + "#EXT-X-TARGETDURATION:4\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXTINF:4.00000,\n" + + "fileSequence0.ts"; + // The playlist finishes 1 second before the current time. This should not affect the target + // live offset set in the media item. + SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:05.0+00:00")); + HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist); + MediaItem mediaItem = + new MediaItem.Builder() + .setUri(playlistUri) + .setLiveConfiguration( + new MediaItem.LiveConfiguration.Builder().setTargetOffsetMs(1000).build()) + .build(); + HlsMediaSource mediaSource = factory.createMediaSource(mediaItem); + + Timeline timeline = prepareAndWaitForTimeline(mediaSource); + + Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); + assertThat(window.liveConfiguration.minPlaybackSpeed).isEqualTo(1f); + assertThat(window.liveConfiguration.maxPlaybackSpeed).isEqualTo(1f); + assertThat(window.defaultPositionUs).isEqualTo(0); + } + + @Test + public void + loadLivePlaylist_noHoldBackInPlaylistAndPlaybackSpeedInMediaItem_usesMediaItemConfiguration() + throws TimeoutException, ParserException { + String playlistUri = "fake://foo.bar/media0/playlist.m3u8"; + // The playlist has no hold back defined. + String playlist = + "#EXTM3U\n" + + "#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.0+00:00\n" + + "#EXT-X-TARGETDURATION:4\n" + + "#EXT-X-VERSION:3\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXTINF:4.00000,\n" + + "fileSequence0.ts"; + // The playlist finishes 1 second before the current time. This should not affect the target + // live offset set in the media item. + SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:05.0+00:00")); + HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist); + MediaItem mediaItem = + new MediaItem.Builder() + .setUri(playlistUri) + .setLiveConfiguration( + new MediaItem.LiveConfiguration.Builder() + .setTargetOffsetMs(1000) + .setMinPlaybackSpeed(0.94f) + .setMaxPlaybackSpeed(1.02f) + .build()) + .build(); + HlsMediaSource mediaSource = factory.createMediaSource(mediaItem); + + Timeline timeline = prepareAndWaitForTimeline(mediaSource); + + Timeline.Window window = timeline.getWindow(0, new Timeline.Window()); + assertThat(window.liveConfiguration).isEqualTo(mediaItem.liveConfiguration); + assertThat(window.defaultPositionUs).isEqualTo(0); + } + @Test public void loadLivePlaylist_targetLiveOffsetLargerThanLiveWindow_targetLiveOffsetIsWithinLiveWindow() From f40f290a30aaa968acfb7985ae7ec9fb6708a863 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Thu, 13 Jan 2022 10:43:52 +0000 Subject: [PATCH 012/251] Remove Transformer-specific things from MediaCodecAdapter. PiperOrigin-RevId: 421514944 --- .../AsynchronousMediaCodecAdapter.java | 25 +----- .../mediacodec/MediaCodecAdapter.java | 90 +------------------ .../SynchronousMediaCodecAdapter.java | 51 +---------- .../test/utils/CapturingRenderersFactory.java | 12 --- 4 files changed, 8 insertions(+), 170 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index 715f1b183a..dd9b97d8dd 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -109,8 +109,7 @@ import java.nio.ByteBuffer; configuration.mediaFormat, configuration.surface, configuration.crypto, - configuration.flags, - configuration.createInputSurface); + configuration.flags); return codecAdapter; } catch (Exception e) { if (codecAdapter != null) { @@ -139,7 +138,6 @@ import java.nio.ByteBuffer; private final boolean enableImmediateCodecStartAfterFlush; private boolean codecReleased; @State private int state; - @Nullable private Surface inputSurface; private AsynchronousMediaCodecAdapter( MediaCodec codec, @@ -159,15 +157,11 @@ import java.nio.ByteBuffer; @Nullable MediaFormat mediaFormat, @Nullable Surface surface, @Nullable MediaCrypto crypto, - int flags, - boolean createInputSurface) { + int flags) { asynchronousMediaCodecCallback.initialize(codec); TraceUtil.beginSection("configureCodec"); codec.configure(mediaFormat, surface, crypto, flags); TraceUtil.endSection(); - if (createInputSurface) { - inputSurface = codec.createInputSurface(); - } bufferEnqueuer.start(); TraceUtil.beginSection("startCodec"); codec.start(); @@ -223,12 +217,6 @@ import java.nio.ByteBuffer; return codec.getInputBuffer(index); } - @Override - @Nullable - public Surface getInputSurface() { - return inputSurface; - } - @Override @Nullable public ByteBuffer getOutputBuffer(int index) { @@ -263,9 +251,6 @@ import java.nio.ByteBuffer; } state = STATE_SHUT_DOWN; } finally { - if (inputSurface != null) { - inputSurface.release(); - } if (!codecReleased) { codec.release(); codecReleased = true; @@ -301,12 +286,6 @@ import java.nio.ByteBuffer; codec.setVideoScalingMode(scalingMode); } - @Override - public void signalEndOfInputStream() { - maybeBlockOnQueueing(); - codec.signalEndOfInputStream(); - } - @Override @RequiresApi(26) public PersistableBundle getMetrics() { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java index c188c831f0..6fa902bd7a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java @@ -57,13 +57,7 @@ public interface MediaCodecAdapter { Format format, @Nullable MediaCrypto crypto) { return new Configuration( - codecInfo, - mediaFormat, - format, - /* surface= */ null, - crypto, - /* flags= */ 0, - /* createInputSurface= */ false); + codecInfo, mediaFormat, format, /* surface= */ null, crypto, /* flags= */ 0); } /** @@ -82,55 +76,7 @@ public interface MediaCodecAdapter { Format format, @Nullable Surface surface, @Nullable MediaCrypto crypto) { - return new Configuration( - codecInfo, - mediaFormat, - format, - surface, - crypto, - /* flags= */ 0, - /* createInputSurface= */ false); - } - - /** - * Creates a configuration for audio encoding. - * - * @param codecInfo See {@link #codecInfo}. - * @param mediaFormat See {@link #mediaFormat}. - * @param format See {@link #format}. - * @return The created instance. - */ - public static Configuration createForAudioEncoding( - MediaCodecInfo codecInfo, MediaFormat mediaFormat, Format format) { - return new Configuration( - codecInfo, - mediaFormat, - format, - /* surface= */ null, - /* crypto= */ null, - MediaCodec.CONFIGURE_FLAG_ENCODE, - /* createInputSurface= */ false); - } - - /** - * Creates a configuration for video encoding. - * - * @param codecInfo See {@link #codecInfo}. - * @param mediaFormat See {@link #mediaFormat}. - * @param format See {@link #format}. - * @return The created instance. - */ - @RequiresApi(18) - public static Configuration createForVideoEncoding( - MediaCodecInfo codecInfo, MediaFormat mediaFormat, Format format) { - return new Configuration( - codecInfo, - mediaFormat, - format, - /* surface= */ null, - /* crypto= */ null, - MediaCodec.CONFIGURE_FLAG_ENCODE, - /* createInputSurface= */ true); + return new Configuration(codecInfo, mediaFormat, format, surface, crypto, /* flags= */ 0); } /** Information about the {@link MediaCodec} being configured. */ @@ -147,17 +93,8 @@ public interface MediaCodecAdapter { @Nullable public final Surface surface; /** For DRM protected playbacks, a {@link MediaCrypto} to use for decryption. */ @Nullable public final MediaCrypto crypto; - /** - * Specify CONFIGURE_FLAG_ENCODE to configure the component as an encoder. - * - * @see MediaCodec#configure - */ + /** See {@link MediaCodec#configure}. */ public final int flags; - /** - * Whether to request a {@link Surface} and use it as to the input to an encoder. This can only - * be set to {@code true} on API 18+. - */ - public final boolean createInputSurface; private Configuration( MediaCodecInfo codecInfo, @@ -165,15 +102,13 @@ public interface MediaCodecAdapter { Format format, @Nullable Surface surface, @Nullable MediaCrypto crypto, - int flags, - boolean createInputSurface) { + int flags) { this.codecInfo = codecInfo; this.mediaFormat = mediaFormat; this.format = format; this.surface = surface; this.crypto = crypto; this.flags = flags; - this.createInputSurface = createInputSurface; } } @@ -231,14 +166,6 @@ public interface MediaCodecAdapter { @Nullable ByteBuffer getInputBuffer(int index); - /** - * Returns the input {@link Surface}, or null if the input is not a surface. - * - * @see MediaCodec#createInputSurface() - */ - @Nullable - Surface getInputSurface(); - /** * Returns a read-only ByteBuffer for a dequeued output buffer index. * @@ -331,15 +258,6 @@ public interface MediaCodecAdapter { /** Whether the adapter needs to be reconfigured before it is used. */ boolean needsReconfiguration(); - /** - * Signals the encoder of end-of-stream on input. The call can only be used when the encoder - * receives its input from a {@link Surface surface}. - * - * @see MediaCodec#signalEndOfInputStream() - */ - @RequiresApi(18) - void signalEndOfInputStream(); - /** * Returns metrics data about the current codec instance. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java index 5f97cf81e2..1d5d4cb27d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/SynchronousMediaCodecAdapter.java @@ -25,7 +25,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.PersistableBundle; import android.view.Surface; -import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.media3.common.C; @@ -48,7 +47,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { @Override public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException { @Nullable MediaCodec codec = null; - @Nullable Surface inputSurface = null; try { codec = createCodec(configuration); TraceUtil.beginSection("configureCodec"); @@ -58,24 +56,11 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { configuration.crypto, configuration.flags); TraceUtil.endSection(); - - if (configuration.createInputSurface) { - if (Util.SDK_INT >= 18) { - inputSurface = Api18.createCodecInputSurface(codec); - } else { - throw new IllegalStateException( - "Encoding from a surface is only supported on API 18 and up."); - } - } - TraceUtil.beginSection("startCodec"); codec.start(); TraceUtil.endSection(); - return new SynchronousMediaCodecAdapter(codec, inputSurface); + return new SynchronousMediaCodecAdapter(codec); } catch (IOException | RuntimeException e) { - if (inputSurface != null) { - inputSurface.release(); - } if (codec != null) { codec.release(); } @@ -95,13 +80,11 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { } private final MediaCodec codec; - @Nullable private final Surface inputSurface; @Nullable private ByteBuffer[] inputByteBuffers; @Nullable private ByteBuffer[] outputByteBuffers; - private SynchronousMediaCodecAdapter(MediaCodec mediaCodec, @Nullable Surface inputSurface) { + private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) { this.codec = mediaCodec; - this.inputSurface = inputSurface; if (Util.SDK_INT < 21) { inputByteBuffers = codec.getInputBuffers(); outputByteBuffers = codec.getOutputBuffers(); @@ -146,12 +129,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { } } - @Override - @Nullable - public Surface getInputSurface() { - return inputSurface; - } - @Override @Nullable public ByteBuffer getOutputBuffer(int index) { @@ -195,18 +172,9 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { public void release() { inputByteBuffers = null; outputByteBuffers = null; - if (inputSurface != null) { - inputSurface.release(); - } codec.release(); } - @Override - @RequiresApi(18) - public void signalEndOfInputStream() { - Api18.signalEndOfInputStream(codec); - } - @Override @RequiresApi(23) public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) { @@ -239,19 +207,4 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { public PersistableBundle getMetrics() { return codec.getMetrics(); } - - @RequiresApi(18) - private static final class Api18 { - private Api18() {} - - @DoNotInline - public static Surface createCodecInputSurface(MediaCodec codec) { - return codec.createInputSurface(); - } - - @DoNotInline - public static void signalEndOfInputStream(MediaCodec codec) { - codec.signalEndOfInputStream(); - } - } } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java index 8355b7f4a7..c08eeffbe0 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java @@ -197,12 +197,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa return inputBuffer; } - @Nullable - @Override - public Surface getInputSurface() { - return delegate.getInputSurface(); - } - @Nullable @Override public ByteBuffer getOutputBuffer(int index) { @@ -272,12 +266,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa delegate.setVideoScalingMode(scalingMode); } - @RequiresApi(18) - @Override - public void signalEndOfInputStream() { - delegate.signalEndOfInputStream(); - } - @RequiresApi(26) @Override public PersistableBundle getMetrics() { From 1022812dab1e7d51e543d6202efe9c7c6ee62e83 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 13 Jan 2022 12:26:13 +0000 Subject: [PATCH 013/251] Remove Allocator.release(Allocation[]) and references PiperOrigin-RevId: 421530365 --- .../media3/exoplayer/upstream/Allocator.java | 7 ------- .../exoplayer/upstream/DefaultAllocator.java | 14 ++------------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Allocator.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Allocator.java index 59cc85a6bb..3f06e795c1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Allocator.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Allocator.java @@ -50,13 +50,6 @@ public interface Allocator { */ void release(Allocation allocation); - /** - * Releases an array of {@link Allocation Allocations} back to the allocator. - * - * @param allocations The array of {@link Allocation}s being released. - */ - void release(Allocation[] allocations); - /** * Releases all {@link Allocation Allocations} in the chain starting at the given {@link * AllocationNode}. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultAllocator.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultAllocator.java index 83bbac44fe..af42d8b71d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultAllocator.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultAllocator.java @@ -33,7 +33,6 @@ public final class DefaultAllocator implements Allocator { private final boolean trimOnReset; private final int individualAllocationSize; @Nullable private final byte[] initialAllocationBlock; - private final Allocation[] singleAllocationReleaseHolder; private int targetBufferSize; private int allocatedCount; @@ -78,7 +77,6 @@ public final class DefaultAllocator implements Allocator { } else { initialAllocationBlock = null; } - singleAllocationReleaseHolder = new Allocation[1]; } public synchronized void reset() { @@ -116,16 +114,8 @@ public final class DefaultAllocator implements Allocator { @Override public synchronized void release(Allocation allocation) { - singleAllocationReleaseHolder[0] = allocation; - release(singleAllocationReleaseHolder); - } - - @Override - public synchronized void release(Allocation[] allocations) { - for (Allocation allocation : allocations) { - availableAllocations[availableCount++] = allocation; - } - allocatedCount -= allocations.length; + availableAllocations[availableCount++] = allocation; + allocatedCount--; // Wake up threads waiting for the allocated size to drop. notifyAll(); } From 4ea08d5117af3428d36717e5e8e6f83cc1bb579c Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Thu, 13 Jan 2022 13:59:02 +0000 Subject: [PATCH 014/251] Transformer GL: Add pixel test instructions for physical devices Expected images are taken on emulators, so a larger acceptable difference from expected images must be accepted on physical devices. PiperOrigin-RevId: 421543441 --- .../transformer/FrameEditorDataProcessingTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java index 43338be573..5944285000 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java @@ -42,7 +42,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -/** Test for frame processing via {@link FrameEditor#processData()}. */ +/** + * Pixel test for frame processing via {@link FrameEditor#processData()}. Expected images are taken + * from emulators, so tests on physical devices may fail. To test on physical devices, please modify + * the MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE. + */ @RunWith(AndroidJUnit4.class) public final class FrameEditorDataProcessingTest { @@ -54,6 +58,12 @@ public final class FrameEditorDataProcessingTest { * test to pass. The value is chosen so that differences in decoder behavior across emulator * versions shouldn't affect whether the test passes, but substantial distortions introduced by * changes in the behavior of the frame editor will cause the test to fail. + * + *

To run this test on physical devices, please use a value of 5f, rather than 0.1f. This + * higher value will ignore some very small errors, but will allow for some differences caused by + * graphics implementations to be ignored. When the difference is close to the threshold, manually + * inspect expected/actual bitmaps to confirm failure, as it's possible this is caused by a + * difference in the codec or graphics implementation as opposed to a FrameEditor issue. */ private static final float MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE = 0.1f; /** Timeout for dequeueing buffers from the codec, in microseconds. */ From 4d5bf7c065f6bc1599be2651dd74180549061fa5 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Thu, 13 Jan 2022 15:32:21 +0000 Subject: [PATCH 015/251] Use specific error code for exceptions during encoding/decoding. After this change exceptions throw by MediaCodec during encoding/decoding will result in TransformationExceptions with ERROR_CODE_ENCODING_FAILED/ERROR_CODE_DECODING_FAILED. Before this change ERROR_CODE_FAILED_RUNTIME_CHECK was used. PiperOrigin-RevId: 421560396 --- .../transformer/AudioSamplePipeline.java | 22 +-- .../androidx/media3/transformer/Codec.java | 147 +++++++++++++++--- .../transformer/DefaultCodecFactory.java | 14 +- .../media3/transformer/SamplePipeline.java | 10 +- .../transformer/TransformationException.java | 18 ++- .../transformer/TransformerBaseRenderer.java | 10 +- .../transformer/TransformerVideoRenderer.java | 6 +- .../transformer/VideoSamplePipeline.java | 10 +- 8 files changed, 184 insertions(+), 53 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioSamplePipeline.java index 8376c4d157..fe605a5351 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioSamplePipeline.java @@ -113,17 +113,17 @@ import java.nio.ByteBuffer; @Override @Nullable - public DecoderInputBuffer dequeueInputBuffer() { + public DecoderInputBuffer dequeueInputBuffer() throws TransformationException { return decoder.maybeDequeueInputBuffer(decoderInputBuffer) ? decoderInputBuffer : null; } @Override - public void queueInputBuffer() { + public void queueInputBuffer() throws TransformationException { decoder.queueInputBuffer(decoderInputBuffer); } @Override - public boolean processData() { + public boolean processData() throws TransformationException { if (sonicAudioProcessor.isActive()) { return feedEncoderFromSonic() || feedSonicFromDecoder(); } else { @@ -133,13 +133,13 @@ import java.nio.ByteBuffer; @Override @Nullable - public Format getOutputFormat() { + public Format getOutputFormat() throws TransformationException { return encoder != null ? encoder.getOutputFormat() : null; } @Override @Nullable - public DecoderInputBuffer getOutputBuffer() { + public DecoderInputBuffer getOutputBuffer() throws TransformationException { if (encoder != null) { encoderOutputBuffer.data = encoder.getOutputBuffer(); if (encoderOutputBuffer.data != null) { @@ -152,7 +152,7 @@ import java.nio.ByteBuffer; } @Override - public void releaseOutputBuffer() { + public void releaseOutputBuffer() throws TransformationException { checkStateNotNull(encoder).releaseOutputBuffer(); } @@ -174,7 +174,7 @@ import java.nio.ByteBuffer; * Attempts to pass decoder output data to the encoder, and returns whether it may be possible to * pass more data immediately by calling this method again. */ - private boolean feedEncoderFromDecoder() { + private boolean feedEncoderFromDecoder() throws TransformationException { if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) { return false; } @@ -203,7 +203,7 @@ import java.nio.ByteBuffer; * Attempts to pass audio processor output data to the encoder, and returns whether it may be * possible to pass more data immediately by calling this method again. */ - private boolean feedEncoderFromSonic() { + private boolean feedEncoderFromSonic() throws TransformationException { if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) { return false; } @@ -226,7 +226,7 @@ import java.nio.ByteBuffer; * Attempts to process decoder output data, and returns whether it may be possible to process more * data immediately by calling this method again. */ - private boolean feedSonicFromDecoder() { + private boolean feedSonicFromDecoder() throws TransformationException { if (drainingSonicForSpeedChange) { if (sonicAudioProcessor.isEnded() && !sonicOutputBuffer.hasRemaining()) { flushSonicAndSetSpeed(currentSpeed); @@ -267,7 +267,7 @@ import java.nio.ByteBuffer; * Feeds as much data as possible between the current position and limit of the specified {@link * ByteBuffer} to the encoder, and advances its position by the number of bytes fed. */ - private void feedEncoder(ByteBuffer inputBuffer) { + private void feedEncoder(ByteBuffer inputBuffer) throws TransformationException { ByteBuffer encoderInputBufferData = checkNotNull(encoderInputBuffer.data); int bufferLimit = inputBuffer.limit(); inputBuffer.limit(min(bufferLimit, inputBuffer.position() + encoderInputBufferData.capacity())); @@ -283,7 +283,7 @@ import java.nio.ByteBuffer; encoder.queueInputBuffer(encoderInputBuffer); } - private void queueEndOfStreamToEncoder() { + private void queueEndOfStreamToEncoder() throws TransformationException { checkState(checkNotNull(encoderInputBuffer.data).position() == 0); encoderInputBuffer.timeUs = nextEncoderInputBufferTimeUs; encoderInputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java index 575df2a55e..817e9c866c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java @@ -109,6 +109,7 @@ public final class Codec { private final BufferInfo outputBufferInfo; private final MediaCodec mediaCodec; + private final Format configurationFormat; @Nullable private final Surface inputSurface; private @MonotonicNonNull Format outputFormat; @@ -119,15 +120,36 @@ public final class Codec { private boolean inputStreamEnded; private boolean outputStreamEnded; - /** Creates a {@code Codec} from a configured and started {@link MediaCodec}. */ - public Codec(MediaCodec mediaCodec, @Nullable Surface inputSurface) { + /** + * Creates a {@code Codec} from a configured and started {@link MediaCodec}. + * + * @param mediaCodec The configured and started {@link MediaCodec}. + * @param configurationFormat See {@link #getConfigurationFormat()}. + * @param inputSurface The input {@link Surface} if the {@link MediaCodec} receives input from a + * surface. + */ + public Codec(MediaCodec mediaCodec, Format configurationFormat, @Nullable Surface inputSurface) { this.mediaCodec = mediaCodec; + this.configurationFormat = configurationFormat; this.inputSurface = inputSurface; outputBufferInfo = new BufferInfo(); inputBufferIndex = C.INDEX_UNSET; outputBufferIndex = C.INDEX_UNSET; } + /** + * Returns the {@link Format} used for configuring the codec. + * + *

The configuration {@link Format} is the input {@link Format} used by the {@link + * DecoderFactory} or output {@link Format} used by the {@link EncoderFactory} for selecting and + * configuring the underlying {@link MediaCodec}. + */ + // TODO(b/214012830): Use this to check whether the Format passed to the factory and actual + // configuration format differ to see whether fallback was applied. + public Format getConfigurationFormat() { + return configurationFormat; + } + /** Returns the input {@link Surface}, or null if the input is not a surface. */ @Nullable public Surface getInputSurface() { @@ -139,18 +161,28 @@ public final class Codec { * * @param inputBuffer The buffer where the dequeued buffer data is stored. * @return Whether an input buffer is ready to be used. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ @EnsuresNonNullIf(expression = "#1.data", result = true) - public boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) { + public boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) + throws TransformationException { if (inputStreamEnded) { return false; } if (inputBufferIndex < 0) { - inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0); + try { + inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0); + } catch (RuntimeException e) { + throw createTransformationException(e); + } if (inputBufferIndex < 0) { return false; } - inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex); + try { + inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex); + } catch (RuntimeException e) { + throw createTransformationException(e); + } inputBuffer.clear(); } checkNotNull(inputBuffer.data); @@ -160,8 +192,13 @@ public final class Codec { /** * Queues an input buffer to the decoder. No buffers may be queued after an {@link * DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued. + * + * @param inputBuffer The {@link DecoderInputBuffer input buffer}. + * @throws IllegalStateException If called again after an {@link + * DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ - public void queueInputBuffer(DecoderInputBuffer inputBuffer) { + public void queueInputBuffer(DecoderInputBuffer inputBuffer) throws TransformationException { checkState( !inputStreamEnded, "Input buffer can not be queued after the input stream has ended."); @@ -176,32 +213,64 @@ public final class Codec { inputStreamEnded = true; flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; } - mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); + try { + mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); + } catch (RuntimeException e) { + throw createTransformationException(e); + } inputBufferIndex = C.INDEX_UNSET; inputBuffer.data = null; } - public void signalEndOfInputStream() { - mediaCodec.signalEndOfInputStream(); + /** + * Signals end-of-stream on input to a video encoder. + * + *

This method does not need to be called for audio/video decoders or audio encoders. For these + * the {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag should be set on the last input buffer + * {@link #queueInputBuffer(DecoderInputBuffer) queued}. + * + * @throws IllegalStateException If the codec is not an encoder receiving input from a {@link + * Surface}. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + */ + public void signalEndOfInputStream() throws TransformationException { + checkState(mediaCodec.getCodecInfo().isEncoder() && inputSurface != null); + try { + mediaCodec.signalEndOfInputStream(); + } catch (RuntimeException e) { + throw createTransformationException(e); + } } - /** Returns the current output format, if available. */ + /** + * Returns the current output format, if available. + * + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + */ @Nullable - public Format getOutputFormat() { + public Format getOutputFormat() throws TransformationException { // The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now. maybeDequeueOutputBuffer(); return outputFormat; } - /** Returns the current output {@link ByteBuffer}, if available. */ + /** + * Returns the current output {@link ByteBuffer}, if available. + * + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + */ @Nullable - public ByteBuffer getOutputBuffer() { + public ByteBuffer getOutputBuffer() throws TransformationException { return maybeDequeueAndSetOutputBuffer() ? outputBuffer : null; } - /** Returns the {@link BufferInfo} associated with the current output buffer, if available. */ + /** + * Returns the {@link BufferInfo} associated with the current output buffer, if available. + * + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + */ @Nullable - public BufferInfo getOutputBufferInfo() { + public BufferInfo getOutputBufferInfo() throws TransformationException { return maybeDequeueOutputBuffer() ? outputBufferInfo : null; } @@ -210,8 +279,10 @@ public final class Codec { * *

This should be called after the buffer has been processed. The next output buffer will not * be available until the previous has been released. + * + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ - public void releaseOutputBuffer() { + public void releaseOutputBuffer() throws TransformationException { releaseOutputBuffer(/* render= */ false); } @@ -223,10 +294,17 @@ public final class Codec { * *

This should be called after the buffer has been processed. The next output buffer will not * be available until the previous has been released. + * + * @param render Whether the buffer needs to be sent to the output {@link Surface}. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ - public void releaseOutputBuffer(boolean render) { + public void releaseOutputBuffer(boolean render) throws TransformationException { outputBuffer = null; - mediaCodec.releaseOutputBuffer(outputBufferIndex, render); + try { + mediaCodec.releaseOutputBuffer(outputBufferIndex, render); + } catch (RuntimeException e) { + throw createTransformationException(e); + } outputBufferIndex = C.INDEX_UNSET; } @@ -248,13 +326,18 @@ public final class Codec { * Tries obtaining an output buffer and sets {@link #outputBuffer} to the obtained output buffer. * * @return {@code true} if a buffer is successfully obtained, {@code false} otherwise. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ - private boolean maybeDequeueAndSetOutputBuffer() { + private boolean maybeDequeueAndSetOutputBuffer() throws TransformationException { if (!maybeDequeueOutputBuffer()) { return false; } - outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex)); + try { + outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex)); + } catch (RuntimeException e) { + throw createTransformationException(e); + } outputBuffer.position(outputBufferInfo.offset); outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); return true; @@ -263,8 +346,10 @@ public final class Codec { /** * Returns true if there is already an output buffer pending. Otherwise attempts to dequeue an * output buffer and returns whether there is a new output buffer. + * + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. */ - private boolean maybeDequeueOutputBuffer() { + private boolean maybeDequeueOutputBuffer() throws TransformationException { if (outputBufferIndex >= 0) { return true; } @@ -272,7 +357,11 @@ public final class Codec { return false; } - outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0); + try { + outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0); + } catch (RuntimeException e) { + throw createTransformationException(e); + } if (outputBufferIndex < 0) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { outputFormat = getFormat(mediaCodec.getOutputFormat()); @@ -294,6 +383,20 @@ public final class Codec { return true; } + private TransformationException createTransformationException(Exception cause) { + boolean isEncoder = mediaCodec.getCodecInfo().isEncoder(); + boolean isVideo = MimeTypes.isVideo(configurationFormat.sampleMimeType); + String componentName = (isVideo ? "Video" : "Audio") + (isEncoder ? "Encoder" : "Decoder"); + return TransformationException.createForCodec( + cause, + componentName, + configurationFormat, + mediaCodec.getName(), + isEncoder + ? TransformationException.ERROR_CODE_ENCODING_FAILED + : TransformationException.ERROR_CODE_DECODING_FAILED); + } + private static Format getFormat(MediaFormat mediaFormat) { ImmutableList.Builder csdBuffers = new ImmutableList.Builder<>(); int csdIndex = 0; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java index 2e290c8f22..e5187be972 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodecFactory.java @@ -138,12 +138,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; if (inputSurface != null) { inputSurface.release(); } + @Nullable String mediaCodecName = null; if (mediaCodec != null) { + mediaCodecName = mediaCodec.getName(); mediaCodec.release(); } - throw createTransformationException(e, format, isVideo, isDecoder); + throw createTransformationException(e, format, isVideo, isDecoder, mediaCodecName); } - return new Codec(mediaCodec, inputSurface); + return new Codec(mediaCodec, format, inputSurface); } private static void configureCodec( @@ -167,13 +169,18 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } private static TransformationException createTransformationException( - Exception cause, Format format, boolean isVideo, boolean isDecoder) { + Exception cause, + Format format, + boolean isVideo, + boolean isDecoder, + @Nullable String mediaCodecName) { String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { return TransformationException.createForCodec( cause, componentName, format, + mediaCodecName, isDecoder ? TransformationException.ERROR_CODE_DECODER_INIT_FAILED : TransformationException.ERROR_CODE_ENCODER_INIT_FAILED); @@ -183,6 +190,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; cause, componentName, format, + mediaCodecName, isDecoder ? TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED : TransformationException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SamplePipeline.java index d8c016e50d..b2aa387111 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SamplePipeline.java @@ -29,7 +29,7 @@ import androidx.media3.decoder.DecoderInputBuffer; /** Returns a buffer if the pipeline is ready to accept input, and {@code null} otherwise. */ @Nullable - DecoderInputBuffer dequeueInputBuffer(); + DecoderInputBuffer dequeueInputBuffer() throws TransformationException; /** * Informs the pipeline that its input buffer contains new input. @@ -37,7 +37,7 @@ import androidx.media3.decoder.DecoderInputBuffer; *

Should be called after filling the input buffer from {@link #dequeueInputBuffer()} with new * input. */ - void queueInputBuffer(); + void queueInputBuffer() throws TransformationException; /** * Processes the input data and returns whether more data can be processed by calling this method @@ -47,18 +47,18 @@ import androidx.media3.decoder.DecoderInputBuffer; /** Returns the output format of the pipeline if available, and {@code null} otherwise. */ @Nullable - Format getOutputFormat(); + Format getOutputFormat() throws TransformationException; /** Returns an output buffer if the pipeline has produced output, and {@code null} otherwise */ @Nullable - DecoderInputBuffer getOutputBuffer(); + DecoderInputBuffer getOutputBuffer() throws TransformationException; /** * Releases the pipeline's output buffer. * *

Should be called when the output buffer from {@link #getOutputBuffer()} is no longer needed. */ - void releaseOutputBuffer(); + void releaseOutputBuffer() throws TransformationException; /** Returns whether the pipeline has ended. */ boolean isEnded(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index d980b5a8da..db780db91f 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -21,6 +21,7 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; +import android.media.MediaCodec; import android.os.SystemClock; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -218,14 +219,25 @@ public final class TransformationException extends Exception { * * @param cause The cause of the failure. * @param componentName The name of the component used, e.g. 'VideoEncoder'. - * @param format The {@link Format} used for the decoder/encoder. + * @param configurationFormat The {@link Format} used for configuring the decoder/encoder. + * @param mediaCodecName The name of the {@link MediaCodec} used, if known. * @param errorCode See {@link #errorCode}. * @return The created instance. */ public static TransformationException createForCodec( - Throwable cause, String componentName, Format format, int errorCode) { + Throwable cause, + String componentName, + Format configurationFormat, + @Nullable String mediaCodecName, + int errorCode) { return new TransformationException( - componentName + " error, format = " + format, cause, errorCode); + componentName + + " error, format = " + + configurationFormat + + ", mediaCodecName=" + + mediaCodecName, + cause, + errorCode); } /** diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index b72b9fc641..9e39456dd0 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -139,7 +139,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; protected abstract boolean ensureConfigured() throws TransformationException; @RequiresNonNull({"samplePipeline", "#1.data"}) - protected void maybeQueueSampleToPipeline(DecoderInputBuffer inputBuffer) { + protected void maybeQueueSampleToPipeline(DecoderInputBuffer inputBuffer) + throws TransformationException { samplePipeline.queueInputBuffer(); } @@ -147,9 +148,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * Attempts to write sample pipeline output data to the muxer. * * @return Whether it may be possible to write more data immediately by calling this method again. + * @throws Muxer.MuxerException If a muxing problem occurs. + * @throws TransformationException If a {@link SamplePipeline} problem occurs. */ @RequiresNonNull("samplePipeline") - private boolean feedMuxerFromPipeline() throws Muxer.MuxerException { + private boolean feedMuxerFromPipeline() throws Muxer.MuxerException, TransformationException { if (!muxerWrapperTrackAdded) { @Nullable Format samplePipelineOutputFormat = samplePipeline.getOutputFormat(); if (samplePipelineOutputFormat == null) { @@ -185,9 +188,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * Attempts to read input data and pass the input data to the sample pipeline. * * @return Whether it may be possible to read more data immediately by calling this method again. + * @throws TransformationException If a {@link SamplePipeline} problem occurs. */ @RequiresNonNull("samplePipeline") - private boolean feedPipelineFromInput() { + private boolean feedPipelineFromInput() throws TransformationException { @Nullable DecoderInputBuffer samplePipelineInputBuffer = samplePipeline.dequeueInputBuffer(); if (samplePipelineInputBuffer == null) { return false; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java index f351cad24a..cf7c3622c9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java @@ -122,10 +122,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** * Queues the input buffer to the sample pipeline unless it should be dropped because of slow * motion flattening. + * + * @param inputBuffer The {@link DecoderInputBuffer}. + * @throws TransformationException If a {@link SamplePipeline} problem occurs. */ @Override @RequiresNonNull({"samplePipeline", "#1.data"}) - protected void maybeQueueSampleToPipeline(DecoderInputBuffer inputBuffer) { + protected void maybeQueueSampleToPipeline(DecoderInputBuffer inputBuffer) + throws TransformationException { ByteBuffer data = inputBuffer.data; boolean shouldDropSample = sefSlowMotionFlattener != null && sefSlowMotionFlattener.dropOrTransformSample(inputBuffer); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java index f63f7ded9e..d1f7819929 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java @@ -131,12 +131,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override @Nullable - public DecoderInputBuffer dequeueInputBuffer() { + public DecoderInputBuffer dequeueInputBuffer() throws TransformationException { return decoder.maybeDequeueInputBuffer(decoderInputBuffer) ? decoderInputBuffer : null; } @Override - public void queueInputBuffer() { + public void queueInputBuffer() throws TransformationException { decoder.queueInputBuffer(decoderInputBuffer); } @@ -217,7 +217,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override @Nullable - public Format getOutputFormat() { + public Format getOutputFormat() throws TransformationException { Format format = encoder.getOutputFormat(); return format == null ? null @@ -226,7 +226,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override @Nullable - public DecoderInputBuffer getOutputBuffer() { + public DecoderInputBuffer getOutputBuffer() throws TransformationException { encoderOutputBuffer.data = encoder.getOutputBuffer(); if (encoderOutputBuffer.data == null) { return null; @@ -238,7 +238,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public void releaseOutputBuffer() { + public void releaseOutputBuffer() throws TransformationException { encoder.releaseOutputBuffer(); } From bfce8f54562d2f858a19e034df267995e19fa06a Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 13 Jan 2022 16:49:07 +0000 Subject: [PATCH 016/251] Rename StyledPlayerView to PlayerView This commit leaves some 'styled' references, specifically: * exo_styled_XXX dimension names * exo_styled_controls_XXX drawable IDs * exo_styled_XXX color names * ExoStyledControls.XXX style names PiperOrigin-RevId: 421576554 --- .../media3/demo/cast/MainActivity.java | 4 +- .../media3/demo/cast/PlayerManager.java | 12 +- .../src/main/res/layout/main_activity.xml | 2 +- .../androidx/media3/demo/gl/MainActivity.java | 4 +- .../gl/src/main/res/layout/main_activity.xml | 2 +- .../media3/demo/main/PlayerActivity.java | 10 +- .../src/main/res/layout/player_activity.xml | 2 +- .../media3/demo/session/PlayerActivity.kt | 4 +- .../src/main/res/layout/activity_player.xml | 2 +- libraries/decoder_av1/README.md | 14 +- libraries/decoder_vp9/README.md | 14 +- libraries/ui/proguard-rules.txt | 2 +- ...ontrolView.java => PlayerControlView.java} | 28 ++-- ...va => PlayerControlViewLayoutManager.java} | 6 +- ...{StyledPlayerView.java => PlayerView.java} | 142 +++++++++--------- ...xml => exo_player_control_ffwd_button.xml} | 0 ...l => exo_player_control_rewind_button.xml} | 0 ...xml => exo_player_control_ffwd_button.xml} | 0 ...l => exo_player_control_rewind_button.xml} | 0 ...l_view.xml => exo_player_control_view.xml} | 4 +- ...ed_player_view.xml => exo_player_view.xml} | 0 libraries/ui/src/main/res/values/attrs.xml | 10 +- 22 files changed, 128 insertions(+), 134 deletions(-) rename libraries/ui/src/main/java/androidx/media3/ui/{StyledPlayerControlView.java => PlayerControlView.java} (98%) rename libraries/ui/src/main/java/androidx/media3/ui/{StyledPlayerControlViewLayoutManager.java => PlayerControlViewLayoutManager.java} (99%) rename libraries/ui/src/main/java/androidx/media3/ui/{StyledPlayerView.java => PlayerView.java} (90%) rename libraries/ui/src/main/res/layout-v23/{exo_styled_player_control_ffwd_button.xml => exo_player_control_ffwd_button.xml} (100%) rename libraries/ui/src/main/res/layout-v23/{exo_styled_player_control_rewind_button.xml => exo_player_control_rewind_button.xml} (100%) rename libraries/ui/src/main/res/layout/{exo_styled_player_control_ffwd_button.xml => exo_player_control_ffwd_button.xml} (100%) rename libraries/ui/src/main/res/layout/{exo_styled_player_control_rewind_button.xml => exo_player_control_rewind_button.xml} (100%) rename libraries/ui/src/main/res/layout/{exo_styled_player_control_view.xml => exo_player_control_view.xml} (97%) rename libraries/ui/src/main/res/layout/{exo_styled_player_view.xml => exo_player_view.xml} (100%) diff --git a/demos/cast/src/main/java/androidx/media3/demo/cast/MainActivity.java b/demos/cast/src/main/java/androidx/media3/demo/cast/MainActivity.java index 7d0e3cd387..d6aec8c4a1 100644 --- a/demos/cast/src/main/java/androidx/media3/demo/cast/MainActivity.java +++ b/demos/cast/src/main/java/androidx/media3/demo/cast/MainActivity.java @@ -36,7 +36,7 @@ import androidx.media3.common.MediaItem; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.ExoPlayer; -import androidx.media3.ui.StyledPlayerView; +import androidx.media3.ui.PlayerView; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -52,7 +52,7 @@ import com.google.android.gms.dynamite.DynamiteModule; public class MainActivity extends AppCompatActivity implements OnClickListener, PlayerManager.Listener { - private StyledPlayerView playerView; + private PlayerView playerView; private PlayerManager playerManager; private RecyclerView mediaQueueList; private MediaQueueListAdapter mediaQueueListAdapter; diff --git a/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java b/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java index d9660807bb..c59ad55139 100644 --- a/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java +++ b/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java @@ -28,8 +28,8 @@ import androidx.media3.common.Player.TimelineChangeReason; import androidx.media3.common.Timeline; import androidx.media3.common.TracksInfo; import androidx.media3.exoplayer.ExoPlayer; -import androidx.media3.ui.StyledPlayerControlView; -import androidx.media3.ui.StyledPlayerView; +import androidx.media3.ui.PlayerControlView; +import androidx.media3.ui.PlayerView; import com.google.android.gms.cast.framework.CastContext; import java.util.ArrayList; @@ -51,7 +51,7 @@ import java.util.ArrayList; } private final Context context; - private final StyledPlayerView playerView; + private final PlayerView playerView; private final Player localPlayer; private final CastPlayer castPlayer; private final ArrayList mediaQueue; @@ -66,11 +66,11 @@ import java.util.ArrayList; * * @param context A {@link Context}. * @param listener A {@link Listener} for queue position changes. - * @param playerView The {@link StyledPlayerView} for playback. + * @param playerView The {@link PlayerView} for playback. * @param castContext The {@link CastContext}. */ public PlayerManager( - Context context, Listener listener, StyledPlayerView playerView, CastContext castContext) { + Context context, Listener listener, PlayerView playerView, CastContext castContext) { this.context = context; this.listener = listener; this.playerView = playerView; @@ -270,7 +270,7 @@ import java.util.ArrayList; R.drawable.ic_baseline_cast_connected_400, /* theme= */ null)); } else { // currentPlayer == localPlayer - playerView.setControllerShowTimeoutMs(StyledPlayerControlView.DEFAULT_SHOW_TIMEOUT_MS); + playerView.setControllerShowTimeoutMs(PlayerControlView.DEFAULT_SHOW_TIMEOUT_MS); playerView.setDefaultArtwork(null); } diff --git a/demos/cast/src/main/res/layout/main_activity.xml b/demos/cast/src/main/res/layout/main_activity.xml index 86b6abe7b3..81320bb75b 100644 --- a/demos/cast/src/main/res/layout/main_activity.xml +++ b/demos/cast/src/main/res/layout/main_activity.xml @@ -20,7 +20,7 @@ android:layout_height="match_parent" android:keepScreenOn="true"> - - - = mutableListOf() diff --git a/demos/session/src/main/res/layout/activity_player.xml b/demos/session/src/main/res/layout/activity_player.xml index e1246c50cf..0caff0b445 100644 --- a/demos/session/src/main/res/layout/activity_player.xml +++ b/demos/session/src/main/res/layout/activity_player.xml @@ -26,7 +26,7 @@ android:layout_height="300dp" android:layout_width="match_parent" > - (android.content.Context); diff --git a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java similarity index 98% rename from libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlView.java rename to libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java index dbc5a75e82..65a07b6eb5 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java @@ -158,7 +158,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * below for more details. *

    *
  • Corresponding method: None - *
  • Default: {@code R.layout.exo_styled_player_control_view} + *
  • Default: {@code R.layout.exo_player_control_view} *
*
  • All attributes that can be set on {@link DefaultTimeBar} can also be set on a * StyledPlayerControlView, and will be propagated to the inflated {@link DefaultTimeBar} @@ -193,7 +193,7 @@ import java.util.concurrent.CopyOnWriteArrayList; *

    Overriding the layout file

    * * To customize the layout of StyledPlayerControlView throughout your app, or just for certain - * configurations, you can define {@code exo_styled_player_control_view.xml} layout files in your + * configurations, you can define {@code exo_player_control_view.xml} layout files in your * application {@code res/layout*} directories. But, in this case, you need to be careful since the * default animation implementation expects certain relative positions between children. See also Specifying a custom layout file. @@ -297,18 +297,18 @@ import java.util.concurrent.CopyOnWriteArrayList; * *

    Specifying a custom layout file

    * - * Defining your own {@code exo_styled_player_control_view.xml} is useful to customize the layout of + * Defining your own {@code exo_player_control_view.xml} is useful to customize the layout of * StyledPlayerControlView throughout your application. It's also possible to customize the layout * for a single instance in a layout file. This is achieved by setting the {@code * controller_layout_id} attribute on a StyledPlayerControlView. This will cause the specified - * layout to be inflated instead of {@code exo_styled_player_control_view.xml} for only the instance - * on which the attribute is set. + * layout to be inflated instead of {@code exo_player_control_view.xml} for only the instance on + * which the attribute is set. * *

    You need to be careful when you set the {@code controller_layout_id}, because the default * animation implementation expects certain relative positions between children. */ @UnstableApi -public class StyledPlayerControlView extends FrameLayout { +public class PlayerControlView extends FrameLayout { static { MediaLibraryInfo.registerModule("media3.ui"); @@ -426,7 +426,7 @@ public class StyledPlayerControlView extends FrameLayout { private boolean[] extraPlayedAdGroups; private long currentWindowOffset; - private StyledPlayerControlViewLayoutManager controlViewLayoutManager; + private PlayerControlViewLayoutManager controlViewLayoutManager; private Resources resources; private RecyclerView settingsView; @@ -448,15 +448,15 @@ public class StyledPlayerControlView extends FrameLayout { @Nullable private View playbackSpeedButton; @Nullable private View audioTrackButton; - public StyledPlayerControlView(Context context) { + public PlayerControlView(Context context) { this(context, /* attrs= */ null); } - public StyledPlayerControlView(Context context, @Nullable AttributeSet attrs) { + public PlayerControlView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, /* defStyleAttr= */ 0); } - public StyledPlayerControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + public PlayerControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, attrs); } @@ -466,13 +466,13 @@ public class StyledPlayerControlView extends FrameLayout { "nullness:method.invocation", "nullness:methodref.receiver.bound" }) - public StyledPlayerControlView( + public PlayerControlView( Context context, @Nullable AttributeSet attrs, int defStyleAttr, @Nullable AttributeSet playbackAttrs) { super(context, attrs, defStyleAttr); - int controllerLayoutId = R.layout.exo_styled_player_control_view; + int controllerLayoutId = R.layout.exo_player_control_view; showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; repeatToggleModes = DEFAULT_REPEAT_TOGGLE_MODES; timeBarMinUpdateIntervalMs = DEFAULT_TIME_BAR_MIN_UPDATE_INTERVAL_MS; @@ -646,7 +646,7 @@ public class StyledPlayerControlView extends FrameLayout { updateButton(/* enabled= */ false, vrButton); } - controlViewLayoutManager = new StyledPlayerControlViewLayoutManager(this); + controlViewLayoutManager = new PlayerControlViewLayoutManager(this); controlViewLayoutManager.setAnimationEnabled(animationEnabled); String[] settingTexts = new String[2]; @@ -1767,7 +1767,7 @@ public class StyledPlayerControlView extends FrameLayout { @Override public void onClick(View view) { - @Nullable Player player = StyledPlayerControlView.this.player; + @Nullable Player player = PlayerControlView.this.player; if (player == null) { return; } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlViewLayoutManager.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlViewLayoutManager.java similarity index 99% rename from libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlViewLayoutManager.java rename to libraries/ui/src/main/java/androidx/media3/ui/PlayerControlViewLayoutManager.java index b08d58a1a7..2bee086110 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerControlViewLayoutManager.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlViewLayoutManager.java @@ -31,7 +31,7 @@ import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.List; -/* package */ final class StyledPlayerControlViewLayoutManager { +/* package */ final class PlayerControlViewLayoutManager { private static final long ANIMATION_INTERVAL_MS = 2_000; private static final long DURATION_FOR_HIDING_ANIMATION_MS = 250; private static final long DURATION_FOR_SHOWING_ANIMATION_MS = 250; @@ -48,7 +48,7 @@ import java.util.List; // Int for defining the UX state where the views are being animated to be shown. private static final int UX_STATE_ANIMATING_SHOW = 4; - private final StyledPlayerControlView playerControlView; + private final PlayerControlView playerControlView; @Nullable private final View controlsBackground; @Nullable private final ViewGroup centerControls; @@ -84,7 +84,7 @@ import java.util.List; private boolean animationEnabled; @SuppressWarnings({"nullness:method.invocation", "nullness:methodref.receiver.bound"}) - public StyledPlayerControlViewLayoutManager(StyledPlayerControlView playerControlView) { + public PlayerControlViewLayoutManager(PlayerControlView playerControlView) { this.playerControlView = playerControlView; showAllBarsRunnable = this::showAllBars; hideAllBarsRunnable = this::hideAllBars; diff --git a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java similarity index 90% rename from libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerView.java rename to libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java index 46d8558de7..e9c104f8de 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/StyledPlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java @@ -75,15 +75,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** * A high level view for {@link Player} media playbacks. It displays video, subtitles and album art - * during playback, and displays playback controls using a {@link StyledPlayerControlView}. + * during playback, and displays playback controls using a {@link PlayerControlView}. * - *

    A StyledPlayerView can be customized by setting attributes (or calling corresponding methods), + *

    A PlayerView can be customized by setting attributes (or calling corresponding methods), * overriding drawables, overriding the view's layout file, or by specifying a custom view layout * file. * *

    Attributes

    * - * The following attributes can be set on a StyledPlayerView when used in a layout XML file: + * The following attributes can be set on a PlayerView when used in a layout XML file: * *
      *
    • {@code use_artwork} - Whether artwork is used if available in audio streams. @@ -159,33 +159,33 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * for more details. *
        *
      • Corresponding method: None - *
      • Default: {@code R.layout.exo_styled_player_view} + *
      • Default: {@code R.layout.exo_player_view} *
      *
    • {@code controller_layout_id} - Specifies the id of the layout resource to be - * inflated by the child {@link StyledPlayerControlView}. See below for more details. + * inflated by the child {@link PlayerControlView}. See below for more details. *
        *
      • Corresponding method: None - *
      • Default: {@code R.layout.exo_styled_player_control_view} + *
      • Default: {@code R.layout.exo_player_control_view} *
      - *
    • All attributes that can be set on {@link StyledPlayerControlView} and {@link - * DefaultTimeBar} can also be set on a StyledPlayerView, and will be propagated to the - * inflated {@link StyledPlayerControlView} unless the layout is overridden to specify a - * custom {@code exo_controller} (see below). + *
    • All attributes that can be set on {@link PlayerControlView} and {@link DefaultTimeBar} can + * also be set on a PlayerView, and will be propagated to the inflated {@link + * PlayerControlView} unless the layout is overridden to specify a custom {@code + * exo_controller} (see below). *
    * *

    Overriding drawables

    * - * The drawables used by {@link StyledPlayerControlView} (with its default layout file) can be - * overridden by drawables with the same names defined in your application. See the {@link - * StyledPlayerControlView} documentation for a list of drawables that can be overridden. + * The drawables used by {@link PlayerControlView} (with its default layout file) can be overridden + * by drawables with the same names defined in your application. See the {@link PlayerControlView} + * documentation for a list of drawables that can be overridden. * *

    Overriding the layout file

    * - * To customize the layout of StyledPlayerView throughout your app, or just for certain - * configurations, you can define {@code exo_styled_player_view.xml} layout files in your - * application {@code res/layout*} directories. These layouts will override the one provided by the - * library, and will be inflated for use by StyledPlayerView. The view identifies and binds its - * children by looking for the following ids: + * To customize the layout of PlayerView throughout your app, or just for certain configurations, + * you can define {@code exo_player_view.xml} layout files in your application {@code res/layout*} + * directories. These layouts will override the one provided by the library, and will be inflated + * for use by PlayerView. The view identifies and binds its children by looking for the following + * ids: * *
      *
    • {@code exo_content_frame} - A frame whose aspect ratio is resized based on the video @@ -219,17 +219,17 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; *
    • Type: {@link TextView} *
    *
  • {@code exo_controller_placeholder} - A placeholder that's replaced with the inflated - * {@link StyledPlayerControlView}. Ignored if an {@code exo_controller} view exists. + * {@link PlayerControlView}. Ignored if an {@code exo_controller} view exists. *
      *
    • Type: {@link View} *
    - *
  • {@code exo_controller} - An already inflated {@link StyledPlayerControlView}. Allows - * use of a custom extension of {@link StyledPlayerControlView}. {@link - * StyledPlayerControlView} and {@link DefaultTimeBar} attributes set on the StyledPlayerView - * will not be automatically propagated through to this instance. If a view exists with this - * id, any {@code exo_controller_placeholder} view will be ignored. + *
  • {@code exo_controller} - An already inflated {@link PlayerControlView}. Allows use + * of a custom extension of {@link PlayerControlView}. {@link PlayerControlView} and {@link + * DefaultTimeBar} attributes set on the PlayerView will not be automatically propagated + * through to this instance. If a view exists with this id, any {@code + * exo_controller_placeholder} view will be ignored. *
      - *
    • Type: {@link StyledPlayerControlView} + *
    • Type: {@link PlayerControlView} *
    *
  • {@code exo_ad_overlay} - A {@link FrameLayout} positioned on top of the player which * is used to show ad UI (if applicable). @@ -248,14 +248,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * *

    Specifying a custom layout file

    * - * Defining your own {@code exo_styled_player_view.xml} is useful to customize the layout of - * StyledPlayerView throughout your application. It's also possible to customize the layout for a - * single instance in a layout file. This is achieved by setting the {@code player_layout_id} - * attribute on a StyledPlayerView. This will cause the specified layout to be inflated instead of - * {@code exo_styled_player_view.xml} for only the instance on which the attribute is set. + * Defining your own {@code exo_player_view.xml} is useful to customize the layout of PlayerView + * throughout your application. It's also possible to customize the layout for a single instance in + * a layout file. This is achieved by setting the {@code player_layout_id} attribute on a + * PlayerView. This will cause the specified layout to be inflated instead of {@code + * exo_player_view.xml} for only the instance on which the attribute is set. */ @UnstableApi -public class StyledPlayerView extends FrameLayout implements AdViewProvider { +public class PlayerView extends FrameLayout implements AdViewProvider { /** * Determines when the buffering view is shown. One of {@link #SHOW_BUFFERING_NEVER}, {@link @@ -293,13 +293,13 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { @Nullable private final SubtitleView subtitleView; @Nullable private final View bufferingView; @Nullable private final TextView errorMessageView; - @Nullable private final StyledPlayerControlView controller; + @Nullable private final PlayerControlView controller; @Nullable private final FrameLayout adOverlayFrameLayout; @Nullable private final FrameLayout overlayFrameLayout; @Nullable private Player player; private boolean useController; - @Nullable private StyledPlayerControlView.VisibilityListener controllerVisibilityListener; + @Nullable private PlayerControlView.VisibilityListener controllerVisibilityListener; private boolean useArtwork; @Nullable private Drawable defaultArtwork; private @ShowBuffering int showBuffering; @@ -315,16 +315,16 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { private static final int PICTURE_TYPE_FRONT_COVER = 3; private static final int PICTURE_TYPE_NOT_SET = -1; - public StyledPlayerView(Context context) { + public PlayerView(Context context) { this(context, /* attrs= */ null); } - public StyledPlayerView(Context context, @Nullable AttributeSet attrs) { + public PlayerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, /* defStyleAttr= */ 0); } @SuppressWarnings({"nullness:argument", "nullness:method.invocation"}) - public StyledPlayerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + public PlayerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); componentListener = new ComponentListener(); @@ -353,13 +353,13 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { boolean shutterColorSet = false; int shutterColor = 0; - int playerLayoutId = R.layout.exo_styled_player_view; + int playerLayoutId = R.layout.exo_player_view; boolean useArtwork = true; int defaultArtworkId = 0; boolean useController = true; int surfaceType = SURFACE_TYPE_SURFACE_VIEW; int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT; - int controllerShowTimeoutMs = StyledPlayerControlView.DEFAULT_SHOW_TIMEOUT_MS; + int controllerShowTimeoutMs = PlayerControlView.DEFAULT_SHOW_TIMEOUT_MS; boolean controllerHideOnTouch = true; boolean controllerAutoShow = true; boolean controllerHideDuringAds = true; @@ -369,32 +369,28 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { context .getTheme() .obtainStyledAttributes( - attrs, R.styleable.StyledPlayerView, defStyleAttr, /* defStyleRes= */ 0); + attrs, R.styleable.PlayerView, defStyleAttr, /* defStyleRes= */ 0); try { - shutterColorSet = a.hasValue(R.styleable.StyledPlayerView_shutter_background_color); - shutterColor = - a.getColor(R.styleable.StyledPlayerView_shutter_background_color, shutterColor); - playerLayoutId = - a.getResourceId(R.styleable.StyledPlayerView_player_layout_id, playerLayoutId); - useArtwork = a.getBoolean(R.styleable.StyledPlayerView_use_artwork, useArtwork); + shutterColorSet = a.hasValue(R.styleable.PlayerView_shutter_background_color); + shutterColor = a.getColor(R.styleable.PlayerView_shutter_background_color, shutterColor); + playerLayoutId = a.getResourceId(R.styleable.PlayerView_player_layout_id, playerLayoutId); + useArtwork = a.getBoolean(R.styleable.PlayerView_use_artwork, useArtwork); defaultArtworkId = - a.getResourceId(R.styleable.StyledPlayerView_default_artwork, defaultArtworkId); - useController = a.getBoolean(R.styleable.StyledPlayerView_use_controller, useController); - surfaceType = a.getInt(R.styleable.StyledPlayerView_surface_type, surfaceType); - resizeMode = a.getInt(R.styleable.StyledPlayerView_resize_mode, resizeMode); + a.getResourceId(R.styleable.PlayerView_default_artwork, defaultArtworkId); + useController = a.getBoolean(R.styleable.PlayerView_use_controller, useController); + surfaceType = a.getInt(R.styleable.PlayerView_surface_type, surfaceType); + resizeMode = a.getInt(R.styleable.PlayerView_resize_mode, resizeMode); controllerShowTimeoutMs = - a.getInt(R.styleable.StyledPlayerView_show_timeout, controllerShowTimeoutMs); + a.getInt(R.styleable.PlayerView_show_timeout, controllerShowTimeoutMs); controllerHideOnTouch = - a.getBoolean(R.styleable.StyledPlayerView_hide_on_touch, controllerHideOnTouch); - controllerAutoShow = - a.getBoolean(R.styleable.StyledPlayerView_auto_show, controllerAutoShow); - showBuffering = a.getInteger(R.styleable.StyledPlayerView_show_buffering, showBuffering); + a.getBoolean(R.styleable.PlayerView_hide_on_touch, controllerHideOnTouch); + controllerAutoShow = a.getBoolean(R.styleable.PlayerView_auto_show, controllerAutoShow); + showBuffering = a.getInteger(R.styleable.PlayerView_show_buffering, showBuffering); keepContentOnPlayerReset = a.getBoolean( - R.styleable.StyledPlayerView_keep_content_on_player_reset, - keepContentOnPlayerReset); + R.styleable.PlayerView_keep_content_on_player_reset, keepContentOnPlayerReset); controllerHideDuringAds = - a.getBoolean(R.styleable.StyledPlayerView_hide_during_ads, controllerHideDuringAds); + a.getBoolean(R.styleable.PlayerView_hide_during_ads, controllerHideDuringAds); } finally { a.recycle(); } @@ -451,9 +447,9 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { break; } surfaceView.setLayoutParams(params); - // We don't want surfaceView to be clickable separately to the StyledPlayerView itself, but we + // We don't want surfaceView to be clickable separately to the PlayerView itself, but we // do want to register as an OnClickListener so that surfaceView implementations can propagate - // click events up to the StyledPlayerView by calling their own performClick method. + // click events up to the PlayerView by calling their own performClick method. surfaceView.setOnClickListener(componentListener); surfaceView.setClickable(false); contentFrame.addView(surfaceView, 0); @@ -496,14 +492,14 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { } // Playback control view. - StyledPlayerControlView customController = findViewById(R.id.exo_controller); + PlayerControlView customController = findViewById(R.id.exo_controller); View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder); if (customController != null) { this.controller = customController; } else if (controllerPlaceholder != null) { // Propagate attrs as playbackAttrs so that StyledPlayerControlView's custom attributes are // transferred, but standard attributes (e.g. background) are not. - this.controller = new StyledPlayerControlView(context, null, 0, attrs); + this.controller = new PlayerControlView(context, null, 0, attrs); controller.setId(R.id.exo_controller); controller.setLayoutParams(controllerPlaceholder.getLayoutParams()); ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent()); @@ -533,9 +529,7 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { * @param newPlayerView The new view to attach to the player. */ public static void switchTargetView( - Player player, - @Nullable StyledPlayerView oldPlayerView, - @Nullable StyledPlayerView newPlayerView) { + Player player, @Nullable PlayerView oldPlayerView, @Nullable PlayerView newPlayerView) { if (oldPlayerView == newPlayerView) { return; } @@ -561,10 +555,10 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { * Sets the {@link Player} to use. * *

    To transition a {@link Player} from targeting one view to another, it's recommended to use - * {@link #switchTargetView(Player, StyledPlayerView, StyledPlayerView)} rather than this method. - * If you do wish to use this method directly, be sure to attach the player to the new view - * before calling {@code setPlayer(null)} to detach it from the old one. This ordering is - * significantly more efficient and may allow for more seamless transitions. + * {@link #switchTargetView(Player, PlayerView, PlayerView)} rather than this method. If you do + * wish to use this method directly, be sure to attach the player to the new view before + * calling {@code setPlayer(null)} to detach it from the old one. This ordering is significantly + * more efficient and may allow for more seamless transitions. * * @param player The {@link Player} to use, or {@code null} to detach the current player. Only * players which are accessed on the main thread are supported ({@code @@ -913,13 +907,13 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { } /** - * Sets the {@link StyledPlayerControlView.VisibilityListener}. + * Sets the {@link PlayerControlView.VisibilityListener}. * * @param listener The listener to be notified about visibility changes, or null to remove the * current listener. */ public void setControllerVisibilityListener( - @Nullable StyledPlayerControlView.VisibilityListener listener) { + @Nullable PlayerControlView.VisibilityListener listener) { Assertions.checkStateNotNull(controller); if (this.controllerVisibilityListener == listener) { return; @@ -934,13 +928,13 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { } /** - * Sets the {@link StyledPlayerControlView.OnFullScreenModeChangedListener}. + * Sets the {@link PlayerControlView.OnFullScreenModeChangedListener}. * * @param listener The listener to be notified when the fullscreen button is clicked, or null to * remove the current listener and hide the fullscreen button. */ public void setControllerOnFullScreenModeChangedListener( - @Nullable StyledPlayerControlView.OnFullScreenModeChangedListener listener) { + @Nullable PlayerControlView.OnFullScreenModeChangedListener listener) { Assertions.checkStateNotNull(controller); controller.setOnFullScreenModeChangedListener(listener); } @@ -1496,7 +1490,7 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { implements Player.Listener, OnLayoutChangeListener, OnClickListener, - StyledPlayerControlView.VisibilityListener { + PlayerControlView.VisibilityListener { private final Period period; private @Nullable Object lastPeriodUidWithTracks; @@ -1531,7 +1525,7 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider { // Suppress the update if transitioning to an unprepared period within the same window. This // is necessary to avoid closing the shutter when such a transition occurs. See: // https://github.com/google/ExoPlayer/issues/5507. - Player player = checkNotNull(StyledPlayerView.this.player); + Player player = checkNotNull(PlayerView.this.player); Timeline timeline = player.getCurrentTimeline(); if (timeline.isEmpty()) { lastPeriodUidWithTracks = null; diff --git a/libraries/ui/src/main/res/layout-v23/exo_styled_player_control_ffwd_button.xml b/libraries/ui/src/main/res/layout-v23/exo_player_control_ffwd_button.xml similarity index 100% rename from libraries/ui/src/main/res/layout-v23/exo_styled_player_control_ffwd_button.xml rename to libraries/ui/src/main/res/layout-v23/exo_player_control_ffwd_button.xml diff --git a/libraries/ui/src/main/res/layout-v23/exo_styled_player_control_rewind_button.xml b/libraries/ui/src/main/res/layout-v23/exo_player_control_rewind_button.xml similarity index 100% rename from libraries/ui/src/main/res/layout-v23/exo_styled_player_control_rewind_button.xml rename to libraries/ui/src/main/res/layout-v23/exo_player_control_rewind_button.xml diff --git a/libraries/ui/src/main/res/layout/exo_styled_player_control_ffwd_button.xml b/libraries/ui/src/main/res/layout/exo_player_control_ffwd_button.xml similarity index 100% rename from libraries/ui/src/main/res/layout/exo_styled_player_control_ffwd_button.xml rename to libraries/ui/src/main/res/layout/exo_player_control_ffwd_button.xml diff --git a/libraries/ui/src/main/res/layout/exo_styled_player_control_rewind_button.xml b/libraries/ui/src/main/res/layout/exo_player_control_rewind_button.xml similarity index 100% rename from libraries/ui/src/main/res/layout/exo_styled_player_control_rewind_button.xml rename to libraries/ui/src/main/res/layout/exo_player_control_rewind_button.xml diff --git a/libraries/ui/src/main/res/layout/exo_styled_player_control_view.xml b/libraries/ui/src/main/res/layout/exo_player_control_view.xml similarity index 97% rename from libraries/ui/src/main/res/layout/exo_styled_player_control_view.xml rename to libraries/ui/src/main/res/layout/exo_player_control_view.xml index a83bb1c72c..0a5ad9a21d 100644 --- a/libraries/ui/src/main/res/layout/exo_styled_player_control_view.xml +++ b/libraries/ui/src/main/res/layout/exo_player_control_view.xml @@ -136,12 +136,12 @@ - + - + diff --git a/libraries/ui/src/main/res/layout/exo_styled_player_view.xml b/libraries/ui/src/main/res/layout/exo_player_view.xml similarity index 100% rename from libraries/ui/src/main/res/layout/exo_styled_player_view.xml rename to libraries/ui/src/main/res/layout/exo_player_view.xml diff --git a/libraries/ui/src/main/res/values/attrs.xml b/libraries/ui/src/main/res/values/attrs.xml index 921f56fc44..9442753966 100644 --- a/libraries/ui/src/main/res/values/attrs.xml +++ b/libraries/ui/src/main/res/values/attrs.xml @@ -24,7 +24,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -105,7 +105,7 @@ - + From 379b6bb7e1c396fcf4f1139c272335cee430b692 Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 13 Jan 2022 16:56:59 +0000 Subject: [PATCH 017/251] Promote MappedTrackInfo.RendererSupport IntDef to public This is referred to from the public API surface, so it should also be public: https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/trackselection/MappingTrackSelector.MappedTrackInfo.html#getRendererSupport(int) #minor-release PiperOrigin-RevId: 421578232 --- .../media3/exoplayer/trackselection/MappingTrackSelector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index 00981a4973..9e9051d646 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -70,7 +70,7 @@ public abstract class MappingTrackSelector extends TrackSelector { RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS, RENDERER_SUPPORT_PLAYABLE_TRACKS }) - @interface RendererSupport {} + public @interface RendererSupport {} /** The renderer does not have any associated tracks. */ public static final int RENDERER_SUPPORT_NO_TRACKS = 0; /** From 78c07b56a77456ce39b34b9b1971f340b91d6137 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 14 Jan 2022 00:08:10 +0000 Subject: [PATCH 018/251] Reword javadoc of TracksInfo.isTypeSupportedOrEmpty The existing wording would be correct if prefixed with "Returns false if [...]", but it seems confusing to a document a boolean method in terms the condition it returns false - so I reworded it in terms of when it returns true. #minor-release PiperOrigin-RevId: 421682584 --- .../src/main/java/androidx/media3/common/TracksInfo.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index a127b9b104..6f01120154 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -228,7 +228,10 @@ public final class TracksInfo implements Bundleable { return trackGroupInfos; } - /** Returns if there is at least one track of type {@code trackType} but none are supported. */ + /** + * Returns true if at least one track of type {@code trackType} is {@link + * TrackGroupInfo#isTrackSupported(int) supported}, or there are no tracks of this type. + */ public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) { boolean supported = true; for (int i = 0; i < trackGroupInfos.size(); i++) { @@ -243,7 +246,7 @@ public final class TracksInfo implements Bundleable { return supported; } - /** Returns if at least one track of the type {@code trackType} is selected for playback. */ + /** Returns true if at least one track of the type {@code trackType} is selected for playback. */ public boolean isTypeSelected(@C.TrackType int trackType) { for (int i = 0; i < trackGroupInfos.size(); i++) { TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i); From fca783bb578359d67365eaf829c07a4e34f5577a Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 14 Jan 2022 11:18:19 +0000 Subject: [PATCH 019/251] Fix deprecation suppression in RendererCapabilities This string is case-sensitive. PiperOrigin-RevId: 421781437 --- .../java/androidx/media3/exoplayer/RendererCapabilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index 929c8077e7..53fe394e47 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -29,7 +29,7 @@ import java.lang.annotation.RetentionPolicy; public interface RendererCapabilities { /** @deprecated Use {@link C.FormatSupport} instead. */ - @SuppressWarnings("Deprecation") + @SuppressWarnings("deprecation") @Documented @Retention(RetentionPolicy.SOURCE) @IntDef({ From b1533980a849e9a1661d66c5e8f984dea1b8f45b Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 14 Jan 2022 11:19:59 +0000 Subject: [PATCH 020/251] Specify the video ID used in the Widevine DASH samples in the demo app This value is the default used by widevine_test at proxy.uat.widevine.com, but it's not easy to find that info so it's clearer to document it explicitly here for consistency with the "policy tests" section below where all the URLs contain a video_id parameter. Issue: google/ExoPlayer#9852 PiperOrigin-RevId: 421781663 --- demos/main/src/main/assets/media.exolist.json | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index d1db406667..0b479ff6d5 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -35,31 +35,31 @@ "name": "HD (cenc)", "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "UHD (cenc)", "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "HD (cbcs)", "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "UHD (cbcs)", "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_uhd.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "Secure -> Clear -> Secure (cenc)", "uri": "https://storage.googleapis.com/exoplayer-test-media-1/widevine/tears_enc_clear_enc.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test", + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test", "drm_session_for_clear_content": true } ] @@ -71,25 +71,25 @@ "name": "HD (cenc, full-sample)", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "UHD (cenc, full-sample)", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "HD (cenc, sub-sample)", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "UHD (cenc, sub-sample)", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_uhd.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" } ] }, @@ -100,13 +100,13 @@ "name": "HD (cenc)", "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" }, { "name": "UHD (cenc)", "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_uhd.mpd", "drm_scheme": "widevine", - "drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test" } ] }, From 65adbbb745db9942916bbf4ce5bb8a369853ceee Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 14 Jan 2022 12:27:18 +0000 Subject: [PATCH 021/251] Transformer GL: Clarify variables and comments. Simplifying and clarifying variables, and adding comments. Tested by confirming demo-gl and demo-transformer both correctly display videos PiperOrigin-RevId: 421792079 --- .../demo/gl/BitmapOverlayVideoProcessor.java | 18 ++------------- .../androidx/media3/common/util/GlUtil.java | 23 +++++++++++++++++++ .../main/assets/shaders/vertex_shader.glsl | 4 ++-- .../media3/transformer/FrameEditor.java | 19 +++------------ .../transformer/TransformationRequest.java | 7 ++++++ .../transformer/VideoSamplePipeline.java | 21 +++++++++-------- 6 files changed, 49 insertions(+), 43 deletions(-) diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java index b83fe2bf46..65472412b0 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java @@ -86,23 +86,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; throw new IllegalStateException(e); } program.setBufferAttribute( - "a_position", - new float[] { - -1, -1, 0, 1, - 1, -1, 0, 1, - -1, 1, 0, 1, - 1, 1, 0, 1 - }, - 4); + "a_position", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); program.setBufferAttribute( - "a_texcoord", - new float[] { - 0, 0, 0, 1, - 1, 0, 0, 1, - 0, 1, 0, 1, - 1, 1, 0, 1 - }, - 4); + "a_texcoord", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); GLES20.glGenTextures(1, textures, 0); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index f08f0549fe..2269101d34 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -203,6 +203,9 @@ public final class GlUtil { /** Whether to throw a {@link GlException} in case of an OpenGL error. */ public static boolean glAssertionsEnabled = false; + /** Number of vertices in a rectangle. */ + public static final int RECTANGLE_VERTICES_COUNT = 4; + private static final String TAG = "GlUtil"; private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content"; @@ -211,6 +214,26 @@ public final class GlUtil { /** Class only contains static methods. */ private GlUtil() {} + /** Bounds of normalized device coordinates, commonly used for defining viewport boundaries. */ + public static float[] getNormalizedCoordinateBounds() { + return new float[] { + -1, -1, 0, 1, + 1, -1, 0, 1, + -1, 1, 0, 1, + 1, 1, 0, 1 + }; + } + + /** Typical bounds used for sampling from textures. */ + public static float[] getTextureCoordinateBounds() { + return new float[] { + 0, 0, 0, 1, + 1, 0, 0, 1, + 0, 1, 0, 1, + 1, 1, 0, 1 + }; + } + /** * Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible. * If {@code true}, the device supports a protected output path for DRM content when using GL. diff --git a/libraries/transformer/src/main/assets/shaders/vertex_shader.glsl b/libraries/transformer/src/main/assets/shaders/vertex_shader.glsl index 3fd3e553fc..4f5e883390 100644 --- a/libraries/transformer/src/main/assets/shaders/vertex_shader.glsl +++ b/libraries/transformer/src/main/assets/shaders/vertex_shader.glsl @@ -11,12 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -attribute vec4 aPosition; +attribute vec4 aFramePosition; attribute vec4 aTexCoords; uniform mat4 uTexTransform; uniform mat4 uTransformationMatrix; varying vec2 vTexCoords; void main() { - gl_Position = uTransformationMatrix * aPosition; + gl_Position = uTransformationMatrix * aFramePosition; vTexCoords = (uTexTransform * aTexCoords).xy; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index 169b0671f4..929a0c7c10 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -131,24 +131,11 @@ import java.util.concurrent.atomic.AtomicInteger; GlUtil.Program glProgram = new GlUtil.Program(context, VERTEX_SHADER_FILE_PATH, FRAGMENT_SHADER_FILE_PATH); + // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. glProgram.setBufferAttribute( - "aPosition", - new float[] { - -1.0f, -1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - }, - /* size= */ 4); + "aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); glProgram.setBufferAttribute( - "aTexCoords", - new float[] { - 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 0.0f, 1.0f, - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - }, - /* size= */ 4); + "aTexCoords", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); glProgram.setSamplerTexIdUniform("uTexSampler", textureId, /* unit= */ 0); float[] transformationMatrixArray = getGlMatrixArray(transformationMatrix); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java index 9f371280ff..728800af8c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java @@ -63,6 +63,9 @@ public final class TransformationRequest { *

    This can be used to perform operations supported by {@link Matrix}, like scaling and * rotating the video. * + *

    The video dimensions will be on the x axis, from -aspectRatio to aspectRatio, and on the y + * axis, from -1 to 1. + * *

    For now, resolution will not be affected by this method. * * @param transformationMatrix The transformation to apply to video frames. @@ -73,6 +76,10 @@ public final class TransformationRequest { // allow transformations to change the resolution, by scaling to the appropriate min/max // values. This will also be required to create the VertexTransformation class, in order to // have aspect ratio helper methods (which require resolution to change). + + // TODO(b/213198690): Consider changing how transformationMatrix is applied, so that + // dimensions will be from -1 to 1 on both x and y axes, but transformations will be applied + // in a predictable manner. this.transformationMatrix = transformationMatrix; return this; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java index d1f7819929..f448093999 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoSamplePipeline.java @@ -63,12 +63,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Scale width and height to desired transformationRequest.outputHeight, preserving aspect // ratio. // TODO(internal b/209781577): Think about which edge length should be set for portrait videos. - float inputAspectRatio = (float) inputFormat.width / inputFormat.height; + float inputFormatAspectRatio = (float) inputFormat.width / inputFormat.height; int outputWidth = inputFormat.width; int outputHeight = inputFormat.height; if (transformationRequest.outputHeight != C.LENGTH_UNSET && transformationRequest.outputHeight != inputFormat.height) { - outputWidth = Math.round(inputAspectRatio * transformationRequest.outputHeight); + outputWidth = Math.round(inputFormatAspectRatio * transformationRequest.outputHeight); outputHeight = transformationRequest.outputHeight; } @@ -82,14 +82,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } else { outputRotationDegrees = inputFormat.rotationDegrees; } - if ((inputFormat.rotationDegrees % 180) != 0) { - inputAspectRatio = 1.0f / inputAspectRatio; - } + float displayAspectRatio = + (inputFormat.rotationDegrees % 180) == 0 + ? inputFormatAspectRatio + : 1.0f / inputFormatAspectRatio; - // Scale frames by input aspect ratio, to account for FrameEditor normalized device coordinates - // (-1 to 1) and preserve frame relative dimensions during transformations (ex. rotations). - transformationRequest.transformationMatrix.preScale(inputAspectRatio, 1); - transformationRequest.transformationMatrix.postScale(1.0f / inputAspectRatio, 1); + // Scale frames by input aspect ratio, to account for FrameEditor's square normalized device + // coordinates (-1 to 1) and preserve frame relative dimensions during transformations + // (ex. rotations). After this scaling, transformationMatrix operations operate on a rectangle + // for x from -displayAspectRatio to displayAspectRatio, and y from -1 to 1 + transformationRequest.transformationMatrix.preScale(displayAspectRatio, 1); + transformationRequest.transformationMatrix.postScale(1.0f / displayAspectRatio, 1); // The decoder rotates videos to their intended display orientation. The frameEditor rotates // them back for improved encoder compatibility. From a9e75d8e3ae3bcad3440990532abcbde0fa0a0a5 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 14 Jan 2022 14:33:15 +0000 Subject: [PATCH 022/251] De-duplicate track selection code. We currently run (almost) the same code for all track types. De-duplicate this by using a single method that takes functional interfaces for track-type dependent logic. This has the benefit that all track-type dependent logic is contained within their subclasses and the generic logic doesn't need to make any assumption about the eligibility of tracks for selection or adaptation, and doesn't need to access Parameters. Make this change for audio and text only for now. Video can be updated in a subsequent change. PiperOrigin-RevId: 421811411 --- .../trackselection/DefaultTrackSelector.java | 572 ++++++++++-------- 1 file changed, 312 insertions(+), 260 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 9f18d407be..a04c905929 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -15,6 +15,9 @@ */ package androidx.media3.exoplayer.trackselection; +import static java.util.Collections.max; + +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Point; import android.os.Bundle; @@ -56,6 +59,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -1335,6 +1339,26 @@ public class DefaultTrackSelector extends MappingTrackSelector { } } + /** + * The extent to which tracks are eligible for selection. One of {@link + * #SELECTION_ELIGIBILITY_NO}, {@link #SELECTION_ELIGIBILITY_FIXED} or {@link + * #SELECTION_ELIGIBILITY_ADAPTIVE}. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({SELECTION_ELIGIBILITY_NO, SELECTION_ELIGIBILITY_FIXED, SELECTION_ELIGIBILITY_ADAPTIVE}) + protected @interface SelectionEligibility {} + + /** Track is not eligible for selection. */ + protected static final int SELECTION_ELIGIBILITY_NO = 0; + /** Track is eligible for a fixed selection with one track. */ + protected static final int SELECTION_ELIGIBILITY_FIXED = 1; + /** + * Track is eligible for both a fixed selection and as part of an adaptive selection with multiple + * tracks. + */ + protected static final int SELECTION_ELIGIBILITY_ADAPTIVE = 2; + /** * If a dimension (i.e. width or height) of a video is greater or equal to this fraction of the * corresponding viewport dimension, then the video is considered as filling the viewport (in that @@ -1634,7 +1658,6 @@ public class DefaultTrackSelector extends MappingTrackSelector { ExoTrackSelection.@NullableType Definition[] definitions = new ExoTrackSelection.Definition[rendererCount]; - boolean seenVideoRendererWithMappedTracks = false; boolean selectedVideoTracks = false; for (int i = 0; i < rendererCount; i++) { if (C.TRACK_TYPE_VIDEO == mappedTrackInfo.getRendererType(i)) { @@ -1648,78 +1671,40 @@ public class DefaultTrackSelector extends MappingTrackSelector { /* enableAdaptiveTrackSelection= */ true); selectedVideoTracks = definitions[i] != null; } - seenVideoRendererWithMappedTracks |= mappedTrackInfo.getTrackGroups(i).length > 0; } } - @Nullable AudioTrackScore selectedAudioTrackScore = null; - @Nullable String selectedAudioLanguage = null; - int selectedAudioRendererIndex = C.INDEX_UNSET; - for (int i = 0; i < rendererCount; i++) { - if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) { - boolean enableAdaptiveTrackSelection = - params.allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks; - @Nullable - Pair audioSelection = - selectAudioTrack( - mappedTrackInfo.getTrackGroups(i), - rendererFormatSupports[i], - rendererMixedMimeTypeAdaptationSupports[i], - params, - enableAdaptiveTrackSelection); - if (audioSelection != null - && (selectedAudioTrackScore == null - || audioSelection.second.compareTo(selectedAudioTrackScore) > 0)) { - if (selectedAudioRendererIndex != C.INDEX_UNSET) { - // We've already made a selection for another audio renderer, but it had a lower - // score. Clear the selection for that renderer. - definitions[selectedAudioRendererIndex] = null; - } - ExoTrackSelection.Definition definition = audioSelection.first; - definitions[i] = definition; - // We assume that audio tracks in the same group have matching language. - selectedAudioLanguage = definition.group.getFormat(definition.tracks[0]).language; - selectedAudioTrackScore = audioSelection.second; - selectedAudioRendererIndex = i; - } - } + @Nullable + Pair selectedAudio = + selectAudioTrack( + mappedTrackInfo, + rendererFormatSupports, + rendererMixedMimeTypeAdaptationSupports, + params); + if (selectedAudio != null) { + definitions[selectedAudio.second] = selectedAudio.first; + } + + @Nullable + String selectedAudioLanguage = + selectedAudio == null + ? null + : selectedAudio.first.group.getFormat(selectedAudio.first.tracks[0]).language; + @Nullable + Pair selectedText = + selectTextTrack(mappedTrackInfo, rendererFormatSupports, params, selectedAudioLanguage); + if (selectedText != null) { + definitions[selectedText.second] = selectedText.first; } - @Nullable TextTrackScore selectedTextTrackScore = null; - int selectedTextRendererIndex = C.INDEX_UNSET; for (int i = 0; i < rendererCount; i++) { int trackType = mappedTrackInfo.getRendererType(i); - switch (trackType) { - case C.TRACK_TYPE_VIDEO: - case C.TRACK_TYPE_AUDIO: - // Already done. Do nothing. - break; - case C.TRACK_TYPE_TEXT: - @Nullable - Pair textSelection = - selectTextTrack( - mappedTrackInfo.getTrackGroups(i), - rendererFormatSupports[i], - params, - selectedAudioLanguage); - if (textSelection != null - && (selectedTextTrackScore == null - || textSelection.second.compareTo(selectedTextTrackScore) > 0)) { - if (selectedTextRendererIndex != C.INDEX_UNSET) { - // We've already made a selection for another text renderer, but it had a lower score. - // Clear the selection for that renderer. - definitions[selectedTextRendererIndex] = null; - } - definitions[i] = textSelection.first; - selectedTextTrackScore = textSelection.second; - selectedTextRendererIndex = i; - } - break; - default: - definitions[i] = - selectOtherTrack( - trackType, mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params); - break; + if (trackType != C.TRACK_TYPE_VIDEO + && trackType != C.TRACK_TYPE_AUDIO + && trackType != C.TRACK_TYPE_TEXT) { + definitions[i] = + selectOtherTrack( + trackType, mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params); } } @@ -2033,187 +2018,80 @@ public class DefaultTrackSelector extends MappingTrackSelector { /** * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a - * {@link ExoTrackSelection} for an audio renderer. + * {@link ExoTrackSelection.Definition} for an audio track selection. * - * @param groups The {@link TrackGroupArray} mapped to the renderer. - * @param formatSupport The {@link Capabilities} for each mapped track, indexed by track group and - * track (in that order). - * @param mixedMimeTypeAdaptationSupports The {@link AdaptiveSupport} for mixed MIME type + * @param mappedTrackInfo Mapped track information. + * @param rendererFormatSupports The {@link Capabilities} for each mapped track, indexed by + * renderer, track group and track (in that order). + * @param rendererMixedMimeTypeAdaptationSupports The {@link AdaptiveSupport} for mixed MIME type * adaptation for the renderer. * @param params The selector's current constraint parameters. - * @param enableAdaptiveTrackSelection Whether adaptive track selection is allowed. - * @return The {@link ExoTrackSelection.Definition} and corresponding {@link AudioTrackScore}, or - * null if no selection was made. + * @return A pair of the selected {@link ExoTrackSelection.Definition} and the corresponding + * renderer index, or null if no selection was made. * @throws ExoPlaybackException If an error occurs while selecting the tracks. */ - @SuppressWarnings("unused") + @SuppressLint("WrongConstant") // Lint doesn't understand arrays of IntDefs. @Nullable - protected Pair selectAudioTrack( - TrackGroupArray groups, - @Capabilities int[][] formatSupport, - @AdaptiveSupport int mixedMimeTypeAdaptationSupports, - Parameters params, - boolean enableAdaptiveTrackSelection) + protected Pair selectAudioTrack( + MappedTrackInfo mappedTrackInfo, + @Capabilities int[][][] rendererFormatSupports, + @AdaptiveSupport int[] rendererMixedMimeTypeAdaptationSupports, + Parameters params) throws ExoPlaybackException { - int selectedTrackIndex = C.INDEX_UNSET; - int selectedGroupIndex = C.INDEX_UNSET; - @Nullable AudioTrackScore selectedTrackScore = null; - for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { - TrackGroup trackGroup = groups.get(groupIndex); - @Capabilities int[] trackFormatSupport = formatSupport[groupIndex]; - for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { - if (isSupported( - trackFormatSupport[trackIndex], params.exceedRendererCapabilitiesIfNecessary)) { - Format format = trackGroup.getFormat(trackIndex); - AudioTrackScore trackScore = - new AudioTrackScore(format, params, trackFormatSupport[trackIndex]); - if (!trackScore.isWithinConstraints && !params.exceedAudioConstraintsIfNecessary) { - // Track should not be selected. - continue; - } - if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) { - selectedGroupIndex = groupIndex; - selectedTrackIndex = trackIndex; - selectedTrackScore = trackScore; - } - } + boolean hasVideoRendererWithMappedTracks = false; + for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { + if (C.TRACK_TYPE_VIDEO == mappedTrackInfo.getRendererType(i) + && mappedTrackInfo.getTrackGroups(i).length > 0) { + hasVideoRendererWithMappedTracks = true; + break; } } - - if (selectedGroupIndex == C.INDEX_UNSET) { - return null; - } - - TrackGroup selectedGroup = groups.get(selectedGroupIndex); - - ExoTrackSelection.Definition definition = null; - if (!params.forceHighestSupportedBitrate - && !params.forceLowestBitrate - && enableAdaptiveTrackSelection) { - // If the group of the track with the highest score allows it, try to enable adaptation. - int[] adaptiveTracks = - getAdaptiveAudioTracks( - selectedGroup, - formatSupport[selectedGroupIndex], - selectedTrackIndex, - params.maxAudioBitrate, - params.allowAudioMixedMimeTypeAdaptiveness, - params.allowAudioMixedSampleRateAdaptiveness, - params.allowAudioMixedChannelCountAdaptiveness); - if (adaptiveTracks.length > 1) { - definition = new ExoTrackSelection.Definition(selectedGroup, adaptiveTracks); - } - } - if (definition == null) { - // We didn't make an adaptive selection, so make a fixed one instead. - definition = new ExoTrackSelection.Definition(selectedGroup, selectedTrackIndex); - } - - return Pair.create(definition, Assertions.checkNotNull(selectedTrackScore)); - } - - private static int[] getAdaptiveAudioTracks( - TrackGroup group, - @Capabilities int[] formatSupport, - int primaryTrackIndex, - int maxAudioBitrate, - boolean allowMixedMimeTypeAdaptiveness, - boolean allowMixedSampleRateAdaptiveness, - boolean allowAudioMixedChannelCountAdaptiveness) { - Format primaryFormat = group.getFormat(primaryTrackIndex); - int[] adaptiveIndices = new int[group.length]; - int count = 0; - for (int i = 0; i < group.length; i++) { - if (i == primaryTrackIndex - || isSupportedAdaptiveAudioTrack( - group.getFormat(i), - formatSupport[i], - primaryFormat, - maxAudioBitrate, - allowMixedMimeTypeAdaptiveness, - allowMixedSampleRateAdaptiveness, - allowAudioMixedChannelCountAdaptiveness)) { - adaptiveIndices[count++] = i; - } - } - return Arrays.copyOf(adaptiveIndices, count); - } - - private static boolean isSupportedAdaptiveAudioTrack( - Format format, - @Capabilities int formatSupport, - Format primaryFormat, - int maxAudioBitrate, - boolean allowMixedMimeTypeAdaptiveness, - boolean allowMixedSampleRateAdaptiveness, - boolean allowAudioMixedChannelCountAdaptiveness) { - return isSupported(formatSupport, /* allowExceedsCapabilities= */ false) - && format.bitrate != Format.NO_VALUE - && format.bitrate <= maxAudioBitrate - && (allowAudioMixedChannelCountAdaptiveness - || (format.channelCount != Format.NO_VALUE - && format.channelCount == primaryFormat.channelCount)) - && (allowMixedMimeTypeAdaptiveness - || (format.sampleMimeType != null - && TextUtils.equals(format.sampleMimeType, primaryFormat.sampleMimeType))) - && (allowMixedSampleRateAdaptiveness - || (format.sampleRate != Format.NO_VALUE - && format.sampleRate == primaryFormat.sampleRate)); + boolean hasVideoRendererWithMappedTracksFinal = hasVideoRendererWithMappedTracks; + return selectTracksForType( + C.TRACK_TYPE_AUDIO, + mappedTrackInfo, + rendererFormatSupports, + (rendererIndex, group, support) -> + AudioTrackInfo.createForTrackGroup( + rendererIndex, group, params, support, hasVideoRendererWithMappedTracksFinal), + AudioTrackInfo::compareSelections); } // Text track selection implementation. /** * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a - * {@link ExoTrackSelection} for a text renderer. + * {@link ExoTrackSelection.Definition} for a text track selection. * - * @param groups The {@link TrackGroupArray} mapped to the renderer. - * @param formatSupport The {@link Capabilities} for each mapped track, indexed by track group and - * track (in that order). + * @param mappedTrackInfo Mapped track information. + * @param rendererFormatSupports The {@link Capabilities} for each mapped track, indexed by + * renderer, track group and track (in that order). * @param params The selector's current constraint parameters. * @param selectedAudioLanguage The language of the selected audio track. May be null if the - * selected text track declares no language or no text track was selected. - * @return The {@link ExoTrackSelection.Definition} and corresponding {@link TextTrackScore}, or - * null if no selection was made. + * selected audio track declares no language or no audio track was selected. + * @return A pair of the selected {@link ExoTrackSelection.Definition} and the corresponding + * renderer index, or null if no selection was made. * @throws ExoPlaybackException If an error occurs while selecting the tracks. */ + @SuppressLint("WrongConstant") // Lint doesn't understand arrays of IntDefs. @Nullable - protected Pair selectTextTrack( - TrackGroupArray groups, - @Capabilities int[][] formatSupport, + protected Pair selectTextTrack( + MappedTrackInfo mappedTrackInfo, + @Capabilities int[][][] rendererFormatSupports, Parameters params, @Nullable String selectedAudioLanguage) throws ExoPlaybackException { - @Nullable TrackGroup selectedGroup = null; - int selectedTrackIndex = C.INDEX_UNSET; - @Nullable TextTrackScore selectedTrackScore = null; - for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { - TrackGroup trackGroup = groups.get(groupIndex); - @Capabilities int[] trackFormatSupport = formatSupport[groupIndex]; - for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { - if (isSupported( - trackFormatSupport[trackIndex], params.exceedRendererCapabilitiesIfNecessary)) { - Format format = trackGroup.getFormat(trackIndex); - TextTrackScore trackScore = - new TextTrackScore( - format, params, trackFormatSupport[trackIndex], selectedAudioLanguage); - if (trackScore.isWithinConstraints - && (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0)) { - selectedGroup = trackGroup; - selectedTrackIndex = trackIndex; - selectedTrackScore = trackScore; - } - } - } - } - return selectedGroup == null - ? null - : Pair.create( - new ExoTrackSelection.Definition(selectedGroup, selectedTrackIndex), - Assertions.checkNotNull(selectedTrackScore)); + return selectTracksForType( + C.TRACK_TYPE_TEXT, + mappedTrackInfo, + rendererFormatSupports, + (rendererIndex, group, support) -> + TextTrackInfo.createForTrackGroup( + rendererIndex, group, params, support, selectedAudioLanguage), + TextTrackInfo::compareSelections); } - // General track selection methods. + // Generic track selection methods. /** * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a @@ -2255,6 +2133,64 @@ public class DefaultTrackSelector extends MappingTrackSelector { : new ExoTrackSelection.Definition(selectedGroup, selectedTrackIndex); } + @Nullable + private Pair selectTracksForType( + @C.TrackType int trackType, + MappedTrackInfo mappedTrackInfo, + @Capabilities int[][][] formatSupport, + TrackInfo.Factory trackInfoFactory, + Comparator> selectionComparator) { + ArrayList> possibleSelections = new ArrayList<>(); + int rendererCount = mappedTrackInfo.getRendererCount(); + for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) { + if (trackType == mappedTrackInfo.getRendererType(rendererIndex)) { + TrackGroupArray groups = mappedTrackInfo.getTrackGroups(rendererIndex); + for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { + TrackGroup trackGroup = groups.get(groupIndex); + @Capabilities int[] groupSupport = formatSupport[rendererIndex][groupIndex]; + List trackInfos = trackInfoFactory.create(rendererIndex, trackGroup, groupSupport); + boolean[] usedTrackInSelection = new boolean[trackGroup.length]; + for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { + T trackInfo = trackInfos.get(trackIndex); + @SelectionEligibility int eligibility = trackInfo.getSelectionEligibility(); + if (usedTrackInSelection[trackIndex] || eligibility == SELECTION_ELIGIBILITY_NO) { + continue; + } + List selection; + if (eligibility == SELECTION_ELIGIBILITY_FIXED) { + selection = ImmutableList.of(trackInfo); + } else { + selection = new ArrayList<>(); + selection.add(trackInfo); + for (int i = trackIndex + 1; i < trackGroup.length; i++) { + T otherTrackInfo = trackInfos.get(i); + if (otherTrackInfo.getSelectionEligibility() == SELECTION_ELIGIBILITY_ADAPTIVE) { + if (trackInfo.isCompatibleForAdaptationWith(otherTrackInfo)) { + selection.add(otherTrackInfo); + usedTrackInSelection[i] = true; + } + } + } + } + possibleSelections.add(selection); + } + } + } + } + if (possibleSelections.isEmpty()) { + return null; + } + List bestSelection = max(possibleSelections, selectionComparator); + int[] trackIndices = new int[bestSelection.size()]; + for (int i = 0; i < bestSelection.size(); i++) { + trackIndices[i] = bestSelection.get(i).trackIndex; + } + T firstTrackInfo = bestSelection.get(0); + return Pair.create( + new ExoTrackSelection.Definition(firstTrackInfo.trackGroup, trackIndices), + firstTrackInfo.rendererIndex); + } + // Utility methods. /** @@ -2492,6 +2428,36 @@ public class DefaultTrackSelector extends MappingTrackSelector { return Integer.bitCount(trackRoleFlags & preferredRoleFlags); } + /** Base class for track selection information of a {@link Format}. */ + private abstract static class TrackInfo { + /** Factory for {@link TrackInfo} implementations for a given {@link TrackGroup}. */ + public interface Factory { + List create(int rendererIndex, TrackGroup trackGroup, @Capabilities int[] formatSupports); + } + + public final int rendererIndex; + public final TrackGroup trackGroup; + public final int trackIndex; + public final Format format; + + public TrackInfo(int rendererIndex, TrackGroup trackGroup, int trackIndex) { + this.rendererIndex = rendererIndex; + this.trackGroup = trackGroup; + this.trackIndex = trackIndex; + this.format = trackGroup.getFormat(trackIndex); + } + + /** Returns to what extent the track is {@link SelectionEligibility eligible for selection}. */ + @SelectionEligibility + public abstract int getSelectionEligibility(); + + /** + * Returns whether this track is compatible for an adaptive selection with the specified other + * track. + */ + public abstract boolean isCompatibleForAdaptationWith(TrackInfo otherTrack); + } + /** Represents how well a video track matches the selection {@link Parameters}. */ protected static final class VideoTrackScore implements Comparable { @@ -2588,15 +2554,31 @@ public class DefaultTrackSelector extends MappingTrackSelector { } } - /** Represents how well an audio track matches the selection {@link Parameters}. */ - protected static final class AudioTrackScore implements Comparable { + private static final class AudioTrackInfo extends TrackInfo + implements Comparable { - /** - * Whether the provided format is within the parameter constraints. If {@code false}, the format - * should not be selected. - */ - public final boolean isWithinConstraints; + public static ImmutableList createForTrackGroup( + int rendererIndex, + TrackGroup trackGroup, + Parameters params, + @Capabilities int[] formatSupport, + boolean hasMappedVideoTracks) { + ImmutableList.Builder listBuilder = ImmutableList.builder(); + for (int i = 0; i < trackGroup.length; i++) { + listBuilder.add( + new AudioTrackInfo( + rendererIndex, + trackGroup, + /* trackIndex= */ i, + params, + formatSupport[i], + hasMappedVideoTracks)); + } + return listBuilder.build(); + } + @SelectionEligibility private final int selectionEligibility; + private final boolean isWithinConstraints; @Nullable private final String language; private final Parameters parameters; private final boolean isWithinRendererCapabilities; @@ -2612,7 +2594,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int bitrate; private final int preferredMimeTypeMatchIndex; - public AudioTrackScore(Format format, Parameters parameters, @Capabilities int formatSupport) { + public AudioTrackInfo( + int rendererIndex, + TrackGroup trackGroup, + int trackIndex, + Parameters parameters, + @Capabilities int formatSupport, + boolean hasMappedVideoTracks) { + super(rendererIndex, trackGroup, trackIndex); this.parameters = parameters; this.language = normalizeUndeterminedLanguageToNull(format.language); isWithinRendererCapabilities = @@ -2668,17 +2657,30 @@ public class DefaultTrackSelector extends MappingTrackSelector { } } preferredMimeTypeMatchIndex = bestMimeTypeMatchIndex; + selectionEligibility = evaluateSelectionEligibility(formatSupport, hasMappedVideoTracks); } - /** - * Compares this score with another. - * - * @param other The other score to compare to. - * @return A positive integer if this score is better than the other. Zero if they are equal. A - * negative integer if this score is worse than the other. - */ @Override - public int compareTo(AudioTrackScore other) { + @SelectionEligibility + public int getSelectionEligibility() { + return selectionEligibility; + } + + @Override + public boolean isCompatibleForAdaptationWith(TrackInfo otherTrack) { + return (parameters.allowAudioMixedChannelCountAdaptiveness + || (format.channelCount != Format.NO_VALUE + && format.channelCount == otherTrack.format.channelCount)) + && (parameters.allowAudioMixedMimeTypeAdaptiveness + || (format.sampleMimeType != null + && TextUtils.equals(format.sampleMimeType, otherTrack.format.sampleMimeType))) + && (parameters.allowAudioMixedSampleRateAdaptiveness + || (format.sampleRate != Format.NO_VALUE + && format.sampleRate == otherTrack.format.sampleRate)); + } + + @Override + public int compareTo(AudioTrackInfo other) { // If the formats are within constraints and renderer capabilities then prefer higher values // of channel count, sample rate and bit rate in that order. Otherwise, prefer lower values. Ordering qualityOrdering = @@ -2722,17 +2724,55 @@ public class DefaultTrackSelector extends MappingTrackSelector { Util.areEqual(this.language, other.language) ? qualityOrdering : NO_ORDER) .result(); } + + @SelectionEligibility + private int evaluateSelectionEligibility( + @Capabilities int rendererSupport, boolean hasMappedVideoTracks) { + if (!isSupported(rendererSupport, parameters.exceedRendererCapabilitiesIfNecessary)) { + return SELECTION_ELIGIBILITY_NO; + } + if (!isWithinConstraints && !parameters.exceedAudioConstraintsIfNecessary) { + return SELECTION_ELIGIBILITY_NO; + } + return isSupported(rendererSupport, /* allowExceedsCapabilities= */ false) + && isWithinConstraints + && format.bitrate != Format.NO_VALUE + && !parameters.forceHighestSupportedBitrate + && !parameters.forceLowestBitrate + && (parameters.allowMultipleAdaptiveSelections || !hasMappedVideoTracks) + ? SELECTION_ELIGIBILITY_ADAPTIVE + : SELECTION_ELIGIBILITY_FIXED; + } + + public static int compareSelections(List infos1, List infos2) { + // Compare best tracks of each selection with each other. + return max(infos1).compareTo(max(infos2)); + } } - /** Represents how well a text track matches the selection {@link Parameters}. */ - protected static final class TextTrackScore implements Comparable { + private static final class TextTrackInfo extends TrackInfo implements Comparable { - /** - * Whether the provided format is within the parameter constraints. If {@code false}, the format - * should not be selected. - */ - public final boolean isWithinConstraints; + public static ImmutableList createForTrackGroup( + int rendererIndex, + TrackGroup trackGroup, + Parameters params, + @Capabilities int[] formatSupport, + @Nullable String selectedAudioLanguage) { + ImmutableList.Builder listBuilder = ImmutableList.builder(); + for (int i = 0; i < trackGroup.length; i++) { + listBuilder.add( + new TextTrackInfo( + rendererIndex, + trackGroup, + /* trackIndex= */ i, + params, + formatSupport[i], + selectedAudioLanguage)); + } + return listBuilder.build(); + } + @SelectionEligibility private final int selectionEligibility; private final boolean isWithinRendererCapabilities; private final boolean isDefault; private final boolean isForced; @@ -2742,11 +2782,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int selectedAudioLanguageScore; private final boolean hasCaptionRoleFlags; - public TextTrackScore( - Format format, + public TextTrackInfo( + int rendererIndex, + TrackGroup trackGroup, + int trackIndex, Parameters parameters, @Capabilities int trackFormatSupport, @Nullable String selectedAudioLanguage) { + super(rendererIndex, trackGroup, trackIndex); isWithinRendererCapabilities = isSupported(trackFormatSupport, /* allowExceedsCapabilities= */ false); int maskedSelectionFlags = @@ -2781,22 +2824,31 @@ public class DefaultTrackSelector extends MappingTrackSelector { normalizeUndeterminedLanguageToNull(selectedAudioLanguage) == null; selectedAudioLanguageScore = getFormatLanguageScore(format, selectedAudioLanguage, selectedAudioLanguageUndetermined); - isWithinConstraints = + boolean isWithinConstraints = preferredLanguageScore > 0 || (parameters.preferredTextLanguages.isEmpty() && preferredRoleFlagsScore > 0) || isDefault || (isForced && selectedAudioLanguageScore > 0); + selectionEligibility = + isSupported(trackFormatSupport, parameters.exceedRendererCapabilitiesIfNecessary) + && isWithinConstraints + ? SELECTION_ELIGIBILITY_FIXED + : SELECTION_ELIGIBILITY_NO; } - /** - * Compares this score with another. - * - * @param other The other score to compare to. - * @return A positive integer if this score is better than the other. Zero if they are equal. A - * negative integer if this score is worse than the other. - */ @Override - public int compareTo(TextTrackScore other) { + @SelectionEligibility + public int getSelectionEligibility() { + return selectionEligibility; + } + + @Override + public boolean isCompatibleForAdaptationWith(TrackInfo otherTrack) { + return false; + } + + @Override + public int compareTo(TextTrackInfo other) { ComparisonChain chain = ComparisonChain.start() .compareFalseFirst( @@ -2823,13 +2875,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { } return chain.result(); } + + public static int compareSelections(List infos1, List infos2) { + return infos1.get(0).compareTo(infos2.get(0)); + } } - /** - * Represents how well any other track (non video, audio or text) matches the selection {@link - * Parameters}. - */ - protected static final class OtherTrackScore implements Comparable { + private static final class OtherTrackScore implements Comparable { private final boolean isDefault; private final boolean isWithinRendererCapabilities; From b7f5b5fac97cda9a402cdc959d92136033f76367 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 14 Jan 2022 15:01:20 +0000 Subject: [PATCH 023/251] GL: Update BitmapOverlayVideoProcessor naming conventions. To be more readable and consistent with Transformer GL. Tested by running gl-demo with no crash. PiperOrigin-RevId: 421815519 --- ...itmap_overlay_video_processor_fragment.glsl | 18 +++++++++--------- .../bitmap_overlay_video_processor_vertex.glsl | 12 ++++++------ .../demo/gl/BitmapOverlayVideoProcessor.java | 14 +++++++------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/demos/gl/src/main/assets/bitmap_overlay_video_processor_fragment.glsl b/demos/gl/src/main/assets/bitmap_overlay_video_processor_fragment.glsl index 17fec0601d..1c39979a50 100644 --- a/demos/gl/src/main/assets/bitmap_overlay_video_processor_fragment.glsl +++ b/demos/gl/src/main/assets/bitmap_overlay_video_processor_fragment.glsl @@ -15,19 +15,19 @@ #extension GL_OES_EGL_image_external : require precision mediump float; // External texture containing video decoder output. -uniform samplerExternalOES tex_sampler_0; +uniform samplerExternalOES uTexSampler0; // Texture containing the overlap bitmap. -uniform sampler2D tex_sampler_1; +uniform sampler2D uTexSampler1; // Horizontal scaling factor for the overlap bitmap. -uniform float scaleX; +uniform float uScaleX; // Vertical scaling factory for the overlap bitmap. -uniform float scaleY; -varying vec2 v_texcoord; +uniform float uScaleY; +varying vec2 vTexCoords; void main() { - vec4 videoColor = texture2D(tex_sampler_0, v_texcoord); - vec4 overlayColor = texture2D(tex_sampler_1, - vec2(v_texcoord.x * scaleX, - v_texcoord.y * scaleY)); + vec4 videoColor = texture2D(uTexSampler0, vTexCoords); + vec4 overlayColor = texture2D(uTexSampler1, + vec2(vTexCoords.x * uScaleX, + vTexCoords.y * uScaleY)); // Blend the video decoder output and the overlay bitmap. gl_FragColor = videoColor * (1.0 - overlayColor.a) + overlayColor * overlayColor.a; diff --git a/demos/gl/src/main/assets/bitmap_overlay_video_processor_vertex.glsl b/demos/gl/src/main/assets/bitmap_overlay_video_processor_vertex.glsl index 1cb01b8293..b10aa6880e 100644 --- a/demos/gl/src/main/assets/bitmap_overlay_video_processor_vertex.glsl +++ b/demos/gl/src/main/assets/bitmap_overlay_video_processor_vertex.glsl @@ -11,11 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -attribute vec4 a_position; -attribute vec4 a_texcoord; -uniform mat4 tex_transform; -varying vec2 v_texcoord; +attribute vec4 aFramePosition; +attribute vec4 aTexCoords; +uniform mat4 uTexTransform; +varying vec2 vTexCoords; void main() { - gl_Position = a_position; - v_texcoord = (tex_transform * a_texcoord).xy; + gl_Position = aFramePosition; + vTexCoords = (uTexTransform * aTexCoords).xy; } diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java index 65472412b0..cf3945be12 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java @@ -86,9 +86,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; throw new IllegalStateException(e); } program.setBufferAttribute( - "a_position", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); + "aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); program.setBufferAttribute( - "a_texcoord", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); + "aTexCoords", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); GLES20.glGenTextures(1, textures, 0); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); @@ -118,11 +118,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Run the shader program. GlUtil.Program program = checkNotNull(this.program); - program.setSamplerTexIdUniform("tex_sampler_0", frameTexture, /* unit= */ 0); - program.setSamplerTexIdUniform("tex_sampler_1", textures[0], /* unit= */ 1); - program.setFloatUniform("scaleX", bitmapScaleX); - program.setFloatUniform("scaleY", bitmapScaleY); - program.setFloatsUniform("tex_transform", transformMatrix); + program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0); + program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1); + program.setFloatUniform("uScaleX", bitmapScaleX); + program.setFloatUniform("uScaleY", bitmapScaleY); + program.setFloatsUniform("uTexTransform", transformMatrix); program.bindAttributesAndUniforms(); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); From 4ab10484ad23350e10c0aa8b999a9176a983c8e4 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Fri, 14 Jan 2022 17:07:43 +0000 Subject: [PATCH 024/251] Add FallbackListener. The app will be notified about fallback using a callback on Transformer.Listener. Fallback may be applied separately for the audio and video options, so an intermediate internal FallbackListener is needed to accumulate and merge the track-specific changes to the TransformationRequest. PiperOrigin-RevId: 421839991 --- .../media3/transformer/FallbackListener.java | 104 ++++++++++++++ .../transformer/TransformationRequest.java | 33 ++--- .../media3/transformer/Transformer.java | 14 ++ .../transformer/FallbackListenerTest.java | 134 ++++++++++++++++++ 4 files changed, 267 insertions(+), 18 deletions(-) create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java create mode 100644 libraries/transformer/src/test/java/androidx/media3/transformer/FallbackListenerTest.java diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java new file mode 100644 index 0000000000..05463fa906 --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java @@ -0,0 +1,104 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.media3.transformer; + +import static androidx.media3.common.util.Assertions.checkState; + +import androidx.media3.common.C; +import androidx.media3.common.MediaItem; +import androidx.media3.common.util.ListenerSet; +import androidx.media3.common.util.Util; + +/** + * Listener for fallback {@link TransformationRequest TransformationRequests} from the audio and + * video renderers. + */ +/* package */ final class FallbackListener { + + private final MediaItem mediaItem; + private final TransformationRequest originalTransformationRequest; + private final ListenerSet transformerListeners; + + private TransformationRequest fallbackTransformationRequest; + private int trackCount; + + /** + * Creates a new instance. + * + * @param mediaItem The {@link MediaItem} to transform. + * @param transformerListeners The {@link Transformer.Listener listeners} to forward events to. + * @param originalTransformationRequest The original {@link TransformationRequest}. + */ + public FallbackListener( + MediaItem mediaItem, + ListenerSet transformerListeners, + TransformationRequest originalTransformationRequest) { + this.mediaItem = mediaItem; + this.transformerListeners = transformerListeners; + this.originalTransformationRequest = originalTransformationRequest; + this.fallbackTransformationRequest = originalTransformationRequest; + } + + /** + * Registers an output track. + * + *

    All tracks must be registered before a transformation request is {@link + * #onTransformationRequestFinalized(TransformationRequest) finalized}. + */ + public void registerTrack() { + trackCount++; + } + + /** + * Updates the fallback {@link TransformationRequest}. + * + *

    Should be called with the final {@link TransformationRequest} for each track after all + * fallback has been applied. Calls {@link Transformer.Listener#onFallbackApplied(MediaItem, + * TransformationRequest, TransformationRequest)} once this method has been called for each track. + * + * @param transformationRequest The final {@link TransformationRequest} for a track. + * @throws IllegalStateException If called for more tracks than registered using {@link + * #registerTrack()}. + */ + public void onTransformationRequestFinalized(TransformationRequest transformationRequest) { + checkState(trackCount-- > 0); + + TransformationRequest.Builder fallbackRequestBuilder = + fallbackTransformationRequest.buildUpon(); + if (!Util.areEqual( + transformationRequest.audioMimeType, originalTransformationRequest.audioMimeType)) { + fallbackRequestBuilder.setAudioMimeType(transformationRequest.audioMimeType); + } + if (!Util.areEqual( + transformationRequest.videoMimeType, originalTransformationRequest.videoMimeType)) { + fallbackRequestBuilder.setVideoMimeType(transformationRequest.videoMimeType); + } + if (transformationRequest.outputHeight != originalTransformationRequest.outputHeight) { + fallbackRequestBuilder.setResolution(transformationRequest.outputHeight); + } + fallbackTransformationRequest = fallbackRequestBuilder.build(); + + if (trackCount == 0 && !originalTransformationRequest.equals(fallbackTransformationRequest)) { + transformerListeners.queueEvent( + /* eventFlag= */ C.INDEX_UNSET, + listener -> + listener.onFallbackApplied( + mediaItem, originalTransformationRequest, fallbackTransformationRequest)); + transformerListeners.flushEvents(); + } + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java index 728800af8c..3e5734446e 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java @@ -24,6 +24,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.extractor.mp4.Mp4Extractor; +import com.google.common.collect.ImmutableSet; /** A media transformation request. */ @UnstableApi @@ -32,6 +33,9 @@ public final class TransformationRequest { /** A builder for {@link TransformationRequest} instances. */ public static final class Builder { + private static final ImmutableSet SUPPORTED_OUTPUT_HEIGHTS = + ImmutableSet.of(144, 240, 360, 480, 720, 1080, 1440, 2160); + private Matrix transformationMatrix; private boolean flattenForSlowMotion; private int outputHeight; @@ -115,8 +119,9 @@ public final class TransformationRequest { } /** - * Sets the output resolution using the output height. The default value is the same height as - * the input. Output width will scale to preserve the input video's aspect ratio. + * Sets the output resolution using the output height. The default value {@link C#LENGTH_UNSET} + * corresponds to using the same height as the input. Output width will scale to preserve the + * input video's aspect ratio. * *

    For now, only "popular" heights like 144, 240, 360, 480, 720, 1080, 1440, or 2160 are * supported, to ensure compatibility on different devices. @@ -130,24 +135,16 @@ public final class TransformationRequest { // TODO(b/201293185): Restructure to input a Presentation class. // TODO(b/201293185): Check encoder codec capabilities in order to allow arbitrary // resolutions and reasonable fallbacks. - if (outputHeight != 144 - && outputHeight != 240 - && outputHeight != 360 - && outputHeight != 480 - && outputHeight != 720 - && outputHeight != 1080 - && outputHeight != 1440 - && outputHeight != 2160) { - throw new IllegalArgumentException( - "Please use a height of 144, 240, 360, 480, 720, 1080, 1440, or 2160."); + if (outputHeight != C.LENGTH_UNSET && !SUPPORTED_OUTPUT_HEIGHTS.contains(outputHeight)) { + throw new IllegalArgumentException("Unsupported outputHeight: " + outputHeight); } this.outputHeight = outputHeight; return this; } /** - * Sets the video MIME type of the output. The default value is to use the same MIME type as the - * input. Supported values are: + * Sets the video MIME type of the output. The default value is {@code null} which corresponds + * to using the same MIME type as the input. Supported MIME types are: * *

    * @@ -592,6 +594,7 @@ public interface ExoPlayer extends Player { clock = Clock.DEFAULT; releaseTimeoutMs = DEFAULT_RELEASE_TIMEOUT_MS; detachSurfaceTimeoutMs = DEFAULT_DETACH_SURFACE_TIMEOUT_MS; + usePlatformDiagnostics = true; } /** @@ -971,6 +974,28 @@ public interface ExoPlayer extends Player { return this; } + /** + * Sets whether the player reports diagnostics data to the Android platform. + * + *

    If enabled, the player will use the {@link android.media.metrics.MediaMetricsManager} to + * create a {@link android.media.metrics.PlaybackSession} and forward playback events and + * performance data to this session. This helps to provide system performance and debugging + * information for media playback on the device. This data may also be collected by Google if sharing usage and diagnostics + * data is enabled by the user of the device. + * + * @param usePlatformDiagnostics Whether the player reports diagnostics data to the Android + * platform. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + @UnstableApi + public Builder setUsePlatformDiagnostics(boolean usePlatformDiagnostics) { + checkState(!buildCalled); + this.usePlatformDiagnostics = usePlatformDiagnostics; + return this; + } + /** * Sets the {@link Clock} that will be used by the player. Should only be set for testing * purposes. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 6cfdc10e35..e076dad419 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -139,6 +139,7 @@ import androidx.media3.exoplayer.PlayerMessage.Target; import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; +import androidx.media3.exoplayer.analytics.MediaMetricsListener; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.audio.AudioRendererEventListener; import androidx.media3.exoplayer.metadata.MetadataOutput; @@ -371,7 +372,11 @@ import java.util.concurrent.TimeoutException; playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); - PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); + PlayerId playerId = + Util.SDK_INT < 31 + ? new PlayerId() + : Api31.registerMediaMetricsListener( + applicationContext, /* player= */ this, builder.usePlatformDiagnostics); internalPlayer = new ExoPlayerImplInternal( renderers, @@ -3057,9 +3062,17 @@ import java.util.concurrent.TimeoutException; private Api31() {} @DoNotInline - public static PlayerId createPlayerId() { - // TODO: Create a MediaMetricsListener and obtain LogSessionId from it. - return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); + public static PlayerId registerMediaMetricsListener( + Context context, ExoPlayerImpl player, boolean usePlatformDiagnostics) { + @Nullable MediaMetricsListener listener = MediaMetricsListener.create(context); + if (listener == null) { + Log.w(TAG, "MediaMetricsService unavailable."); + return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); + } + if (usePlatformDiagnostics) { + player.addAnalyticsListener(listener); + } + return new PlayerId(listener.getLogSessionId()); } } } From 92a6cc10a438b0e24e6f0c8dc2566caa597e0e1e Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 3 Feb 2022 12:09:38 +0000 Subject: [PATCH 128/251] Mark some unreleased IntDefs as TYPE_USE only The longer list of targets is only necessary for backwards compatibility with existing Kotlin code that will stop compiling if the position of the annotation becomes 'wrong' by marking it only TYPE_USE. Since none of these IntDefs have been released (except in media3 alpha1) we don't need to maintain this compatibility. Also add a comment to all the places that *do* need the longer list of targets, in order to explain why it's there and discourage copy-pasting when defining new IntDefs in future. Also fix some single-element arrays to remove the array notation. #minor-release PiperOrigin-RevId: 426108537 --- .../androidx/media3/common/AdOverlayInfo.java | 2 ++ .../main/java/androidx/media3/common/C.java | 16 +++++++++++++++- .../androidx/media3/common/DeviceInfo.java | 5 +++-- .../androidx/media3/common/MediaMetadata.java | 4 ++++ .../media3/common/PlaybackException.java | 2 ++ .../java/androidx/media3/common/Player.java | 18 ++++++++++++++++++ .../androidx/media3/common/TrackSelection.java | 5 +++-- .../java/androidx/media3/common/text/Cue.java | 8 ++++++++ .../exoplayer/analytics/PlaybackStats.java | 4 ++-- .../exoplayer/audio/SpatializerDelegate.java | 6 +----- .../media3/exoplayer/hls/HlsMediaSource.java | 4 ++-- .../media3/extractor/wav/WavExtractor.java | 4 ++-- .../media3/session/SessionCommand.java | 4 ++-- .../androidx/media3/session/SessionToken.java | 9 ++------- .../transformer/TransformationException.java | 6 +----- 15 files changed, 67 insertions(+), 30 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java b/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java index 120fcfe782..44259fcbc3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java @@ -37,6 +37,8 @@ public final class AdOverlayInfo { * The purpose of the overlay. One of {@link #PURPOSE_CONTROLS}, {@link #PURPOSE_CLOSE_AD}, {@link * #PURPOSE_OTHER} or {@link #PURPOSE_NOT_VISIBLE}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index bf465116ed..95e8fc1059 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -279,7 +279,7 @@ public final class C { /** Represents the behavior affecting whether spatialization will be used. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) + @Target(TYPE_USE) @IntDef({SPATIALIZATION_BEHAVIOR_AUTO, SPATIALIZATION_BEHAVIOR_NEVER}) public @interface SpatializationBehavior {} @@ -333,6 +333,8 @@ public final class C { * #CONTENT_TYPE_MUSIC}, {@link #CONTENT_TYPE_SONIFICATION}, {@link #CONTENT_TYPE_SPEECH} or * {@link #CONTENT_TYPE_UNKNOWN}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -362,6 +364,8 @@ public final class C { *

    Note that {@code FLAG_HW_AV_SYNC} is not available because the player takes care of setting * the flag when tunneling is enabled via a track selector. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -383,6 +387,8 @@ public final class C { * #USAGE_NOTIFICATION_RINGTONE}, {@link #USAGE_UNKNOWN}, {@link #USAGE_VOICE_COMMUNICATION} or * {@link #USAGE_VOICE_COMMUNICATION_SIGNALLING}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -452,6 +458,8 @@ public final class C { * Capture policies for audio attributes. One of {@link #ALLOW_CAPTURE_BY_ALL}, {@link * #ALLOW_CAPTURE_BY_NONE} or {@link #ALLOW_CAPTURE_BY_SYSTEM}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -618,6 +626,8 @@ public final class C { * Track selection flags. Possible flag values are {@link #SELECTION_FLAG_DEFAULT}, {@link * #SELECTION_FLAG_FORCED} and {@link #SELECTION_FLAG_AUTOSELECT}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1044,6 +1054,8 @@ public final class C { * Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link * #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} or {@link #WAKE_MODE_NETWORK}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1081,6 +1093,8 @@ public final class C { * {@link #ROLE_FLAG_TRANSCRIBES_DIALOG}, {@link #ROLE_FLAG_EASY_TO_READ} and {@link * #ROLE_FLAG_TRICK_PLAY}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java b/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java index 0aa619812d..0251443f1e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java @@ -15,12 +15,13 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -31,7 +32,7 @@ public final class DeviceInfo implements Bundleable { /** Types of playback. One of {@link #PLAYBACK_TYPE_LOCAL} or {@link #PLAYBACK_TYPE_REMOTE}. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef({ PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE, diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index 16c4c89ed4..eec5d3868a 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -521,6 +521,8 @@ public final class MediaMetadata implements Bundleable { * href="https://www.bluetooth.com/specifications/specs/a-v-remote-control-profile-1-6-2/">Bluetooth * AVRCP 1.6.2). */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -559,6 +561,8 @@ public final class MediaMetadata implements Bundleable { *

    Values sourced from the ID3 v2.4 specification (See section 4.14 of * https://id3.org/id3v2.4.0-frames). */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java b/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java index 2caa881539..7193467a74 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java +++ b/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java @@ -46,6 +46,8 @@ public class PlaybackException extends Exception implements Bundleable { *

    This list of errors may be extended in future versions, and {@link Player} implementations * may define custom error codes. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 87582bc1fe..c23647633c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -1086,6 +1086,8 @@ public interface Player { * Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or * {@link #STATE_ENDED}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1118,6 +1120,8 @@ public interface Player { * #PLAY_WHEN_READY_CHANGE_REASON_REMOTE} or {@link * #PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1145,6 +1149,8 @@ public interface Player { * of {@link #PLAYBACK_SUPPRESSION_REASON_NONE} or {@link * #PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1162,6 +1168,8 @@ public interface Player { * Repeat modes for playback. One of {@link #REPEAT_MODE_OFF}, {@link #REPEAT_MODE_ONE} or {@link * #REPEAT_MODE_ALL}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1194,6 +1202,8 @@ public interface Player { * #DISCONTINUITY_REASON_SKIP}, {@link #DISCONTINUITY_REASON_REMOVE} or {@link * #DISCONTINUITY_REASON_INTERNAL}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1233,6 +1243,8 @@ public interface Player { * Reasons for timeline changes. One of {@link #TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED} or {@link * #TIMELINE_CHANGE_REASON_SOURCE_UPDATE}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1254,6 +1266,8 @@ public interface Player { * #MEDIA_ITEM_TRANSITION_REASON_AUTO}, {@link #MEDIA_ITEM_TRANSITION_REASON_SEEK} or {@link * #MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1287,6 +1301,8 @@ public interface Player { * *

    One of the {@link Player}{@code .EVENT_*} values. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -1373,6 +1389,8 @@ public interface Player { * #COMMAND_SET_VIDEO_SURFACE}, {@link #COMMAND_GET_TEXT}, {@link * #COMMAND_SET_TRACK_SELECTION_PARAMETERS} or {@link #COMMAND_GET_TRACK_INFOS}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelection.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelection.java index 9eedf818b4..a3d73a567e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelection.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelection.java @@ -15,10 +15,11 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -38,7 +39,7 @@ public interface TrackSelection { */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef( open = true, value = {TYPE_UNSET}) diff --git a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java index 11574e88ed..6515a64125 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java @@ -59,6 +59,8 @@ public final class Cue implements Bundleable { * The type of anchor, which may be unset. One of {@link #TYPE_UNSET}, {@link #ANCHOR_TYPE_START}, * {@link #ANCHOR_TYPE_MIDDLE} or {@link #ANCHOR_TYPE_END}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -87,6 +89,8 @@ public final class Cue implements Bundleable { * The type of line, which may be unset. One of {@link #TYPE_UNSET}, {@link #LINE_TYPE_FRACTION} * or {@link #LINE_TYPE_NUMBER}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -104,6 +108,8 @@ public final class Cue implements Bundleable { * {@link #TEXT_SIZE_TYPE_FRACTIONAL}, {@link #TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING} or {@link * #TEXT_SIZE_TYPE_ABSOLUTE}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @@ -128,6 +134,8 @@ public final class Cue implements Bundleable { * The type of vertical layout for this cue, which may be unset (i.e. horizontal). One of {@link * #TYPE_UNSET}, {@link #VERTICAL_TYPE_RL} or {@link #VERTICAL_TYPE_LR}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStats.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStats.java index c9dc844191..583d2dfea2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStats.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStats.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.analytics; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.SystemClock; import androidx.annotation.IntDef; @@ -26,7 +27,6 @@ import androidx.media3.common.Format; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -174,7 +174,7 @@ public final class PlaybackStats { */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef({ PLAYBACK_STATE_NOT_STARTED, PLAYBACK_STATE_JOINING_BACKGROUND, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java index 8aae1d7817..d2778905b5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java @@ -16,10 +16,6 @@ package androidx.media3.exoplayer.audio; import static androidx.media3.common.util.Assertions.checkStateNotNull; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; @@ -52,7 +48,7 @@ import java.util.concurrent.Executor; /** Level of support for audio spatialization. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) + @Target(TYPE_USE) @IntDef({ SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL, SPATIALIZER_IMMERSIVE_LEVEL_NONE, diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java index 7610aa8286..7dbfcddeae 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.hls; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.os.Looper; @@ -57,7 +58,6 @@ import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.extractor.Extractor; import java.io.IOException; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.List; @@ -85,7 +85,7 @@ public final class HlsMediaSource extends BaseMediaSource */ @Documented @Retention(SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef({METADATA_TYPE_ID3, METADATA_TYPE_EMSG}) public @interface MetadataType {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java index aee49edfe4..76461fa7cf 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java @@ -17,6 +17,7 @@ package androidx.media3.extractor.wav; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import androidx.annotation.IntDef; @@ -38,7 +39,6 @@ import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.WavUtil; import java.io.IOException; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -65,7 +65,7 @@ public final class WavExtractor implements Extractor { /** Parser state. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef({ STATE_READING_FILE_TYPE, STATE_READING_RF64_SAMPLE_DATA_SIZE, diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java b/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java index c14e0e1d13..660b93e7ed 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java @@ -17,6 +17,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import android.text.TextUtils; @@ -29,7 +30,6 @@ import androidx.media3.session.MediaLibraryService.LibraryParams; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -46,7 +46,7 @@ public final class SessionCommand implements Bundleable { /** Command codes of session commands. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({ElementType.TYPE_USE}) + @Target(TYPE_USE) @IntDef({ COMMAND_CODE_CUSTOM, COMMAND_CODE_SESSION_SET_MEDIA_URI, diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java index aded73fff6..0ed15c4c3f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java @@ -17,12 +17,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE_PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.ComponentName; import android.content.Context; @@ -78,7 +73,7 @@ public final class SessionToken implements Bundleable { /** Types of {@link SessionToken}. */ @Documented @Retention(RetentionPolicy.SOURCE) - @Target({FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER}) + @Target(TYPE_USE) @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE}) public @interface TokenType {} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index 270205258a..2e1721d53d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -15,10 +15,6 @@ */ package androidx.media3.transformer; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import android.media.MediaCodec; @@ -51,7 +47,7 @@ public final class TransformationException extends Exception { // TODO(b/209469847): Update the javadoc once the underlying values are fixed. @Documented @Retention(RetentionPolicy.SOURCE) - @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) + @Target(TYPE_USE) @IntDef( open = true, value = { From ddfd79bb983dea68fc85237c57968f6a88ec30be Mon Sep 17 00:00:00 2001 From: kimvde Date: Thu, 3 Feb 2022 12:42:16 +0000 Subject: [PATCH 129/251] Use SpeedChangingAudioProcessor in Transformer PiperOrigin-RevId: 426113559 --- .../mp4/sample_sef_slow_motion.mp4.dump | 218 +++++++++--------- .../AudioTranscodingSamplePipeline.java | 107 +++------ .../SpeedChangingAudioProcessor.java | 2 + .../VideoTranscodingSamplePipeline.java | 7 +- 4 files changed, 155 insertions(+), 179 deletions(-) diff --git a/libraries/test_data/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump b/libraries/test_data/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump index a83228a55c..bace6d6cfa 100644 --- a/libraries/test_data/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump +++ b/libraries/test_data/src/test/assets/transformerdumps/mp4/sample_sef_slow_motion.mp4.dump @@ -132,148 +132,154 @@ sample: presentationTimeUs = 0 sample: trackIndex = 0 - dataHashCode = 1000136444 - size = 140 + dataHashCode = -1948569090 + size = 72 isKeyFrame = true presentationTimeUs = 417 sample: trackIndex = 0 - dataHashCode = 217961709 - size = 172 - isKeyFrame = true - presentationTimeUs = 3334 -sample: - trackIndex = 0 - dataHashCode = -879376936 - size = 176 - isKeyFrame = true - presentationTimeUs = 6917 -sample: - trackIndex = 0 - dataHashCode = 1259979587 - size = 192 - isKeyFrame = true - presentationTimeUs = 10584 -sample: - trackIndex = 0 - dataHashCode = 907407225 - size = 188 - isKeyFrame = true - presentationTimeUs = 14584 -sample: - trackIndex = 0 - dataHashCode = -904354707 - size = 176 - isKeyFrame = true - presentationTimeUs = 18500 -sample: - trackIndex = 0 - dataHashCode = 1001385853 - size = 172 - isKeyFrame = true - presentationTimeUs = 22167 -sample: - trackIndex = 0 - dataHashCode = 1545716086 - size = 196 - isKeyFrame = true - presentationTimeUs = 25750 -sample: - trackIndex = 0 - dataHashCode = 358710839 - size = 180 - isKeyFrame = true - presentationTimeUs = 29834 -sample: - trackIndex = 0 - dataHashCode = -671124798 - size = 140 - isKeyFrame = true - presentationTimeUs = 33584 -sample: - trackIndex = 0 - dataHashCode = -945404910 - size = 120 - isKeyFrame = true - presentationTimeUs = 36500 -sample: - trackIndex = 0 - dataHashCode = 1881048379 - size = 88 - isKeyFrame = true - presentationTimeUs = 39000 -sample: - trackIndex = 0 - dataHashCode = 1059579897 - size = 88 - isKeyFrame = true - presentationTimeUs = 40834 -sample: - trackIndex = 0 - dataHashCode = 1496098648 + dataHashCode = -1316750072 size = 84 isKeyFrame = true - presentationTimeUs = 42667 + presentationTimeUs = 1917 sample: trackIndex = 0 - dataHashCode = 250093960 - size = 751 + dataHashCode = 1016428949 + size = 88 isKeyFrame = true - presentationTimeUs = 44417 + presentationTimeUs = 3667 sample: trackIndex = 0 - dataHashCode = 1895536226 - size = 1045 + dataHashCode = -1127325245 + size = 96 isKeyFrame = true - presentationTimeUs = 60063 + presentationTimeUs = 5500 sample: trackIndex = 0 - dataHashCode = 1723596464 - size = 947 + dataHashCode = 1148147726 + size = 92 isKeyFrame = true - presentationTimeUs = 81834 + presentationTimeUs = 7500 sample: trackIndex = 0 - dataHashCode = -978803114 - size = 946 + dataHashCode = -2125685540 + size = 76 isKeyFrame = true - presentationTimeUs = 101563 + presentationTimeUs = 9417 sample: trackIndex = 0 - dataHashCode = 387377078 - size = 946 + dataHashCode = 473329679 + size = 24 isKeyFrame = true - presentationTimeUs = 121271 + presentationTimeUs = 11000 sample: trackIndex = 0 - dataHashCode = -132658698 - size = 901 + dataHashCode = 240990900 + size = 176 isKeyFrame = true - presentationTimeUs = 140980 + presentationTimeUs = 11500 sample: trackIndex = 0 - dataHashCode = 1495036471 - size = 899 + dataHashCode = 777637182 + size = 196 isKeyFrame = true - presentationTimeUs = 159750 + presentationTimeUs = 15167 sample: trackIndex = 0 - dataHashCode = 304440590 - size = 878 + dataHashCode = 1872106264 + size = 180 isKeyFrame = true - presentationTimeUs = 178480 + presentationTimeUs = 19250 sample: trackIndex = 0 - dataHashCode = -1955900344 - size = 112 + dataHashCode = -1520711499 + size = 140 isKeyFrame = true - presentationTimeUs = 196771 + presentationTimeUs = 23000 sample: trackIndex = 0 - dataHashCode = 88896626 - size = 116 + dataHashCode = 1580199067 + size = 232 isKeyFrame = true - presentationTimeUs = 199105 + presentationTimeUs = 25917 +sample: + trackIndex = 0 + dataHashCode = 475464086 + size = 184 + isKeyFrame = true + presentationTimeUs = 30750 +sample: + trackIndex = 0 + dataHashCode = -211754132 + size = 172 + isKeyFrame = true + presentationTimeUs = 34584 +sample: + trackIndex = 0 + dataHashCode = 1236547164 + size = 172 + isKeyFrame = true + presentationTimeUs = 38167 +sample: + trackIndex = 0 + dataHashCode = -2064216186 + size = 188 + isKeyFrame = true + presentationTimeUs = 41750 +sample: + trackIndex = 0 + dataHashCode = -682950885 + size = 260 + isKeyFrame = true + presentationTimeUs = 45667 +sample: + trackIndex = 0 + dataHashCode = 1301206627 + size = 236 + isKeyFrame = true + presentationTimeUs = 51084 +sample: + trackIndex = 0 + dataHashCode = 256580525 + size = 236 + isKeyFrame = true + presentationTimeUs = 56000 +sample: + trackIndex = 0 + dataHashCode = -1086601304 + size = 236 + isKeyFrame = true + presentationTimeUs = 60917 +sample: + trackIndex = 0 + dataHashCode = -2046131588 + size = 224 + isKeyFrame = true + presentationTimeUs = 65834 +sample: + trackIndex = 0 + dataHashCode = 1550955865 + size = 224 + isKeyFrame = true + presentationTimeUs = 70500 +sample: + trackIndex = 0 + dataHashCode = -274800552 + size = 220 + isKeyFrame = true + presentationTimeUs = 75167 +sample: + trackIndex = 0 + dataHashCode = 382420909 + size = 224 + isKeyFrame = true + presentationTimeUs = 79750 +sample: + trackIndex = 0 + dataHashCode = -1431575865 + size = 232 + isKeyFrame = true + presentationTimeUs = 84417 sample: trackIndex = 1 dataHashCode = -968901399 diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java index 94d77c7c6a..0a101e52e6 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java @@ -20,7 +20,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.Math.min; -import android.media.MediaCodec.BufferInfo; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -28,9 +27,9 @@ import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.exoplayer.audio.AudioProcessor; import androidx.media3.exoplayer.audio.AudioProcessor.AudioFormat; -import androidx.media3.exoplayer.audio.SonicAudioProcessor; import java.nio.ByteBuffer; import java.util.List; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.dataflow.qual.Pure; /** @@ -43,22 +42,18 @@ import org.checkerframework.dataflow.qual.Pure; private final Codec decoder; private final DecoderInputBuffer decoderInputBuffer; - private final SonicAudioProcessor sonicAudioProcessor; - private final SpeedProvider speedProvider; - private final boolean flattenForSlowMotion; + @Nullable private final SpeedChangingAudioProcessor speedChangingAudioProcessor; private final Codec encoder; private final AudioFormat encoderInputAudioFormat; private final DecoderInputBuffer encoderInputBuffer; private final DecoderInputBuffer encoderOutputBuffer; + private ByteBuffer processorOutputBuffer; + private long nextEncoderInputBufferTimeUs; private long encoderBufferDurationRemainder; - private ByteBuffer sonicOutputBuffer; - private boolean drainingSonicForSpeedChange; - private float currentSpeed; - public AudioTranscodingSamplePipeline( Format inputFormat, TransformationRequest transformationRequest, @@ -74,13 +69,8 @@ import org.checkerframework.dataflow.qual.Pure; encoderOutputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); - this.decoder = decoderFactory.createForAudioDecoding(inputFormat); + decoder = decoderFactory.createForAudioDecoding(inputFormat); - this.flattenForSlowMotion = transformationRequest.flattenForSlowMotion; - sonicAudioProcessor = new SonicAudioProcessor(); - sonicOutputBuffer = AudioProcessor.EMPTY_BUFFER; - speedProvider = new SegmentSpeedProvider(inputFormat); - currentSpeed = speedProvider.getSpeed(0); AudioFormat encoderInputAudioFormat = new AudioFormat( inputFormat.sampleRate, @@ -88,18 +78,21 @@ import org.checkerframework.dataflow.qual.Pure; // The decoder uses ENCODING_PCM_16BIT by default. // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers C.ENCODING_PCM_16BIT); - if (flattenForSlowMotion) { + if (transformationRequest.flattenForSlowMotion) { + speedChangingAudioProcessor = + new SpeedChangingAudioProcessor(new SegmentSpeedProvider(inputFormat)); try { - encoderInputAudioFormat = sonicAudioProcessor.configure(encoderInputAudioFormat); + encoderInputAudioFormat = speedChangingAudioProcessor.configure(encoderInputAudioFormat); } catch (AudioProcessor.UnhandledAudioFormatException impossible) { throw new IllegalStateException(impossible); } - sonicAudioProcessor.setSpeed(currentSpeed); - sonicAudioProcessor.setPitch(currentSpeed); - sonicAudioProcessor.flush(); + speedChangingAudioProcessor.flush(); + } else { + speedChangingAudioProcessor = null; } - this.encoderInputAudioFormat = encoderInputAudioFormat; + processorOutputBuffer = AudioProcessor.EMPTY_BUFFER; + this.encoderInputAudioFormat = encoderInputAudioFormat; Format requestedOutputFormat = new Format.Builder() .setSampleMimeType( @@ -130,8 +123,8 @@ import org.checkerframework.dataflow.qual.Pure; @Override public boolean processData() throws TransformationException { - if (sonicAudioProcessor.isActive()) { - return feedEncoderFromSonic() || feedSonicFromDecoder(); + if (speedChangingAudioProcessor != null) { + return feedEncoderFromProcessor() || feedProcessorFromDecoder(); } else { return feedEncoderFromDecoder(); } @@ -167,7 +160,9 @@ import org.checkerframework.dataflow.qual.Pure; @Override public void release() { - sonicAudioProcessor.reset(); + if (speedChangingAudioProcessor != null) { + speedChangingAudioProcessor.reset(); + } decoder.release(); encoder.release(); } @@ -190,10 +185,7 @@ import org.checkerframework.dataflow.qual.Pure; if (decoderOutputBuffer == null) { return false; } - if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) { - flushSonicAndSetSpeed(currentSpeed); - return false; - } + feedEncoder(decoderOutputBuffer); if (!decoderOutputBuffer.hasRemaining()) { decoder.releaseOutputBuffer(); @@ -205,22 +197,23 @@ import org.checkerframework.dataflow.qual.Pure; * Attempts to pass audio processor output data to the encoder, and returns whether it may be * possible to pass more data immediately by calling this method again. */ - private boolean feedEncoderFromSonic() throws TransformationException { + @RequiresNonNull("speedChangingAudioProcessor") + private boolean feedEncoderFromProcessor() throws TransformationException { if (!encoder.maybeDequeueInputBuffer(encoderInputBuffer)) { return false; } - if (!sonicOutputBuffer.hasRemaining()) { - sonicOutputBuffer = sonicAudioProcessor.getOutput(); - if (!sonicOutputBuffer.hasRemaining()) { - if (decoder.isEnded() && sonicAudioProcessor.isEnded()) { + if (!processorOutputBuffer.hasRemaining()) { + processorOutputBuffer = speedChangingAudioProcessor.getOutput(); + if (!processorOutputBuffer.hasRemaining()) { + if (decoder.isEnded() && speedChangingAudioProcessor.isEnded()) { queueEndOfStreamToEncoder(); } return false; } } - feedEncoder(sonicOutputBuffer); + feedEncoder(processorOutputBuffer); return true; } @@ -228,37 +221,27 @@ import org.checkerframework.dataflow.qual.Pure; * Attempts to process decoder output data, and returns whether it may be possible to process more * data immediately by calling this method again. */ - private boolean feedSonicFromDecoder() throws TransformationException { - if (drainingSonicForSpeedChange) { - if (sonicAudioProcessor.isEnded() && !sonicOutputBuffer.hasRemaining()) { - flushSonicAndSetSpeed(currentSpeed); - drainingSonicForSpeedChange = false; - } - return false; - } - - // Sonic invalidates any previous output buffer when more input is queued, so we don't queue if - // there is output still to be processed. - if (sonicOutputBuffer.hasRemaining()) { + @RequiresNonNull("speedChangingAudioProcessor") + private boolean feedProcessorFromDecoder() throws TransformationException { + // Audio processors invalidate any previous output buffer when more input is queued, so we don't + // queue if there is output still to be processed. + if (processorOutputBuffer.hasRemaining() + || speedChangingAudioProcessor.getOutput().hasRemaining()) { return false; } if (decoder.isEnded()) { - sonicAudioProcessor.queueEndOfStream(); + speedChangingAudioProcessor.queueEndOfStream(); return false; } - checkState(!sonicAudioProcessor.isEnded()); + checkState(!speedChangingAudioProcessor.isEnded()); @Nullable ByteBuffer decoderOutputBuffer = decoder.getOutputBuffer(); if (decoderOutputBuffer == null) { return false; } - if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) { - sonicAudioProcessor.queueEndOfStream(); - drainingSonicForSpeedChange = true; - return false; - } - sonicAudioProcessor.queueInput(decoderOutputBuffer); + + speedChangingAudioProcessor.queueInput(decoderOutputBuffer); if (!decoderOutputBuffer.hasRemaining()) { decoder.releaseOutputBuffer(); } @@ -294,22 +277,6 @@ import org.checkerframework.dataflow.qual.Pure; encoder.queueInputBuffer(encoderInputBuffer); } - private boolean isSpeedChanging(BufferInfo bufferInfo) { - if (!flattenForSlowMotion) { - return false; - } - float newSpeed = speedProvider.getSpeed(bufferInfo.presentationTimeUs); - boolean speedChanging = newSpeed != currentSpeed; - currentSpeed = newSpeed; - return speedChanging; - } - - private void flushSonicAndSetSpeed(float speed) { - sonicAudioProcessor.setSpeed(speed); - sonicAudioProcessor.setPitch(speed); - sonicAudioProcessor.flush(); - } - private void computeNextEncoderInputBufferTimeUs( long bytesWritten, int bytesPerFrame, int sampleRate) { // The calculation below accounts for remainders and rounding. Without that it corresponds to diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SpeedChangingAudioProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SpeedChangingAudioProcessor.java index 5a3840f6cc..0ce639cca8 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SpeedChangingAudioProcessor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SpeedChangingAudioProcessor.java @@ -28,6 +28,8 @@ import java.nio.ByteBuffer; /** * An {@link AudioProcessor} that changes the speed of audio samples depending on their timestamp. */ +// TODO(b/198772621): Consider making the processor inactive and skipping it in the processor chain +// when speed is 1. /* package */ final class SpeedChangingAudioProcessor extends BaseAudioProcessor { /** The speed provider that provides the speed for each timestamp. */ diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 20a0e83737..dee399daa2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -30,7 +30,6 @@ import androidx.media3.common.Format; import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderInputBuffer; import java.util.List; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.dataflow.qual.Pure; /** @@ -42,11 +41,11 @@ import org.checkerframework.dataflow.qual.Pure; private final DecoderInputBuffer decoderInputBuffer; private final Codec decoder; + @Nullable private final FrameEditor frameEditor; + private final Codec encoder; private final DecoderInputBuffer encoderOutputBuffer; - private @MonotonicNonNull FrameEditor frameEditor; - private boolean waitingForFrameEditorInput; public VideoTranscodingSamplePipeline( @@ -139,6 +138,8 @@ import org.checkerframework.dataflow.qual.Pure; /* outputSurface= */ checkNotNull(encoder.getInputSurface()), transformationRequest.enableHdrEditing, debugViewProvider); + } else { + frameEditor = null; } decoder = From 7507da51a02015ad6f98b7f9e0fea88feb8045d7 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Thu, 3 Feb 2022 13:27:43 +0000 Subject: [PATCH 130/251] Make GlProgram an outer class. This change makes GlUtil.Program an outer class named GlProgram, and also moves private static helpers as well as the inner classes Attribute and Uniform which were only used by GlUtil.Program to GlProgram. Other static utility methods remain in GlUtil. No functional changes intended. PiperOrigin-RevId: 426119299 --- .../demo/gl/BitmapOverlayVideoProcessor.java | 7 +- .../media3/common/util/GlProgram.java | 403 ++++++++++++++++++ .../androidx/media3/common/util/GlUtil.java | 379 +--------------- .../video/VideoDecoderGLSurfaceView.java | 5 +- .../video/spherical/ProjectionRenderer.java | 7 +- .../media3/transformer/FrameEditor.java | 12 +- 6 files changed, 421 insertions(+), 392 deletions(-) create mode 100644 libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java index cf3945be12..33e44323a3 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java @@ -27,6 +27,7 @@ import android.graphics.drawable.BitmapDrawable; import android.opengl.GLES20; import android.opengl.GLUtils; import androidx.media3.common.C; +import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import java.io.IOException; import java.util.Locale; @@ -50,7 +51,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final Bitmap logoBitmap; private final Canvas overlayCanvas; - private GlUtil.@MonotonicNonNull Program program; + private @MonotonicNonNull GlProgram program; private float bitmapScaleX; private float bitmapScaleY; @@ -78,7 +79,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; public void initialize() { try { program = - new GlUtil.Program( + new GlProgram( context, /* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl", /* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl"); @@ -117,7 +118,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GlUtil.checkGlError(); // Run the shader program. - GlUtil.Program program = checkNotNull(this.program); + GlProgram program = checkNotNull(this.program); program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0); program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1); program.setFloatUniform("uScaleX", bitmapScaleX); diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java new file mode 100644 index 0000000000..681ec57433 --- /dev/null +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common.util; + +import static androidx.media3.common.util.Assertions.checkNotNull; + +import android.content.Context; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import androidx.annotation.Nullable; +import java.io.IOException; +import java.nio.Buffer; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a GLSL shader program. + * + *

    After constructing a program, keep a reference for its lifetime and call {@link #delete()} (or + * release the current GL context) when it's no longer needed. + */ +@UnstableApi +public final class GlProgram { + + // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt + private static final int GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT = 0x8BE7; + /** The identifier of a compiled and linked GLSL shader program. */ + private final int programId; + + private final Attribute[] attributes; + private final Uniform[] uniforms; + private final Map attributeByName; + private final Map uniformByName; + + /** + * Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code. + * + * @param context The {@link Context}. + * @param vertexShaderFilePath The path to a vertex shader program. + * @param fragmentShaderFilePath The path to a fragment shader program. + * @throws IOException When failing to read shader files. + */ + public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath) + throws IOException { + this( + GlUtil.loadAsset(context, vertexShaderFilePath), + GlUtil.loadAsset(context, fragmentShaderFilePath)); + } + + /** + * Creates a GL shader program from vertex and fragment shader GLSL GLES20 code. + * + *

    This involves slow steps, like compiling, linking, and switching the GL program, so do not + * call this in fast rendering loops. + * + * @param vertexShaderGlsl The vertex shader program. + * @param fragmentShaderGlsl The fragment shader program. + */ + public GlProgram(String vertexShaderGlsl, String fragmentShaderGlsl) { + programId = GLES20.glCreateProgram(); + GlUtil.checkGlError(); + + // Add the vertex and fragment shaders. + addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl); + addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl); + + // Link and use the program, and enumerate attributes/uniforms. + GLES20.glLinkProgram(programId); + int[] linkStatus = new int[] {GLES20.GL_FALSE}; + GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0); + if (linkStatus[0] != GLES20.GL_TRUE) { + GlUtil.throwGlException( + "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId)); + } + GLES20.glUseProgram(programId); + attributeByName = new HashMap<>(); + int[] attributeCount = new int[1]; + GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0); + attributes = new Attribute[attributeCount[0]]; + for (int i = 0; i < attributeCount[0]; i++) { + Attribute attribute = Attribute.create(programId, i); + attributes[i] = attribute; + attributeByName.put(attribute.name, attribute); + } + uniformByName = new HashMap<>(); + int[] uniformCount = new int[1]; + GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0); + uniforms = new Uniform[uniformCount[0]]; + for (int i = 0; i < uniformCount[0]; i++) { + Uniform uniform = Uniform.create(programId, i); + uniforms[i] = uniform; + uniformByName.put(uniform.name, uniform); + } + GlUtil.checkGlError(); + } + + private static void addShader(int programId, int type, String glsl) { + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, glsl); + GLES20.glCompileShader(shader); + + int[] result = new int[] {GLES20.GL_FALSE}; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0); + if (result[0] != GLES20.GL_TRUE) { + GlUtil.throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl); + } + + GLES20.glAttachShader(programId, shader); + GLES20.glDeleteShader(shader); + GlUtil.checkGlError(); + } + + private static int getAttributeLocation(int programId, String attributeName) { + return GLES20.glGetAttribLocation(programId, attributeName); + } + + /** Returns the location of an {@link Attribute}. */ + private int getAttributeLocation(String attributeName) { + return getAttributeLocation(programId, attributeName); + } + + private static int getUniformLocation(int programId, String uniformName) { + return GLES20.glGetUniformLocation(programId, uniformName); + } + + /** Returns the location of a {@link Uniform}. */ + public int getUniformLocation(String uniformName) { + return getUniformLocation(programId, uniformName); + } + + /** + * Uses the program. + * + *

    Call this in the rendering loop to switch between different programs. + */ + public void use() { + // TODO(b/214975934): When multiple GL programs are supported by Transformer, make sure + // to call use() to switch between programs. + GLES20.glUseProgram(programId); + GlUtil.checkGlError(); + } + + /** Deletes the program. Deleted programs cannot be used again. */ + public void delete() { + GLES20.glDeleteProgram(programId); + GlUtil.checkGlError(); + } + + /** + * Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute + * array. + */ + public int getAttributeArrayLocationAndEnable(String attributeName) { + int location = getAttributeLocation(attributeName); + GLES20.glEnableVertexAttribArray(location); + GlUtil.checkGlError(); + return location; + } + + /** Sets a float buffer type attribute. */ + public void setBufferAttribute(String name, float[] values, int size) { + checkNotNull(attributeByName.get(name)).setBuffer(values, size); + } + + /** Sets a texture sampler type uniform. */ + public void setSamplerTexIdUniform(String name, int texId, int unit) { + checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit); + } + + /** Sets a float type uniform. */ + public void setFloatUniform(String name, float value) { + checkNotNull(uniformByName.get(name)).setFloat(value); + } + + /** Sets a float array type uniform. */ + public void setFloatsUniform(String name, float[] value) { + checkNotNull(uniformByName.get(name)).setFloats(value); + } + + /** Binds all attributes and uniforms in the program. */ + public void bindAttributesAndUniforms() { + for (Attribute attribute : attributes) { + attribute.bind(); + } + for (Uniform uniform : uniforms) { + uniform.bind(); + } + } + + /** Returns the length of the null-terminated C string in {@code cString}. */ + private static int getCStringLength(byte[] cString) { + for (int i = 0; i < cString.length; ++i) { + if (cString[i] == '\0') { + return i; + } + } + return cString.length; + } + + /** + * GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}. + */ + private static final class Attribute { + + /* Returns the attribute at the given index in the program. */ + public static Attribute create(int programId, int index) { + int[] length = new int[1]; + GLES20.glGetProgramiv( + programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0); + byte[] nameBytes = new byte[length[0]]; + + GLES20.glGetActiveAttrib( + programId, + index, + length[0], + /* unusedLength */ new int[1], + /* lengthOffset= */ 0, + /* unusedSize */ new int[1], + /* sizeOffset= */ 0, + /* unusedType */ new int[1], + /* typeOffset= */ 0, + nameBytes, + /* nameOffset= */ 0); + String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes)); + int location = getAttributeLocation(programId, name); + + return new Attribute(name, index, location); + } + + /** The name of the attribute in the GLSL sources. */ + public final String name; + + private final int index; + private final int location; + + @Nullable private Buffer buffer; + private int size; + + private Attribute(String name, int index, int location) { + this.name = name; + this.index = index; + this.location = location; + } + + /** + * Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size} + * elements) to this {@link Attribute}. + * + * @param buffer Buffer to bind to this attribute. + * @param size Number of elements per vertex. + */ + public void setBuffer(float[] buffer, int size) { + this.buffer = GlUtil.createBuffer(buffer); + this.size = size; + } + + /** + * Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}. + * + *

    Should be called before each drawing call. + */ + public void bind() { + Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind"); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0); + GLES20.glVertexAttribPointer( + location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer); + GLES20.glEnableVertexAttribArray(index); + GlUtil.checkGlError(); + } + } + + /** + * GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}. + */ + private static final class Uniform { + + /** Returns the uniform at the given index in the program. */ + public static Uniform create(int programId, int index) { + int[] length = new int[1]; + GLES20.glGetProgramiv( + programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0); + + int[] type = new int[1]; + byte[] nameBytes = new byte[length[0]]; + + GLES20.glGetActiveUniform( + programId, + index, + length[0], + /* unusedLength */ new int[1], + /* lengthOffset= */ 0, + /* unusedSize */ new int[1], + /*sizeOffset= */ 0, + type, + /* typeOffset= */ 0, + nameBytes, + /* nameOffset= */ 0); + String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes)); + int location = getUniformLocation(programId, name); + + return new Uniform(name, location, type[0]); + } + + /** The name of the uniform in the GLSL sources. */ + public final String name; + + private final int location; + private final int type; + private final float[] value; + + private int texId; + private int unit; + + private Uniform(String name, int location, int type) { + this.name = name; + this.location = location; + this.type = type; + this.value = new float[16]; + } + + /** + * Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform. + * + * @param texId The GL texture identifier from which to sample. + * @param unit The GL texture unit index. + */ + public void setSamplerTexId(int texId, int unit) { + this.texId = texId; + this.unit = unit; + } + + /** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */ + public void setFloat(float value) { + this.value[0] = value; + } + + /** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */ + public void setFloats(float[] value) { + System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length); + } + + /** + * Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link + * #setFloat(float)} or {@link #setFloats(float[])}. + * + *

    Should be called before each drawing call. + */ + public void bind() { + if (type == GLES20.GL_FLOAT) { + GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0); + GlUtil.checkGlError(); + return; + } + + if (type == GLES20.GL_FLOAT_MAT3) { + GLES20.glUniformMatrix3fv( + location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0); + GlUtil.checkGlError(); + return; + } + + if (type == GLES20.GL_FLOAT_MAT4) { + GLES20.glUniformMatrix4fv( + location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0); + GlUtil.checkGlError(); + return; + } + + if (texId == 0) { + throw new IllegalStateException("No call to setSamplerTexId() before bind."); + } + GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit); + if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) { + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId); + } else if (type == GLES20.GL_SAMPLER_2D) { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); + } else { + throw new IllegalStateException("Unexpected uniform type: " + type); + } + GLES20.glUniform1i(location, unit); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri( + GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GlUtil.checkGlError(); + } + } +} diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index 879ade7037..b763c6b620 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -16,7 +16,6 @@ package androidx.media3.common.util; import static android.opengl.GLU.gluErrorString; -import static androidx.media3.common.util.Assertions.checkNotNull; import android.content.Context; import android.content.pm.PackageManager; @@ -33,13 +32,10 @@ import androidx.annotation.RequiresApi; import androidx.media3.common.C; import java.io.IOException; import java.io.InputStream; -import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.HashMap; -import java.util.Map; import javax.microedition.khronos.egl.EGL10; /** OpenGL ES utilities. */ @@ -55,152 +51,6 @@ public final class GlUtil { } } - /** - * Represents a GLSL shader program. - * - *

    After constructing a program, keep a reference for its lifetime and call {@link #delete()} - * (or release the current GL context) when it's no longer needed. - */ - public static final class Program { - /** The identifier of a compiled and linked GLSL shader program. */ - private final int programId; - - private final Attribute[] attributes; - private final Uniform[] uniforms; - private final Map attributeByName; - private final Map uniformByName; - - /** - * Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code. - * - * @param context The {@link Context}. - * @param vertexShaderFilePath The path to a vertex shader program. - * @param fragmentShaderFilePath The path to a fragment shader program. - * @throws IOException When failing to read shader files. - */ - public Program(Context context, String vertexShaderFilePath, String fragmentShaderFilePath) - throws IOException { - this(loadAsset(context, vertexShaderFilePath), loadAsset(context, fragmentShaderFilePath)); - } - - /** - * Creates a GL shader program from vertex and fragment shader GLSL GLES20 code. - * - *

    This involves slow steps, like compiling, linking, and switching the GL program, so do not - * call this in fast rendering loops. - * - * @param vertexShaderGlsl The vertex shader program. - * @param fragmentShaderGlsl The fragment shader program. - */ - public Program(String vertexShaderGlsl, String fragmentShaderGlsl) { - programId = GLES20.glCreateProgram(); - checkGlError(); - - // Add the vertex and fragment shaders. - addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl); - addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl); - - // Link and use the program, and enumerate attributes/uniforms. - GLES20.glLinkProgram(programId); - int[] linkStatus = new int[] {GLES20.GL_FALSE}; - GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0); - if (linkStatus[0] != GLES20.GL_TRUE) { - throwGlException( - "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId)); - } - GLES20.glUseProgram(programId); - attributeByName = new HashMap<>(); - int[] attributeCount = new int[1]; - GLES20.glGetProgramiv( - programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0); - attributes = new Attribute[attributeCount[0]]; - for (int i = 0; i < attributeCount[0]; i++) { - Attribute attribute = Attribute.create(programId, i); - attributes[i] = attribute; - attributeByName.put(attribute.name, attribute); - } - uniformByName = new HashMap<>(); - int[] uniformCount = new int[1]; - GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0); - uniforms = new Uniform[uniformCount[0]]; - for (int i = 0; i < uniformCount[0]; i++) { - Uniform uniform = Uniform.create(programId, i); - uniforms[i] = uniform; - uniformByName.put(uniform.name, uniform); - } - checkGlError(); - } - - /** - * Uses the program. - * - *

    Call this in the rendering loop to switch between different programs. - */ - public void use() { - // TODO(http://b/205002913): When multiple GL programs are supported by Transformer, make sure - // to call use() to switch between programs. - GLES20.glUseProgram(programId); - checkGlError(); - } - - /** Deletes the program. Deleted programs cannot be used again. */ - public void delete() { - GLES20.glDeleteProgram(programId); - checkGlError(); - } - - /** - * Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute - * array. - */ - public int getAttributeArrayLocationAndEnable(String attributeName) { - int location = getAttributeLocation(attributeName); - GLES20.glEnableVertexAttribArray(location); - checkGlError(); - return location; - } - - /** Returns the location of an {@link Attribute}. */ - private int getAttributeLocation(String attributeName) { - return GlUtil.getAttributeLocation(programId, attributeName); - } - - /** Returns the location of a {@link Uniform}. */ - public int getUniformLocation(String uniformName) { - return GlUtil.getUniformLocation(programId, uniformName); - } - - /** Sets a float buffer type attribute. */ - public void setBufferAttribute(String name, float[] values, int size) { - checkNotNull(attributeByName.get(name)).setBuffer(values, size); - } - - /** Sets a texture sampler type uniform. */ - public void setSamplerTexIdUniform(String name, int texId, int unit) { - checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit); - } - - /** Sets a float type uniform. */ - public void setFloatUniform(String name, float value) { - checkNotNull(uniformByName.get(name)).setFloat(value); - } - - /** Sets a float array type uniform. */ - public void setFloatsUniform(String name, float[] value) { - checkNotNull(uniformByName.get(name)).setFloats(value); - } - - /** Binds all attributes and uniforms in the program. */ - public void bindAttributesAndUniforms() { - for (Attribute attribute : attributes) { - attribute.bind(); - } - for (Uniform uniform : uniforms) { - uniform.bind(); - } - } - } - /** Whether to throw a {@link GlException} in case of an OpenGL error. */ public static boolean glAssertionsEnabled = false; @@ -214,8 +64,6 @@ public final class GlUtil { // https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context"; - // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt - private static final int GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT = 0x8BE7; // https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt private static final int EGL_GL_COLORSPACE_KHR = 0x309D; // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt @@ -462,31 +310,7 @@ public final class GlUtil { return texId[0]; } - private static void addShader(int programId, int type, String glsl) { - int shader = GLES20.glCreateShader(type); - GLES20.glShaderSource(shader, glsl); - GLES20.glCompileShader(shader); - - int[] result = new int[] {GLES20.GL_FALSE}; - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0); - if (result[0] != GLES20.GL_TRUE) { - throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl); - } - - GLES20.glAttachShader(programId, shader); - GLES20.glDeleteShader(shader); - checkGlError(); - } - - private static int getAttributeLocation(int programId, String attributeName) { - return GLES20.glGetAttribLocation(programId, attributeName); - } - - private static int getUniformLocation(int programId, String uniformName) { - return GLES20.glGetUniformLocation(programId, uniformName); - } - - private static void throwGlException(String errorMsg) { + /* package */ static void throwGlException(String errorMsg) { Log.e(TAG, errorMsg); if (glAssertionsEnabled) { throw new GlException(errorMsg); @@ -499,207 +323,6 @@ public final class GlUtil { } } - /** Returns the length of the null-terminated string in {@code strVal}. */ - private static int strlen(byte[] strVal) { - for (int i = 0; i < strVal.length; ++i) { - if (strVal[i] == '\0') { - return i; - } - } - return strVal.length; - } - - /** - * GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}. - */ - private static final class Attribute { - - /* Returns the attribute at the given index in the program. */ - public static Attribute create(int programId, int index) { - int[] length = new int[1]; - GLES20.glGetProgramiv( - programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0); - byte[] nameBytes = new byte[length[0]]; - - GLES20.glGetActiveAttrib( - programId, - index, - length[0], - /* unusedLength */ new int[1], - /* lengthOffset= */ 0, - /* unusedSize */ new int[1], - /* sizeOffset= */ 0, - /* unusedType */ new int[1], - /* typeOffset= */ 0, - nameBytes, - /* nameOffset= */ 0); - String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes)); - int location = getAttributeLocation(programId, name); - - return new Attribute(name, index, location); - } - - /** The name of the attribute in the GLSL sources. */ - public final String name; - - private final int index; - private final int location; - - @Nullable private Buffer buffer; - private int size; - - private Attribute(String name, int index, int location) { - this.name = name; - this.index = index; - this.location = location; - } - - /** - * Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size} - * elements) to this {@link Attribute}. - * - * @param buffer Buffer to bind to this attribute. - * @param size Number of elements per vertex. - */ - public void setBuffer(float[] buffer, int size) { - this.buffer = createBuffer(buffer); - this.size = size; - } - - /** - * Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}. - * - *

    Should be called before each drawing call. - */ - public void bind() { - Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind"); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0); - GLES20.glVertexAttribPointer( - location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer); - GLES20.glEnableVertexAttribArray(index); - checkGlError(); - } - } - - /** - * GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}. - */ - private static final class Uniform { - - /** Returns the uniform at the given index in the program. */ - public static Uniform create(int programId, int index) { - int[] length = new int[1]; - GLES20.glGetProgramiv( - programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0); - - int[] type = new int[1]; - byte[] nameBytes = new byte[length[0]]; - - GLES20.glGetActiveUniform( - programId, - index, - length[0], - /* unusedLength */ new int[1], - /* lengthOffset= */ 0, - /* unusedSize */ new int[1], - /*sizeOffset= */ 0, - type, - /* typeOffset= */ 0, - nameBytes, - /* nameOffset= */ 0); - String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes)); - int location = getUniformLocation(programId, name); - - return new Uniform(name, location, type[0]); - } - - /** The name of the uniform in the GLSL sources. */ - public final String name; - - private final int location; - private final int type; - private final float[] value; - - private int texId; - private int unit; - - private Uniform(String name, int location, int type) { - this.name = name; - this.location = location; - this.type = type; - this.value = new float[16]; - } - - /** - * Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform. - * - * @param texId The GL texture identifier from which to sample. - * @param unit The GL texture unit index. - */ - public void setSamplerTexId(int texId, int unit) { - this.texId = texId; - this.unit = unit; - } - - /** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */ - public void setFloat(float value) { - this.value[0] = value; - } - - /** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */ - public void setFloats(float[] value) { - System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length); - } - - /** - * Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link - * #setFloat(float)} or {@link #setFloats(float[])}. - * - *

    Should be called before each drawing call. - */ - public void bind() { - if (type == GLES20.GL_FLOAT) { - GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0); - checkGlError(); - return; - } - - if (type == GLES20.GL_FLOAT_MAT3) { - GLES20.glUniformMatrix3fv( - location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0); - checkGlError(); - return; - } - - if (type == GLES20.GL_FLOAT_MAT4) { - GLES20.glUniformMatrix4fv( - location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0); - checkGlError(); - return; - } - - if (texId == 0) { - throw new IllegalStateException("No call to setSamplerTexId() before bind."); - } - GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit); - if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) { - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId); - } else if (type == GLES20.GL_SAMPLER_2D) { - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); - } else { - throw new IllegalStateException("Unexpected uniform type: " + type); - } - GLES20.glUniform1i(location, unit); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri( - GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri( - GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - checkGlError(); - } - } - @RequiresApi(17) private static final class Api17 { private Api17() {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java index a4a29ec46c..a24d17096b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java @@ -23,6 +23,7 @@ import android.opengl.GLSurfaceView; import android.util.AttributeSet; import androidx.annotation.Nullable; import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.decoder.VideoDecoderOutputBuffer; @@ -146,7 +147,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView // glDrawArrays uses it. private final FloatBuffer[] textureCoords; - private GlUtil.@MonotonicNonNull Program program; + private @MonotonicNonNull GlProgram program; private int colorMatrixLocation; // Accessed only from the GL thread. @@ -167,7 +168,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView @Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { - program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER); + program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); int posLocation = program.getAttributeArrayLocationAndEnable("in_pos"); GLES20.glVertexAttribPointer( posLocation, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java index b02cf870d6..2503733f36 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java @@ -21,6 +21,7 @@ import android.opengl.GLES11Ext; import android.opengl.GLES20; import androidx.annotation.Nullable; import androidx.media3.common.C; +import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import java.nio.FloatBuffer; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -87,7 +88,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private int stereoMode; @Nullable private MeshData leftMeshData; @Nullable private MeshData rightMeshData; - private GlUtil.@MonotonicNonNull Program program; + private @MonotonicNonNull GlProgram program; // Program related GL items. These are only valid if Program is valid. private int mvpMatrixHandle; @@ -114,7 +115,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Initializes of the GL components. */ public void init() { - program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER); + program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER); mvpMatrixHandle = program.getUniformLocation("uMvpMatrix"); uTexMatrixHandle = program.getUniformLocation("uTexMatrix"); positionHandle = program.getAttributeArrayLocationAndEnable("aPosition"); @@ -148,7 +149,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } GLES20.glUniformMatrix3fv(uTexMatrixHandle, 1, false, texMatrix, 0); - // TODO(b/205002913): Update to use GlUtil.Uniform.bind(). + // TODO(b/205002913): Update to use GlProgram.Uniform.bind(). GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index ff201364a0..57f8be3a1f 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -31,6 +31,7 @@ import android.view.Surface; import android.view.SurfaceView; import androidx.annotation.Nullable; import androidx.media3.common.C; +import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @@ -86,7 +87,7 @@ import java.util.concurrent.atomic.AtomicInteger; EGLContext eglContext; EGLSurface eglSurface; int textureId; - GlUtil.Program glProgram; + GlProgram glProgram; @Nullable EGLSurface debugPreviewEglSurface = null; try { eglDisplay = GlUtil.createEglDisplay(); @@ -141,7 +142,7 @@ import java.util.concurrent.atomic.AtomicInteger; debugPreviewHeight); } - private static GlUtil.Program configureGlProgram( + private static GlProgram configureGlProgram( Context context, Matrix transformationMatrix, int textureId, @@ -157,8 +158,7 @@ import java.util.concurrent.atomic.AtomicInteger; enableExperimentalHdrEditing ? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH : FRAGMENT_SHADER_COPY_EXTERNAL_PATH; - GlUtil.Program glProgram = - new GlUtil.Program(context, vertexShaderFilePath, fragmentShaderFilePath); + GlProgram glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. glProgram.setBufferAttribute( @@ -242,7 +242,7 @@ import java.util.concurrent.atomic.AtomicInteger; private final AtomicInteger availableInputFrameCount; private final SurfaceTexture inputSurfaceTexture; private final Surface inputSurface; - private final GlUtil.Program glProgram; + private final GlProgram glProgram; private final int outputWidth; private final int outputHeight; @Nullable private final EGLSurface debugPreviewEglSurface; @@ -256,7 +256,7 @@ import java.util.concurrent.atomic.AtomicInteger; EGLContext eglContext, EGLSurface eglSurface, int textureId, - GlUtil.Program glProgram, + GlProgram glProgram, int outputWidth, int outputHeight, @Nullable EGLSurface debugPreviewEglSurface, From e2d4bd15cdce85de273464c14a6e3a2a580b62f2 Mon Sep 17 00:00:00 2001 From: claincly Date: Thu, 3 Feb 2022 19:09:56 +0000 Subject: [PATCH 131/251] Add option to disable encoder fallback. We use the top priority encoder (sorted by EncoderSelector) and the requested output format for encoding. PiperOrigin-RevId: 426191800 --- .../transformer/DefaultEncoderFactory.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 9fe038957b..7af2582486 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -18,7 +18,6 @@ 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.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.transformer.CodecFactoryUtil.createCodec; @@ -41,22 +40,32 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** A default implementation of {@link Codec.EncoderFactory}. */ @UnstableApi public final class DefaultEncoderFactory implements Codec.EncoderFactory { - // TODO(b/214973843): Add option to disable fallback. private static final int DEFAULT_COLOR_FORMAT = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; private static final int DEFAULT_FRAME_RATE = 60; private static final int DEFAULT_I_FRAME_INTERVAL_SECS = 1; @Nullable private final EncoderSelector videoEncoderSelector; + private final boolean disableFallback; - /** Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}. */ + /** + * Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}, and + * format fallback enabled. + * + *

    With format fallback enabled, and when the requested {@link Format} is not supported, {@code + * DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link + * Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME + * type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc. + */ public DefaultEncoderFactory() { - this(EncoderSelector.DEFAULT); + this(EncoderSelector.DEFAULT, /* disableFallback= */ false); } /** Creates a new instance. */ - public DefaultEncoderFactory(EncoderSelector videoEncoderSelector) { + public DefaultEncoderFactory( + @Nullable EncoderSelector videoEncoderSelector, boolean disableFallback) { this.videoEncoderSelector = videoEncoderSelector; + this.disableFallback = disableFallback; } @Override @@ -98,7 +107,8 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { @Nullable Pair encoderAndClosestFormatSupport = - findEncoderWithClosestFormatSupport(format, videoEncoderSelector, allowedMimeTypes); + findEncoderWithClosestFormatSupport( + format, videoEncoderSelector, allowedMimeTypes, disableFallback); if (encoderAndClosestFormatSupport == null) { throw createTransformationException( new IllegalArgumentException( @@ -145,16 +155,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { @RequiresNonNull("#1.sampleMimeType") @Nullable private static Pair findEncoderWithClosestFormatSupport( - Format requestedFormat, EncoderSelector encoderSelector, List allowedMimeTypes) { + Format requestedFormat, + EncoderSelector encoderSelector, + List allowedMimeTypes, + boolean disableFallback) { + String requestedMimeType = requestedFormat.sampleMimeType; @Nullable - String mimeType = - findFallbackMimeType(encoderSelector, requestedFormat.sampleMimeType, allowedMimeTypes); - if (mimeType == null) { + String mimeType = findFallbackMimeType(encoderSelector, requestedMimeType, allowedMimeTypes); + if (mimeType == null || (disableFallback && !requestedMimeType.equals(mimeType))) { return null; } List encodersForMimeType = encoderSelector.selectEncoderInfos(mimeType); - checkState(!encodersForMimeType.isEmpty()); + if (encodersForMimeType.isEmpty()) { + return null; + } + if (disableFallback) { + return Pair.create(encodersForMimeType.get(0), requestedFormat); + } ImmutableList filteredEncoders = filterEncoders( encodersForMimeType, From dc6bf507c7de1a044d45ff3a2529d005e7c837b6 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 10:02:09 +0000 Subject: [PATCH 132/251] Redefine the SSAI URI format with an "ssai" scheme instead of "imadai" This allows us to remove the IMA naming from DefaultMediaSourceFactory's SSAI integration. #minor-release PiperOrigin-RevId: 426346456 --- .../main/java/androidx/media3/common/C.java | 4 ++++ .../source/DefaultMediaSourceFactory.java | 24 +++++++++---------- .../ServerSideAdInsertionStreamRequest.java | 9 +++---- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 95e8fc1059..8eba741733 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -29,6 +29,7 @@ import android.media.AudioManager; import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; +import android.net.Uri; import android.view.Surface; import androidx.annotation.IntDef; import androidx.annotation.RequiresApi; @@ -125,6 +126,9 @@ public final class C { /** The name of the sans-serif font family. */ @UnstableApi public static final String SANS_SERIF_NAME = "sans-serif"; + /** The {@link Uri#getScheme() URI scheme} used for content with server side ad insertion. */ + @UnstableApi public static final String SSAI_SCHEME = "ssai"; + /** * Types of crypto implementation. May be one of {@link #CRYPTO_TYPE_NONE}, {@link * #CRYPTO_TYPE_UNSUPPORTED} or {@link #CRYPTO_TYPE_FRAMEWORK}. May also be an app-defined value diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java index 9e6649ce28..4e2070b043 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java @@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.castNonNull; import android.content.Context; +import android.net.Uri; import androidx.annotation.Nullable; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; @@ -106,7 +107,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { private final DataSource.Factory dataSourceFactory; private final DelegateFactoryLoader delegateFactoryLoader; - @Nullable private MediaSource.Factory imaServerSideAdInsertionMediaSourceFactory; + @Nullable private MediaSource.Factory serverSideAdInsertionMediaSourceFactory; @Nullable private AdsLoader.Provider adsLoaderProvider; @Nullable private AdViewProvider adViewProvider; @Nullable private LoadErrorHandlingPolicy loadErrorHandlingPolicy; @@ -212,22 +213,19 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { } /** - * Sets the {@link MediaSource.Factory} used to handle {@link MediaItem} instances containing IMA Dynamic Ad Insertion URIs. + * Sets the {@link MediaSource.Factory} used to handle {@link MediaItem} instances containing a + * {@link Uri} identified as resolving to content with server side ad insertion (SSAI). * - *

    In most cases this will be an {@code ImaServerSideAdInsertionMediaSource.Factory} from the - * IMA extension. + *

    SSAI URIs are those with a {@link Uri#getScheme() scheme} of {@link C#SSAI_SCHEME}. * - *

    IMA DAI URIs are those with a scheme of {@code "imadai"}. - * - * @param imaServerSideAdInsertionMediaSourceFactory The {@link MediaSource.Factory} for IMA DAI + * @param serverSideAdInsertionMediaSourceFactory The {@link MediaSource.Factory} for SSAI * content, or {@code null} to remove a previously set {@link MediaSource.Factory}. * @return This factory, for convenience. */ @UnstableApi - public DefaultMediaSourceFactory setImaServerSideAdInsertionMediaSourceFactory( - @Nullable MediaSource.Factory imaServerSideAdInsertionMediaSourceFactory) { - this.imaServerSideAdInsertionMediaSourceFactory = imaServerSideAdInsertionMediaSourceFactory; + public DefaultMediaSourceFactory setServerSideAdInsertionMediaSourceFactory( + @Nullable MediaSource.Factory serverSideAdInsertionMediaSourceFactory) { + this.serverSideAdInsertionMediaSourceFactory = serverSideAdInsertionMediaSourceFactory; return this; } @@ -324,8 +322,8 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { public MediaSource createMediaSource(MediaItem mediaItem) { Assertions.checkNotNull(mediaItem.localConfiguration); @Nullable String scheme = mediaItem.localConfiguration.uri.getScheme(); - if (scheme != null && scheme.equals("imadai")) { - return checkNotNull(imaServerSideAdInsertionMediaSourceFactory).createMediaSource(mediaItem); + if (scheme != null && scheme.equals(C.SSAI_SCHEME)) { + return checkNotNull(serverSideAdInsertionMediaSourceFactory).createMediaSource(mediaItem); } @C.ContentType int type = diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java index 9fbd3eee9f..f9026648d2 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java @@ -246,7 +246,7 @@ import java.util.Map; } } - private static final String SCHEME = "imadai"; + private static final String IMA_AUTHORITY = "dai.google.com"; private static final String ADS_ID = "adsId"; private static final String ASSET_KEY = "assetKey"; private static final String API_KEY = "apiKey"; @@ -341,7 +341,8 @@ import java.util.Map; /** Returns a corresponding {@link Uri}. */ public Uri toUri() { Uri.Builder dataUriBuilder = new Uri.Builder(); - dataUriBuilder.scheme(SCHEME); + dataUriBuilder.scheme(C.SSAI_SCHEME); + dataUriBuilder.authority(IMA_AUTHORITY); dataUriBuilder.appendQueryParameter(ADS_ID, adsId); if (loadVideoTimeoutMs != DEFAULT_LOAD_VIDEO_TIMEOUT_MS) { dataUriBuilder.appendQueryParameter( @@ -433,8 +434,8 @@ import java.util.Map; public static ServerSideAdInsertionStreamRequest fromUri(Uri uri) { ServerSideAdInsertionStreamRequest.Builder request = new ServerSideAdInsertionStreamRequest.Builder(); - if (!SCHEME.equals(uri.getScheme())) { - throw new IllegalArgumentException("Invalid scheme."); + if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) { + throw new IllegalArgumentException("Invalid URI scheme or authority."); } request.setAdsId(checkNotNull(uri.getQueryParameter(ADS_ID))); request.setAssetKey(uri.getQueryParameter(ASSET_KEY)); From 37d47e1d131ebc085af84bb7b4f614b567f9fb52 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 10:10:41 +0000 Subject: [PATCH 133/251] Mark LegacyPlayerView as deprecated This type is still known as PlayerView in exoplayer2 #minor-release PiperOrigin-RevId: 426348286 --- .../ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java index e1b3e4d3bc..5c2bd1545f 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java @@ -252,7 +252,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * single instance in a layout file. This is achieved by setting the {@code player_layout_id} * attribute on a LegacyPlayerView. This will cause the specified layout to be inflated instead of * {@code exo_legacy_player_view.xml} for only the instance on which the attribute is set. + * + * @deprecated Use {@link PlayerView} instead. */ +@Deprecated @UnstableApi public class LegacyPlayerView extends FrameLayout implements AdViewProvider { From 7b9eaaed1458bb5d946f3931367496af31a643f7 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 10:28:22 +0000 Subject: [PATCH 134/251] Fix ForwardingPlayer @Override and @Deprecated annotations These were messed up in https://github.com/androidx/media/commit/74c6ef9ba096fe64e767a739b97debca5185a375 Also suppress deprecation warnings when we're just forwarding a deprecated method to the delegate. #minor-release PiperOrigin-RevId: 426351791 --- .../media3/common/ForwardingPlayer.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index 6f3a21dfc1..d87d0a8975 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -39,12 +39,12 @@ public class ForwardingPlayer implements Player { this.player = player; } - @Deprecated + @Override public Looper getApplicationLooper() { return player.getApplicationLooper(); } - @Deprecated + @Override public void addListener(Listener listener) { player.addListener(new ForwardingListener(this, listener)); } @@ -255,12 +255,14 @@ public class ForwardingPlayer implements Player { player.seekForward(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean hasPrevious() { return player.hasPrevious(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean hasPreviousWindow() { @@ -272,12 +274,14 @@ public class ForwardingPlayer implements Player { return player.hasPreviousMediaItem(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public void previous() { player.previous(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public void seekToPreviousWindow() { @@ -299,12 +303,14 @@ public class ForwardingPlayer implements Player { return player.getMaxSeekToPreviousPosition(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean hasNext() { return player.hasNext(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean hasNextWindow() { @@ -316,12 +322,14 @@ public class ForwardingPlayer implements Player { return player.hasNextMediaItem(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public void next() { player.next(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public void seekToNextWindow() { @@ -358,6 +366,7 @@ public class ForwardingPlayer implements Player { player.stop(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public void stop(boolean reset) { @@ -369,11 +378,15 @@ public class ForwardingPlayer implements Player { player.release(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method + @Deprecated @Override public TrackGroupArray getCurrentTrackGroups() { return player.getCurrentTrackGroups(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method + @Deprecated @Override public TrackSelectionArray getCurrentTrackSelections() { return player.getCurrentTrackSelections(); @@ -425,6 +438,7 @@ public class ForwardingPlayer implements Player { return player.getCurrentPeriodIndex(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public int getCurrentWindowIndex() { @@ -436,6 +450,7 @@ public class ForwardingPlayer implements Player { return player.getCurrentMediaItemIndex(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public int getNextWindowIndex() { @@ -447,6 +462,7 @@ public class ForwardingPlayer implements Player { return player.getNextMediaItemIndex(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public int getPreviousWindowIndex() { @@ -499,6 +515,7 @@ public class ForwardingPlayer implements Player { return player.getTotalBufferedDuration(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean isCurrentWindowDynamic() { @@ -510,6 +527,7 @@ public class ForwardingPlayer implements Player { return player.isCurrentMediaItemDynamic(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean isCurrentWindowLive() { @@ -526,6 +544,7 @@ public class ForwardingPlayer implements Player { return player.getCurrentLiveOffset(); } + @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override public boolean isCurrentWindowSeekable() { From 9a5238b0f72385b2af2529e2f8b291357a4998fa Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 11:15:47 +0000 Subject: [PATCH 135/251] Explicitly document every overridden method in ForwardingPlayer This makes the delegation model more explicit, and prevents the javadoc compiler from just pulling in the Player javadoc automatically - which can lead to some confusion when some method definitions in Player depend on other methods (e.g. seekForward() is defined in terms of getSeekForwardIncrement()). Issue: google/ExoPlayer#9897 #minor-release PiperOrigin-RevId: 426359004 --- .../media3/common/ForwardingPlayer.java | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index d87d0a8975..c0ea8befcc 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -39,222 +39,266 @@ public class ForwardingPlayer implements Player { this.player = player; } + /** Calls {@link Player#getApplicationLooper()} on the delegate and returns the result. */ @Override public Looper getApplicationLooper() { return player.getApplicationLooper(); } + /** Calls {@link Player#addListener(Listener)} on the delegate. */ @Override public void addListener(Listener listener) { player.addListener(new ForwardingListener(this, listener)); } + /** Calls {@link Player#removeListener(Listener)} on the delegate. */ @Override public void removeListener(Listener listener) { player.removeListener(new ForwardingListener(this, listener)); } + /** Calls {@link Player#setMediaItems(List)} on the delegate. */ @Override public void setMediaItems(List mediaItems) { player.setMediaItems(mediaItems); } + /** Calls {@link Player#setMediaItems(List, boolean)} ()} on the delegate. */ @Override public void setMediaItems(List mediaItems, boolean resetPosition) { player.setMediaItems(mediaItems, resetPosition); } + /** Calls {@link Player#setMediaItems(List, int, long)} on the delegate. */ @Override public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { player.setMediaItems(mediaItems, startIndex, startPositionMs); } + /** Calls {@link Player#setMediaItem(MediaItem)} on the delegate. */ @Override public void setMediaItem(MediaItem mediaItem) { player.setMediaItem(mediaItem); } + /** Calls {@link Player#setMediaItem(MediaItem, long)} on the delegate. */ @Override public void setMediaItem(MediaItem mediaItem, long startPositionMs) { player.setMediaItem(mediaItem, startPositionMs); } + /** Calls {@link Player#setMediaItem(MediaItem, boolean)} on the delegate. */ @Override public void setMediaItem(MediaItem mediaItem, boolean resetPosition) { player.setMediaItem(mediaItem, resetPosition); } + /** Calls {@link Player#addMediaItem(MediaItem)} on the delegate. */ @Override public void addMediaItem(MediaItem mediaItem) { player.addMediaItem(mediaItem); } + /** Calls {@link Player#addMediaItem(int, MediaItem)} on the delegate. */ @Override public void addMediaItem(int index, MediaItem mediaItem) { player.addMediaItem(index, mediaItem); } + /** Calls {@link Player#addMediaItems(List)} on the delegate. */ @Override public void addMediaItems(List mediaItems) { player.addMediaItems(mediaItems); } + /** Calls {@link Player#addMediaItems(int, List)} on the delegate. */ @Override public void addMediaItems(int index, List mediaItems) { player.addMediaItems(index, mediaItems); } + /** Calls {@link Player#moveMediaItem(int, int)} on the delegate. */ @Override public void moveMediaItem(int currentIndex, int newIndex) { player.moveMediaItem(currentIndex, newIndex); } + /** Calls {@link Player#moveMediaItems(int, int, int)} on the delegate. */ @Override public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { player.moveMediaItems(fromIndex, toIndex, newIndex); } + /** Calls {@link Player#removeMediaItem(int)} on the delegate. */ @Override public void removeMediaItem(int index) { player.removeMediaItem(index); } + /** Calls {@link Player#removeMediaItems(int, int)} on the delegate. */ @Override public void removeMediaItems(int fromIndex, int toIndex) { player.removeMediaItems(fromIndex, toIndex); } + /** Calls {@link Player#clearMediaItems()} on the delegate. */ @Override public void clearMediaItems() { player.clearMediaItems(); } + /** Calls {@link Player#isCommandAvailable(int)} on the delegate and returns the result. */ @Override public boolean isCommandAvailable(@Command int command) { return player.isCommandAvailable(command); } + /** Calls {@link Player#canAdvertiseSession()} on the delegate and returns the result. */ @Override public boolean canAdvertiseSession() { return player.canAdvertiseSession(); } + /** Calls {@link Player#getAvailableCommands()} on the delegate and returns the result. */ @Override public Commands getAvailableCommands() { return player.getAvailableCommands(); } + /** Calls {@link Player#prepare()} on the delegate. */ @Override public void prepare() { player.prepare(); } + /** Calls {@link Player#getPlaybackState()} on the delegate and returns the result. */ @Override public int getPlaybackState() { return player.getPlaybackState(); } + /** Calls {@link Player#getPlaybackSuppressionReason()} on the delegate and returns the result. */ @Override public int getPlaybackSuppressionReason() { return player.getPlaybackSuppressionReason(); } + /** Calls {@link Player#isPlaying()} on the delegate and returns the result. */ @Override public boolean isPlaying() { return player.isPlaying(); } + /** Calls {@link Player#getPlayerError()} on the delegate and returns the result. */ @Nullable @Override public PlaybackException getPlayerError() { return player.getPlayerError(); } + /** Calls {@link Player#play()} on the delegate. */ @Override public void play() { player.play(); } + /** Calls {@link Player#pause()} on the delegate. */ @Override public void pause() { player.pause(); } + /** Calls {@link Player#setPlayWhenReady(boolean)} on the delegate. */ @Override public void setPlayWhenReady(boolean playWhenReady) { player.setPlayWhenReady(playWhenReady); } + /** Calls {@link Player#getPlayWhenReady()} on the delegate and returns the result. */ @Override public boolean getPlayWhenReady() { return player.getPlayWhenReady(); } + /** Calls {@link Player#setRepeatMode(int)} on the delegate. */ @Override public void setRepeatMode(@RepeatMode int repeatMode) { player.setRepeatMode(repeatMode); } + /** Calls {@link Player#getRepeatMode()} on the delegate and returns the result. */ @Override public int getRepeatMode() { return player.getRepeatMode(); } + /** Calls {@link Player#setShuffleModeEnabled(boolean)} on the delegate. */ @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { player.setShuffleModeEnabled(shuffleModeEnabled); } + /** Calls {@link Player#getShuffleModeEnabled()} on the delegate and returns the result. */ @Override public boolean getShuffleModeEnabled() { return player.getShuffleModeEnabled(); } + /** Calls {@link Player#isLoading()} on the delegate and returns the result. */ @Override public boolean isLoading() { return player.isLoading(); } + /** Calls {@link Player#seekToDefaultPosition()} on the delegate. */ @Override public void seekToDefaultPosition() { player.seekToDefaultPosition(); } + /** Calls {@link Player#seekToDefaultPosition(int)} on the delegate. */ @Override public void seekToDefaultPosition(int mediaItemIndex) { player.seekToDefaultPosition(mediaItemIndex); } + /** Calls {@link Player#seekTo(long)} on the delegate. */ @Override public void seekTo(long positionMs) { player.seekTo(positionMs); } + /** Calls {@link Player#seekTo(int, long)} on the delegate. */ @Override public void seekTo(int mediaItemIndex, long positionMs) { player.seekTo(mediaItemIndex, positionMs); } + /** Calls {@link Player#getSeekBackIncrement()} on the delegate and returns the result. */ @Override public long getSeekBackIncrement() { return player.getSeekBackIncrement(); } + /** Calls {@link Player#seekBack()} on the delegate. */ @Override public void seekBack() { player.seekBack(); } + /** Calls {@link Player#getSeekForwardIncrement()} on the delegate and returns the result. */ @Override public long getSeekForwardIncrement() { return player.getSeekForwardIncrement(); } + /** Calls {@link Player#seekForward()} on the delegate. */ @Override public void seekForward() { player.seekForward(); } + /** Calls {@link Player#hasPrevious()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -262,6 +306,7 @@ public class ForwardingPlayer implements Player { return player.hasPrevious(); } + /** Calls {@link Player#hasPreviousWindow()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -269,11 +314,13 @@ public class ForwardingPlayer implements Player { return player.hasPreviousWindow(); } + /** Calls {@link Player#hasPreviousMediaItem()} on the delegate and returns the result. */ @Override public boolean hasPreviousMediaItem() { return player.hasPreviousMediaItem(); } + /** Calls {@link Player#previous()} on the delegate. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -281,6 +328,7 @@ public class ForwardingPlayer implements Player { player.previous(); } + /** Calls {@link Player#seekToPreviousWindow()} on the delegate. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -288,21 +336,25 @@ public class ForwardingPlayer implements Player { player.seekToPreviousWindow(); } + /** Calls {@link Player#seekToPreviousMediaItem()} on the delegate. */ @Override public void seekToPreviousMediaItem() { player.seekToPreviousMediaItem(); } + /** Calls {@link Player#seekToPrevious()} on the delegate. */ @Override public void seekToPrevious() { player.seekToPrevious(); } + /** Calls {@link Player#getMaxSeekToPreviousPosition()} on the delegate and returns the result. */ @Override public long getMaxSeekToPreviousPosition() { return player.getMaxSeekToPreviousPosition(); } + /** Calls {@link Player#hasNext()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -310,6 +362,7 @@ public class ForwardingPlayer implements Player { return player.hasNext(); } + /** Calls {@link Player#hasNextWindow()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -317,11 +370,13 @@ public class ForwardingPlayer implements Player { return player.hasNextWindow(); } + /** Calls {@link Player#hasNextMediaItem()} on the delegate and returns the result. */ @Override public boolean hasNextMediaItem() { return player.hasNextMediaItem(); } + /** Calls {@link Player#next()} on the delegate. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -329,6 +384,7 @@ public class ForwardingPlayer implements Player { player.next(); } + /** Calls {@link Player#seekToNextWindow()} on the delegate. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -336,36 +392,43 @@ public class ForwardingPlayer implements Player { player.seekToNextWindow(); } + /** Calls {@link Player#seekToNextMediaItem()} on the delegate. */ @Override public void seekToNextMediaItem() { player.seekToNextMediaItem(); } + /** Calls {@link Player#seekToNext()} on the delegate. */ @Override public void seekToNext() { player.seekToNext(); } + /** Calls {@link Player#setPlaybackParameters(PlaybackParameters)} on the delegate. */ @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { player.setPlaybackParameters(playbackParameters); } + /** Calls {@link Player#setPlaybackSpeed(float)} on the delegate. */ @Override public void setPlaybackSpeed(float speed) { player.setPlaybackSpeed(speed); } + /** Calls {@link Player#getPlaybackParameters()} on the delegate and returns the result. */ @Override public PlaybackParameters getPlaybackParameters() { return player.getPlaybackParameters(); } + /** Calls {@link Player#stop()} on the delegate. */ @Override public void stop() { player.stop(); } + /** Calls {@link Player#stop(boolean)} on the delegate. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -373,11 +436,13 @@ public class ForwardingPlayer implements Player { player.stop(reset); } + /** Calls {@link Player#release()} on the delegate. */ @Override public void release() { player.release(); } + /** Calls {@link Player#getCurrentTrackGroups()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -385,6 +450,7 @@ public class ForwardingPlayer implements Player { return player.getCurrentTrackGroups(); } + /** Calls {@link Player#getCurrentTrackSelections()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -392,52 +458,62 @@ public class ForwardingPlayer implements Player { return player.getCurrentTrackSelections(); } + /** Calls {@link Player#getCurrentTracksInfo()} on the delegate and returns the result. */ @Override public TracksInfo getCurrentTracksInfo() { return player.getCurrentTracksInfo(); } + /** Calls {@link Player#getTrackSelectionParameters()} on the delegate and returns the result. */ @Override public TrackSelectionParameters getTrackSelectionParameters() { return player.getTrackSelectionParameters(); } + /** Calls {@link Player#setTrackSelectionParameters(TrackSelectionParameters)} on the delegate. */ @Override public void setTrackSelectionParameters(TrackSelectionParameters parameters) { player.setTrackSelectionParameters(parameters); } + /** Calls {@link Player#getMediaMetadata()} on the delegate and returns the result. */ @Override public MediaMetadata getMediaMetadata() { return player.getMediaMetadata(); } + /** Calls {@link Player#getPlaylistMetadata()} on the delegate and returns the result. */ @Override public MediaMetadata getPlaylistMetadata() { return player.getPlaylistMetadata(); } + /** Calls {@link Player#setPlaylistMetadata(MediaMetadata)} on the delegate. */ @Override public void setPlaylistMetadata(MediaMetadata mediaMetadata) { player.setPlaylistMetadata(mediaMetadata); } + /** Calls {@link Player#getCurrentManifest()} on the delegate and returns the result. */ @Nullable @Override public Object getCurrentManifest() { return player.getCurrentManifest(); } + /** Calls {@link Player#getCurrentTimeline()} on the delegate and returns the result. */ @Override public Timeline getCurrentTimeline() { return player.getCurrentTimeline(); } + /** Calls {@link Player#getCurrentPeriodIndex()} on the delegate and returns the result. */ @Override public int getCurrentPeriodIndex() { return player.getCurrentPeriodIndex(); } + /** Calls {@link Player#getCurrentWindowIndex()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -445,11 +521,13 @@ public class ForwardingPlayer implements Player { return player.getCurrentWindowIndex(); } + /** Calls {@link Player#getCurrentMediaItemIndex()} on the delegate and returns the result. */ @Override public int getCurrentMediaItemIndex() { return player.getCurrentMediaItemIndex(); } + /** Calls {@link Player#getNextWindowIndex()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -457,11 +535,13 @@ public class ForwardingPlayer implements Player { return player.getNextWindowIndex(); } + /** Calls {@link Player#getNextMediaItemIndex()} on the delegate and returns the result. */ @Override public int getNextMediaItemIndex() { return player.getNextMediaItemIndex(); } + /** Calls {@link Player#getPreviousWindowIndex()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -469,52 +549,62 @@ public class ForwardingPlayer implements Player { return player.getPreviousWindowIndex(); } + /** Calls {@link Player#getPreviousMediaItemIndex()} on the delegate and returns the result. */ @Override public int getPreviousMediaItemIndex() { return player.getPreviousMediaItemIndex(); } + /** Calls {@link Player#getCurrentMediaItem()} on the delegate and returns the result. */ @Nullable @Override public MediaItem getCurrentMediaItem() { return player.getCurrentMediaItem(); } + /** Calls {@link Player#getMediaItemCount()} on the delegate and returns the result. */ @Override public int getMediaItemCount() { return player.getMediaItemCount(); } + /** Calls {@link Player#getMediaItemAt(int)} on the delegate and returns the result. */ @Override public MediaItem getMediaItemAt(int index) { return player.getMediaItemAt(index); } + /** Calls {@link Player#getDuration()} on the delegate and returns the result. */ @Override public long getDuration() { return player.getDuration(); } + /** Calls {@link Player#getCurrentPosition()} on the delegate and returns the result. */ @Override public long getCurrentPosition() { return player.getCurrentPosition(); } + /** Calls {@link Player#getBufferedPosition()} on the delegate and returns the result. */ @Override public long getBufferedPosition() { return player.getBufferedPosition(); } + /** Calls {@link Player#getBufferedPercentage()} on the delegate and returns the result. */ @Override public int getBufferedPercentage() { return player.getBufferedPercentage(); } + /** Calls {@link Player#getTotalBufferedDuration()} on the delegate and returns the result. */ @Override public long getTotalBufferedDuration() { return player.getTotalBufferedDuration(); } + /** Calls {@link Player#isCurrentWindowDynamic()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -522,11 +612,13 @@ public class ForwardingPlayer implements Player { return player.isCurrentWindowDynamic(); } + /** Calls {@link Player#isCurrentMediaItemDynamic()} on the delegate and returns the result. */ @Override public boolean isCurrentMediaItemDynamic() { return player.isCurrentMediaItemDynamic(); } + /** Calls {@link Player#isCurrentWindowLive()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -534,16 +626,19 @@ public class ForwardingPlayer implements Player { return player.isCurrentWindowLive(); } + /** Calls {@link Player#isCurrentMediaItemLive()} on the delegate and returns the result. */ @Override public boolean isCurrentMediaItemLive() { return player.isCurrentMediaItemLive(); } + /** Calls {@link Player#getCurrentLiveOffset()} on the delegate and returns the result. */ @Override public long getCurrentLiveOffset() { return player.getCurrentLiveOffset(); } + /** Calls {@link Player#isCurrentWindowSeekable()} on the delegate and returns the result. */ @SuppressWarnings("deprecation") // Forwarding to deprecated method @Deprecated @Override @@ -551,141 +646,169 @@ public class ForwardingPlayer implements Player { return player.isCurrentWindowSeekable(); } + /** Calls {@link Player#isCurrentMediaItemSeekable()} on the delegate and returns the result. */ @Override public boolean isCurrentMediaItemSeekable() { return player.isCurrentMediaItemSeekable(); } + /** Calls {@link Player#isPlayingAd()} on the delegate and returns the result. */ @Override public boolean isPlayingAd() { return player.isPlayingAd(); } + /** Calls {@link Player#getCurrentAdGroupIndex()} on the delegate and returns the result. */ @Override public int getCurrentAdGroupIndex() { return player.getCurrentAdGroupIndex(); } + /** Calls {@link Player#getCurrentAdIndexInAdGroup()} on the delegate and returns the result. */ @Override public int getCurrentAdIndexInAdGroup() { return player.getCurrentAdIndexInAdGroup(); } + /** Calls {@link Player#getContentDuration()} on the delegate and returns the result. */ @Override public long getContentDuration() { return player.getContentDuration(); } + /** Calls {@link Player#getContentPosition()} on the delegate and returns the result. */ @Override public long getContentPosition() { return player.getContentPosition(); } + /** Calls {@link Player#getContentBufferedPosition()} on the delegate and returns the result. */ @Override public long getContentBufferedPosition() { return player.getContentBufferedPosition(); } + /** Calls {@link Player#getAudioAttributes()} on the delegate and returns the result. */ @Override public AudioAttributes getAudioAttributes() { return player.getAudioAttributes(); } + /** Calls {@link Player#setVolume(float)} on the delegate. */ @Override public void setVolume(float volume) { player.setVolume(volume); } + /** Calls {@link Player#getVolume()} on the delegate and returns the result. */ @Override public float getVolume() { return player.getVolume(); } + /** Calls {@link Player#getVideoSize()} on the delegate and returns the result. */ @Override public VideoSize getVideoSize() { return player.getVideoSize(); } + /** Calls {@link Player#clearVideoSurface()} on the delegate. */ @Override public void clearVideoSurface() { player.clearVideoSurface(); } + /** Calls {@link Player#clearVideoSurface(Surface)} on the delegate. */ @Override public void clearVideoSurface(@Nullable Surface surface) { player.clearVideoSurface(surface); } + /** Calls {@link Player#setVideoSurface(Surface)} on the delegate. */ @Override public void setVideoSurface(@Nullable Surface surface) { player.setVideoSurface(surface); } + /** Calls {@link Player#setVideoSurfaceHolder(SurfaceHolder)} on the delegate. */ @Override public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { player.setVideoSurfaceHolder(surfaceHolder); } + /** Calls {@link Player#clearVideoSurfaceHolder(SurfaceHolder)} on the delegate. */ @Override public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { player.clearVideoSurfaceHolder(surfaceHolder); } + /** Calls {@link Player#setVideoSurfaceView(SurfaceView)} on the delegate. */ @Override public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { player.setVideoSurfaceView(surfaceView); } + /** Calls {@link Player#clearVideoSurfaceView(SurfaceView)} on the delegate. */ @Override public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { player.clearVideoSurfaceView(surfaceView); } + /** Calls {@link Player#setVideoTextureView(TextureView)} on the delegate. */ @Override public void setVideoTextureView(@Nullable TextureView textureView) { player.setVideoTextureView(textureView); } + /** Calls {@link Player#clearVideoTextureView(TextureView)} on the delegate. */ @Override public void clearVideoTextureView(@Nullable TextureView textureView) { player.clearVideoTextureView(textureView); } + /** Calls {@link Player#getCurrentCues()} on the delegate and returns the result. */ @Override public List getCurrentCues() { return player.getCurrentCues(); } + /** Calls {@link Player#getDeviceInfo()} on the delegate and returns the result. */ @Override public DeviceInfo getDeviceInfo() { return player.getDeviceInfo(); } + /** Calls {@link Player#getDeviceVolume()} on the delegate and returns the result. */ @Override public int getDeviceVolume() { return player.getDeviceVolume(); } + /** Calls {@link Player#isDeviceMuted()} on the delegate and returns the result. */ @Override public boolean isDeviceMuted() { return player.isDeviceMuted(); } + /** Calls {@link Player#setDeviceVolume(int)} on the delegate. */ @Override public void setDeviceVolume(int volume) { player.setDeviceVolume(volume); } + /** Calls {@link Player#increaseDeviceVolume()} on the delegate. */ @Override public void increaseDeviceVolume() { player.increaseDeviceVolume(); } + /** Calls {@link Player#decreaseDeviceVolume()} on the delegate. */ @Override public void decreaseDeviceVolume() { player.decreaseDeviceVolume(); } + /** Calls {@link Player#setDeviceMuted(boolean)} on the delegate. */ @Override public void setDeviceMuted(boolean muted) { player.setDeviceMuted(muted); From f6baffc490793fab5d9739e15aa13b71b4863d33 Mon Sep 17 00:00:00 2001 From: claincly Date: Fri, 4 Feb 2022 11:50:06 +0000 Subject: [PATCH 136/251] Roll forward of https://github.com/androidx/media/commit/2ed1deb52d2632c9d7f620be7d1d4172fa55d561. Reason for not rolling back the rollback https://github.com/androidx/media/commit/d68b790077efb33749a98750ce8ffed91d4f44ed: file name changed and file content moved, the automated tool is unable to correctly apply the change. Apply suggested AVC profile depending on the API version. Use `AVCProfileHigh` only when there's encoder support. PiperOrigin-RevId: 426363780 --- .../transformer/DefaultEncoderFactory.java | 45 +++++++++++++++++-- .../media3/transformer/EncoderUtil.java | 19 +++++++- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 7af2582486..3c2312a6d8 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -31,6 +31,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil; import com.google.common.collect.ImmutableList; import java.util.ArrayList; @@ -119,29 +120,65 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { /* mediaCodecName= */ null); } + MediaCodecInfo encoderInfo = encoderAndClosestFormatSupport.first; format = encoderAndClosestFormatSupport.second; - MediaFormat mediaFormat = - MediaFormat.createVideoFormat( - checkNotNull(format.sampleMimeType), format.width, format.height); + String mimeType = checkNotNull(format.sampleMimeType); + MediaFormat mediaFormat = MediaFormat.createVideoFormat(mimeType, format.width, format.height); mediaFormat.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.averageBitrate); @Nullable Pair codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format); if (codecProfileAndLevel != null) { + // The codecProfileAndLevel is supported by the encoder. mediaFormat.setInteger(MediaFormat.KEY_PROFILE, codecProfileAndLevel.first); if (SDK_INT >= 23) { mediaFormat.setInteger(MediaFormat.KEY_LEVEL, codecProfileAndLevel.second); } } + // TODO(b/210593256): Remove overriding profile/level (before API 29) after switching to in-app + // muxing. + if (mimeType.equals(MimeTypes.VIDEO_H264)) { + // Applying suggested profile/level settings from + // https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles + if (Util.SDK_INT >= 29) { + if (EncoderUtil.isProfileLevelSupported( + encoderInfo, + mimeType, + MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, + EncoderUtil.LEVEL_UNSET)) { + // Use the highest supported profile and use B-frames. + mediaFormat.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 1); + } + } else if (Util.SDK_INT >= 26) { + if (EncoderUtil.isProfileLevelSupported( + encoderInfo, + mimeType, + MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, + EncoderUtil.LEVEL_UNSET)) { + // Use the highest-supported profile, but disable the generation of B-frames. This + // accommodates some limitations in the MediaMuxer in these system versions. + mediaFormat.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + mediaFormat.setInteger(MediaFormat.KEY_LATENCY, 1); + } + } else { + // Use the baseline profile for safest results. + mediaFormat.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); + } + } + mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, DEFAULT_COLOR_FORMAT); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, DEFAULT_I_FRAME_INTERVAL_SECS); return createCodec( format, mediaFormat, - encoderAndClosestFormatSupport.first.getName(), + encoderInfo.getName(), /* isVideo= */ true, /* isDecoder= */ false, /* outputSurface= */ null); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java index d4257549d1..a95d29ffae 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java @@ -25,6 +25,7 @@ import android.util.Pair; import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; @@ -37,6 +38,9 @@ import java.util.List; @UnstableApi public final class EncoderUtil { + /** A value to indicate the encoding level is not set. */ + public static final int LEVEL_UNSET = Format.NO_VALUE; + private static final List encoders = new ArrayList<>(); /** @@ -108,14 +112,25 @@ public final class EncoderUtil { : null; } - /** Returns whether the {@link MediaCodecInfo encoder} supports the given profile and level. */ + /** + * Checks whether the {@link MediaCodecInfo encoder} supports the given profile and level. + * + * @param encoderInfo The {@link MediaCodecInfo encoderInfo}. + * @param mimeType The {@link MimeTypes MIME type}. + * @param profile The encoding profile. + * @param level The encoding level, specify {@link #LEVEL_UNSET} if checking whether the encoder + * supports a specific profile. + * @return Whether the profile and level (if set) is supported by the encoder. + */ public static boolean isProfileLevelSupported( MediaCodecInfo encoderInfo, String mimeType, int profile, int level) { + // TODO(b/214964116): Merge into MediaCodecUtil. MediaCodecInfo.CodecProfileLevel[] profileLevels = encoderInfo.getCapabilitiesForType(mimeType).profileLevels; for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) { - if (profileLevel.profile == profile && profileLevel.level == level) { + if (profileLevel.profile == profile + && (level == LEVEL_UNSET || profileLevel.level == level)) { return true; } } From 8149ac8922eb93c2dec2b3b108997d58a89b7ca2 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 12:41:28 +0000 Subject: [PATCH 137/251] Mark all non-public IntDefs as only TYPE_USE This only changes IntDefs that cannot be used by apps because they're either private or package-private. A follow-up change will fix the positions of existing usages to match this new config. #minor-release PiperOrigin-RevId: 426372273 --- .../java/androidx/media3/common/AdPlaybackState.java | 4 ++++ .../java/androidx/media3/common/AudioAttributes.java | 4 ++++ .../src/main/java/androidx/media3/common/ColorInfo.java | 4 ++++ .../src/main/java/androidx/media3/common/DeviceInfo.java | 1 + .../src/main/java/androidx/media3/common/Format.java | 4 ++++ .../main/java/androidx/media3/common/HeartRating.java | 3 +++ .../src/main/java/androidx/media3/common/MediaItem.java | 5 +++++ .../main/java/androidx/media3/common/MediaMetadata.java | 1 + .../java/androidx/media3/common/PercentageRating.java | 3 +++ .../java/androidx/media3/common/PlaybackParameters.java | 4 ++++ .../src/main/java/androidx/media3/common/Player.java | 2 ++ .../src/main/java/androidx/media3/common/Rating.java | 5 +++++ .../src/main/java/androidx/media3/common/StarRating.java | 3 +++ .../main/java/androidx/media3/common/ThumbRating.java | 3 +++ .../src/main/java/androidx/media3/common/Timeline.java | 5 +++++ .../src/main/java/androidx/media3/common/TrackGroup.java | 3 +++ .../java/androidx/media3/common/TrackGroupArray.java | 4 ++++ .../androidx/media3/common/TrackSelectionOverrides.java | 4 ++++ .../androidx/media3/common/TrackSelectionParameters.java | 3 +++ .../src/main/java/androidx/media3/common/TracksInfo.java | 4 ++++ .../src/main/java/androidx/media3/common/VideoSize.java | 4 ++++ .../src/main/java/androidx/media3/common/text/Cue.java | 1 + .../main/java/androidx/media3/database/VersionTable.java | 4 ++++ .../androidx/media3/exoplayer/AudioFocusManager.java | 3 +++ .../media3/exoplayer/audio/AudioTimestampPoller.java | 4 ++++ .../exoplayer/audio/AudioTrackPositionTracker.java | 3 +++ .../media3/exoplayer/audio/DecoderAudioRenderer.java | 2 ++ .../exoplayer/audio/SilenceSkippingAudioProcessor.java | 3 +++ .../mediacodec/AsynchronousMediaCodecAdapter.java | 4 ++++ .../mediacodec/DefaultMediaCodecAdapterFactory.java | 6 ++++++ .../media3/exoplayer/mediacodec/MediaCodecRenderer.java | 6 ++++++ .../media3/exoplayer/text/ExoplayerCuesDecoder.java | 5 +++++ .../androidx/media3/exoplayer/text/TextRenderer.java | 3 +++ .../exoplayer/trackselection/DefaultTrackSelector.java | 4 ++++ .../java/androidx/media3/exoplayer/upstream/Loader.java | 3 +++ .../media3/exoplayer/video/DecoderVideoRenderer.java | 9 ++++++--- .../media3/exoplayer/video/spherical/Projection.java | 4 ++++ .../androidx/media3/exoplayer/dash/DashMediaPeriod.java | 3 +++ .../androidx/media3/exoplayer/hls/HlsChunkSource.java | 5 +++++ .../java/androidx/media3/exoplayer/ima/AdTagLoader.java | 3 +++ .../media3/exoplayer/rtsp/RtspAuthenticationInfo.java | 6 ++++++ .../java/androidx/media3/exoplayer/rtsp/RtspClient.java | 3 +++ .../media3/exoplayer/rtsp/RtspMessageChannel.java | 3 +++ .../java/androidx/media3/exoplayer/rtsp/RtspRequest.java | 4 ++++ .../androidx/media3/extractor/flac/FlacExtractor.java | 3 +++ .../java/androidx/media3/extractor/flv/FlvExtractor.java | 3 +++ .../androidx/media3/extractor/jpeg/JpegExtractor.java | 3 +++ .../androidx/media3/extractor/mkv/DefaultEbmlReader.java | 4 ++++ .../java/androidx/media3/extractor/mp4/Mp4Extractor.java | 4 ++++ .../java/androidx/media3/extractor/mp4/SefReader.java | 4 ++++ .../media3/extractor/text/SubtitleExtractor.java | 5 +++++ .../androidx/media3/extractor/text/ssa/SsaStyle.java | 3 +++ .../media3/extractor/text/ttml/TextEmphasis.java | 4 ++++ .../androidx/media3/extractor/text/ttml/TtmlStyle.java | 7 +++++++ .../media3/extractor/text/webvtt/WebvttCssStyle.java | 4 ++++ .../media3/extractor/text/webvtt/WebvttCueParser.java | 3 +++ .../java/androidx/media3/extractor/ts/Ac3Reader.java | 3 +++ .../java/androidx/media3/extractor/ts/Ac4Reader.java | 3 +++ .../java/androidx/media3/extractor/ts/H263Reader.java | 5 +++++ .../main/java/androidx/media3/session/CommandButton.java | 3 +++ .../java/androidx/media3/session/ConnectionRequest.java | 3 +++ .../java/androidx/media3/session/ConnectionState.java | 3 +++ .../main/java/androidx/media3/session/LibraryResult.java | 1 + .../androidx/media3/session/MediaLibraryService.java | 3 +++ .../main/java/androidx/media3/session/PlayerInfo.java | 3 +++ .../java/androidx/media3/session/SessionCommand.java | 1 + .../java/androidx/media3/session/SessionCommands.java | 3 +++ .../androidx/media3/session/SessionPositionInfo.java | 4 ++++ .../main/java/androidx/media3/session/SessionResult.java | 1 + .../main/java/androidx/media3/session/SessionToken.java | 2 ++ .../androidx/media3/session/SessionTokenImplBase.java | 3 +++ .../androidx/media3/session/SessionTokenImplLegacy.java | 3 +++ .../java/androidx/media3/test/utils/DumpFileAsserts.java | 3 +++ .../androidx/media3/test/utils/WebServerDispatcher.java | 3 +++ 74 files changed, 258 insertions(+), 3 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index cc6557646b..22ca06106b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -18,6 +18,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.Bundle; @@ -30,6 +31,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import org.checkerframework.checker.nullness.compatqual.NullableType; @@ -343,6 +345,7 @@ public final class AdPlaybackState implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TIME_US, FIELD_COUNT, @@ -913,6 +916,7 @@ public final class AdPlaybackState implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_AD_GROUPS, FIELD_AD_RESUME_POSITION_US, diff --git a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java index ac8049f1f4..82ba84f652 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java +++ b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.DoNotInline; import androidx.annotation.IntDef; @@ -25,6 +27,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; /** @@ -180,6 +183,7 @@ public final class AudioAttributes implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_CONTENT_TYPE, FIELD_FLAGS, diff --git a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java index 4a5770756b..fb27d2d18c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -22,6 +24,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import org.checkerframework.dataflow.qual.Pure; @@ -163,6 +166,7 @@ public final class ColorInfo implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_COLOR_SPACE, FIELD_COLOR_RANGE, diff --git a/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java b/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java index 0251443f1e..2daeb92ef2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/DeviceInfo.java @@ -89,6 +89,7 @@ public final class DeviceInfo implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_PLAYBACK_TYPE, FIELD_MIN_VOLUME, FIELD_MAX_VOLUME}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index bb6cfc86f7..dd6460c9cf 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -25,6 +27,7 @@ import com.google.common.base.Joiner; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1417,6 +1420,7 @@ public final class Format implements Bundleable { // Bundleable implementation. @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_ID, FIELD_LABEL, diff --git a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java index 6f8b45e44a..4a7117764c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.IntDef; @@ -25,6 +26,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * A rating expressed as "heart" or "no heart". It can be used to indicate whether the content is a @@ -81,6 +83,7 @@ public final class HeartRating extends Rating { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_HEART}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java index 106bbeb959..29e85b22b1 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java @@ -17,6 +17,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.Bundle; @@ -31,6 +32,7 @@ import com.google.common.collect.ImmutableMap; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1210,6 +1212,7 @@ public final class MediaItem implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TARGET_OFFSET_MS, FIELD_MIN_OFFSET_MS, @@ -1625,6 +1628,7 @@ public final class MediaItem implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_START_POSITION_MS, FIELD_END_POSITION_MS, @@ -1773,6 +1777,7 @@ public final class MediaItem implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_MEDIA_ID, FIELD_LIVE_CONFIGURATION, diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index eec5d3868a..7a847c24e2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -833,6 +833,7 @@ public final class MediaMetadata implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TITLE, FIELD_ARTIST, diff --git a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java index e14012f877..f110efba16 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.FloatRange; @@ -26,6 +27,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A rating expressed as a percentage. */ public final class PercentageRating extends Rating { @@ -79,6 +81,7 @@ public final class PercentageRating extends Rating { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RATING_TYPE, FIELD_PERCENT}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/PlaybackParameters.java b/libraries/common/src/main/java/androidx/media3/common/PlaybackParameters.java index 4ad574aa54..84881a55ce 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PlaybackParameters.java +++ b/libraries/common/src/main/java/androidx/media3/common/PlaybackParameters.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.CheckResult; import androidx.annotation.FloatRange; @@ -26,6 +28,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Parameters that apply to playback, including speed setting. */ public final class PlaybackParameters implements Bundleable { @@ -121,6 +124,7 @@ public final class PlaybackParameters implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_SPEED, FIELD_PITCH}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index c23647633c..aed681f729 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -598,6 +598,7 @@ public interface Player { // Bundleable implementation. @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_MEDIA_ITEM_INDEX, FIELD_MEDIA_ITEM, @@ -895,6 +896,7 @@ public interface Player { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_COMMANDS}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/Rating.java b/libraries/common/src/main/java/androidx/media3/common/Rating.java index 1ff404aa35..e0c8aa735e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Rating.java +++ b/libraries/common/src/main/java/androidx/media3/common/Rating.java @@ -15,12 +15,15 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * A rating for media content. The style of a rating can be one of {@link HeartRating}, {@link @@ -41,6 +44,7 @@ public abstract class Rating implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ RATING_TYPE_UNSET, RATING_TYPE_HEART, @@ -58,6 +62,7 @@ public abstract class Rating implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RATING_TYPE}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/StarRating.java b/libraries/common/src/main/java/androidx/media3/common/StarRating.java index 8ee113fc1f..c6547dde33 100644 --- a/libraries/common/src/main/java/androidx/media3/common/StarRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/StarRating.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.FloatRange; @@ -27,6 +28,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A rating expressed as a fractional number of stars. */ public final class StarRating extends Rating { @@ -106,6 +108,7 @@ public final class StarRating extends Rating { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RATING_TYPE, FIELD_MAX_STARS, FIELD_STAR_RATING}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java index b73d3c4f83..3c68986b98 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.IntDef; @@ -25,6 +26,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A rating expressed as "thumbs up" or "thumbs down". */ public final class ThumbRating extends Rating { @@ -78,6 +80,7 @@ public final class ThumbRating extends Rating { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_THUMBS_UP}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/Timeline.java b/libraries/common/src/main/java/androidx/media3/common/Timeline.java index 09e2dd0a9d..498eed4870 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Timeline.java +++ b/libraries/common/src/main/java/androidx/media3/common/Timeline.java @@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.Bundle; @@ -37,6 +38,7 @@ import com.google.errorprone.annotations.InlineMe; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; @@ -414,6 +416,7 @@ public abstract class Timeline implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_MEDIA_ITEM, FIELD_PRESENTATION_START_TIME_MS, @@ -903,6 +906,7 @@ public abstract class Timeline implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_WINDOW_INDEX, FIELD_DURATION_US, @@ -1362,6 +1366,7 @@ public abstract class Timeline implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_WINDOWS, FIELD_PERIODS, diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java index b393e43d3d..f0c85adfb8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.CheckResult; @@ -29,6 +30,7 @@ import com.google.common.collect.Lists; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; @@ -139,6 +141,7 @@ public final class TrackGroup implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_FORMATS, FIELD_ID}) private @interface FieldNumber {} diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroupArray.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroupArray.java index a9dc8242ad..671d9848fa 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroupArray.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroupArray.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -25,6 +27,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; /** An immutable array of {@link TrackGroup}s. */ @@ -105,6 +108,7 @@ public final class TrackGroupArray implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TRACK_GROUPS, }) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java index 5c6b54a7b0..16fffadb49 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java @@ -18,6 +18,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList; import static androidx.media3.common.util.BundleableUtil.toBundleArrayList; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.util.Collections.max; import static java.util.Collections.min; @@ -31,6 +32,7 @@ import com.google.common.primitives.Ints; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -181,6 +183,7 @@ public final class TrackSelectionOverrides implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TRACK_GROUP, FIELD_TRACKS, @@ -269,6 +272,7 @@ public final class TrackSelectionOverrides implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_OVERRIDES, }) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java index 4f7f6b8e8d..91ac8c3a9d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java @@ -18,6 +18,7 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.BundleableUtil.fromNullableBundle; import static com.google.common.base.MoreObjects.firstNonNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.graphics.Point; @@ -35,6 +36,7 @@ import com.google.common.primitives.Ints; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Locale; import java.util.Set; import org.checkerframework.checker.initialization.qual.UnknownInitialization; @@ -984,6 +986,7 @@ public class TrackSelectionParameters implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_PREFERRED_AUDIO_LANGUAGES, FIELD_PREFERRED_AUDIO_ROLE_FLAGS, diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 577ceb116d..066a92152e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList; import static androidx.media3.common.util.BundleableUtil.fromNullableBundle; import static androidx.media3.common.util.BundleableUtil.toBundleArrayList; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.IntDef; @@ -31,6 +32,7 @@ import com.google.common.primitives.Booleans; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; @@ -195,6 +197,7 @@ public final class TracksInfo implements Bundleable { // Bundleable implementation. @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TRACK_GROUP, FIELD_TRACK_SUPPORT, @@ -330,6 +333,7 @@ public final class TracksInfo implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TRACK_GROUP_INFOS, }) diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoSize.java b/libraries/common/src/main/java/androidx/media3/common/VideoSize.java index 15bb48ba0b..9c25c257ae 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoSize.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoSize.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.FloatRange; import androidx.annotation.IntDef; @@ -24,6 +26,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Represents the video size. */ public final class VideoSize implements Bundleable { @@ -131,6 +134,7 @@ public final class VideoSize implements Bundleable { // Bundleable implementation. @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_WIDTH, FIELD_HEIGHT, diff --git a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java index 6515a64125..d5f04a3540 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java @@ -968,6 +968,7 @@ public final class Cue implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_TEXT, FIELD_TEXT_ALIGNMENT, diff --git a/libraries/database/src/main/java/androidx/media3/database/VersionTable.java b/libraries/database/src/main/java/androidx/media3/database/VersionTable.java index 9802b9d5b9..74be07e9b7 100644 --- a/libraries/database/src/main/java/androidx/media3/database/VersionTable.java +++ b/libraries/database/src/main/java/androidx/media3/database/VersionTable.java @@ -15,6 +15,8 @@ */ package androidx.media3.database; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; @@ -26,6 +28,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Utility methods for accessing versions of media library database components. This allows them to @@ -75,6 +78,7 @@ public final class VersionTable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FEATURE_OFFLINE, FEATURE_CACHE_CONTENT_METADATA, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index 9e67e01811..580f18fc89 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.media.AudioFocusRequest; @@ -34,6 +35,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Manages requesting and responding to changes in audio focus. */ @@ -78,6 +80,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Audio focus state. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ AUDIO_FOCUS_STATE_NO_FOCUS, AUDIO_FOCUS_STATE_HAVE_FOCUS, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTimestampPoller.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTimestampPoller.java index 2aedbe1909..3bc69a4588 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTimestampPoller.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTimestampPoller.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.audio; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.annotation.TargetApi; import android.media.AudioTimestamp; import android.media.AudioTrack; @@ -26,6 +28,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Polls the {@link AudioTrack} timestamp, if the platform supports it, taking care of polling at @@ -49,6 +52,7 @@ import java.lang.annotation.RetentionPolicy; /** Timestamp polling states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_INITIALIZING, STATE_TIMESTAMP, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java index 6d0c680309..1101ac09bf 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java @@ -18,6 +18,7 @@ package androidx.media3.exoplayer.audio; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.media.AudioTimestamp; import android.media.AudioTrack; @@ -30,6 +31,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; /** @@ -111,6 +113,7 @@ import java.lang.reflect.Method; /** {@link AudioTrack} playback states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({PLAYSTATE_STOPPED, PLAYSTATE_PAUSED, PLAYSTATE_PLAYING}) private @interface PlayState {} /** @see AudioTrack#PLAYSTATE_STOPPED */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 83a37c55db..2f9ac4accf 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -21,6 +21,7 @@ import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO; import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT; import static com.google.common.base.MoreObjects.firstNonNull; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Handler; import android.os.SystemClock; @@ -97,6 +98,7 @@ public abstract class DecoderAudioRenderer< @Documented @Retention(RetentionPolicy.SOURCE) + @java.lang.annotation.Target(TYPE_USE) @IntDef({ REINITIALIZATION_STATE_NONE, REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java index f91b56a7a1..1390cc0aff 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.audio; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.media3.common.C; @@ -25,6 +26,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; /** @@ -53,6 +55,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor { /** Trimming states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_NOISY, STATE_MAYBE_SILENT, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index dd9b97d8dd..e4f465c46e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -16,6 +16,8 @@ package androidx.media3.exoplayer.mediacodec; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; @@ -36,6 +38,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; /** @@ -124,6 +127,7 @@ import java.nio.ByteBuffer; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STATE_CREATED, STATE_INITIALIZED, STATE_SHUT_DOWN}) private @interface State {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index 3fc07a0fd4..d77786a0e7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.mediacodec; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.MediaCodec; import androidx.annotation.IntDef; import androidx.media3.common.MimeTypes; @@ -22,8 +24,10 @@ import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import java.io.IOException; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * The default {@link MediaCodecAdapter.Factory}. @@ -37,7 +41,9 @@ import java.lang.annotation.RetentionPolicy; @UnstableApi public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.Factory { + @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({MODE_DEFAULT, MODE_ENABLED, MODE_DISABLED}) private @interface Mode {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index 6068e56264..be578bf02b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -29,6 +29,7 @@ import static androidx.media3.exoplayer.source.SampleStream.FLAG_OMIT_SAMPLE_DAT import static androidx.media3.exoplayer.source.SampleStream.FLAG_PEEK; import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.TargetApi; import android.media.MediaCodec; @@ -78,6 +79,7 @@ import androidx.media3.extractor.NalUnitUtil; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayDeque; @@ -212,6 +214,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ RECONFIGURATION_STATE_NONE, RECONFIGURATION_STATE_WRITE_PENDING, @@ -230,6 +233,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({DRAIN_STATE_NONE, DRAIN_STATE_SIGNAL_END_OF_STREAM, DRAIN_STATE_WAIT_END_OF_STREAM}) private @interface DrainState {} /** The codec is not being drained. */ @@ -241,6 +245,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ DRAIN_ACTION_NONE, DRAIN_ACTION_FLUSH, @@ -259,6 +264,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ ADAPTATION_WORKAROUND_MODE_NEVER, ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java index a47bc4e8e8..4c668d3dc9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java @@ -18,6 +18,7 @@ package androidx.media3.exoplayer.text; 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 java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -32,8 +33,10 @@ import androidx.media3.extractor.text.SubtitleDecoderException; import androidx.media3.extractor.text.SubtitleInputBuffer; import androidx.media3.extractor.text.SubtitleOutputBuffer; import com.google.common.collect.ImmutableList; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; @@ -44,6 +47,8 @@ import java.util.List; */ @UnstableApi public final class ExoplayerCuesDecoder implements SubtitleDecoder { + @Documented + @Target(TYPE_USE) @IntDef(value = {INPUT_BUFFER_AVAILABLE, INPUT_BUFFER_DEQUEUED, INPUT_BUFFER_QUEUED}) @Retention(RetentionPolicy.SOURCE) private @interface InputBufferState {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java index cfaa9311b8..e6dac32716 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.text; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Handler; import android.os.Handler.Callback; @@ -43,6 +44,7 @@ import androidx.media3.extractor.text.SubtitleOutputBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.List; @@ -60,6 +62,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ REPLACEMENT_STATE_NONE, REPLACEMENT_STATE_SIGNAL_END_OF_STREAM, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index d407421078..5b83a7f22c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.trackselection; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.util.Collections.max; import android.annotation.SuppressLint; @@ -58,6 +59,7 @@ import com.google.common.primitives.Ints; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -1118,6 +1120,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_EXCEED_VIDEO_CONSTRAINTS_IF_NECESSARY, FIELD_ALLOW_VIDEO_MIXED_MIME_TYPE_ADAPTIVENESS, @@ -1379,6 +1382,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_GROUP_INDEX, FIELD_TRACKS, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Loader.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Loader.java index 006fb117cb..a033f1b4bb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Loader.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/Loader.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.upstream; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.os.Handler; @@ -34,6 +35,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; @@ -145,6 +147,7 @@ public final class Loader implements LoaderErrorThrower { /** Types of action that can be taken in response to a load error. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ ACTION_TYPE_RETRY, ACTION_TYPE_RETRY_AND_RESET_ERROR_COUNT, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java index a7b2c9ce86..8d2144084f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java @@ -20,6 +20,7 @@ import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_RE import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO; import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Handler; import android.os.SystemClock; @@ -49,7 +50,7 @@ import androidx.media3.exoplayer.DecoderReuseEvaluation; import androidx.media3.exoplayer.ExoPlaybackException; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.FormatHolder; -import androidx.media3.exoplayer.PlayerMessage.Target; +import androidx.media3.exoplayer.PlayerMessage; import androidx.media3.exoplayer.drm.DrmSession; import androidx.media3.exoplayer.drm.DrmSession.DrmSessionException; import androidx.media3.exoplayer.source.SampleStream.ReadDataResult; @@ -57,12 +58,13 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener.EventDispatche import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Decodes and renders video using a {@link Decoder}. * - *

    This renderer accepts the following messages sent via {@link ExoPlayer#createMessage(Target)} - * on the playback thread: + *

    This renderer accepts the following messages sent via {@link + * ExoPlayer#createMessage(PlayerMessage.Target)} on the playback thread: * *

      *
    • Message with type {@link #MSG_SET_VIDEO_OUTPUT} to set the output surface. The message @@ -81,6 +83,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { /** Decoder reinitialization states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ REINITIALIZATION_STATE_NONE, REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/Projection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/Projection.java index 1dba7ef4c1..3eb3532032 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/Projection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/Projection.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.video.spherical; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.C.StereoMode; @@ -22,6 +24,7 @@ import androidx.media3.common.util.Assertions; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** The projection mesh used with 360/VR videos. */ /* package */ final class Projection { @@ -29,6 +32,7 @@ import java.lang.annotation.RetentionPolicy; /** Enforces allowed (sub) mesh draw modes. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({DRAW_MODE_TRIANGLES, DRAW_MODE_TRIANGLES_STRIP, DRAW_MODE_TRIANGLES_FAN}) public @interface DrawMode {} /** Triangle draw mode. */ diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index 326ac356b9..be20b5e054 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.dash; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import android.util.SparseArray; @@ -60,6 +61,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.IdentityHashMap; @@ -911,6 +913,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({CATEGORY_PRIMARY, CATEGORY_EMBEDDED, CATEGORY_MANIFEST_EVENTS}) public @interface TrackGroupCategory {} diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index c408e2eb3b..b2f0f200df 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.hls; import static androidx.media3.common.util.Assertions.checkNotNull; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.SystemClock; @@ -50,8 +51,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.primitives.Ints; import java.io.IOException; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -89,6 +92,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * Chunk publication state. One of {@link #CHUNK_PUBLICATION_STATE_PRELOAD}, {@link * #CHUNK_PUBLICATION_STATE_PUBLISHED}, {@link #CHUNK_PUBLICATION_STATE_REMOVED}. */ + @Documented + @Target(TYPE_USE) @IntDef({ CHUNK_PUBLICATION_STATE_PRELOAD, CHUNK_PUBLICATION_STATE_PUBLISHED, diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java index 38fcaa1e8d..b55ec3b9ac 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java @@ -23,6 +23,7 @@ import static androidx.media3.exoplayer.ima.ImaUtil.TIMEOUT_UNSET; import static androidx.media3.exoplayer.ima.ImaUtil.getAdGroupTimesUsForCuePoints; import static androidx.media3.exoplayer.ima.ImaUtil.getImaLooper; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.net.Uri; @@ -69,6 +70,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -108,6 +110,7 @@ import java.util.Map; /** The state of ad playback. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({IMA_AD_STATE_NONE, IMA_AD_STATE_PLAYING, IMA_AD_STATE_PAUSED}) private @interface ImaAdState {} diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java index d56c8ed840..ecb9994f3f 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.rtsp; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.net.Uri; import android.util.Base64; import androidx.annotation.IntDef; @@ -22,8 +24,10 @@ import androidx.media3.common.ParserException; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.rtsp.RtspMessageUtil.RtspAuthUserInfo; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -32,7 +36,9 @@ import java.security.NoSuchAlgorithmException; /* package */ final class RtspAuthenticationInfo { /** The supported authentication methods. */ + @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({BASIC, DIGEST}) @interface AuthenticationMechanism {} diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java index 60109565a5..e8b3fe7867 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java @@ -35,6 +35,7 @@ import static androidx.media3.exoplayer.rtsp.RtspRequest.METHOD_TEARDOWN; import static androidx.media3.exoplayer.rtsp.RtspRequest.METHOD_UNSET; import static com.google.common.base.Strings.nullToEmpty; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.Handler; @@ -61,6 +62,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.Socket; import java.util.ArrayDeque; import java.util.HashMap; @@ -78,6 +80,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({RTSP_STATE_UNINITIALIZED, RTSP_STATE_INIT, RTSP_STATE_READY, RTSP_STATE_PLAYING}) public @interface RtspState {} /** RTSP uninitialized state, the state before sending any SETUP request. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java index 2b8c639dd0..dda52e8f98 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java @@ -19,6 +19,7 @@ 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.exoplayer.rtsp.RtspMessageUtil.isRtspStartLine; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Handler; import android.os.HandlerThread; @@ -43,6 +44,7 @@ import java.io.OutputStream; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.Socket; import java.nio.charset.Charset; import java.util.ArrayList; @@ -341,6 +343,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STATE_READING_FIRST_LINE, STATE_READING_HEADER, STATE_READING_BODY}) @interface ReadingState {} diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java index d07c52ed98..6d9e5f35e8 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java @@ -16,12 +16,15 @@ package androidx.media3.exoplayer.rtsp; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.net.Uri; import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Represents an RTSP request. */ @UnstableApi @@ -49,6 +52,7 @@ import java.lang.annotation.RetentionPolicy; */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( value = { METHOD_UNSET, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java index f7256aff41..bf005a2317 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java @@ -18,6 +18,7 @@ package androidx.media3.extractor.flac; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -42,6 +43,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -79,6 +81,7 @@ public final class FlacExtractor implements Extractor { /** Parser state. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_READ_ID3_METADATA, STATE_GET_STREAM_MARKER_AND_INFO_BLOCK_BYTES, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/FlvExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/FlvExtractor.java index 490b2570fb..c21ebf3243 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/FlvExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/FlvExtractor.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.flv; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.media3.common.C; @@ -33,6 +34,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -46,6 +48,7 @@ public final class FlvExtractor implements Extractor { /** Extractor states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_READING_FLV_HEADER, STATE_SKIPPING_TO_TAG_HEADER, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java index 1bb6d1c025..700d5538f7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.jpeg; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -37,6 +38,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Extracts JPEG image using the Exif format. */ @@ -46,6 +48,7 @@ public final class JpegExtractor implements Extractor { /** Parser states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_READING_MARKER, STATE_READING_SEGMENT_LENGTH, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/DefaultEbmlReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/DefaultEbmlReader.java index 1792349041..2132c77f45 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/DefaultEbmlReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/DefaultEbmlReader.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.mkv; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.ParserException; @@ -25,6 +27,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayDeque; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -34,6 +37,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ELEMENT_STATE_READ_ID, ELEMENT_STATE_READ_CONTENT_SIZE, ELEMENT_STATE_READ_CONTENT}) private @interface ElementState {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index 12568a6f02..c9b5bb0fe5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -22,6 +22,7 @@ import static androidx.media3.extractor.mp4.Sniffer.BRAND_HEIC; import static androidx.media3.extractor.mp4.Sniffer.BRAND_QUICKTIME; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import androidx.annotation.IntDef; @@ -54,6 +55,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; @@ -101,6 +103,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { /** Parser states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_READING_ATOM_HEADER, STATE_READING_ATOM_PAYLOAD, @@ -117,6 +120,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { /** Supported file types. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FILE_TYPE_MP4, FILE_TYPE_QUICKTIME, FILE_TYPE_HEIC}) private @interface FileType {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java index f101326fe1..84170809eb 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.mp4; import static androidx.media3.extractor.Extractor.RESULT_SEEK; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.media3.common.C; @@ -31,6 +32,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; @@ -44,6 +46,7 @@ import java.util.List; /** Reader states. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_SHOULD_CHECK_FOR_SEF, STATE_CHECKING_FOR_SEF, @@ -60,6 +63,7 @@ import java.util.List; /** Supported data types. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ TYPE_SLOW_MOTION_DATA, TYPE_SUPER_SLOW_MOTION_DATA, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java index e6d6ec3900..46720375d1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java @@ -17,6 +17,7 @@ package androidx.media3.extractor.text; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -37,8 +38,10 @@ import androidx.media3.extractor.TrackOutput; import com.google.common.primitives.Ints; import java.io.IOException; import java.io.InterruptedIOException; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -46,7 +49,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Generic extractor for extracting subtitles from various subtitle formats. */ @UnstableApi public class SubtitleExtractor implements Extractor { + @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_CREATED, STATE_INITIALIZED, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java index 22ba22f352..7bfd5785a5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java @@ -18,6 +18,7 @@ package androidx.media3.extractor.text.ssa; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.extractor.text.ssa.SsaDecoder.STYLE_LINE_PREFIX; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.graphics.Color; @@ -35,6 +36,7 @@ import com.google.common.base.Ascii; import com.google.common.primitives.Ints; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -61,6 +63,7 @@ import java.util.regex.Pattern; *
    • {@link #SSA_ALIGNMENT_TOP_RIGHT} *
    */ + @Target(TYPE_USE) @IntDef({ SSA_ALIGNMENT_UNKNOWN, SSA_ALIGNMENT_BOTTOM_LEFT, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java index b252556925..a9e11e3642 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java @@ -15,6 +15,7 @@ */ package androidx.media3.extractor.text.ttml; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.text.TextUtils; @@ -29,6 +30,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.Set; import java.util.regex.Pattern; @@ -41,6 +43,7 @@ import java.util.regex.Pattern; @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({ TextEmphasisSpan.MARK_SHAPE_NONE, TextEmphasisSpan.MARK_SHAPE_CIRCLE, @@ -58,6 +61,7 @@ import java.util.regex.Pattern; @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({ TextAnnotation.POSITION_UNKNOWN, TextAnnotation.POSITION_BEFORE, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java index e795c2b601..a2b71b0463 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.text.ttml; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.graphics.Typeface; import android.text.Layout; import androidx.annotation.IntDef; @@ -23,6 +25,7 @@ import androidx.media3.common.text.TextAnnotation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Style object of a TtmlNode */ /* package */ final class TtmlStyle { @@ -32,6 +35,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, STYLE_BOLD_ITALIC}) @@ -44,6 +48,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({UNSPECIFIED, FONT_SIZE_UNIT_PIXEL, FONT_SIZE_UNIT_EM, FONT_SIZE_UNIT_PERCENT}) public @interface FontSizeUnit {} @@ -53,6 +58,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({UNSPECIFIED, OFF, ON}) private @interface OptionalBoolean {} @@ -61,6 +67,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({UNSPECIFIED, RUBY_TYPE_CONTAINER, RUBY_TYPE_BASE, RUBY_TYPE_TEXT, RUBY_TYPE_DELIMITER}) public @interface RubyType {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 8ac266e6cb..168b5a76d7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.text.webvtt; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.graphics.Typeface; import android.text.TextUtils; import androidx.annotation.ColorInt; @@ -26,6 +28,7 @@ import com.google.common.base.Ascii; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -73,6 +76,7 @@ public final class WebvttCssStyle { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({UNSPECIFIED, OFF, ON}) private @interface OptionalBoolean {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java index 85a9b8007b..a80d381502 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.text.webvtt; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.graphics.Color; @@ -47,6 +48,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -76,6 +78,7 @@ public final class WebvttCueParser { */ @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({ TEXT_ALIGNMENT_START, TEXT_ALIGNMENT_CENTER, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java index 4fdb39300b..527f32d965 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.ts; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -34,6 +35,7 @@ import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -43,6 +45,7 @@ public final class Ac3Reader implements ElementaryStreamReader { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STATE_FINDING_SYNC, STATE_READING_HEADER, STATE_READING_SAMPLE}) private @interface State {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java index 708c535916..238b861ad7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.ts; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -34,6 +35,7 @@ import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -43,6 +45,7 @@ public final class Ac4Reader implements ElementaryStreamReader { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STATE_FINDING_SYNC, STATE_READING_HEADER, STATE_READING_SAMPLE}) private @interface State {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index 11ec5a3532..efa84e123f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -18,6 +18,7 @@ package androidx.media3.extractor.ts; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.castNonNull; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -32,8 +33,10 @@ import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.NalUnitUtil; import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; +import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import java.util.Collections; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -313,7 +316,9 @@ public final class H263Reader implements ElementaryStreamReader { private static final byte[] START_CODE = new byte[] {0, 0, 1}; + @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ STATE_SKIP_TO_VISUAL_OBJECT_SEQUENCE_START, STATE_EXPECT_VISUAL_OBJECT_START, diff --git a/libraries/session/src/main/java/androidx/media3/session/CommandButton.java b/libraries/session/src/main/java/androidx/media3/session/CommandButton.java index 215b55c9dd..070dc7e7c2 100644 --- a/libraries/session/src/main/java/androidx/media3/session/CommandButton.java +++ b/libraries/session/src/main/java/androidx/media3/session/CommandButton.java @@ -17,6 +17,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.DrawableRes; @@ -29,6 +30,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; /** @@ -195,6 +197,7 @@ public final class CommandButton implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_SESSION_COMMAND, FIELD_PLAYER_COMMAND, diff --git a/libraries/session/src/main/java/androidx/media3/session/ConnectionRequest.java b/libraries/session/src/main/java/androidx/media3/session/ConnectionRequest.java index dd48a35bfd..fbcf9c95f5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/ConnectionRequest.java +++ b/libraries/session/src/main/java/androidx/media3/session/ConnectionRequest.java @@ -17,6 +17,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.IntDef; @@ -26,6 +27,7 @@ import androidx.media3.common.MediaLibraryInfo; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Created by {@link MediaController} to send its state to the {@link MediaSession} to request to @@ -56,6 +58,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_VERSION, FIELD_PACKAGE_NAME, FIELD_PID, FIELD_CONNECTION_HINTS}) private @interface FieldNumber {} diff --git a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java index 8ca1859d1e..d073ee3e95 100644 --- a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java +++ b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java @@ -16,6 +16,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.app.PendingIntent; import android.os.Bundle; @@ -29,6 +30,7 @@ import androidx.media3.common.util.BundleableUtil; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Created by {@link MediaSession} to send its state to the {@link MediaController} when the @@ -75,6 +77,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_VERSION, FIELD_SESSION_BINDER, diff --git a/libraries/session/src/main/java/androidx/media3/session/LibraryResult.java b/libraries/session/src/main/java/androidx/media3/session/LibraryResult.java index 68ee14c7fe..17c921eff4 100644 --- a/libraries/session/src/main/java/androidx/media3/session/LibraryResult.java +++ b/libraries/session/src/main/java/androidx/media3/session/LibraryResult.java @@ -250,6 +250,7 @@ public final class LibraryResult implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_RESULT_CODE, FIELD_COMPLETION_TIME_MS, diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index ab632ef646..d4892b0e5b 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotEmpty; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.session.LibraryResult.RESULT_ERROR_NOT_SUPPORTED; +import static java.lang.annotation.ElementType.TYPE_USE; import android.app.PendingIntent; import android.content.Context; @@ -41,6 +42,7 @@ import com.google.common.util.concurrent.ListenableFuture; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Superclass to be extended by services hosting {@link MediaLibrarySession media library sessions}. @@ -650,6 +652,7 @@ public abstract class MediaLibraryService extends MediaSessionService { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_EXTRAS, FIELD_RECENT, diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java index db43c26273..342796e7cc 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerInfo.java @@ -20,6 +20,7 @@ import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; import static androidx.media3.common.Player.STATE_IDLE; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import android.os.SystemClock; @@ -48,6 +49,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; /** @@ -649,6 +651,7 @@ import java.util.List; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_PLAYBACK_PARAMETERS, FIELD_REPEAT_MODE, diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java b/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java index 660b93e7ed..256a92c3ff 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionCommand.java @@ -172,6 +172,7 @@ public final class SessionCommand implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_COMMAND_CODE, FIELD_CUSTOM_ACTION, FIELD_CUSTOM_EXTRAS}) private @interface FieldNumber {} diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionCommands.java b/libraries/session/src/main/java/androidx/media3/session/SessionCommands.java index 54122b31c2..fa9b6dabea 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionCommands.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionCommands.java @@ -18,6 +18,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.session.SessionCommand.COMMAND_CODE_CUSTOM; +import static java.lang.annotation.ElementType.TYPE_USE; import android.os.Bundle; import androidx.annotation.IntDef; @@ -31,6 +32,7 @@ import com.google.common.collect.ImmutableSet; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -227,6 +229,7 @@ public final class SessionCommands implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_SESSION_COMMANDS}) private @interface FieldNumber {} diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java b/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java index 89790c6516..ed75a7603d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionPositionInfo.java @@ -15,6 +15,8 @@ */ package androidx.media3.session; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -26,6 +28,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Position information to be shared between session and controller. @@ -160,6 +163,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_POSITION_INFO, FIELD_IS_PLAYING_AD, diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionResult.java b/libraries/session/src/main/java/androidx/media3/session/SessionResult.java index 44fd177511..f4ca56cf39 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionResult.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionResult.java @@ -177,6 +177,7 @@ public final class SessionResult implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_RESULT_CODE, FIELD_EXTRAS, FIELD_COMPLETION_TIME_MS}) private @interface FieldNumber {} diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java index 0ed15c4c3f..fd9d1f0280 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionToken.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionToken.java @@ -424,6 +424,7 @@ public final class SessionToken implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FIELD_IMPL_TYPE, FIELD_IMPL}) private @interface FieldNumber {} @@ -433,6 +434,7 @@ public final class SessionToken implements Bundleable { /** Types of {@link SessionTokenImpl} */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({IMPL_TYPE_BASE, IMPL_TYPE_LEGACY}) private @interface SessionTokenImplType {} diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplBase.java b/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplBase.java index ad5bdc94c5..d7725fd51e 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplBase.java @@ -18,6 +18,7 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotEmpty; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.ComponentName; import android.os.Bundle; @@ -31,6 +32,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /* package */ final class SessionTokenImplBase implements SessionToken.SessionTokenImpl { @@ -184,6 +186,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_UID, FIELD_TYPE, diff --git a/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplLegacy.java index 2429afc3f6..b44b95a058 100644 --- a/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/SessionTokenImplLegacy.java @@ -22,6 +22,7 @@ import static androidx.media3.session.SessionToken.TYPE_BROWSER_SERVICE_LEGACY; import static androidx.media3.session.SessionToken.TYPE_LIBRARY_SERVICE; import static androidx.media3.session.SessionToken.TYPE_SESSION; import static androidx.media3.session.SessionToken.TYPE_SESSION_LEGACY; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.ComponentName; import android.os.Bundle; @@ -34,6 +35,7 @@ import com.google.common.base.Objects; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /* package */ final class SessionTokenImplLegacy implements SessionTokenImpl { @@ -169,6 +171,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FIELD_LEGACY_TOKEN, FIELD_UID, diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java index 9ed8538349..7cab3c2d8a 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java @@ -16,6 +16,7 @@ package androidx.media3.test.utils; import static com.google.common.truth.Truth.assertWithMessage; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import androidx.annotation.IntDef; @@ -29,6 +30,7 @@ import java.io.PrintWriter; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Helper class to enable assertions based on golden-data dump files. @@ -53,6 +55,7 @@ public class DumpFileAsserts { /** Possible actions to take with the dumps passed to {@link #assertOutput}. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {COMPARE_WITH_EXISTING, WRITE_TO_LOCAL, WRITE_TO_DEVICE}) diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java index 99b75bd9a7..d252878fee 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java @@ -21,6 +21,7 @@ import static androidx.media3.test.utils.WebServerDispatcher.Resource.GZIP_SUPPO import static androidx.media3.test.utils.WebServerDispatcher.Resource.GZIP_SUPPORT_FORCED; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import androidx.annotation.IntDef; @@ -34,6 +35,7 @@ import com.google.common.collect.Maps; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; @@ -68,6 +70,7 @@ public class WebServerDispatcher extends Dispatcher { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({GZIP_SUPPORT_DISABLED, GZIP_SUPPORT_ENABLED, GZIP_SUPPORT_FORCED}) private @interface GzipSupport {} From 24749339db7dfd8a602809bfd4b28558128f1e0a Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 4 Feb 2022 15:05:45 +0000 Subject: [PATCH 138/251] Support relative MPD.Location URIs #minor-release Issue: google/ExoPlayer#9939 PiperOrigin-RevId: 426394339 --- .../dash/manifest/DashManifestParser.java | 2 +- .../dash/manifest/DashManifestParserTest.java | 30 +++++++++++++++++++ ...sample_mpd_live_location_redirect_absolute | 21 +++++++++++++ ...sample_mpd_live_location_redirect_relative | 21 +++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_absolute create mode 100644 libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_relative diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java index e1d4e070b6..9ca9fc2102 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java @@ -163,7 +163,7 @@ public class DashManifestParser extends DefaultHandler } else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) { utcTiming = parseUtcTiming(xpp); } else if (XmlPullParserUtil.isStartTag(xpp, "Location")) { - location = Uri.parse(xpp.nextText()); + location = UriUtil.resolveToUri(documentBaseUri.toString(), xpp.nextText()); } else if (XmlPullParserUtil.isStartTag(xpp, "ServiceDescription")) { serviceDescription = parseServiceDescription(xpp); } else if (XmlPullParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) { diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/manifest/DashManifestParserTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/manifest/DashManifestParserTest.java index cc6d480e0e..1238d756fd 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/manifest/DashManifestParserTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/manifest/DashManifestParserTest.java @@ -46,6 +46,10 @@ import org.xmlpull.v1.XmlPullParserFactory; public class DashManifestParserTest { private static final String SAMPLE_MPD_LIVE = "media/mpd/sample_mpd_live"; + private static final String SAMPLE_MPD_LIVE_LOCATION_REDIRECT_RELATIVE = + "media/mpd/sample_mpd_live_location_redirect_relative"; + private static final String SAMPLE_MPD_LIVE_LOCATION_REDIRECT_ABSOLUTE = + "media/mpd/sample_mpd_live_location_redirect_absolute"; private static final String SAMPLE_MPD_UNKNOWN_MIME_TYPE = "media/mpd/sample_mpd_unknown_mime_type"; private static final String SAMPLE_MPD_SEGMENT_TEMPLATE = "media/mpd/sample_mpd_segment_template"; @@ -201,6 +205,32 @@ public class DashManifestParserTest { assertThat(manifest.programInformation).isEqualTo(expectedProgramInformation); } + @Test + public void parseMediaPresentationDescription_locationRedirectRelative() throws IOException { + DashManifestParser parser = new DashManifestParser(); + DashManifest manifest = + parser.parse( + Uri.parse("https://example.com/a/b/test.mpd"), + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), + SAMPLE_MPD_LIVE_LOCATION_REDIRECT_RELATIVE)); + Uri expectedLocation = Uri.parse("https://example.com/a/relative/redirect.mpd"); + assertThat(manifest.location).isEqualTo(expectedLocation); + } + + @Test + public void parseMediaPresentationDescription_locationRedirectAbsolute() throws IOException { + DashManifestParser parser = new DashManifestParser(); + DashManifest manifest = + parser.parse( + Uri.parse("https://example.com/a/b/test.mpd"), + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), + SAMPLE_MPD_LIVE_LOCATION_REDIRECT_ABSOLUTE)); + Uri expectedLocation = Uri.parse("https://example2.com/absolute/redirect.mpd"); + assertThat(manifest.location).isEqualTo(expectedLocation); + } + @Test public void parseMediaPresentationDescription_images() throws IOException { DashManifestParser parser = new DashManifestParser(); diff --git a/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_absolute b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_absolute new file mode 100644 index 0000000000..a6b368e163 --- /dev/null +++ b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_absolute @@ -0,0 +1,21 @@ + + + https://example2.com/absolute/redirect.mpd + + + + + + + + + diff --git a/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_relative b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_relative new file mode 100644 index 0000000000..63db7de219 --- /dev/null +++ b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_live_location_redirect_relative @@ -0,0 +1,21 @@ + + + ../relative/redirect.mpd + + + + + + + + + From b3476e442a84239a2e82a64f781da95fce9be9ed Mon Sep 17 00:00:00 2001 From: hschlueter Date: Fri, 4 Feb 2022 15:55:29 +0000 Subject: [PATCH 139/251] Disable fallback in tests that don't test fallback. Fallback is only disabled for robolectric and instrumentation tests. For MH tests, fallback is not disabled, as it may be needed due to the broad range of devices available. PiperOrigin-RevId: 426403167 --- .../transformer/TransformerEndToEndTest.java | 4 + .../transformer/DefaultEncoderFactory.java | 18 ++-- .../transformer/TransformerEndToEndTest.java | 89 +++++++++++-------- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java index 585c027962..cd3fbc5bc3 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -47,6 +47,8 @@ public class TransformerEndToEndTest { .setTransformationRequest( new TransformationRequest.Builder().setVideoMimeType(MimeTypes.VIDEO_H264).build()) .setMuxerFactory(muxerFactory) + .setEncoderFactory( + new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* disableFallback= */ true)) .build(); // Result of the following command: // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames bear-vp9.webm @@ -78,6 +80,8 @@ public class TransformerEndToEndTest { .setTransformationMatrix(transformationMatrix) .build()) .setMuxerFactory(muxerFactory) + .setEncoderFactory( + new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* disableFallback= */ true)) .build(); // Result of the following command: // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4 diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 3c2312a6d8..5735e79c76 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -75,9 +75,18 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { // TODO(b/210591626) Add encoder selection for audio. checkArgument(!allowedMimeTypes.isEmpty()); if (!allowedMimeTypes.contains(format.sampleMimeType)) { - // TODO(b/210591626): Pick fallback MIME type using same strategy as for encoder - // capabilities limitations. - format = format.buildUpon().setSampleMimeType(allowedMimeTypes.get(0)).build(); + if (!disableFallback) { + // TODO(b/210591626): Pick fallback MIME type using same strategy as for encoder + // capabilities limitations. + format = format.buildUpon().setSampleMimeType(allowedMimeTypes.get(0)).build(); + } else { + throw createTransformationException( + new IllegalArgumentException("The requested output format is not supported."), + format, + /* isVideo= */ false, + /* isDecoder= */ false, + /* mediaCodecName= */ null); + } } MediaFormat mediaFormat = MediaFormat.createAudioFormat( @@ -112,8 +121,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { format, videoEncoderSelector, allowedMimeTypes, disableFallback); if (encoderAndClosestFormatSupport == null) { throw createTransformationException( - new IllegalArgumentException( - "No encoder available that supports the requested output format."), + new IllegalArgumentException("The requested output format is not supported."), format, /* isVideo= */ true, /* isDecoder= */ false, diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java index b87ce94875..e3178ad40b 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -66,7 +66,6 @@ import org.robolectric.shadows.ShadowMediaCodec; /** End-to-end test for {@link Transformer}. */ @RunWith(AndroidJUnit4.class) public final class TransformerEndToEndTest { - // TODO(b/214973843): Disable fallback for all tests that aren't specifically testing fallback. private static final String URI_PREFIX = "asset:///media/"; private static final String FILE_VIDEO_ONLY = "mp4/sample_18byte_nclx_colr.mp4"; @@ -103,7 +102,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_videoOnlyPassthrough_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -114,7 +113,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioOnlyPassthrough_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER); @@ -128,7 +127,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioOnlyTranscoding_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer @@ -145,7 +144,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioAndVideo_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -157,7 +156,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_withSubtitles_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build()) .build(); @@ -172,7 +171,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_successiveTransformations_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); // Transform first media item. @@ -189,7 +188,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_concurrentTransformations_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -200,7 +199,8 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_removeAudio_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().setRemoveAudio(true).build(); + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ true).setRemoveAudio(true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -212,7 +212,8 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_removeVideo_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().setRemoveVideo(true).build(); + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ true).setRemoveVideo(true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -228,7 +229,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -249,7 +250,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -276,7 +277,7 @@ public final class TransformerEndToEndTest { TransformationRequest fallbackTransformationRequest = new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build(); Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ false) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -301,7 +302,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer1 = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -320,7 +321,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder().setFlattenForSlowMotion(true).build()) .build(); @@ -336,7 +337,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType( @@ -357,7 +358,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withAudioDecoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer @@ -377,7 +378,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withVideoEncoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder() + createTransformerBuilder(/* disableFallback= */ true) .setTransformationRequest( new TransformationRequest.Builder() .setVideoMimeType(MimeTypes.VIDEO_H263) // unsupported encoder MIME type @@ -395,7 +396,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_withIoError_completesWithError() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri("asset:///non-existing-path.mp4"); transformer.startTransformation(mediaItem, outputPath); @@ -405,6 +406,21 @@ public final class TransformerEndToEndTest { assertThat(exception.errorCode).isEqualTo(TransformationException.ERROR_CODE_IO_FILE_NOT_FOUND); } + @Test + public void startTransformation_withAudioMuxerFormatUnsupported_completesWithError() + throws Exception { + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER); + + transformer.startTransformation(mediaItem, outputPath); + TransformationException exception = TransformerTestRunner.runUntilError(transformer); + + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(exception).hasMessageThat().contains("audio"); + assertThat(exception.errorCode) + .isEqualTo(TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); + } + @Test public void startTransformation_withAudioMuxerFormatFallback_completesSuccessfully() throws Exception { @@ -413,7 +429,8 @@ public final class TransformerEndToEndTest { new TransformationRequest.Builder().build(); TransformationRequest fallbackTransformationRequest = new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build(); - Transformer transformer = createTransformerBuilder().addListener(mockListener).build(); + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ false).addListener(mockListener).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER); transformer.startTransformation(mediaItem, outputPath); @@ -425,13 +442,9 @@ public final class TransformerEndToEndTest { .onFallbackApplied(mediaItem, originalTransformationRequest, fallbackTransformationRequest); } - // TODO(b/214012830): Add a test to check that the correct exception is thrown when the muxer - // doesn't support the output sample MIME type inferred from the input once it is possible to - // disable fallback. - @Test public void startTransformation_afterCancellation_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -450,7 +463,8 @@ public final class TransformerEndToEndTest { HandlerThread anotherThread = new HandlerThread("AnotherThread"); anotherThread.start(); Looper looper = anotherThread.getLooper(); - Transformer transformer = createTransformerBuilder().setLooper(looper).build(); + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ true).setLooper(looper).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); AtomicReference exception = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -475,7 +489,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); @@ -502,7 +516,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_knownDuration_returnsConsistentStates() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); @@ -548,7 +562,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_knownDuration_givesIncreasingPercentages() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); List progresses = new ArrayList<>(); Handler progressHandler = @@ -583,7 +597,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_noCurrentTransformation_returnsNoTransformation() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); @Transformer.ProgressState int stateBeforeTransform = transformer.getProgress(progressHolder); @@ -597,7 +611,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_unknownDuration_returnsConsistentStates() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_UNKNOWN_DURATION); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); @@ -640,7 +654,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -664,7 +678,7 @@ public final class TransformerEndToEndTest { @Test public void cancel_afterCompletion_doesNotThrow() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -674,7 +688,7 @@ public final class TransformerEndToEndTest { @Test public void cancel_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder().build(); + Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -696,8 +710,11 @@ public final class TransformerEndToEndTest { assertThat(illegalStateException.get()).isNotNull(); } - private Transformer.Builder createTransformerBuilder() { - return new Transformer.Builder(context).setClock(clock).setMuxerFactory(new TestMuxerFactory()); + private Transformer.Builder createTransformerBuilder(boolean disableFallback) { + return new Transformer.Builder(context) + .setClock(clock) + .setMuxerFactory(new TestMuxerFactory()) + .setEncoderFactory(new DefaultEncoderFactory(EncoderSelector.DEFAULT, disableFallback)); } private static void createEncodersAndDecoders() { From 0bf21f2e204c489c0ad58bba0b3f43eb6fb4c688 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:13:07 +0000 Subject: [PATCH 140/251] Reshuffle the 'legacy' attrs.xml and drawables.xml files Keep values related to LegacyPlayerView in attrs_legacy_player_view.xml and put all values related to LegacyPlayerControlView back in their original locations. We plan to remove LegacyPlayerView (and attrs_legacy_player_view.xml) from media3, but will keep LegacyPlayerControlView - so the separation of the XML files needs to reflect this split. #minor-release PiperOrigin-RevId: 426406973 --- libraries/ui/src/main/res/values/attrs.xml | 27 ++++++++++++++++ ...egacy.xml => attrs_legacy_player_view.xml} | 27 ---------------- .../ui/src/main/res/values/drawables.xml | 15 +++++++++ .../src/main/res/values/drawables_legacy.xml | 31 ------------------- 4 files changed, 42 insertions(+), 58 deletions(-) rename libraries/ui/src/main/res/values/{attrs_legacy.xml => attrs_legacy_player_view.xml} (67%) delete mode 100644 libraries/ui/src/main/res/values/drawables_legacy.xml diff --git a/libraries/ui/src/main/res/values/attrs.xml b/libraries/ui/src/main/res/values/attrs.xml index 9442753966..172b9acf0e 100644 --- a/libraries/ui/src/main/res/values/attrs.xml +++ b/libraries/ui/src/main/res/values/attrs.xml @@ -135,6 +135,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/ui/src/main/res/values/attrs_legacy.xml b/libraries/ui/src/main/res/values/attrs_legacy_player_view.xml similarity index 67% rename from libraries/ui/src/main/res/values/attrs_legacy.xml rename to libraries/ui/src/main/res/values/attrs_legacy_player_view.xml index f042fa825f..47528a29de 100644 --- a/libraries/ui/src/main/res/values/attrs_legacy.xml +++ b/libraries/ui/src/main/res/values/attrs_legacy_player_view.xml @@ -51,31 +51,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libraries/ui/src/main/res/values/drawables.xml b/libraries/ui/src/main/res/values/drawables.xml index 9f3227b29e..6c65577a44 100644 --- a/libraries/ui/src/main/res/values/drawables.xml +++ b/libraries/ui/src/main/res/values/drawables.xml @@ -23,6 +23,21 @@ @drawable/exo_icon_stop @drawable/exo_icon_circular_play + @drawable/exo_icon_play + @drawable/exo_icon_pause + @drawable/exo_icon_next + @drawable/exo_icon_previous + @drawable/exo_icon_fastforward + @drawable/exo_icon_rewind + @drawable/exo_icon_repeat_all + @drawable/exo_icon_repeat_off + @drawable/exo_icon_repeat_one + @drawable/exo_icon_shuffle_off + @drawable/exo_icon_shuffle_on + @drawable/exo_icon_fullscreen_enter + @drawable/exo_icon_fullscreen_exit + @drawable/exo_icon_vr + @drawable/exo_ic_play_circle_filled @drawable/exo_ic_pause_circle_filled @drawable/exo_ic_skip_next diff --git a/libraries/ui/src/main/res/values/drawables_legacy.xml b/libraries/ui/src/main/res/values/drawables_legacy.xml deleted file mode 100644 index 6460a974bc..0000000000 --- a/libraries/ui/src/main/res/values/drawables_legacy.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - @drawable/exo_icon_play - @drawable/exo_icon_pause - @drawable/exo_icon_next - @drawable/exo_icon_previous - @drawable/exo_icon_fastforward - @drawable/exo_icon_rewind - @drawable/exo_icon_repeat_all - @drawable/exo_icon_repeat_off - @drawable/exo_icon_repeat_one - @drawable/exo_icon_shuffle_off - @drawable/exo_icon_shuffle_on - @drawable/exo_icon_fullscreen_enter - @drawable/exo_icon_fullscreen_exit - @drawable/exo_icon_vr - From 14f9d9c62ccd9d3d7b66f8279920b73037e7d630 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:16:46 +0000 Subject: [PATCH 141/251] Remove @C.AudioManagerOffloadMode IntDef This is only used in DefaultAudioSink, so we could move it there and make it private - but at that point we might as well refer to the underlying AudioManager constants instead. #minor-release PiperOrigin-RevId: 426407661 --- .../main/java/androidx/media3/common/C.java | 24 ------------------- .../exoplayer/audio/DefaultAudioSink.java | 16 ++++++------- 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 8eba741733..7bc08cb47a 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -508,30 +508,6 @@ public final class C { public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; - /** - * Playback offload mode. One of {@link #PLAYBACK_OFFLOAD_NOT_SUPPORTED},{@link - * #PLAYBACK_OFFLOAD_SUPPORTED} or {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED}. - */ - @UnstableApi - @IntDef({ - PLAYBACK_OFFLOAD_NOT_SUPPORTED, - PLAYBACK_OFFLOAD_SUPPORTED, - PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AudioManagerOffloadMode {} - /** See AudioManager#PLAYBACK_OFFLOAD_NOT_SUPPORTED */ - @UnstableApi - public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED = - AudioManager.PLAYBACK_OFFLOAD_NOT_SUPPORTED; - /** See AudioManager#PLAYBACK_OFFLOAD_SUPPORTED */ - @UnstableApi - public static final int PLAYBACK_OFFLOAD_SUPPORTED = AudioManager.PLAYBACK_OFFLOAD_SUPPORTED; - /** See AudioManager#PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED */ - @UnstableApi - public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED = - AudioManager.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED; - /** * Flags which can apply to a buffer containing a media sample. Possible flag values are {@link * #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_LAST_SAMPLE}, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index f0c5db919d..bfdfb580b9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -1824,13 +1824,13 @@ public final class DefaultAudioSink implements AudioSink { AudioFormat audioFormat = getAudioFormat(format.sampleRate, channelConfig, encoding); switch (getOffloadedPlaybackSupport(audioFormat, audioAttributes.getAudioAttributesV21())) { - case C.PLAYBACK_OFFLOAD_NOT_SUPPORTED: + case AudioManager.PLAYBACK_OFFLOAD_NOT_SUPPORTED: return false; - case C.PLAYBACK_OFFLOAD_SUPPORTED: + case AudioManager.PLAYBACK_OFFLOAD_SUPPORTED: boolean isGapless = format.encoderDelay != 0 || format.encoderPadding != 0; boolean gaplessSupportRequired = offloadMode == OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED; return !isGapless || !gaplessSupportRequired; - case C.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED: + case AudioManager.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED: return true; default: throw new IllegalStateException(); @@ -1838,22 +1838,20 @@ public final class DefaultAudioSink implements AudioSink { } @RequiresApi(29) - // Return values of AudioManager.getPlaybackOffloadSupport are equal to C.AudioManagerOffloadMode. - @SuppressLint("WrongConstant") - @C.AudioManagerOffloadMode + @SuppressLint("InlinedApi") private int getOffloadedPlaybackSupport( AudioFormat audioFormat, android.media.AudioAttributes audioAttributes) { if (Util.SDK_INT >= 31) { return AudioManager.getPlaybackOffloadSupport(audioFormat, audioAttributes); } if (!AudioManager.isOffloadedPlaybackSupported(audioFormat, audioAttributes)) { - return C.PLAYBACK_OFFLOAD_NOT_SUPPORTED; + return AudioManager.PLAYBACK_OFFLOAD_NOT_SUPPORTED; } // Manual testing has shown that Pixels on Android 11 support gapless offload. if (Util.SDK_INT == 30 && Util.MODEL.startsWith("Pixel")) { - return C.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED; + return AudioManager.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED; } - return C.PLAYBACK_OFFLOAD_SUPPORTED; + return AudioManager.PLAYBACK_OFFLOAD_SUPPORTED; } private static boolean isOffloadedPlayback(AudioTrack audioTrack) { From 0e856402a22b4dbf841a8322f2056136648e6510 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:17:25 +0000 Subject: [PATCH 142/251] Move @AudioFocusGain from C to AudioFocusManager and make it private This is only used inside AudioFocusManager, it doesn't need to public. Also mark it TYPE_USE and update the position to match. #minor-release PiperOrigin-RevId: 426407790 --- .../main/java/androidx/media3/common/C.java | 32 ---------- .../media3/exoplayer/AudioFocusManager.java | 63 ++++++++++++++----- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 7bc08cb47a..594706bb30 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -476,38 +476,6 @@ public final class C { /** See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}. */ public static final int ALLOW_CAPTURE_BY_SYSTEM = AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM; - /** - * Audio focus types. One of {@link #AUDIOFOCUS_NONE}, {@link #AUDIOFOCUS_GAIN}, {@link - * #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or {@link - * #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. - */ - @UnstableApi - @Documented - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - AUDIOFOCUS_NONE, - AUDIOFOCUS_GAIN, - AUDIOFOCUS_GAIN_TRANSIENT, - AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, - AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE - }) - public @interface AudioFocusGain {} - /** @see AudioManager#AUDIOFOCUS_NONE */ - @UnstableApi public static final int AUDIOFOCUS_NONE = AudioManager.AUDIOFOCUS_NONE; - /** @see AudioManager#AUDIOFOCUS_GAIN */ - @UnstableApi public static final int AUDIOFOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT */ - @UnstableApi - public static final int AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK */ - @UnstableApi - public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE */ - @UnstableApi - public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = - AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; - /** * Flags which can apply to a buffer containing a media sample. Possible flag values are {@link * #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_LAST_SAMPLE}, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index 580f18fc89..d330c22b6a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -97,6 +97,37 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Audio focus has been temporarily lost, but playback may continue with reduced volume. */ private static final int AUDIO_FOCUS_STATE_LOSS_TRANSIENT_DUCK = 3; + /** + * Audio focus types. One of {@link #AUDIOFOCUS_NONE}, {@link #AUDIOFOCUS_GAIN}, {@link + * #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or {@link + * #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({ + AUDIOFOCUS_NONE, + AUDIOFOCUS_GAIN, + AUDIOFOCUS_GAIN_TRANSIENT, + AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, + AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE + }) + private @interface AudioFocusGain {} + /** @see AudioManager#AUDIOFOCUS_NONE */ + @SuppressWarnings("InlinedApi") + private static final int AUDIOFOCUS_NONE = AudioManager.AUDIOFOCUS_NONE; + /** @see AudioManager#AUDIOFOCUS_GAIN */ + private static final int AUDIOFOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN; + /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT */ + private static final int AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; + /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK */ + private static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; + /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE */ + @SuppressWarnings("InlinedApi") + private static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; + private static final String TAG = "AudioFocusManager"; private static final float VOLUME_MULTIPLIER_DUCK = 0.2f; @@ -108,7 +139,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private AudioAttributes audioAttributes; @AudioFocusState private int audioFocusState; - @C.AudioFocusGain private int focusGainToRequest; + private @AudioFocusGain int focusGainToRequest; private float volumeMultiplier = VOLUME_MULTIPLIER_DEFAULT; private @MonotonicNonNull AudioFocusRequest audioFocusRequest; @@ -149,7 +180,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.audioAttributes = audioAttributes; focusGainToRequest = convertAudioAttributesToFocusGain(audioAttributes); Assertions.checkArgument( - focusGainToRequest == C.AUDIOFOCUS_GAIN || focusGainToRequest == C.AUDIOFOCUS_NONE, + focusGainToRequest == AUDIOFOCUS_GAIN || focusGainToRequest == AUDIOFOCUS_NONE, "Automatic handling of audio focus is only available for USAGE_MEDIA and USAGE_GAME."); } } @@ -187,7 +218,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } private boolean shouldAbandonAudioFocusIfHeld(@Player.State int playbackState) { - return playbackState == Player.STATE_IDLE || focusGainToRequest != C.AUDIOFOCUS_GAIN; + return playbackState == Player.STATE_IDLE || focusGainToRequest != AUDIOFOCUS_GAIN; } @PlayerCommand @@ -268,12 +299,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param audioAttributes The audio attributes associated with this focus request. * @return The type of audio focus gain that should be requested. */ - @C.AudioFocusGain - private static int convertAudioAttributesToFocusGain(@Nullable AudioAttributes audioAttributes) { + private static @AudioFocusGain int convertAudioAttributesToFocusGain( + @Nullable AudioAttributes audioAttributes) { if (audioAttributes == null) { // Don't handle audio focus. It may be either video only contents or developers // want to have more finer grained control. (e.g. adding audio focus listener) - return C.AUDIOFOCUS_NONE; + return AUDIOFOCUS_NONE; } switch (audioAttributes.usage) { @@ -281,13 +312,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // during the phone call when AUDIOFOCUS_GAIN_TRANSIENT is requested for that. // Don't request audio focus here. case C.USAGE_VOICE_COMMUNICATION_SIGNALLING: - return C.AUDIOFOCUS_NONE; + return AUDIOFOCUS_NONE; // Javadoc says 'AUDIOFOCUS_GAIN: Examples of uses of this focus gain are for music // playback, for a game or a video player' case C.USAGE_GAME: case C.USAGE_MEDIA: - return C.AUDIOFOCUS_GAIN; + return AUDIOFOCUS_GAIN; // Special usages: USAGE_UNKNOWN shouldn't be used. Request audio focus to prevent // multiple media playback happen at the same time. @@ -296,13 +327,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; TAG, "Specify a proper usage in the audio attributes for audio focus" + " handling. Using AUDIOFOCUS_GAIN by default."); - return C.AUDIOFOCUS_GAIN; + return AUDIOFOCUS_GAIN; // Javadoc says 'AUDIOFOCUS_GAIN_TRANSIENT: An example is for playing an alarm, or // during a VoIP call' case C.USAGE_ALARM: case C.USAGE_VOICE_COMMUNICATION: - return C.AUDIOFOCUS_GAIN_TRANSIENT; + return AUDIOFOCUS_GAIN_TRANSIENT; // Javadoc says 'AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: Examples are when playing // driving directions or notifications' @@ -314,28 +345,28 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; case C.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case C.USAGE_NOTIFICATION_EVENT: case C.USAGE_NOTIFICATION_RINGTONE: - return C.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; + return AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; // Javadoc says 'AUDIOFOCUS_GAIN_EXCLUSIVE: This is typically used if you are doing // audio recording or speech recognition'. // Assistant is considered as both recording and notifying developer case C.USAGE_ASSISTANT: if (Util.SDK_INT >= 19) { - return C.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; + return AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; } else { - return C.AUDIOFOCUS_GAIN_TRANSIENT; + return AUDIOFOCUS_GAIN_TRANSIENT; } // Special usages: case C.USAGE_ASSISTANCE_ACCESSIBILITY: if (audioAttributes.contentType == C.CONTENT_TYPE_SPEECH) { // Voice shouldn't be interrupted by other playback. - return C.AUDIOFOCUS_GAIN_TRANSIENT; + return AUDIOFOCUS_GAIN_TRANSIENT; } - return C.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; + return AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; default: Log.w(TAG, "Unidentified audio usage: " + audioAttributes.usage); - return C.AUDIOFOCUS_NONE; + return AUDIOFOCUS_NONE; } } From 6fc5f9800ecd1ec1045efcbcb55f9453e68d6ba1 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:29:02 +0000 Subject: [PATCH 143/251] Mark @C.SelectionReason as TYPE_USE (only) and use it in more places This is not backwards compatible if the @SelectionReason annotation is used in Kotlin code, but before this change there aren't many library surfaces that return a value annotated with @SelectionReason, so it seems relatively unlikely that it is in use in any/many apps. A follow-up change will fix the positions of existing usages to match this new config. #minor-release PiperOrigin-RevId: 426409877 --- .../common/src/main/java/androidx/media3/common/C.java | 1 + .../media3/exoplayer/source/MediaLoadData.java | 2 +- .../media3/exoplayer/source/MergingMediaPeriod.java | 2 +- .../trackselection/AdaptiveTrackSelection.java | 6 +++--- .../exoplayer/trackselection/ExoTrackSelection.java | 1 + .../exoplayer/trackselection/FixedTrackSelection.java | 10 +++++++--- .../androidx/media3/exoplayer/hls/HlsChunkSource.java | 2 +- .../androidx/media3/test/utils/FakeTrackSelection.java | 2 +- .../androidx/media3/test/utils/MediaPeriodAsserts.java | 2 +- 9 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 594706bb30..084d3a4b61 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -745,6 +745,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( open = true, value = { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java index 8a0cf3f371..f3a6a04ed5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java @@ -43,7 +43,7 @@ public final class MediaLoadData { * One of the {@link SelectionReason selection reasons} if the data belongs to a track. {@link * C#SELECTION_REASON_UNKNOWN} otherwise. */ - public final int trackSelectionReason; + public final @C.SelectionReason int trackSelectionReason; /** * Optional data associated with the selection of the track to which the data belongs. Null if the * data does not belong to a track. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java index 584c40a378..cc6b34ebb4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java @@ -548,7 +548,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return trackSelection.getSelectionReason(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java index 3e4dd4d41d..40dfa34a12 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java @@ -316,7 +316,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { private float playbackSpeed; private int selectedIndex; - private int reason; + private @C.SelectionReason int reason; private long lastBufferEvaluationMs; @Nullable private MediaChunk lastBufferEvaluationMediaChunk; @@ -449,7 +449,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { } int previousSelectedIndex = selectedIndex; - int previousReason = reason; + @C.SelectionReason int previousReason = reason; int formatIndexOfPreviousChunk = queue.isEmpty() ? C.INDEX_UNSET : indexOf(Iterables.getLast(queue).trackFormat); if (formatIndexOfPreviousChunk != C.INDEX_UNSET) { @@ -487,7 +487,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return reason; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ExoTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ExoTrackSelection.java index 858e557b08..b8df05df1a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ExoTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/ExoTrackSelection.java @@ -125,6 +125,7 @@ public interface ExoTrackSelection extends TrackSelection { int getSelectedIndex(); /** Returns the reason for the current track selection. */ + @C.SelectionReason int getSelectionReason(); /** Returns optional data associated with the current track selection. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/FixedTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/FixedTrackSelection.java index 8eacc3688f..d6c57bb730 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/FixedTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/FixedTrackSelection.java @@ -28,7 +28,7 @@ import java.util.List; @UnstableApi public final class FixedTrackSelection extends BaseTrackSelection { - private final int reason; + private final @C.SelectionReason int reason; @Nullable private final Object data; /** @@ -56,7 +56,11 @@ public final class FixedTrackSelection extends BaseTrackSelection { * @param data Optional data associated with the track selection. */ public FixedTrackSelection( - TrackGroup group, int track, @Type int type, int reason, @Nullable Object data) { + TrackGroup group, + int track, + @Type int type, + @C.SelectionReason int reason, + @Nullable Object data) { super(group, /* tracks= */ new int[] {track}, type); this.reason = reason; this.data = data; @@ -78,7 +82,7 @@ public final class FixedTrackSelection extends BaseTrackSelection { } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return reason; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index b2f0f200df..e054b113ca 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -915,7 +915,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java index 129d706e7c..0308109666 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java @@ -116,7 +116,7 @@ public final class FakeTrackSelection implements ExoTrackSelection { } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaPeriodAsserts.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaPeriodAsserts.java index 3a9c0f2a7a..71e0a49fe4 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaPeriodAsserts.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/MediaPeriodAsserts.java @@ -240,7 +240,7 @@ public final class MediaPeriodAsserts { } @Override - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } From 8554ae5a02242cc3430853a05781bd50a3514178 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:30:09 +0000 Subject: [PATCH 144/251] Make @LogLevel public and TYPE_USE #minor-release PiperOrigin-RevId: 426410137 --- .../src/main/java/androidx/media3/common/util/Log.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Log.java b/libraries/common/src/main/java/androidx/media3/common/util/Log.java index beb8810fee..98b61a6290 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Log.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Log.java @@ -15,6 +15,8 @@ */ package androidx.media3.common.util; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.text.TextUtils; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -22,6 +24,7 @@ import androidx.annotation.Size; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.UnknownHostException; import org.checkerframework.dataflow.qual.Pure; @@ -35,8 +38,9 @@ public final class Log { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({LOG_LEVEL_ALL, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR, LOG_LEVEL_OFF}) - @interface LogLevel {} + public @interface LogLevel {} /** Log level to log all messages. */ public static final int LOG_LEVEL_ALL = 0; /** Log level to only log informative, warning and error messages. */ From 01c814e2f0eaea7c2677e8748fa96e59e1d816ca Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 16:30:40 +0000 Subject: [PATCH 145/251] Mark all public 'rarely used' IntDefs as only TYPE_USE This is a breaking change if the annotation itself is in use in Kotlin code. It's judged that the IntDefs in this commit are unlikely to be referred to often in Kotlin code. This is because they're either: - Related to esoteric parts of the library, or - In a common part of the library but only returned from methods (and never passed to callback methods). A follow-up change will fix the positions of existing usages to match this new config. #minor-release PiperOrigin-RevId: 426410237 --- .../main/java/androidx/media3/common/C.java | 11 ++++ .../androidx/media3/common/FileTypes.java | 3 ++ .../media3/common/PlaybackException.java | 1 + .../media3/common/text/TextAnnotation.java | 3 ++ .../media3/common/text/TextEmphasisSpan.java | 4 ++ .../media3/common/util/EGLSurfaceTexture.java | 4 ++ .../media3/common/util/NotificationUtil.java | 3 ++ .../androidx/media3/datasource/DataSpec.java | 5 ++ .../media3/datasource/HttpDataSource.java | 4 ++ .../datasource/cache/CacheDataSource.java | 3 ++ .../media3/decoder/DecoderInputBuffer.java | 4 ++ .../media3/decoder/flac/FlacExtractor.java | 3 ++ .../media3/exoplayer/AudioFocusManager.java | 1 + .../exoplayer/DecoderReuseEvaluation.java | 4 ++ .../exoplayer/DefaultRenderersFactory.java | 4 ++ .../androidx/media3/exoplayer/Renderer.java | 54 ++++++++++--------- .../exoplayer/RendererCapabilities.java | 9 ++++ .../media3/exoplayer/audio/AudioSink.java | 4 ++ .../exoplayer/audio/DefaultAudioSink.java | 4 ++ .../drm/DefaultDrmSessionManager.java | 3 ++ .../media3/exoplayer/drm/ExoMediaDrm.java | 4 ++ .../exoplayer/source/ClippingMediaSource.java | 3 ++ .../exoplayer/source/MergingMediaSource.java | 3 ++ .../media3/exoplayer/source/SampleStream.java | 5 ++ .../exoplayer/source/ads/AdsMediaSource.java | 3 ++ .../trackselection/DefaultTrackSelector.java | 1 + .../upstream/LoadErrorHandlingPolicy.java | 3 ++ .../hls/playlist/HlsMediaPlaylist.java | 3 ++ .../androidx/media3/extractor/AacUtil.java | 4 ++ .../androidx/media3/extractor/Ac3Util.java | 4 ++ .../media3/extractor/BinarySearchSeeker.java | 4 ++ .../androidx/media3/extractor/Extractor.java | 4 ++ .../media3/extractor/TrackOutput.java | 4 ++ .../media3/extractor/amr/AmrExtractor.java | 4 ++ .../media3/extractor/flac/FlacExtractor.java | 1 + .../media3/extractor/mkv/EbmlProcessor.java | 4 ++ .../extractor/mkv/MatroskaExtractor.java | 3 ++ .../media3/extractor/mp3/Mp3Extractor.java | 4 ++ .../extractor/mp4/FragmentedMp4Extractor.java | 3 ++ .../media3/extractor/mp4/Mp4Extractor.java | 1 + .../androidx/media3/extractor/mp4/Track.java | 4 ++ .../extractor/text/webvtt/WebvttCssStyle.java | 2 + .../media3/extractor/ts/AdtsExtractor.java | 3 ++ .../ts/DefaultTsPayloadReaderFactory.java | 4 ++ .../media3/extractor/ts/TsExtractor.java | 3 ++ .../media3/extractor/ts/TsPayloadReader.java | 4 ++ .../session/PlayerNotificationManager.java | 4 ++ .../media3/transformer/Transformer.java | 3 ++ .../media3/ui/AspectRatioFrameLayout.java | 4 ++ .../media3/ui/CaptionStyleCompat.java | 4 ++ .../androidx/media3/ui/LegacyPlayerView.java | 3 ++ .../media3/ui/PlayerNotificationManager.java | 4 ++ .../java/androidx/media3/ui/PlayerView.java | 3 ++ .../java/androidx/media3/ui/SubtitleView.java | 3 ++ 54 files changed, 220 insertions(+), 24 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 084d3a4b61..f22b9b4e85 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -163,6 +163,7 @@ public final class C { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC}) @UnstableApi public @interface CryptoMode {} @@ -191,6 +192,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ Format.NO_VALUE, ENCODING_INVALID, @@ -226,6 +228,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ Format.NO_VALUE, ENCODING_INVALID, @@ -484,6 +487,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { @@ -516,6 +520,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef(value = {VIDEO_OUTPUT_MODE_NONE, VIDEO_OUTPUT_MODE_YUV, VIDEO_OUTPUT_MODE_SURFACE_YUV}) public @interface VideoOutputMode {} /** Video decoder output mode is not set. */ @@ -651,6 +656,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( open = true, value = { @@ -849,6 +855,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ Format.NO_VALUE, STEREO_MODE_MONO, @@ -876,6 +883,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_SPACE_BT709, COLOR_SPACE_BT601, COLOR_SPACE_BT2020}) public @interface ColorSpace {} /** @see MediaFormat#COLOR_STANDARD_BT709 */ @@ -892,6 +900,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_TRANSFER_SDR, COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG}) public @interface ColorTransfer {} /** @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO */ @@ -908,6 +917,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL}) public @interface ColorRange {} /** @see MediaFormat#COLOR_RANGE_LIMITED */ @@ -919,6 +929,7 @@ public final class C { @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ Format.NO_VALUE, PROJECTION_RECTANGULAR, diff --git a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java index d6400447bd..50a988f980 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java @@ -16,6 +16,7 @@ package androidx.media3.common; import static androidx.media3.common.MimeTypes.normalizeMimeType; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import androidx.annotation.IntDef; @@ -25,6 +26,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; import java.util.Map; @@ -39,6 +41,7 @@ public final class FileTypes { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG }) diff --git a/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java b/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java index 7193467a74..4e7850de45 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java +++ b/libraries/common/src/main/java/androidx/media3/common/PlaybackException.java @@ -410,6 +410,7 @@ public class PlaybackException extends Exception implements Bundleable { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( open = true, value = { diff --git a/libraries/common/src/main/java/androidx/media3/common/text/TextAnnotation.java b/libraries/common/src/main/java/androidx/media3/common/text/TextAnnotation.java index 4edf7a60ae..e96c6cf60e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/TextAnnotation.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/TextAnnotation.java @@ -15,12 +15,14 @@ */ package androidx.media3.common.text; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; /** Properties of a text annotation (i.e. ruby, text emphasis marks). */ @UnstableApi @@ -57,6 +59,7 @@ public final class TextAnnotation { */ @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({POSITION_UNKNOWN, POSITION_BEFORE, POSITION_AFTER}) public @interface Position {} diff --git a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java index b853f24cad..68d6e236e5 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java @@ -15,12 +15,14 @@ */ package androidx.media3.common.text; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; /** * A styling span for text emphasis marks. @@ -50,6 +52,7 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan { */ @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({MARK_SHAPE_NONE, MARK_SHAPE_CIRCLE, MARK_SHAPE_DOT, MARK_SHAPE_SESAME}) public @interface MarkShape {} @@ -71,6 +74,7 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan { */ @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({MARK_FILL_UNKNOWN, MARK_FILL_FILLED, MARK_FILL_OPEN}) public @interface MarkFill {} diff --git a/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java b/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java index fb4b561845..ee76ed476b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/EGLSurfaceTexture.java @@ -15,6 +15,8 @@ */ package androidx.media3.common.util; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.opengl.EGLConfig; @@ -29,6 +31,7 @@ import androidx.annotation.RequiresApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Generates a {@link SurfaceTexture} using EGL/GLES functions. */ @RequiresApi(17) @@ -47,6 +50,7 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER}) public @interface SecureMode {} diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java index bca6f0e589..e857b0817c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java @@ -16,6 +16,7 @@ package androidx.media3.common.util; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.app.Notification; @@ -29,6 +30,7 @@ import androidx.annotation.StringRes; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Utility methods for displaying {@link Notification Notifications}. */ @SuppressLint("InlinedApi") @@ -42,6 +44,7 @@ public final class NotificationUtil { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ IMPORTANCE_UNSPECIFIED, IMPORTANCE_NONE, diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java index 4f8f8667f9..9927ec238d 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java @@ -15,6 +15,8 @@ */ package androidx.media3.datasource; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.net.Uri; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -25,6 +27,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -234,6 +237,7 @@ public final class DataSpec { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { @@ -276,6 +280,7 @@ public final class DataSpec { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD}) public @interface HttpMethod {} /** HTTP GET method. */ diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java index 5564dfa0b3..4352008b78 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java @@ -15,6 +15,8 @@ */ package androidx.media3.datasource; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.text.TextUtils; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -28,6 +30,7 @@ import java.io.InterruptedIOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.SocketTimeoutException; import java.util.Collections; import java.util.HashMap; @@ -190,6 +193,7 @@ public interface HttpDataSource extends DataSource { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({TYPE_OPEN, TYPE_READ, TYPE_CLOSE}) public @interface Type {} diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java index 56b736f739..8802d8b187 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java @@ -18,6 +18,7 @@ package androidx.media3.datasource.cache; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import androidx.annotation.IntDef; @@ -42,6 +43,7 @@ import java.io.InterruptedIOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.List; import java.util.Map; @@ -330,6 +332,7 @@ public final class CacheDataSource implements DataSource { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java index 276aaa2fdd..05a0164493 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java @@ -15,6 +15,8 @@ */ package androidx.media3.decoder; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -24,6 +26,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; @@ -68,6 +71,7 @@ public class DecoderInputBuffer extends Buffer { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ BUFFER_REPLACEMENT_MODE_DISABLED, BUFFER_REPLACEMENT_MODE_NORMAL, diff --git a/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacExtractor.java b/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacExtractor.java index e65e2de18a..8d2d84d209 100644 --- a/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacExtractor.java +++ b/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacExtractor.java @@ -16,6 +16,7 @@ package androidx.media3.decoder.flac; import static androidx.media3.common.util.Util.getPcmEncoding; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -41,6 +42,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -63,6 +65,7 @@ public final class FlacExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_DISABLE_ID3_METADATA}) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index d330c22b6a..68203349aa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -64,6 +64,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ PLAYER_COMMAND_DO_NOT_PLAY, PLAYER_COMMAND_WAIT_FOR_CALLBACK, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java index e02bddc7a3..0257b4af1d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java @@ -18,6 +18,7 @@ package androidx.media3.exoplayer; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotEmpty; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -27,6 +28,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * The result of an evaluation to determine whether a decoder can be reused for a new input format. @@ -37,6 +39,7 @@ public final class DecoderReuseEvaluation { /** Possible outcomes of the evaluation. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ REUSE_RESULT_NO, REUSE_RESULT_YES_WITH_FLUSH, @@ -59,6 +62,7 @@ public final class DecoderReuseEvaluation { /** Possible reasons why reuse is not possible. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 3d11c23acd..61e3c0f5e8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.content.Context; import android.media.MediaCodec; import android.media.PlaybackParams; @@ -43,6 +45,7 @@ import androidx.media3.exoplayer.video.spherical.CameraMotionRenderer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.ArrayList; @@ -62,6 +65,7 @@ public class DefaultRenderersFactory implements RenderersFactory { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({EXTENSION_RENDERER_MODE_OFF, EXTENSION_RENDERER_MODE_ON, EXTENSION_RENDERER_MODE_PREFER}) public @interface ExtensionRendererMode {} /** Do not allow use of extension renderers. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java index 0572c04ea8..850421e315 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.MediaCodec; import android.view.Surface; import androidx.annotation.IntDef; @@ -26,7 +28,6 @@ import androidx.media3.common.Format; import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; -import androidx.media3.exoplayer.PlayerMessage.Target; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.source.SampleStream; import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; @@ -36,6 +37,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Renders media read from a {@link SampleStream}. @@ -90,6 +92,7 @@ public interface Renderer extends PlayerMessage.Target { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( open = true, value = { @@ -108,8 +111,9 @@ public interface Renderer extends PlayerMessage.Target { public @interface MessageType {} /** * The type of a message that can be passed to a video renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload is normally a {@link Surface}, however - * some video renderers may accept other outputs (e.g., {@link VideoDecoderOutputBufferRenderer}). + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload is normally a {@link + * Surface}, however some video renderers may accept other outputs (e.g., {@link + * VideoDecoderOutputBufferRenderer}). * *

    If the receiving renderer does not support the payload type as an output, then it will clear * any existing output that it has. @@ -117,15 +121,15 @@ public interface Renderer extends PlayerMessage.Target { int MSG_SET_VIDEO_OUTPUT = 1; /** * A type of a message that can be passed to an audio renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be a {@link Float} with 0 being - * silence and 1 being unity gain. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be a {@link Float} + * with 0 being silence and 1 being unity gain. */ int MSG_SET_VOLUME = 2; /** * A type of a message that can be passed to an audio renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be an {@link AudioAttributes} - * instance that will configure the underlying audio track. If not set, the default audio - * attributes will be used. They are suitable for general media playback. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be an {@link + * AudioAttributes} instance that will configure the underlying audio track. If not set, the + * default audio attributes will be used. They are suitable for general media playback. * *

    Setting the audio attributes during playback may introduce a short gap in audio output as * the audio track is recreated. A new audio session id will also be generated. @@ -144,8 +148,8 @@ public interface Renderer extends PlayerMessage.Target { int MSG_SET_AUDIO_ATTRIBUTES = 3; /** * The type of a message that can be passed to a {@link MediaCodec}-based video renderer via - * {@link ExoPlayer#createMessage(Target)}. The message payload should be one of the integer - * scaling modes in {@link C.VideoScalingMode}. + * {@link ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be one of the + * integer scaling modes in {@link C.VideoScalingMode}. * *

    Note that the scaling mode only applies if the {@link Surface} targeted by the renderer is * owned by a {@link android.view.SurfaceView}. @@ -153,45 +157,46 @@ public interface Renderer extends PlayerMessage.Target { int MSG_SET_SCALING_MODE = 4; /** * The type of a message that can be passed to a video renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be one of the integer strategy - * constants in {@link C.VideoChangeFrameRateStrategy}. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be one of the + * integer strategy constants in {@link C.VideoChangeFrameRateStrategy}. */ int MSG_SET_CHANGE_FRAME_RATE_STRATEGY = 5; /** * A type of a message that can be passed to an audio renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be an {@link AuxEffectInfo} - * instance representing an auxiliary audio effect for the underlying audio track. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be an {@link + * AuxEffectInfo} instance representing an auxiliary audio effect for the underlying audio track. */ int MSG_SET_AUX_EFFECT_INFO = 6; /** * The type of a message that can be passed to a video renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be a {@link + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be a {@link * VideoFrameMetadataListener} instance, or null. */ int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = 7; /** * The type of a message that can be passed to a camera motion renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be a {@link CameraMotionListener} - * instance, or null. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be a {@link + * CameraMotionListener} instance, or null. */ int MSG_SET_CAMERA_MOTION_LISTENER = 8; /** * The type of a message that can be passed to an audio renderer via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be a {@link Boolean} instance - * telling whether to enable or disable skipping silences in the audio stream. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be a {@link Boolean} + * instance telling whether to enable or disable skipping silences in the audio stream. */ int MSG_SET_SKIP_SILENCE_ENABLED = 9; /** * The type of a message that can be passed to audio and video renderers via {@link - * ExoPlayer#createMessage(Target)}. The message payload should be an {@link Integer} instance - * representing the audio session ID that will be attached to the underlying audio track. Video - * renderers that support tunneling will use the audio session ID when tunneling is enabled. + * ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be an {@link + * Integer} instance representing the audio session ID that will be attached to the underlying + * audio track. Video renderers that support tunneling will use the audio session ID when + * tunneling is enabled. */ int MSG_SET_AUDIO_SESSION_ID = 10; /** * The type of a message that can be passed to a {@link Renderer} via {@link - * ExoPlayer#createMessage(Target)}, to inform the renderer that it can schedule waking up another - * component. + * ExoPlayer#createMessage(PlayerMessage.Target)}, to inform the renderer that it can schedule + * waking up another component. * *

    The message payload must be a {@link WakeupListener} instance. */ @@ -208,6 +213,7 @@ public interface Renderer extends PlayerMessage.Target { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_STARTED}) @interface State {} /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index 53fe394e47..755b2c77b7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.annotation.SuppressLint; import androidx.annotation.IntDef; import androidx.media3.common.C; @@ -23,6 +25,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Defines the capabilities of a {@link Renderer}. */ @UnstableApi @@ -32,6 +35,7 @@ public interface RendererCapabilities { @SuppressWarnings("deprecation") @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ FORMAT_HANDLED, FORMAT_EXCEEDS_CAPABILITIES, @@ -60,6 +64,7 @@ public interface RendererCapabilities { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ADAPTIVE_SEAMLESS, ADAPTIVE_NOT_SEAMLESS, ADAPTIVE_NOT_SUPPORTED}) @interface AdaptiveSupport {} @@ -81,6 +86,7 @@ public interface RendererCapabilities { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({TUNNELING_SUPPORTED, TUNNELING_NOT_SUPPORTED}) @interface TunnelingSupport {} @@ -99,6 +105,7 @@ public interface RendererCapabilities { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ HARDWARE_ACCELERATION_SUPPORTED, HARDWARE_ACCELERATION_NOT_SUPPORTED, @@ -119,6 +126,7 @@ public interface RendererCapabilities { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ DECODER_SUPPORT_PRIMARY, DECODER_SUPPORT_FALLBACK, @@ -167,6 +175,7 @@ public interface RendererCapabilities { @Documented @Retention(RetentionPolicy.SOURCE) // Intentionally empty to prevent assignment or comparison with individual flags without masking. + @Target(TYPE_USE) @IntDef({}) @interface Capabilities {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioSink.java index 65d8a1b954..04d91a3d05 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioSink.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.audio; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.AudioTrack; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -31,6 +33,7 @@ import androidx.media3.exoplayer.analytics.PlayerId; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; /** @@ -260,6 +263,7 @@ public interface AudioSink { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ SINK_FORMAT_SUPPORTED_DIRECTLY, SINK_FORMAT_SUPPORTED_WITH_TRANSCODING, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index bfdfb580b9..d9859a99e6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -21,6 +21,7 @@ import static androidx.media3.exoplayer.audio.AudioCapabilities.DEFAULT_AUDIO_CA import static com.google.common.base.MoreObjects.firstNonNull; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.media.AudioFormat; @@ -58,6 +59,7 @@ import com.google.errorprone.annotations.InlineMeValidationDisabled; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayDeque; @@ -391,6 +393,7 @@ public final class DefaultAudioSink implements AudioSink { /** Audio offload mode configuration. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ OFFLOAD_MODE_DISABLED, OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED, @@ -428,6 +431,7 @@ public final class DefaultAudioSink implements AudioSink { /** Output mode of the audio sink. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({OUTPUT_MODE_PCM, OUTPUT_MODE_OFFLOAD, OUTPUT_MODE_PASSTHROUGH}) public @interface OutputMode {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java index 07fae53002..1a3c8ebcf9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java @@ -19,6 +19,7 @@ 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.Assertions.checkStateNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.media.ResourceBusyException; @@ -49,6 +50,7 @@ import com.google.common.collect.Sets; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -263,6 +265,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({MODE_PLAYBACK, MODE_QUERY, MODE_DOWNLOAD, MODE_RELEASE}) public @interface Mode {} /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java index c88f2a934b..5557e00392 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.drm; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.DeniedByServerException; import android.media.MediaCryptoException; import android.media.MediaDrm; @@ -33,6 +35,7 @@ import androidx.media3.exoplayer.analytics.PlayerId; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -228,6 +231,7 @@ public interface ExoMediaDrm { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ REQUEST_TYPE_UNKNOWN, REQUEST_TYPE_INITIAL, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java index b798b969d2..4263da5d62 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.source; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -32,6 +33,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; /** @@ -50,6 +52,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({REASON_INVALID_PERIOD_COUNT, REASON_NOT_SEEKABLE_TO_START, REASON_START_EXCEEDS_END}) public @interface Reason {} /** The wrapped source doesn't consist of a single period. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java index 17f9e4df8e..c9c589fb74 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.source; import static androidx.media3.common.util.Assertions.checkNotNull; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -32,6 +33,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -52,6 +54,7 @@ public final class MergingMediaSource extends CompositeMediaSource { /** The reason the merge failed. One of {@link #REASON_PERIOD_COUNT_MISMATCH}. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({REASON_PERIOD_COUNT_MISMATCH}) public @interface Reason {} /** The sources have different period counts. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SampleStream.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SampleStream.java index b6d3946597..dc088cd117 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SampleStream.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SampleStream.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.source; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.util.UnstableApi; @@ -25,6 +27,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A stream of media samples (and associated format information). */ @UnstableApi @@ -36,6 +39,7 @@ public interface SampleStream { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_PEEK, FLAG_REQUIRE_FORMAT, FLAG_OMIT_SAMPLE_DATA}) @@ -69,6 +73,7 @@ public interface SampleStream { /** Return values of {@link #readData}. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({C.RESULT_NOTHING_READ, C.RESULT_FORMAT_READ, C.RESULT_BUFFER_READ}) @interface ReadDataResult {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java index 7cb73edc6f..74b3093156 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/AdsMediaSource.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.source.ads; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import android.os.Handler; @@ -47,6 +48,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -74,6 +76,7 @@ public final class AdsMediaSource extends CompositeMediaSource { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({TYPE_AD, TYPE_AD_GROUP, TYPE_ALL_ADS, TYPE_UNEXPECTED}) public @interface Type {} /** Type for when an ad failed to load. The ad will be skipped. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 5b83a7f22c..e8dc20c7e3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -1428,6 +1428,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({SELECTION_ELIGIBILITY_NO, SELECTION_ELIGIBILITY_FIXED, SELECTION_ELIGIBILITY_ADAPTIVE}) protected @interface SelectionEligibility {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java index d1ae4fb4ae..fd40859f2d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.upstream; import static androidx.media3.common.util.Assertions.checkArgument; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -29,6 +30,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * A policy that defines how load errors are handled. @@ -54,6 +56,7 @@ public interface LoadErrorHandlingPolicy { /** Fallback type. One of {@link #FALLBACK_TYPE_LOCATION} or {@link #FALLBACK_TYPE_TRACK}. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({FALLBACK_TYPE_LOCATION, FALLBACK_TYPE_TRACK}) @interface FallbackType {} diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java index efe7bb89a0..c6a43c9eec 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java @@ -17,6 +17,7 @@ package androidx.media3.exoplayer.hls.playlist; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; import androidx.annotation.IntDef; @@ -31,6 +32,7 @@ import com.google.common.collect.Iterables; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -387,6 +389,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({PLAYLIST_TYPE_UNKNOWN, PLAYLIST_TYPE_VOD, PLAYLIST_TYPE_EVENT}) public @interface PlaylistType {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/AacUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/AacUtil.java index 929dbc841b..9c72d87966 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/AacUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/AacUtil.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.ParserException; @@ -24,6 +26,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Utility methods for handling AAC audio streams. */ @UnstableApi @@ -159,6 +162,7 @@ public final class AacUtil { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ AUDIO_OBJECT_TYPE_AAC_LC, AUDIO_OBJECT_TYPE_AAC_SBR, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/Ac3Util.java b/libraries/extractor/src/main/java/androidx/media3/extractor/Ac3Util.java index e6536a66b1..e4a61f3e0b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/Ac3Util.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/Ac3Util.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -29,6 +31,7 @@ import androidx.media3.extractor.Ac3Util.SyncFrameInfo.StreamType; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; /** @@ -47,6 +50,7 @@ public final class Ac3Util { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({STREAM_TYPE_UNDEFINED, STREAM_TYPE_TYPE0, STREAM_TYPE_TYPE1, STREAM_TYPE_TYPE2}) public @interface StreamType {} /** Undefined AC3 stream type. */ diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java index 224de9d730..1ff09dfdff 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -25,6 +27,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * A seeker that supports seeking within a stream by searching for the target frame using binary @@ -407,6 +410,7 @@ public abstract class BinarySearchSeeker { @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ TYPE_TARGET_TIMESTAMP_FOUND, TYPE_POSITION_OVERESTIMATED, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java index 3b221a605d..2c67cf99c5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/Extractor.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.util.UnstableApi; @@ -22,6 +24,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Extracts media data from a container format. */ @UnstableApi @@ -51,6 +54,7 @@ public interface Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef(value = {RESULT_CONTINUE, RESULT_SEEK, RESULT_END_OF_INPUT}) @interface ReadResult {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java index 947fc000f6..710e11a7ad 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -27,6 +29,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; /** Receives track level data extracted by an {@link Extractor}. */ @@ -95,6 +98,7 @@ public interface TrackOutput { /** Defines the part of the sample data to which a call to {@link #sampleData} corresponds. */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({SAMPLE_DATA_PART_MAIN, SAMPLE_DATA_PART_ENCRYPTION, SAMPLE_DATA_PART_SUPPLEMENTAL}) @interface SampleDataPart {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java index ba8610101a..5a4b3e1f52 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.amr; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -38,6 +40,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -62,6 +65,7 @@ public final class AmrExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_ENABLE_CONSTANT_BITRATE_SEEKING, FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS}) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java index bf005a2317..2eacd85b1f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flac/FlacExtractor.java @@ -67,6 +67,7 @@ public final class FlacExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_DISABLE_ID3_METADATA}) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/EbmlProcessor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/EbmlProcessor.java index cab9f2d7be..762e897d9d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/EbmlProcessor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/EbmlProcessor.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.mkv; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.ParserException; import androidx.media3.common.util.UnstableApi; @@ -23,6 +25,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Defines EBML element IDs/types and processes events. */ @UnstableApi @@ -35,6 +38,7 @@ public interface EbmlProcessor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ ELEMENT_TYPE_UNKNOWN, ELEMENT_TYPE_MASTER, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index c2222d5008..6442ad7993 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -21,6 +21,7 @@ import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import android.util.SparseArray; @@ -59,6 +60,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; @@ -87,6 +89,7 @@ public class MatroskaExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_DISABLE_SEEK_FOR_CUES}) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index b1bdadc0e9..936c7be490 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.mp3; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -47,6 +49,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -65,6 +68,7 @@ public final class Mp3Extractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 0b70c32d23..dc83015d88 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -21,6 +21,7 @@ import static androidx.media3.common.util.Util.castNonNull; import static androidx.media3.common.util.Util.nullSafeArrayCopy; import static androidx.media3.extractor.mp4.AtomParsers.parseTraks; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import android.util.SparseArray; @@ -57,6 +58,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -80,6 +82,7 @@ public class FragmentedMp4Extractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index c9b5bb0fe5..b2b8bc74a3 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -76,6 +76,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java index 16a125573c..2259bbae86 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.mp4; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -23,6 +25,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Encapsulates information describing an MP4 track. */ @UnstableApi @@ -34,6 +37,7 @@ public final class Track { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({TRANSFORMATION_NONE, TRANSFORMATION_CEA608_CDAT}) public @interface Transformation {} /** A no-op sample transformation. */ diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 168b5a76d7..29753e1d4f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -51,6 +51,7 @@ public final class WebvttCssStyle { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, STYLE_BOLD_ITALIC}) @@ -67,6 +68,7 @@ public final class WebvttCssStyle { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({UNSPECIFIED, FONT_SIZE_UNIT_PIXEL, FONT_SIZE_UNIT_EM, FONT_SIZE_UNIT_PERCENT}) public @interface FontSizeUnit {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsExtractor.java index 9936323ed1..b62e4dd9d5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsExtractor.java @@ -18,6 +18,7 @@ package androidx.media3.extractor.ts; import static androidx.media3.extractor.metadata.id3.Id3Decoder.ID3_HEADER_LENGTH; import static androidx.media3.extractor.metadata.id3.Id3Decoder.ID3_TAG; import static androidx.media3.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; +import static java.lang.annotation.ElementType.TYPE_USE; import androidx.annotation.IntDef; import androidx.media3.common.C; @@ -41,6 +42,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -58,6 +60,7 @@ public final class AdtsExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = {FLAG_ENABLE_CONSTANT_BITRATE_SEEKING, FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS}) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java index 9303d087c2..105059098f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.ts; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.util.SparseArray; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -28,6 +30,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; @@ -44,6 +47,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java index fd47e6f37e..aa2a36b02b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java @@ -16,6 +16,7 @@ package androidx.media3.extractor.ts; import static androidx.media3.extractor.ts.TsPayloadReader.FLAG_PAYLOAD_UNIT_START_INDICATOR; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -45,6 +46,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -65,6 +67,7 @@ public final class TsExtractor implements Extractor { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({MODE_MULTI_PMT, MODE_SINGLE_PMT, MODE_HLS}) public @interface Mode {} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsPayloadReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsPayloadReader.java index 86dd878b83..66ba7a1b91 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsPayloadReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsPayloadReader.java @@ -15,6 +15,8 @@ */ package androidx.media3.extractor.ts; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.util.SparseArray; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -27,6 +29,7 @@ import androidx.media3.extractor.TrackOutput; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; import java.util.List; @@ -178,6 +181,7 @@ public interface TsPayloadReader { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef( flag = true, value = { diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java index 6b55f5a26f..7796aa2624 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java @@ -32,6 +32,7 @@ import static androidx.media3.common.Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED; import static androidx.media3.common.Player.EVENT_TIMELINE_CHANGED; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkStateNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.app.Notification; import android.app.NotificationChannel; @@ -67,6 +68,7 @@ import com.google.common.util.concurrent.ListenableFuture; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -412,6 +414,7 @@ public class PlayerNotificationManager { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ NotificationCompat.VISIBILITY_PRIVATE, NotificationCompat.VISIBILITY_PUBLIC, @@ -427,6 +430,7 @@ public class PlayerNotificationManager { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ NotificationCompat.PRIORITY_DEFAULT, NotificationCompat.PRIORITY_MAX, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 26969d2211..ffc6bcf8ac 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -23,6 +23,7 @@ import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_BUFFER_FOR_PL import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.graphics.Matrix; @@ -63,6 +64,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -503,6 +505,7 @@ public final class Transformer { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ PROGRESS_STATE_WAITING_FOR_AVAILABILITY, PROGRESS_STATE_AVAILABLE, diff --git a/libraries/ui/src/main/java/androidx/media3/ui/AspectRatioFrameLayout.java b/libraries/ui/src/main/java/androidx/media3/ui/AspectRatioFrameLayout.java index 24f5eec38a..457559555e 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/AspectRatioFrameLayout.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/AspectRatioFrameLayout.java @@ -15,6 +15,8 @@ */ package androidx.media3.ui; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -25,6 +27,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A {@link FrameLayout} that resizes itself to match a specified aspect ratio. */ @UnstableApi @@ -53,6 +56,7 @@ public final class AspectRatioFrameLayout extends FrameLayout { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ RESIZE_MODE_FIT, RESIZE_MODE_FIXED_WIDTH, diff --git a/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java b/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java index 103638c3bc..45373a2aac 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/CaptionStyleCompat.java @@ -15,6 +15,8 @@ */ package androidx.media3.ui; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.graphics.Color; import android.graphics.Typeface; import android.view.accessibility.CaptioningManager; @@ -27,6 +29,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A compatibility wrapper for {@link CaptionStyle}. */ @UnstableApi @@ -39,6 +42,7 @@ public final class CaptionStyleCompat { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ EDGE_TYPE_NONE, EDGE_TYPE_OUTLINE, diff --git a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java index 5c2bd1545f..2aae797968 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java @@ -17,6 +17,7 @@ package androidx.media3.ui; import static androidx.media3.common.Player.COMMAND_GET_TEXT; import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.content.Context; @@ -67,6 +68,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; @@ -265,6 +267,7 @@ public class LegacyPlayerView extends FrameLayout implements AdViewProvider { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({SHOW_BUFFERING_NEVER, SHOW_BUFFERING_WHEN_PLAYING, SHOW_BUFFERING_ALWAYS}) public @interface ShowBuffering {} /** The buffering view is never shown. */ diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java index 142b84cb88..f5c5009c2e 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerNotificationManager.java @@ -30,6 +30,7 @@ import static androidx.media3.common.Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED; import static androidx.media3.common.Player.EVENT_TIMELINE_CHANGED; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; +import static java.lang.annotation.ElementType.TYPE_USE; import android.app.Notification; import android.app.NotificationChannel; @@ -59,6 +60,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -639,6 +641,7 @@ public class PlayerNotificationManager { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ NotificationCompat.VISIBILITY_PRIVATE, NotificationCompat.VISIBILITY_PUBLIC, @@ -654,6 +657,7 @@ public class PlayerNotificationManager { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({ NotificationCompat.PRIORITY_DEFAULT, NotificationCompat.PRIORITY_MAX, diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java index 7bc88cff91..158ff83b85 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java @@ -18,6 +18,7 @@ package androidx.media3.ui; import static androidx.media3.common.Player.COMMAND_GET_TEXT; import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; import android.content.Context; @@ -68,6 +69,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; @@ -187,6 +189,7 @@ public class PlayerView extends FrameLayout implements AdViewProvider { */ @Documented @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) @IntDef({SHOW_BUFFERING_NEVER, SHOW_BUFFERING_WHEN_PLAYING, SHOW_BUFFERING_ALWAYS}) public @interface ShowBuffering {} /** The buffering view is never shown. */ diff --git a/libraries/ui/src/main/java/androidx/media3/ui/SubtitleView.java b/libraries/ui/src/main/java/androidx/media3/ui/SubtitleView.java index b2bac223e7..f996337eaa 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/SubtitleView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/SubtitleView.java @@ -16,6 +16,7 @@ */ package androidx.media3.ui; +import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.content.Context; @@ -36,6 +37,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -113,6 +115,7 @@ public final class SubtitleView extends FrameLayout implements Player.Listener { */ @Documented @Retention(SOURCE) + @Target(TYPE_USE) @IntDef({VIEW_TYPE_CANVAS, VIEW_TYPE_WEB}) public @interface ViewType {} From 2522815d7b8e438b319709157f29bdaf17d57631 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 4 Feb 2022 17:55:43 +0000 Subject: [PATCH 146/251] Add TYPE_USE to 'frequently used' IntDefs in a backwards-compatible way This ensures Kotlin usages of these IntDef annotations in the 'old' position will continue to compile. 'Frequently used' is a subjective judgement. I have a parallel change that marks all the other public IntDefs in the library as TYPE_USE (those that I've judged to be 'rarely used' by apps). A follow-up change will fix the positions of existing usages to be as if they're only TYPE_USE. #minor-release PiperOrigin-RevId: 426427334 --- .../media3/common/AdPlaybackState.java | 7 +++++++ .../main/java/androidx/media3/common/C.java | 18 ++++++++++++++++++ .../media3/common/util/RepeatModeUtil.java | 10 ++++++++++ .../datasource/cache/CacheDataSource.java | 7 +++++++ .../media3/exoplayer/ExoPlaybackException.java | 10 ++++++++++ .../media3/exoplayer/ExoTimeoutException.java | 10 ++++++++++ .../exoplayer/analytics/AnalyticsListener.java | 9 +++++++++ .../media3/exoplayer/drm/DrmSession.java | 10 ++++++++++ .../androidx/media3/exoplayer/drm/DrmUtil.java | 10 ++++++++++ .../exoplayer/drm/UnsupportedDrmException.java | 13 ++++++++++--- .../media3/exoplayer/offline/Download.java | 13 +++++++++++++ .../exoplayer/scheduler/Requirements.java | 10 ++++++++++ .../trackselection/MappingTrackSelector.java | 9 +++++++++ 13 files changed, 133 insertions(+), 3 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index 22ca06106b..3b900b60e2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -18,6 +18,10 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; import static java.lang.Math.max; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; @@ -417,8 +421,11 @@ public final class AdPlaybackState implements Bundleable { * #AD_STATE_AVAILABLE}, {@link #AD_STATE_SKIPPED}, {@link #AD_STATE_PLAYED} or {@link * #AD_STATE_ERROR}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ AD_STATE_UNAVAILABLE, AD_STATE_AVAILABLE, diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index f22b9b4e85..a91181c595 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -303,10 +303,13 @@ public final class C { * #STREAM_TYPE_RING}, {@link #STREAM_TYPE_SYSTEM}, {@link #STREAM_TYPE_VOICE_CALL} or {@link * #STREAM_TYPE_DEFAULT}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @SuppressLint("UniqueConstants") // Intentional duplication to set STREAM_TYPE_DEFAULT. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ STREAM_TYPE_ALARM, STREAM_TYPE_DTMF, @@ -535,10 +538,13 @@ public final class C { * #VIDEO_SCALING_MODE_SCALE_TO_FIT}, {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} or * {@link #VIDEO_SCALING_MODE_DEFAULT}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @SuppressLint("UniqueConstants") // Intentional duplication to set VIDEO_SCALING_MODE_DEFAULT. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ VIDEO_SCALING_MODE_SCALE_TO_FIT, VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING, @@ -557,9 +563,12 @@ public final class C { @UnstableApi public static final int VIDEO_SCALING_MODE_DEFAULT = VIDEO_SCALING_MODE_SCALE_TO_FIT; /** Strategies for calling {@link Surface#setFrameRate}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF, VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS}) public @interface VideoChangeFrameRateStrategy {} /** @@ -614,9 +623,12 @@ public final class C { * Represents a streaming or other media type. One of {@link #TYPE_DASH}, {@link #TYPE_SS}, {@link * #TYPE_HLS}, {@link #TYPE_RTSP} or {@link #TYPE_OTHER}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({TYPE_DASH, TYPE_SS, TYPE_HLS, TYPE_RTSP, TYPE_OTHER}) public @interface ContentType {} /** Value returned by {@link Util#inferContentType(String)} for DASH manifests. */ @@ -967,9 +979,12 @@ public final class C { * #NETWORK_TYPE_4G}, {@link #NETWORK_TYPE_5G_SA}, {@link #NETWORK_TYPE_5G_NSA}, {@link * #NETWORK_TYPE_CELLULAR_UNKNOWN}, {@link #NETWORK_TYPE_ETHERNET} or {@link #NETWORK_TYPE_OTHER}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ NETWORK_TYPE_UNKNOWN, NETWORK_TYPE_OFFLINE, @@ -1130,9 +1145,12 @@ public final class C { * #FORMAT_EXCEEDS_CAPABILITIES}, {@link #FORMAT_UNSUPPORTED_DRM}, {@link * #FORMAT_UNSUPPORTED_SUBTYPE} or {@link #FORMAT_UNSUPPORTED_TYPE}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ FORMAT_HANDLED, FORMAT_EXCEEDS_CAPABILITIES, diff --git a/libraries/common/src/main/java/androidx/media3/common/util/RepeatModeUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/RepeatModeUtil.java index bba07a2707..4214e92dd9 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/RepeatModeUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/RepeatModeUtil.java @@ -15,11 +15,18 @@ */ package androidx.media3.common.util; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.Player; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Util class for repeat mode handling. */ @UnstableApi @@ -30,8 +37,11 @@ public final class RepeatModeUtil { * {@link #REPEAT_TOGGLE_MODE_NONE}, {@link #REPEAT_TOGGLE_MODE_ONE} and {@link * #REPEAT_TOGGLE_MODE_ALL}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef( flag = true, value = {REPEAT_TOGGLE_MODE_NONE, REPEAT_TOGGLE_MODE_ONE, REPEAT_TOGGLE_MODE_ALL}) diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java index 8802d8b187..d39cceb0f0 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java @@ -18,6 +18,10 @@ package androidx.media3.datasource.cache; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import android.net.Uri; @@ -364,8 +368,11 @@ public final class CacheDataSource implements DataSource { * Reasons the cache may be ignored. One of {@link #CACHE_IGNORED_REASON_ERROR} or {@link * #CACHE_IGNORED_REASON_UNSET_LENGTH}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({CACHE_IGNORED_REASON_ERROR, CACHE_IGNORED_REASON_UNSET_LENGTH}) public @interface CacheIgnoredReason {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java index 08aa9aca21..2a2b03ab0f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java @@ -15,6 +15,12 @@ */ package androidx.media3.exoplayer; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.os.Bundle; import android.os.SystemClock; import android.text.TextUtils; @@ -35,6 +41,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Thrown when a non locally recoverable playback failure occurs. */ public final class ExoPlaybackException extends PlaybackException { @@ -44,9 +51,12 @@ public final class ExoPlaybackException extends PlaybackException { * {@link #TYPE_UNEXPECTED} or {@link #TYPE_REMOTE}. Note that new types may be added in the * future and error handling should handle unknown type values. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @UnstableApi @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED, TYPE_REMOTE}) public @interface Type {} /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java index 93f9ac8474..cc1ca1404e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java @@ -15,12 +15,19 @@ */ package androidx.media3.exoplayer; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** A timeout of an operation on the ExoPlayer playback thread. */ @UnstableApi @@ -32,8 +39,11 @@ public final class ExoTimeoutException extends RuntimeException { * {@link #TIMEOUT_OPERATION_UNDEFINED}. Note that new operations may be added in the future and * error handling should handle unknown operation values. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ TIMEOUT_OPERATION_UNDEFINED, TIMEOUT_OPERATION_RELEASE, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 948f3e954a..59d8b333bc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -16,6 +16,11 @@ package androidx.media3.exoplayer.analytics; import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import android.media.MediaCodec; import android.media.MediaCodec.CodecException; @@ -60,6 +65,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * A listener for analytics events. @@ -155,8 +161,11 @@ public interface AnalyticsListener { * *

    One of the {@link AnalyticsListener}{@code .EVENT_*} flags. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ EVENT_TIMELINE_CHANGED, EVENT_MEDIA_ITEM_TRANSITION, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java index 5c1d17c065..73fcb430ee 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java @@ -15,6 +15,12 @@ */ package androidx.media3.exoplayer.drm; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.MediaDrm; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -25,6 +31,7 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Map; import java.util.UUID; @@ -70,8 +77,11 @@ public interface DrmSession { * The state of the DRM session. One of {@link #STATE_RELEASED}, {@link #STATE_ERROR}, {@link * #STATE_OPENING}, {@link #STATE_OPENED} or {@link #STATE_OPENED_WITH_KEYS}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({STATE_RELEASED, STATE_ERROR, STATE_OPENING, STATE_OPENED, STATE_OPENED_WITH_KEYS}) @interface State {} /** The session has been released. This is a terminal state. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java index dd22bb206d..ec43643855 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java @@ -15,6 +15,12 @@ */ package androidx.media3.exoplayer.drm; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.media.DeniedByServerException; import android.media.MediaDrm; import android.media.MediaDrmResetException; @@ -29,14 +35,18 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** DRM-related utility methods. */ @UnstableApi public final class DrmUtil { /** Identifies the operation which caused a DRM-related error. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef( value = { ERROR_SOURCE_EXO_MEDIA_DRM, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java index 34d5e80b84..f42dde626b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java @@ -15,11 +15,16 @@ */ package androidx.media3.exoplayer.drm; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Thrown when the requested DRM scheme is not supported. */ @UnstableApi @@ -29,8 +34,10 @@ public final class UnsupportedDrmException extends Exception { * The reason for the exception. One of {@link #REASON_UNSUPPORTED_SCHEME} or {@link * #REASON_INSTANTIATION_ERROR}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Retention(RetentionPolicy.SOURCE) @Documented - @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({REASON_UNSUPPORTED_SCHEME, REASON_INSTANTIATION_ERROR}) public @interface Reason {} /** The requested DRM scheme is unsupported by the device. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java index 0ba86073d5..9594592d6d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java @@ -15,6 +15,12 @@ */ package androidx.media3.exoplayer.offline; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import androidx.annotation.IntDef; import androidx.media3.common.C; import androidx.media3.common.util.Assertions; @@ -22,6 +28,7 @@ import androidx.media3.common.util.UnstableApi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Represents state of a download. */ @UnstableApi @@ -32,8 +39,11 @@ public final class Download { * #STATE_DOWNLOADING}, {@link #STATE_COMPLETED}, {@link #STATE_FAILED}, {@link #STATE_REMOVING} * or {@link #STATE_RESTARTING}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ STATE_QUEUED, STATE_STOPPED, @@ -71,8 +81,11 @@ public final class Download { public static final int STATE_RESTARTING = 7; /** Failure reasons. Either {@link #FAILURE_REASON_NONE} or {@link #FAILURE_REASON_UNKNOWN}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({FAILURE_REASON_NONE, FAILURE_REASON_UNKNOWN}) public @interface FailureReason {} /** The download isn't failed. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index 916ae5f824..34d435bb47 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -15,6 +15,12 @@ */ package androidx.media3.exoplayer.scheduler; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; + import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -34,6 +40,7 @@ import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** Defines a set of device state requirements. */ @UnstableApi @@ -43,8 +50,11 @@ public final class Requirements implements Parcelable { * Requirement flags. Possible flag values are {@link #NETWORK}, {@link #NETWORK_UNMETERED}, * {@link #DEVICE_IDLE}, {@link #DEVICE_CHARGING} and {@link #DEVICE_STORAGE_NOT_LOW}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef( flag = true, value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING, DEVICE_STORAGE_NOT_LOW}) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index 9e9051d646..980a5a7749 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -17,6 +17,11 @@ package androidx.media3.exoplayer.trackselection; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; import android.util.Pair; import androidx.annotation.IntDef; @@ -43,6 +48,7 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Arrays; import org.checkerframework.checker.nullness.compatqual.NullableType; @@ -62,8 +68,11 @@ public abstract class MappingTrackSelector extends TrackSelector { * {@link #RENDERER_SUPPORT_NO_TRACKS}, {@link #RENDERER_SUPPORT_UNSUPPORTED_TRACKS}, {@link * #RENDERER_SUPPORT_EXCEEDS_CAPABILITIES_TRACKS} or {@link #RENDERER_SUPPORT_PLAYABLE_TRACKS}. */ + // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility + // with Kotlin usages from before TYPE_USE was added. @Documented @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @IntDef({ RENDERER_SUPPORT_NO_TRACKS, RENDERER_SUPPORT_UNSUPPORTED_TRACKS, From 87420e5f9bd1671cd3068185f7358f5faba53e71 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 09:55:53 +0000 Subject: [PATCH 147/251] Fix the position of IntDefs to match TYPE_USE #minor-release PiperOrigin-RevId: 426855255 --- .../java/androidx/media3/cast/CastPlayer.java | 14 +++----- .../media3/common/AdPlaybackState.java | 2 +- .../androidx/media3/common/BasePlayer.java | 3 +- .../main/java/androidx/media3/common/C.java | 4 +-- .../androidx/media3/common/ColorInfo.java | 13 ++++--- .../androidx/media3/common/DrmInitData.java | 3 +- .../androidx/media3/common/FileTypes.java | 10 +++--- .../java/androidx/media3/common/Format.java | 12 +++---- .../androidx/media3/common/HeartRating.java | 2 +- .../androidx/media3/common/MediaMetadata.java | 4 +-- .../androidx/media3/common/MimeTypes.java | 6 ++-- .../media3/common/PercentageRating.java | 2 +- .../java/androidx/media3/common/Player.java | 3 +- .../androidx/media3/common/StarRating.java | 2 +- .../androidx/media3/common/ThumbRating.java | 2 +- .../androidx/media3/common/TrackGroup.java | 3 +- .../androidx/media3/common/TracksInfo.java | 8 ++--- .../java/androidx/media3/common/text/Cue.java | 17 ++++----- .../androidx/media3/common/text/RubySpan.java | 2 +- .../media3/common/text/TextEmphasisSpan.java | 6 ++-- .../common/util/NetworkTypeObserver.java | 12 +++---- .../androidx/media3/common/util/Util.java | 36 +++++++++---------- .../media3/common/util/XmlPullParserUtil.java | 7 ++-- .../datasource/DataSourceException.java | 2 +- .../androidx/media3/datasource/DataSpec.java | 8 ++--- .../media3/datasource/HttpDataSource.java | 6 ++-- .../datasource/cache/CacheDataSource.java | 2 +- .../java/androidx/media3/decoder/Buffer.java | 2 +- .../androidx/media3/decoder/CryptoInfo.java | 2 +- .../media3/decoder/DecoderInputBuffer.java | 2 +- .../decoder/VideoDecoderOutputBuffer.java | 2 +- .../media3/decoder/av1/Gav1Decoder.java | 2 +- .../decoder/av1/Libgav1VideoRenderer.java | 3 +- .../decoder/ffmpeg/FfmpegAudioDecoder.java | 5 ++- .../decoder/ffmpeg/FfmpegAudioRenderer.java | 6 ++-- .../decoder/ffmpeg/FfmpegVideoRenderer.java | 3 +- .../decoder/flac/LibflacAudioRenderer.java | 3 +- .../decoder/opus/LibopusAudioRenderer.java | 3 +- .../decoder/vp9/LibvpxVideoRenderer.java | 3 +- .../media3/decoder/vp9/VpxDecoder.java | 2 +- .../exoplayer/StreamVolumeManagerTest.java | 2 +- .../media3/exoplayer/AudioFocusManager.java | 9 +++-- .../media3/exoplayer/BaseRenderer.java | 6 ++-- .../exoplayer/DecoderReuseEvaluation.java | 4 +-- .../exoplayer/DefaultRenderersFactory.java | 2 +- .../exoplayer/ExoPlaybackException.java | 4 +-- .../media3/exoplayer/ExoPlayerImpl.java | 33 +++++++---------- .../exoplayer/ExoPlayerImplInternal.java | 9 ++--- .../media3/exoplayer/ExoTimeoutException.java | 2 +- .../media3/exoplayer/NoSampleRenderer.java | 6 ++-- .../media3/exoplayer/PlaybackInfo.java | 4 +-- .../exoplayer/RendererCapabilities.java | 25 +++++-------- .../media3/exoplayer/SimpleExoPlayer.java | 12 +++---- .../media3/exoplayer/StreamVolumeManager.java | 2 +- .../analytics/AnalyticsListener.java | 3 +- .../analytics/MediaMetricsListener.java | 4 +-- .../analytics/PlaybackStatsListener.java | 2 +- .../exoplayer/audio/AudioProcessor.java | 2 +- .../exoplayer/audio/DecoderAudioRenderer.java | 11 +++--- .../exoplayer/audio/DefaultAudioSink.java | 15 ++++---- .../exoplayer/audio/ForwardingAudioSink.java | 3 +- .../audio/MediaCodecAudioRenderer.java | 3 +- .../audio/SilenceSkippingAudioProcessor.java | 2 +- .../exoplayer/audio/SpatializerDelegate.java | 3 +- .../exoplayer/audio/TeeAudioProcessor.java | 2 +- .../audio/TrimmingAudioProcessor.java | 2 +- .../exoplayer/drm/DefaultDrmSession.java | 3 +- .../drm/DefaultDrmSessionManager.java | 5 ++- .../media3/exoplayer/drm/DrmSession.java | 2 +- .../exoplayer/drm/DrmSessionManager.java | 3 +- .../media3/exoplayer/drm/DrmUtil.java | 7 ++-- .../exoplayer/drm/DummyExoMediaDrm.java | 3 +- .../media3/exoplayer/drm/ExoMediaDrm.java | 5 ++- .../exoplayer/drm/FrameworkMediaDrm.java | 3 +- .../drm/UnsupportedDrmException.java | 2 +- .../media3/exoplayer/drm/WidevineUtil.java | 3 +- .../AsynchronousMediaCodecAdapter.java | 2 +- .../DefaultMediaCodecAdapterFactory.java | 2 +- .../mediacodec/MediaCodecRenderer.java | 19 +++++----- .../exoplayer/metadata/MetadataRenderer.java | 3 +- .../media3/exoplayer/offline/Download.java | 4 +-- .../exoplayer/offline/DownloadHelper.java | 3 +- .../exoplayer/offline/DownloadManager.java | 5 ++- .../exoplayer/scheduler/Requirements.java | 11 +++--- .../scheduler/RequirementsWatcher.java | 5 ++- .../exoplayer/source/ClippingMediaSource.java | 2 +- .../source/DefaultMediaSourceFactory.java | 3 +- .../exoplayer/source/MediaLoadData.java | 2 +- .../exoplayer/source/MediaSourceFactory.java | 3 +- .../exoplayer/source/MergingMediaSource.java | 2 +- .../source/ProgressiveMediaPeriod.java | 2 +- .../exoplayer/source/SilenceMediaSource.java | 2 +- .../ads/ServerSideAdInsertionMediaSource.java | 6 ++-- .../media3/exoplayer/source/chunk/Chunk.java | 4 +-- .../mediaparser/OutputConsumerAdapterV30.java | 3 +- .../exoplayer/text/ExoplayerCuesDecoder.java | 2 +- .../media3/exoplayer/text/TextRenderer.java | 5 ++- .../trackselection/DefaultTrackSelector.java | 28 ++++++--------- .../trackselection/MappingTrackSelector.java | 31 +++++++--------- .../upstream/DefaultBandwidthMeter.java | 4 +-- .../upstream/LoadErrorHandlingPolicy.java | 2 +- .../exoplayer/upstream/ParsingLoadable.java | 2 +- .../exoplayer/video/DecoderVideoRenderer.java | 4 +-- .../media3/exoplayer/video/DummySurface.java | 3 +- .../video/MediaCodecVideoRenderer.java | 5 ++- .../video/VideoFrameReleaseHelper.java | 2 +- .../video/spherical/CameraMotionRenderer.java | 3 +- .../video/spherical/ProjectionDecoder.java | 15 +++++--- .../video/spherical/SceneRenderer.java | 4 +-- .../media3/exoplayer/ExoPlayerTest.java | 2 +- .../audio/DecoderAudioRendererTest.java | 3 +- ...faultAudioTrackBufferSizeProviderTest.java | 6 ++-- .../DefaultTrackSelectorTest.java | 14 +++----- .../MappingTrackSelectorTest.java | 6 ++-- .../video/DecoderVideoRendererTest.java | 5 ++- .../video/MediaCodecVideoRendererTest.java | 4 +-- .../exoplayer/dash/DashMediaPeriod.java | 2 +- .../dash/manifest/DashManifestParser.java | 23 +++++------- .../hls/DefaultHlsExtractorFactory.java | 2 +- .../media3/exoplayer/hls/HlsChunkSource.java | 3 +- .../hls/playlist/HlsMediaPlaylist.java | 2 +- .../hls/playlist/HlsPlaylistParser.java | 7 ++-- .../media3/exoplayer/ima/AdTagLoader.java | 2 +- .../ServerSideAdInsertionStreamRequest.java | 4 +-- .../media3/exoplayer/ima/FakeExoPlayer.java | 8 ++--- .../rtsp/RtspAuthenticationInfo.java | 2 +- .../media3/exoplayer/rtsp/RtspClient.java | 5 ++- .../exoplayer/rtsp/RtspMediaPeriod.java | 3 +- .../exoplayer/rtsp/RtspMessageChannel.java | 2 +- .../exoplayer/rtsp/RtspMessageUtil.java | 3 +- .../media3/exoplayer/rtsp/RtspRequest.java | 2 +- .../exoplayer/rtsp/reader/RtpH264Reader.java | 5 ++- .../media3/extractor/BinarySearchSeeker.java | 2 +- .../extractor/DefaultExtractorsFactory.java | 18 +++++----- .../media3/extractor/TrackOutput.java | 2 +- .../extractor/TrueHdSampleRechunker.java | 2 +- .../media3/extractor/jpeg/JpegExtractor.java | 6 ++-- .../extractor/mkv/MatroskaExtractor.java | 18 +++++----- .../media3/extractor/mp3/Mp3Extractor.java | 2 +- .../media3/extractor/mp4/AtomParsers.java | 2 +- .../extractor/mp4/FragmentedMp4Extractor.java | 5 ++- .../media3/extractor/mp4/Mp4Extractor.java | 16 ++++----- .../media3/extractor/mp4/SefReader.java | 10 +++--- .../androidx/media3/extractor/mp4/Track.java | 2 +- .../extractor/mp4/TrackEncryptionBox.java | 3 +- .../extractor/text/SubtitleExtractor.java | 2 +- .../media3/extractor/text/ssa/SsaDecoder.java | 6 ++-- .../media3/extractor/text/ssa/SsaStyle.java | 10 +++--- .../extractor/text/ttml/TextEmphasis.java | 6 ++-- .../extractor/text/ttml/TtmlRegion.java | 8 ++--- .../media3/extractor/text/ttml/TtmlStyle.java | 28 +++++++-------- .../extractor/text/webvtt/WebvttCssStyle.java | 21 +++++------ .../text/webvtt/WebvttCueParser.java | 31 +++++++--------- .../media3/extractor/ts/Ac3Reader.java | 2 +- .../media3/extractor/ts/Ac4Reader.java | 2 +- .../ts/DefaultTsPayloadReaderFactory.java | 2 +- .../media3/extractor/ts/H263Reader.java | 2 +- .../media3/extractor/ts/TsExtractor.java | 6 ++-- .../media3/extractor/wav/WavExtractor.java | 7 ++-- .../extractor/mkv/DefaultEbmlReaderTest.java | 3 +- .../extractor/text/ttml/TtmlStyleTest.java | 2 +- .../androidx/media3/session/MockPlayer.java | 12 +++---- .../androidx/media3/test/utils/Action.java | 6 ++-- .../media3/test/utils/CapturingAudioSink.java | 2 +- .../media3/test/utils/DownloadBuilder.java | 2 +- .../media3/test/utils/DumpFileAsserts.java | 2 +- .../media3/test/utils/FakeExoMediaDrm.java | 3 +- .../media3/test/utils/FakeRenderer.java | 3 +- .../media3/test/utils/FakeSampleStream.java | 2 +- .../media3/test/utils/StubPlayer.java | 6 ++-- .../test/utils/WebServerDispatcher.java | 7 ++-- .../test/utils/truth/SpannedSubject.java | 8 ++--- .../TestDownloadManagerListener.java | 2 +- .../media3/transformer/Transformer.java | 5 ++- .../transformer/TransformerBaseRenderer.java | 3 +- 175 files changed, 439 insertions(+), 595 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 4d54065d0e..cc026ba69a 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -150,7 +150,7 @@ public final class CastPlayer extends BasePlayer { private TrackSelectionArray currentTrackSelection; private TracksInfo currentTracksInfo; private Commands availableCommands; - @Player.State private int playbackState; + private @Player.State int playbackState; private int currentWindowIndex; private long lastReportedPositionMs; private int pendingSeekCount; @@ -387,14 +387,12 @@ public final class CastPlayer extends BasePlayer { } @Override - @Player.State - public int getPlaybackState() { + public @Player.State int getPlaybackState() { return playbackState; } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { return Player.PLAYBACK_SUPPRESSION_REASON_NONE; } @@ -574,8 +572,7 @@ public final class CastPlayer extends BasePlayer { } @Override - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { return repeatMode.value; } @@ -1292,8 +1289,7 @@ public final class CastPlayer extends BasePlayer { * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link * Player.RepeatMode}. */ - @RepeatMode - private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { + private static @RepeatMode int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { MediaStatus mediaStatus = remoteMediaClient.getMediaStatus(); if (mediaStatus == null) { // No media session active, yet. diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index 3b900b60e2..b713f32ef3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -67,7 +67,7 @@ public final class AdPlaybackState implements Bundleable { /** The URI of each ad in the ad group. */ public final @NullableType Uri[] uris; /** The state of each ad in the ad group. */ - @AdState public final int[] states; + public final @AdState int[] states; /** The durations of each ad in the ad group, in microseconds. */ public final long[] durationsUs; /** diff --git a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java index f79df0ff90..d782dcb0e6 100644 --- a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java @@ -384,8 +384,7 @@ public abstract class BasePlayer implements Player { : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); } - @RepeatMode - private int getRepeatModeForNavigation() { + private @RepeatMode int getRepeatModeForNavigation() { @RepeatMode int repeatMode = getRepeatMode(); return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; } diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index a91181c595..ef48f66f6d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -1247,8 +1247,8 @@ public final class C { replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)", imports = {"androidx.media3.common.util.Util"}) @Deprecated - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( + int mediaDrmErrorCode) { return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode); } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java index fb27d2d18c..829262bb88 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java @@ -38,8 +38,7 @@ public final class ColorInfo implements Bundleable { * made. */ @Pure - @C.ColorSpace - public static int isoColorPrimariesToColorSpace(int isoColorPrimaries) { + public static @C.ColorSpace int isoColorPrimariesToColorSpace(int isoColorPrimaries) { switch (isoColorPrimaries) { case 1: return C.COLOR_SPACE_BT709; @@ -61,8 +60,8 @@ public final class ColorInfo implements Bundleable { * mapping can be made. */ @Pure - @C.ColorTransfer - public static int isoTransferCharacteristicsToColorTransfer(int isoTransferCharacteristics) { + public static @C.ColorTransfer int isoTransferCharacteristicsToColorTransfer( + int isoTransferCharacteristics) { switch (isoTransferCharacteristics) { case 1: // BT.709. case 6: // SMPTE 170M. @@ -81,20 +80,20 @@ public final class ColorInfo implements Bundleable { * The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link * C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown. */ - @C.ColorSpace public final int colorSpace; + public final @C.ColorSpace int colorSpace; /** * The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link * C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown. */ - @C.ColorRange public final int colorRange; + public final @C.ColorRange int colorRange; /** * The color transfer characteristics of the video. Valid values are {@link C#COLOR_TRANSFER_HLG}, * {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link Format#NO_VALUE} if * unknown. */ - @C.ColorTransfer public final int colorTransfer; + public final @C.ColorTransfer int colorTransfer; /** HdrStaticInfo as defined in CTA-861.3, or null if none specified. */ @Nullable public final byte[] hdrStaticInfo; diff --git a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java index 639fb7ee91..2d9e19caab 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java +++ b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java @@ -52,7 +52,8 @@ public final class DrmInitData implements Comparator, Parcelable { * @param mediaData DRM session acquisition data obtained from the media. * @return A {@link DrmInitData} obtained from merging a media manifest and a media stream. */ - public static @Nullable DrmInitData createSessionCreationData( + @Nullable + public static DrmInitData createSessionCreationData( @Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) { ArrayList result = new ArrayList<>(); String schemeType = null; diff --git a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java index 50a988f980..cc5fd41dee 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java @@ -114,8 +114,8 @@ public final class FileTypes { private FileTypes() {} /** Returns the {@link Type} corresponding to the response headers provided. */ - @FileTypes.Type - public static int inferFileTypeFromResponseHeaders(Map> responseHeaders) { + public static @FileTypes.Type int inferFileTypeFromResponseHeaders( + Map> responseHeaders) { @Nullable List contentTypes = responseHeaders.get(HEADER_CONTENT_TYPE); @Nullable String mimeType = contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.get(0); @@ -127,8 +127,7 @@ public final class FileTypes { * *

    Returns {@link #UNKNOWN} if the mime type is {@code null}. */ - @FileTypes.Type - public static int inferFileTypeFromMimeType(@Nullable String mimeType) { + public static @FileTypes.Type int inferFileTypeFromMimeType(@Nullable String mimeType) { if (mimeType == null) { return FileTypes.UNKNOWN; } @@ -178,8 +177,7 @@ public final class FileTypes { } /** Returns the {@link Type} corresponding to the {@link Uri} provided. */ - @FileTypes.Type - public static int inferFileTypeFromUri(Uri uri) { + public static @FileTypes.Type int inferFileTypeFromUri(Uri uri) { @Nullable String filename = uri.getLastPathSegment(); if (filename == null) { return FileTypes.UNKNOWN; diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index dd6460c9cf..773b9fab37 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -156,14 +156,14 @@ public final class Format implements Bundleable { private int rotationDegrees; private float pixelWidthHeightRatio; @Nullable private byte[] projectionData; - @C.StereoMode private int stereoMode; + private @C.StereoMode int stereoMode; @Nullable private ColorInfo colorInfo; // Audio specific. private int channelCount; private int sampleRate; - @C.PcmEncoding private int pcmEncoding; + private @C.PcmEncoding int pcmEncoding; private int encoderDelay; private int encoderPadding; @@ -173,7 +173,7 @@ public final class Format implements Bundleable { // Provided by the source. - @C.CryptoType private int cryptoType; + private @C.CryptoType int cryptoType; /** Creates a new instance with default values. */ public Builder() { @@ -727,7 +727,7 @@ public final class Format implements Bundleable { * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}. */ - @UnstableApi @C.StereoMode public final int stereoMode; + @UnstableApi public final @C.StereoMode int stereoMode; /** The color metadata associated with the video, or null if not applicable. */ @UnstableApi @Nullable public final ColorInfo colorInfo; @@ -738,7 +738,7 @@ public final class Format implements Bundleable { /** The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. */ public final int sampleRate; /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ - @UnstableApi @C.PcmEncoding public final int pcmEncoding; + @UnstableApi public final @C.PcmEncoding int pcmEncoding; /** * The number of frames to trim from the start of the decoded audio stream, or 0 if not * applicable. @@ -762,7 +762,7 @@ public final class Format implements Bundleable { * {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that * the samples are encrypted using an unsupported crypto type. */ - @UnstableApi @C.CryptoType public final int cryptoType; + @UnstableApi public final @C.CryptoType int cryptoType; // Lazily initialized hashcode. private int hashCode; diff --git a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java index 4a7117764c..08a6b405f3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java @@ -79,7 +79,7 @@ public final class HeartRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_HEART; + private static final @RatingType int TYPE = RATING_TYPE_HEART; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index 7a847c24e2..4d257fe7d4 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -56,11 +56,11 @@ public final class MediaMetadata implements Bundleable { @Nullable private Rating userRating; @Nullable private Rating overallRating; @Nullable private byte[] artworkData; - @Nullable @PictureType private Integer artworkDataType; + @Nullable private @PictureType Integer artworkDataType; @Nullable private Uri artworkUri; @Nullable private Integer trackNumber; @Nullable private Integer totalTrackCount; - @Nullable @FolderType private Integer folderType; + @Nullable private @FolderType Integer folderType; @Nullable private Boolean isPlayable; @Nullable private Integer recordingYear; @Nullable private Integer recordingMonth; diff --git a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java index e1958db3e1..2c6973d1e1 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java @@ -552,8 +552,7 @@ public final class MimeTypes { * @return The corresponding {@link C.Encoding}, or {@link C#ENCODING_INVALID}. */ @UnstableApi - @C.Encoding - public static int getEncoding(String mimeType, @Nullable String codec) { + public static @C.Encoding int getEncoding(String mimeType, @Nullable String codec) { switch (mimeType) { case MimeTypes.AUDIO_MPEG: return C.ENCODING_MP3; @@ -728,8 +727,7 @@ public final class MimeTypes { } /** Returns the encoding for {@link #audioObjectTypeIndication}. */ - @C.Encoding - public int getEncoding() { + public @C.Encoding int getEncoding() { // See AUDIO_OBJECT_TYPE_AAC_* constants in AacUtil. switch (audioObjectTypeIndication) { case 2: diff --git a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java index f110efba16..afc20a1687 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java @@ -77,7 +77,7 @@ public final class PercentageRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_PERCENTAGE; + private static final @RatingType int TYPE = RATING_TYPE_PERCENTAGE; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index aed681f729..1e7ed137e3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -686,8 +686,7 @@ public interface Player { @UnstableApi public static final class Builder { - @Command - private static final int[] SUPPORTED_COMMANDS = { + private static final @Command int[] SUPPORTED_COMMANDS = { COMMAND_PLAY_PAUSE, COMMAND_PREPARE, COMMAND_STOP, diff --git a/libraries/common/src/main/java/androidx/media3/common/StarRating.java b/libraries/common/src/main/java/androidx/media3/common/StarRating.java index c6547dde33..2c38f7cb75 100644 --- a/libraries/common/src/main/java/androidx/media3/common/StarRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/StarRating.java @@ -103,7 +103,7 @@ public final class StarRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_STAR; + private static final @RatingType int TYPE = RATING_TYPE_STAR; private static final int MAX_STARS_DEFAULT = 5; @Documented diff --git a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java index 3c68986b98..cd4ad73473 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java @@ -76,7 +76,7 @@ public final class ThumbRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_THUMB; + private static final @RatingType int TYPE = RATING_TYPE_THUMB; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java index f0c85adfb8..ad0bb81c23 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java @@ -207,8 +207,7 @@ public final class TrackGroup implements Bundleable { return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language; } - @C.RoleFlags - private static int normalizeRoleFlags(@C.RoleFlags int roleFlags) { + private static @C.RoleFlags int normalizeRoleFlags(@C.RoleFlags int roleFlags) { // Treat trick-play and non-trick-play formats as compatible. return roleFlags | C.ROLE_FLAG_TRICK_PLAY; } diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 066a92152e..437f7dda44 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -45,7 +45,7 @@ public final class TracksInfo implements Bundleable { */ public static final class TrackGroupInfo implements Bundleable { private final TrackGroup trackGroup; - @C.FormatSupport private final int[] trackSupport; + private final @C.FormatSupport int[] trackSupport; private final @C.TrackType int trackType; private final boolean[] trackSelected; @@ -83,8 +83,7 @@ public final class TracksInfo implements Bundleable { * @return The {@link C.FormatSupport} of the track. */ @UnstableApi - @C.FormatSupport - public int getTrackSupport(int trackIndex) { + public @C.FormatSupport int getTrackSupport(int trackIndex) { return trackSupport[trackIndex]; } @@ -229,8 +228,7 @@ public final class TracksInfo implements Bundleable { fromNullableBundle( TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP))); checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup - @C.FormatSupport - final int[] trackSupport = + final @C.FormatSupport int[] trackSupport = MoreObjects.firstNonNull( bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]); @C.TrackType diff --git a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java index d5f04a3540..6b9d9ded28 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java @@ -569,7 +569,7 @@ public final class Cue implements Bundleable { @Nullable private Alignment textAlignment; @Nullable private Alignment multiRowAlignment; private float line; - @LineType private int lineType; + private @LineType int lineType; private @AnchorType int lineAnchor; private float position; private @AnchorType int positionAnchor; @@ -730,8 +730,7 @@ public final class Cue implements Bundleable { * @see Cue#lineType */ @Pure - @LineType - public int getLineType() { + public @LineType int getLineType() { return lineType; } @@ -751,8 +750,7 @@ public final class Cue implements Bundleable { * @see Cue#lineAnchor */ @Pure - @AnchorType - public int getLineAnchor() { + public @AnchorType int getLineAnchor() { return lineAnchor; } @@ -794,8 +792,7 @@ public final class Cue implements Bundleable { * @see Cue#positionAnchor */ @Pure - @AnchorType - public int getPositionAnchor() { + public @AnchorType int getPositionAnchor() { return positionAnchor; } @@ -817,8 +814,7 @@ public final class Cue implements Bundleable { * @see Cue#textSizeType */ @Pure - @TextSizeType - public int getTextSizeType() { + public @TextSizeType int getTextSizeType() { return textSizeType; } @@ -936,8 +932,7 @@ public final class Cue implements Bundleable { * @see Cue#verticalType */ @Pure - @VerticalType - public int getVerticalType() { + public @VerticalType int getVerticalType() { return verticalType; } diff --git a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java index 9c6ba1275e..482cb7a92d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java @@ -39,7 +39,7 @@ public final class RubySpan implements LanguageFeatureSpan { public final String rubyText; /** The position of the ruby text relative to the base text. */ - @TextAnnotation.Position public final int position; + public final @TextAnnotation.Position int position; public RubySpan(String rubyText, @TextAnnotation.Position int position) { this.rubyText = rubyText; diff --git a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java index 68d6e236e5..11595c4f84 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java @@ -83,13 +83,13 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan { public static final int MARK_FILL_OPEN = 2; /** The mark shape used for text emphasis. */ - @MarkShape public int markShape; + public @MarkShape int markShape; /** The mark fill for the text emphasis mark. */ - @MarkShape public int markFill; + public @MarkShape int markFill; /** The position of the text emphasis relative to the base text. */ - @TextAnnotation.Position public final int position; + public final @TextAnnotation.Position int position; public TextEmphasisSpan( @MarkShape int shape, @MarkFill int fill, @TextAnnotation.Position int position) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java index ceefb88e0f..9609cff496 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java @@ -85,8 +85,7 @@ public final class NetworkTypeObserver { private final Object networkTypeLock; @GuardedBy("networkTypeLock") - @C.NetworkType - private int networkType; + private @C.NetworkType int networkType; /** * Returns a network type observer instance. @@ -132,8 +131,7 @@ public final class NetworkTypeObserver { } /** Returns the current network type. */ - @C.NetworkType - public int getNetworkType() { + public @C.NetworkType int getNetworkType() { synchronized (networkTypeLock) { return networkType; } @@ -164,8 +162,7 @@ public final class NetworkTypeObserver { } } - @C.NetworkType - private static int getNetworkTypeFromConnectivityManager(Context context) { + private static @C.NetworkType int getNetworkTypeFromConnectivityManager(Context context) { NetworkInfo networkInfo; @Nullable ConnectivityManager connectivityManager = @@ -198,8 +195,7 @@ public final class NetworkTypeObserver { } } - @C.NetworkType - private static int getMobileNetworkType(NetworkInfo networkInfo) { + private static @C.NetworkType int getMobileNetworkType(NetworkInfo networkInfo) { switch (networkInfo.getSubtype()) { case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_GPRS: 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 5e1976f65c..d45d1d35dc 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 @@ -1560,8 +1560,7 @@ public final class Util { * C#ENCODING_PCM_16BIT}, {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. If * the bit depth is unsupported then {@link C#ENCODING_INVALID} is returned. */ - @C.PcmEncoding - public static int getPcmEncoding(int bitDepth) { + public static @C.PcmEncoding int getPcmEncoding(int bitDepth) { switch (bitDepth) { case 8: return C.ENCODING_PCM_8BIT; @@ -1671,8 +1670,7 @@ public final class Util { } /** Returns the {@link C.AudioUsage} corresponding to the specified {@link C.StreamType}. */ - @C.AudioUsage - public static int getAudioUsageForStreamType(@C.StreamType int streamType) { + public static @C.AudioUsage int getAudioUsageForStreamType(@C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: return C.USAGE_ALARM; @@ -1693,8 +1691,8 @@ public final class Util { } /** Returns the {@link C.AudioContentType} corresponding to the specified {@link C.StreamType}. */ - @C.AudioContentType - public static int getAudioContentTypeForStreamType(@C.StreamType int streamType) { + public static @C.AudioContentType int getAudioContentTypeForStreamType( + @C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: case C.STREAM_TYPE_DTMF: @@ -1711,8 +1709,7 @@ public final class Util { } /** Returns the {@link C.StreamType} corresponding to the specified {@link C.AudioUsage}. */ - @C.StreamType - public static int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { + public static @C.StreamType int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { switch (usage) { case C.USAGE_MEDIA: case C.USAGE_GAME: @@ -1762,7 +1759,8 @@ public final class Util { * "clearkey"}. * @return The derived {@link UUID}, or {@code null} if one could not be derived. */ - public static @Nullable UUID getDrmUuid(String drmScheme) { + @Nullable + public static UUID getDrmUuid(String drmScheme) { switch (Ascii.toLowerCase(drmScheme)) { case "widevine": return C.WIDEVINE_UUID; @@ -1784,8 +1782,8 @@ public final class Util { * MediaDrm.ErrorCodes} value. Returns {@link PlaybackException#ERROR_CODE_DRM_SYSTEM_ERROR} if * the provided error code isn't recognised. */ - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( + int mediaDrmErrorCode) { switch (mediaDrmErrorCode) { case MediaDrm.ErrorCodes.ERROR_PROVISIONING_CONFIG: case MediaDrm.ErrorCodes.ERROR_PROVISIONING_PARSE: @@ -1821,8 +1819,7 @@ public final class Util { * @param overrideExtension If not null, used to infer the type. * @return The content type. */ - @ContentType - public static int inferContentType(Uri uri, @Nullable String overrideExtension) { + public static @ContentType int inferContentType(Uri uri, @Nullable String overrideExtension) { return TextUtils.isEmpty(overrideExtension) ? inferContentType(uri) : inferContentType("." + overrideExtension); @@ -1834,8 +1831,7 @@ public final class Util { * @param uri The {@link Uri}. * @return The content type. */ - @ContentType - public static int inferContentType(Uri uri) { + public static @ContentType int inferContentType(Uri uri) { @Nullable String scheme = uri.getScheme(); if (scheme != null && Ascii.equalsIgnoreCase("rtsp", scheme)) { return C.TYPE_RTSP; @@ -1851,8 +1847,7 @@ public final class Util { * @param fileName Name of the file. It can include the path of the file. * @return The content type. */ - @ContentType - public static int inferContentType(String fileName) { + public static @ContentType int inferContentType(String fileName) { fileName = Ascii.toLowerCase(fileName); if (fileName.endsWith(".mpd")) { return C.TYPE_DASH; @@ -1881,8 +1876,8 @@ public final class Util { * @param mimeType If MIME type, or {@code null}. * @return The content type. */ - @ContentType - public static int inferContentTypeForUriAndMimeType(Uri uri, @Nullable String mimeType) { + public static @ContentType int inferContentTypeForUriAndMimeType( + Uri uri, @Nullable String mimeType) { if (mimeType == null) { return Util.inferContentType(uri); } @@ -2032,7 +2027,8 @@ public final class Util { * @return The original value of the file name before it was escaped, or null if the escaped * fileName seems invalid. */ - public static @Nullable String unescapeFileName(String fileName) { + @Nullable + public static String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java index 46794813e8..3266ee427f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java @@ -93,7 +93,8 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - public static @Nullable String getAttributeValue(XmlPullParser xpp, String attributeName) { + @Nullable + public static String getAttributeValue(XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (xpp.getAttributeName(i).equals(attributeName)) { @@ -112,8 +113,8 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - public static @Nullable String getAttributeValueIgnorePrefix( - XmlPullParser xpp, String attributeName) { + @Nullable + public static String getAttributeValueIgnorePrefix(XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (stripPrefix(xpp.getAttributeName(i)).equals(attributeName)) { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java index 5ea554ab78..fa14682255 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java @@ -57,7 +57,7 @@ public class DataSourceException extends IOException { * The reason of this {@link DataSourceException}, should be one of the {@code ERROR_CODE_IO_*} in * {@link PlaybackException.ErrorCode}. */ - @PlaybackException.ErrorCode public final int reason; + public final @PlaybackException.ErrorCode int reason; /** * Constructs a DataSourceException. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java index 9927ec238d..646b5f274a 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java @@ -49,13 +49,13 @@ public final class DataSpec { @Nullable private Uri uri; private long uriPositionOffset; - @HttpMethod private int httpMethod; + private @HttpMethod int httpMethod; @Nullable private byte[] httpBody; private Map httpRequestHeaders; private long position; private long length; @Nullable private String key; - @Flags private int flags; + private @Flags int flags; @Nullable private Object customData; /** Creates a new instance with default values. */ @@ -330,7 +330,7 @@ public final class DataSpec { * The HTTP method to use when requesting the data. This value will be ignored by non-HTTP {@link * DataSource} implementations. */ - @HttpMethod public final int httpMethod; + public final @HttpMethod int httpMethod; /** * The HTTP request body, null otherwise. If the body is non-null, then {@code httpBody.length} @@ -382,7 +382,7 @@ public final class DataSpec { @Nullable public final String key; /** Request {@link Flags flags}. */ - @Flags public final int flags; + public final @Flags int flags; /** * Application specific data. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java index 4352008b78..967a00b49c 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java @@ -233,7 +233,7 @@ public interface HttpDataSource extends DataSource { /** The {@link DataSpec} associated with the current connection. */ public final DataSpec dataSpec; - @Type public final int type; + public final @Type int type; /** * @deprecated Use {@link #HttpDataSourceException(DataSpec, int, int) @@ -349,8 +349,8 @@ public interface HttpDataSource extends DataSource { this.type = type; } - @PlaybackException.ErrorCode - private static int assignErrorCode(@PlaybackException.ErrorCode int errorCode, @Type int type) { + private static @PlaybackException.ErrorCode int assignErrorCode( + @PlaybackException.ErrorCode int errorCode, @Type int type) { return errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED && type == TYPE_OPEN ? PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED : errorCode; diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java index d39cceb0f0..4eae94a1ee 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java @@ -72,7 +72,7 @@ public final class CacheDataSource implements DataSource { @Nullable private DataSource.Factory upstreamDataSourceFactory; @Nullable private PriorityTaskManager upstreamPriorityTaskManager; private int upstreamPriority; - @CacheDataSource.Flags private int flags; + private @CacheDataSource.Flags int flags; @Nullable private CacheDataSource.EventListener eventListener; public Factory() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java index 26101921d9..abbdba9c9b 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java @@ -22,7 +22,7 @@ import androidx.media3.common.util.UnstableApi; @UnstableApi public abstract class Buffer { - @C.BufferFlags private int flags; + private @C.BufferFlags int flags; /** Clears the buffer. */ public void clear() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java index 93443e28a5..4663b38788 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java @@ -48,7 +48,7 @@ public final class CryptoInfo { * * @see android.media.MediaCodec.CryptoInfo#mode */ - @C.CryptoMode public int mode; + public @C.CryptoMode int mode; /** * The number of leading unencrypted bytes in each sub-sample. If null, all bytes are treated as * encrypted and {@link #numBytesOfEncryptedData} must be specified. diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java index 05a0164493..265e2b0685 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java @@ -111,7 +111,7 @@ public class DecoderInputBuffer extends Buffer { */ @Nullable public ByteBuffer supplementalData; - @BufferReplacementMode private final int bufferReplacementMode; + private final @BufferReplacementMode int bufferReplacementMode; private final int paddingSize; /** Returns a new instance that's not able to hold any data. */ diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java index ad71e2d066..b0fb72294b 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java @@ -34,7 +34,7 @@ public class VideoDecoderOutputBuffer extends DecoderOutputBuffer { public int decoderPrivate; /** Output mode. */ - @C.VideoOutputMode public int mode; + public @C.VideoOutputMode int mode; /** RGB buffer for RGB mode. */ @Nullable public ByteBuffer data; diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java index 27b602caef..fb54007a84 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java @@ -41,7 +41,7 @@ public final class Gav1Decoder private final long gav1DecoderContext; - @C.VideoOutputMode private volatile int outputMode; + private volatile @C.VideoOutputMode int outputMode; /** * Creates a Gav1Decoder. diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java index 7df78d3f1d..835c505b1f 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java @@ -128,8 +128,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer { } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!MimeTypes.VIDEO_AV1.equalsIgnoreCase(format.sampleMimeType) || !Gav1Library.isAvailable()) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java index aa24f3176b..0e07287400 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java @@ -41,7 +41,7 @@ import java.util.List; private final String codecName; @Nullable private final byte[] extraData; - @C.PcmEncoding private final int encoding; + private final @C.PcmEncoding int encoding; private final int outputBufferSize; private long nativeContext; // May be reassigned on resetting the codec. @@ -158,8 +158,7 @@ import java.util.List; } /** Returns the encoding of output audio. */ - @C.PcmEncoding - public int getEncoding() { + public @C.PcmEncoding int getEncoding() { return encoding; } diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java index 047ab542dd..f9119e5e43 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java @@ -90,8 +90,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { } @Override - @C.FormatSupport - protected int supportsFormatInternal(Format format) { + protected @C.FormatSupport int supportsFormatInternal(Format format) { boolean drmIsSupported = OpusLibrary.supportsCryptoType(format.cryptoType); if (!OpusLibrary.isAvailable() || !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) { diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java index 0cbc8eb5ab..3f8130c1ad 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java @@ -126,8 +126,7 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer { } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java index 6771056f7b..259ac2a544 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java @@ -49,7 +49,7 @@ public final class VpxDecoder @Nullable private ByteBuffer lastSupplementalData; - @C.VideoOutputMode private volatile int outputMode; + private volatile @C.VideoOutputMode int outputMode; /** * Creates a VP9 decoder. diff --git a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java index 404611de21..df1ea2f4a0 100644 --- a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java +++ b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java @@ -273,7 +273,7 @@ public class StreamVolumeManagerTest { private static class TestListener implements StreamVolumeManager.Listener { - @C.StreamType private int lastStreamType; + private @C.StreamType int lastStreamType; private int lastStreamVolume; private boolean lastStreamVolumeMuted; public final CountDownLatch onStreamVolumeChangedLatch; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index 68203349aa..e07c434534 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -139,7 +139,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private PlayerControl playerControl; @Nullable private AudioAttributes audioAttributes; - @AudioFocusState private int audioFocusState; + private @AudioFocusState int audioFocusState; private @AudioFocusGain int focusGainToRequest; private float volumeMultiplier = VOLUME_MULTIPLIER_DEFAULT; @@ -193,8 +193,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param playbackState The desired playback state. * @return A {@link PlayerCommand} to execute on the player. */ - @PlayerCommand - public int updateAudioFocus(boolean playWhenReady, @Player.State int playbackState) { + public @PlayerCommand int updateAudioFocus( + boolean playWhenReady, @Player.State int playbackState) { if (shouldAbandonAudioFocusIfHeld(playbackState)) { abandonAudioFocusIfHeld(); return playWhenReady ? PLAYER_COMMAND_PLAY_WHEN_READY : PLAYER_COMMAND_DO_NOT_PLAY; @@ -222,8 +222,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return playbackState == Player.STATE_IDLE || focusGainToRequest != AUDIOFOCUS_GAIN; } - @PlayerCommand - private int requestAudioFocus() { + private @PlayerCommand int requestAudioFocus() { if (audioFocusState == AUDIO_FOCUS_STATE_HAVE_FOCUS) { return PLAYER_COMMAND_PLAY_WHEN_READY; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java index f2667947d9..db410d3a8d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java @@ -200,8 +200,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // RendererCapabilities implementation. @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } @@ -424,8 +423,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { * the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and * flags are populated if this exception is thrown, but the read position is not advanced. */ - @ReadDataResult - protected final int readSource( + protected final @ReadDataResult int readSource( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { @ReadDataResult int result = Assertions.checkNotNull(stream).readData(formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java index 0257b4af1d..142555e985 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java @@ -125,13 +125,13 @@ public final class DecoderReuseEvaluation { public final Format newFormat; /** The {@link DecoderReuseResult result} of the evaluation. */ - @DecoderReuseResult public final int result; + public final @DecoderReuseResult int result; /** * {@link DecoderDiscardReasons Reasons} why the decoder cannot be reused. Always {@code 0} if * reuse is possible. May also be {code 0} if reuse is not possible for an unspecified reason. */ - @DecoderDiscardReasons public final int discardReasons; + public final @DecoderDiscardReasons int discardReasons; /** * @param decoderName The name of the decoder. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 61e3c0f5e8..013aab4365 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -95,7 +95,7 @@ public class DefaultRenderersFactory implements RenderersFactory { private final Context context; private final DefaultMediaCodecAdapterFactory codecAdapterFactory; - @ExtensionRendererMode private int extensionRendererMode; + private @ExtensionRendererMode int extensionRendererMode; private long allowedVideoJoiningTimeMs; private boolean enableDecoderFallback; private MediaCodecSelector mediaCodecSelector; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java index 2a2b03ab0f..52bf89b365 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java @@ -85,7 +85,7 @@ public final class ExoPlaybackException extends PlaybackException { @UnstableApi public static final int TYPE_REMOTE = 3; /** The {@link Type} of the playback failure. */ - @UnstableApi @Type public final int type; + @UnstableApi public final @Type int type; /** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */ @UnstableApi @Nullable public final String rendererName; @@ -104,7 +104,7 @@ public final class ExoPlaybackException extends PlaybackException { * renderer for {@link #rendererFormat}. If {@link #rendererFormat} is null, this is {@link * C#FORMAT_HANDLED}. */ - @UnstableApi @FormatSupport public final int rendererFormatSupport; + @UnstableApi public final @FormatSupport int rendererFormatSupport; /** The {@link MediaPeriodId} of the media associated with this error, or null if undetermined. */ @UnstableApi @Nullable public final MediaPeriodId mediaPeriodId; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index e076dad419..ab7c22607d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -216,12 +216,12 @@ import java.util.concurrent.TimeoutException; private final WifiLockManager wifiLockManager; private final long detachSurfaceTimeoutMs; - @RepeatMode private int repeatMode; + private @RepeatMode int repeatMode; private boolean shuffleModeEnabled; private int pendingOperationAcks; - @DiscontinuityReason private int pendingDiscontinuityReason; + private @DiscontinuityReason int pendingDiscontinuityReason; private boolean pendingDiscontinuity; - @PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason; + private @PlayWhenReadyChangeReason int pendingPlayWhenReadyChangeReason; private boolean foregroundMode; private SeekParameters seekParameters; private ShuffleOrder shuffleOrder; @@ -238,8 +238,8 @@ import java.util.concurrent.TimeoutException; @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; private boolean surfaceHolderSurfaceIsVideoOutput; @Nullable private TextureView textureView; - @C.VideoScalingMode private int videoScalingMode; - @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; + private @C.VideoScalingMode int videoScalingMode; + private @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy; private int surfaceWidth; private int surfaceHeight; @Nullable private DecoderCounters videoDecoderCounters; @@ -515,14 +515,12 @@ import java.util.concurrent.TimeoutException; return availableCommands; } - @State - public int getPlaybackState() { + public @State int getPlaybackState() { verifyApplicationThread(); return playbackInfo.playbackState; } - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } @@ -814,8 +812,7 @@ import java.util.concurrent.TimeoutException; } } - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { verifyApplicationThread(); return repeatMode; } @@ -1271,8 +1268,7 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); } - @C.VideoScalingMode - public int getVideoScalingMode() { + public @C.VideoScalingMode int getVideoScalingMode() { return videoScalingMode; } @@ -1287,8 +1283,7 @@ import java.util.concurrent.TimeoutException; TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); } - @C.VideoChangeFrameRateStrategy - public int getVideoChangeFrameRateStrategy() { + public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { return videoChangeFrameRateStrategy; } @@ -2968,15 +2963,13 @@ import java.util.concurrent.TimeoutException; private static final class FrameMetadataListener implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { - @MessageType - public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = + public static final @MessageType int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; - @MessageType - public static final int MSG_SET_CAMERA_MOTION_LISTENER = + public static final @MessageType int MSG_SET_CAMERA_MOTION_LISTENER = Renderer.MSG_SET_CAMERA_MOTION_LISTENER; - @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; + public static final @MessageType int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; @Nullable private CameraMotionListener cameraMotionListener; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 6096159fbc..1bbed758c9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -93,9 +93,9 @@ import java.util.concurrent.atomic.AtomicBoolean; public PlaybackInfo playbackInfo; public int operationAcks; public boolean positionDiscontinuity; - @DiscontinuityReason public int discontinuityReason; + public @DiscontinuityReason int discontinuityReason; public boolean hasPlayWhenReadyChangeReason; - @PlayWhenReadyChangeReason public int playWhenReadyChangeReason; + public @PlayWhenReadyChangeReason int playWhenReadyChangeReason; public PlaybackInfoUpdate(PlaybackInfo playbackInfo) { this.playbackInfo = playbackInfo; @@ -216,7 +216,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private boolean pendingPauseAtEndOfPeriod; private boolean isRebuffering; private boolean shouldContinueLoading; - @Player.RepeatMode private int repeatMode; + private @Player.RepeatMode int repeatMode; private boolean shuffleModeEnabled; private boolean foregroundMode; private boolean requestForRendererSleep; @@ -2943,7 +2943,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return The uid in the new timeline of the first subsequent period, or null if no such period * was found. */ - /* package */ static @Nullable Object resolveSubsequentPeriod( + /* package */ @Nullable + static Object resolveSubsequentPeriod( Timeline.Window window, Timeline.Period period, @Player.RepeatMode int repeatMode, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java index cc1ca1404e..06dd0f9ff7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java @@ -62,7 +62,7 @@ public final class ExoTimeoutException extends RuntimeException { public static final int TIMEOUT_OPERATION_DETACH_SURFACE = 3; /** The operation on the ExoPlayer playback thread that timed out. */ - @TimeoutOperation public final int timeoutOperation; + public final @TimeoutOperation int timeoutOperation; /** * Creates the timeout exception. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java index 95999b5f75..c9cf750d62 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java @@ -169,14 +169,12 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities // RendererCapabilities implementation. @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java index 68fb854178..6dab072666 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java @@ -55,7 +55,7 @@ import java.util.List; /** The start position after a reported position discontinuity, in microseconds. */ public final long discontinuityStartPositionUs; /** The current playback state. One of the {@link Player}.STATE_ constants. */ - @Player.State public final int playbackState; + public final @Player.State int playbackState; /** The current playback error, or null if this is not an error state. */ @Nullable public final ExoPlaybackException playbackError; /** Whether the player is currently loading. */ @@ -71,7 +71,7 @@ import java.util.List; /** Whether playback should proceed when {@link #playbackState} == {@link Player#STATE_READY}. */ public final boolean playWhenReady; /** Reason why playback is suppressed even though {@link #playWhenReady} is {@code true}. */ - @PlaybackSuppressionReason public final int playbackSuppressionReason; + public final @PlaybackSuppressionReason int playbackSuppressionReason; /** The playback parameters. */ public final PlaybackParameters playbackParameters; /** Whether offload scheduling is enabled for the main player loop. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index 755b2c77b7..a51f245cc3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -191,8 +191,7 @@ public interface RendererCapabilities { * @return The combined {@link Capabilities} of the given {@link C.FormatSupport}, {@link * #ADAPTIVE_NOT_SUPPORTED} and {@link #TUNNELING_NOT_SUPPORTED}. */ - @Capabilities - static int create(@C.FormatSupport int formatSupport) { + static @Capabilities int create(@C.FormatSupport int formatSupport) { return create(formatSupport, ADAPTIVE_NOT_SUPPORTED, TUNNELING_NOT_SUPPORTED); } @@ -208,8 +207,7 @@ public interface RendererCapabilities { * @param tunnelingSupport The {@link TunnelingSupport}. * @return The combined {@link Capabilities}. */ - @Capabilities - static int create( + static @Capabilities int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport) { @@ -235,8 +233,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @Capabilities - static int create( + static @Capabilities int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport, @@ -257,8 +254,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @C.FormatSupport - static int getFormatSupport(@Capabilities int supportFlags) { + static @C.FormatSupport int getFormatSupport(@Capabilities int supportFlags) { return supportFlags & FORMAT_SUPPORT_MASK; } @@ -270,8 +266,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @AdaptiveSupport - static int getAdaptiveSupport(@Capabilities int supportFlags) { + static @AdaptiveSupport int getAdaptiveSupport(@Capabilities int supportFlags) { return supportFlags & ADAPTIVE_SUPPORT_MASK; } @@ -283,8 +278,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @TunnelingSupport - static int getTunnelingSupport(@Capabilities int supportFlags) { + static @TunnelingSupport int getTunnelingSupport(@Capabilities int supportFlags) { return supportFlags & TUNNELING_SUPPORT_MASK; } @@ -296,8 +290,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @HardwareAccelerationSupport - static int getHardwareAccelerationSupport(@Capabilities int supportFlags) { + static @HardwareAccelerationSupport int getHardwareAccelerationSupport( + @Capabilities int supportFlags) { return supportFlags & HARDWARE_ACCELERATION_SUPPORT_MASK; } @@ -309,8 +303,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @DecoderSupport - static int getDecoderSupport(@Capabilities int supportFlags) { + static @DecoderSupport int getDecoderSupport(@Capabilities int supportFlags) { return supportFlags & MODE_SUPPORT_MASK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index bffbe5e453..14540012df 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -398,8 +398,7 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @C.VideoScalingMode - public int getVideoScalingMode() { + public @C.VideoScalingMode int getVideoScalingMode() { return player.getVideoScalingMode(); } @@ -410,8 +409,7 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @C.VideoChangeFrameRateStrategy - public int getVideoChangeFrameRateStrategy() { + public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { return player.getVideoChangeFrameRateStrategy(); } @@ -639,14 +637,12 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { return player.getPlaybackState(); } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { return player.getPlaybackSuppressionReason(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java index 43eb76a1a1..c5a8230154 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java @@ -55,7 +55,7 @@ import androidx.media3.common.util.Util; private final AudioManager audioManager; @Nullable private VolumeChangeReceiver receiver; - @C.StreamType private int streamType; + private @C.StreamType int streamType; private int volume; private boolean muted; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 59d8b333bc..54b631389e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -150,8 +150,7 @@ public interface AnalyticsListener { * @param index The index. Must be between 0 (inclusive) and {@link #size()} (exclusive). * @return The {@link EventFlags event} at the given index. */ - @EventFlags - public int get(int index) { + public @EventFlags int get(int index) { return flags.get(index); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java index 419e168b5c..50d07ce7ca 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java @@ -120,7 +120,7 @@ public final class MediaMetricsListener @Nullable private String activeSessionId; @Nullable private PlaybackMetrics.Builder metricsBuilder; - @Player.DiscontinuityReason private int discontinuityReason; + private @Player.DiscontinuityReason int discontinuityReason; private int currentPlaybackState; private int currentNetworkType; @Nullable private PlaybackException pendingPlayerError; @@ -887,7 +887,7 @@ public final class MediaMetricsListener private static final class PendingFormatUpdate { public final Format format; - @C.SelectionReason public final int selectionReason; + public final @C.SelectionReason int selectionReason; public final String sessionId; public PendingFormatUpdate( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java index cb5ff1aa94..76ff331955 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java @@ -84,7 +84,7 @@ public final class PlaybackStatsListener @Nullable private String discontinuityFromSession; private long discontinuityFromPositionMs; - @Player.DiscontinuityReason private int discontinuityReason; + private @Player.DiscontinuityReason int discontinuityReason; private int droppedFrames; @Nullable private Exception nonFatalException; private long bandwidthTimeMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java index de6c34e119..ed7b23813c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java @@ -45,7 +45,7 @@ public interface AudioProcessor { /** The number of interleaved channels. */ public final int channelCount; /** The type of linear PCM encoding. */ - @C.PcmEncoding public final int encoding; + public final @C.PcmEncoding int encoding; /** The number of bytes used to represent one audio frame. */ public final int bytesPerFrame; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 2f9ac4accf..87c3666605 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -138,7 +138,7 @@ public abstract class DecoderAudioRenderer< @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - @ReinitializationState private int decoderReinitializationState; + private @ReinitializationState int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean audioTrackNeedsConfigure; @@ -228,8 +228,7 @@ public abstract class DecoderAudioRenderer< } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @@ -248,8 +247,7 @@ public abstract class DecoderAudioRenderer< * @param format The format, which has an audio {@link Format#sampleMimeType}. * @return The {@link C.FormatSupport} for this {@link Format}. */ - @C.FormatSupport - protected abstract int supportsFormatInternal(Format format); + protected abstract @C.FormatSupport int supportsFormatInternal(Format format); /** * Returns whether the renderer's {@link AudioSink} supports a given {@link Format}. @@ -266,8 +264,7 @@ public abstract class DecoderAudioRenderer< * * @see AudioSink#getFormatSupport(Format) (Format) */ - @SinkFormatSupport - protected final int getSinkFormatSupport(Format format) { + protected final @SinkFormatSupport int getSinkFormatSupport(Format format) { return audioSink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index d9859a99e6..7186ca79a5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -482,8 +482,8 @@ public final class DefaultAudioSink implements AudioSink { private final AudioTrackPositionTracker audioTrackPositionTracker; private final ArrayDeque mediaPositionParametersCheckpoints; private final boolean enableAudioTrackPlaybackParams; - @OffloadMode private final int offloadMode; - @MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29; + private final @OffloadMode int offloadMode; + private @MonotonicNonNull StreamEventCallbackV29 offloadStreamEventCallbackV29; private final PendingExceptionHolder initializationExceptionPendingExceptionHolder; private final PendingExceptionHolder writeExceptionPendingExceptionHolder; @@ -492,7 +492,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private PlayerId playerId; @Nullable private Listener listener; @Nullable private Configuration pendingConfiguration; - @MonotonicNonNull private Configuration configuration; + private @MonotonicNonNull Configuration configuration; @Nullable private AudioTrack audioTrack; private AudioAttributes audioAttributes; @@ -518,7 +518,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private ByteBuffer inputBuffer; private int inputBufferAccessUnitCount; @Nullable private ByteBuffer outputBuffer; - @MonotonicNonNull private byte[] preV21OutputBuffer; + private @MonotonicNonNull byte[] preV21OutputBuffer; private int preV21OutputBufferOffset; private int drainingAudioProcessorIndex; private boolean handledEndOfStream; @@ -661,8 +661,7 @@ public final class DefaultAudioSink implements AudioSink { } @Override - @SinkFormatSupport - public int getFormatSupport(Format format) { + public @SinkFormatSupport int getFormatSupport(Format format) { if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) { if (!Util.isEncodingLinearPcm(format.pcmEncoding)) { Log.w(TAG, "Invalid PCM encoding: " + format.pcmEncoding); @@ -2136,11 +2135,11 @@ public final class DefaultAudioSink implements AudioSink { public final Format inputFormat; public final int inputPcmFrameSize; - @OutputMode public final int outputMode; + public final @OutputMode int outputMode; public final int outputPcmFrameSize; public final int outputSampleRate; public final int outputChannelConfig; - @C.Encoding public final int outputEncoding; + public final @C.Encoding int outputEncoding; public final int bufferSize; public final AudioProcessor[] availableAudioProcessors; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java index eda2b250de..06fdf5afd1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java @@ -50,8 +50,7 @@ public class ForwardingAudioSink implements AudioSink { } @Override - @SinkFormatSupport - public int getFormatSupport(Format format) { + public @SinkFormatSupport int getFormatSupport(Format format) { return sink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 172d0a3ca8..6c929efada 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -286,8 +286,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java index 1390cc0aff..2a0ef051ab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java @@ -88,7 +88,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor { */ private byte[] paddingBuffer; - @State private int state; + private @State int state; private int maybeSilenceBufferSize; private int paddingSize; private boolean hasOutputNoise; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java index d2778905b5..0c1556012c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java @@ -129,8 +129,7 @@ import java.util.concurrent.Executor; } /** Delegates to Spatializer.getImmersiveAudioLevel() */ - @ImmersiveAudioLevel - public int getImmersiveAudioLevel() { + public @ImmersiveAudioLevel int getImmersiveAudioLevel() { try { return (int) Util.castNonNull(getImmersiveAudioLevel.invoke(spatializer)); } catch (IllegalAccessException | InvocationTargetException e) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java index 8e9c8d63e0..aa9c5e87e9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java @@ -128,7 +128,7 @@ public final class TeeAudioProcessor extends BaseAudioProcessor { private int sampleRateHz; private int channelCount; - @C.PcmEncoding private int encoding; + private @C.PcmEncoding int encoding; @Nullable private RandomAccessFile randomAccessFile; private int counter; private int bytesWritten; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java index a3d1173418..9e734f5230 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java @@ -25,7 +25,7 @@ import java.nio.ByteBuffer; /** Audio processor for trimming samples from the start/end of data. */ /* package */ final class TrimmingAudioProcessor extends BaseAudioProcessor { - @C.PcmEncoding private static final int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; + private static final @C.PcmEncoding int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; private int trimStartFrames; private int trimEndFrames; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java index 44dc7d3a05..c64eb57dab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java @@ -254,8 +254,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // DrmSession implementation. @Override - @DrmSession.State - public final int getState() { + public final @DrmSession.State int getState() { return state; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java index 1a3c8ebcf9..adcb450e28 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java @@ -312,7 +312,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { @Nullable private byte[] offlineLicenseKeySetId; private @MonotonicNonNull PlayerId playerId; - /* package */ volatile @Nullable MediaDrmHandler mediaDrmHandler; + /* package */ @Nullable volatile MediaDrmHandler mediaDrmHandler; /** * @param uuid The UUID of the drm scheme. @@ -590,8 +590,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { } @Override - @C.CryptoType - public int getCryptoType(Format format) { + public @C.CryptoType int getCryptoType(Format format) { @C.CryptoType int cryptoType = checkNotNull(exoMediaDrm).getCryptoType(); if (format.drmInitData == null) { int trackType = MimeTypes.getTrackType(format.sampleMimeType); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java index 73fcb430ee..968a3d207f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java @@ -65,7 +65,7 @@ public interface DrmSession { class DrmSessionException extends IOException { /** The {@link PlaybackException.ErrorCode} that corresponds to the failure. */ - @PlaybackException.ErrorCode public final int errorCode; + public final @PlaybackException.ErrorCode int errorCode; public DrmSessionException(Throwable cause, @PlaybackException.ErrorCode int errorCode) { super(cause); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java index a1b1bae06e..0fab623522 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java @@ -66,8 +66,7 @@ public interface DrmSessionManager { } @Override - @C.CryptoType - public int getCryptoType(Format format) { + public @C.CryptoType int getCryptoType(Format format) { return format.drmInitData != null ? C.CRYPTO_TYPE_UNSUPPORTED : C.CRYPTO_TYPE_NONE; } }; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java index ec43643855..a47fb682bb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java @@ -72,8 +72,7 @@ public final class DrmUtil { * @return The {@link PlaybackException.ErrorCode} that corresponds to the given DRM-related * exception. */ - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmException( + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException( Exception exception, @ErrorSource int errorSource) { if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) { return Api21.mediaDrmStateExceptionToErrorCode(exception); @@ -128,8 +127,8 @@ public final class DrmUtil { } @DoNotInline - @PlaybackException.ErrorCode - public static int mediaDrmStateExceptionToErrorCode(Throwable throwable) { + public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode( + Throwable throwable) { @Nullable String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo(); int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java index 89afc0603f..58efd2f082 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java @@ -153,8 +153,7 @@ public final class DummyExoMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return C.CRYPTO_TYPE_UNSUPPORTED; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java index 5557e00392..ba537890c8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java @@ -267,7 +267,7 @@ public interface ExoMediaDrm { private final byte[] data; private final String licenseServerUrl; - @RequestType private final int requestType; + private final @RequestType int requestType; /** * Creates an instance with {@link #REQUEST_TYPE_UNKNOWN}. @@ -307,8 +307,7 @@ public interface ExoMediaDrm { * request does not specify a type. Note that when using a platform {@link MediaDrm} instance, * key requests only specify a type on API levels 23 and above. */ - @RequestType - public int getRequestType() { + public @RequestType int getRequestType() { return requestType; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java index 00bde4ce26..10fda5099a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java @@ -342,8 +342,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return C.CRYPTO_TYPE_FRAMEWORK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java index f42dde626b..9b360d2776 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java @@ -49,7 +49,7 @@ public final class UnsupportedDrmException extends Exception { public static final int REASON_INSTANTIATION_ERROR = 2; /** Either {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ public UnsupportedDrmException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java index 74224d1c21..2815f8c796 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java @@ -39,7 +39,8 @@ public final class WidevineUtil { * @return A {@link Pair} consisting of the remaining license and playback durations in seconds, * or null if called before the session has been opened or after it's been released. */ - public static @Nullable Pair getLicenseDurationRemainingSec(DrmSession drmSession) { + @Nullable + public static Pair getLicenseDurationRemainingSec(DrmSession drmSession) { Map keyStatus = drmSession.queryKeyStatus(); if (keyStatus == null) { return null; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index e4f465c46e..e013c4b990 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -141,7 +141,7 @@ import java.nio.ByteBuffer; private final boolean synchronizeCodecInteractionsWithQueueing; private final boolean enableImmediateCodecStartAfterFlush; private boolean codecReleased; - @State private int state; + private @State int state; private AsynchronousMediaCodecAdapter( MediaCodec codec, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index d77786a0e7..03e5aafd44 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -53,7 +53,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private static final String TAG = "DMCodecAdapterFactory"; - @Mode private int asynchronousMode; + private @Mode int asynchronousMode; private boolean enableSynchronizeCodecInteractionsWithQueueing; private boolean enableImmediateCodecStartAfterFlush; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index be578bf02b..6d328d506a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -327,7 +327,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Nullable private ArrayDeque availableCodecInfos; @Nullable private DecoderInitializationException preferredDecoderInitializationException; @Nullable private MediaCodecInfo codecInfo; - @AdaptationWorkaroundMode private int codecAdaptationWorkaroundMode; + private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsFlushWorkaround; private boolean codecNeedsSosFlushWorkaround; @@ -349,9 +349,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean bypassSampleBufferPending; private boolean bypassDrainAndReinitialize; private boolean codecReconfigured; - @ReconfigurationState private int codecReconfigurationState; - @DrainState private int codecDrainState; - @DrainAction private int codecDrainAction; + private @ReconfigurationState int codecReconfigurationState; + private @DrainState int codecDrainState; + private @DrainAction int codecDrainAction; private boolean codecReceivedBuffers; private boolean codecReceivedEos; private boolean codecHasOutputMediaFormat; @@ -437,14 +437,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } @Override - @AdaptiveSupport - public final int supportsMixedMimeTypeAdaptation() { + public final @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_NOT_SEAMLESS; } @Override - @Capabilities - public final int supportsFormat(Format format) throws ExoPlaybackException { + public final @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { try { return supportsFormat(mediaCodecSelector, format); } catch (DecoderQueryException e) { @@ -460,9 +458,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * @return The {@link Capabilities} for this {@link Format}. * @throws DecoderQueryException If there was an error querying decoders. */ - @Capabilities - protected abstract int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) - throws DecoderQueryException; + protected abstract @Capabilities int supportsFormat( + MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException; /** * Returns a list of decoders that can decode media in the specified format, in priority order. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java index 3c60048f18..4aca3b8164 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java @@ -94,8 +94,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java index 9594592d6d..6eae3298d3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java @@ -99,7 +99,7 @@ public final class Download { /** The download request. */ public final DownloadRequest request; /** The state of the download. */ - @State public final int state; + public final @State int state; /** The first time when download entry is created. */ public final long startTimeMs; /** The last update time. */ @@ -112,7 +112,7 @@ public final class Download { * If {@link #state} is {@link #STATE_FAILED} then this is the cause, otherwise {@link * #FAILURE_REASON_NONE}. */ - @FailureReason public final int failureReason; + public final @FailureReason int failureReason; /* package */ final DownloadProgress progress; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java index 0ba6bc8aa4..0d1ffa7937 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java @@ -1100,8 +1100,7 @@ public final class DownloadHelper { } @Override - @C.SelectionReason - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java index e08e0d5bb2..2679e634f2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java @@ -351,8 +351,7 @@ public final class DownloadManager { * * @return The not met {@link Requirements.RequirementFlags}, or 0 if all requirements are met. */ - @Requirements.RequirementFlags - public int getNotMetRequirements() { + public @Requirements.RequirementFlags int getNotMetRequirements() { return notMetRequirements; } @@ -705,7 +704,7 @@ public final class DownloadManager { private final ArrayList downloads; private final HashMap activeTasks; - @Requirements.RequirementFlags private int notMetRequirements; + private @Requirements.RequirementFlags int notMetRequirements; private boolean downloadsPaused; private int maxParallelDownloads; private int minRetryCount; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index 34d435bb47..4bdd389fbc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -74,7 +74,7 @@ public final class Requirements implements Parcelable { */ public static final int DEVICE_STORAGE_NOT_LOW = 1 << 4; - @RequirementFlags private final int requirements; + private final @RequirementFlags int requirements; /** @param requirements A combination of requirement flags. */ public Requirements(@RequirementFlags int requirements) { @@ -86,8 +86,7 @@ public final class Requirements implements Parcelable { } /** Returns the requirements. */ - @RequirementFlags - public int getRequirements() { + public @RequirementFlags int getRequirements() { return requirements; } @@ -144,8 +143,7 @@ public final class Requirements implements Parcelable { * @param context Any context. * @return The requirements that are not met, or 0. */ - @RequirementFlags - public int getNotMetRequirements(Context context) { + public @RequirementFlags int getNotMetRequirements(Context context) { @RequirementFlags int notMetRequirements = getNotMetNetworkRequirements(context); if (isChargingRequired() && !isDeviceCharging(context)) { notMetRequirements |= DEVICE_CHARGING; @@ -159,8 +157,7 @@ public final class Requirements implements Parcelable { return notMetRequirements; } - @RequirementFlags - private int getNotMetNetworkRequirements(Context context) { + private @RequirementFlags int getNotMetNetworkRequirements(Context context) { if (!isNetworkRequired()) { return 0; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java index ca2380cd42..c7ae614e05 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java @@ -62,7 +62,7 @@ public final class RequirementsWatcher { @Nullable private DeviceStatusChangeReceiver receiver; - @Requirements.RequirementFlags private int notMetRequirements; + private @Requirements.RequirementFlags int notMetRequirements; @Nullable private NetworkCallback networkCallback; /** @@ -83,8 +83,7 @@ public final class RequirementsWatcher { * * @return Initial {@link Requirements.RequirementFlags RequirementFlags} that are not met, or 0. */ - @Requirements.RequirementFlags - public int start() { + public @Requirements.RequirementFlags int start() { notMetRequirements = requirements.getNotMetRequirements(context); IntentFilter filter = new IntentFilter(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java index 4263da5d62..6a734a752b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java @@ -63,7 +63,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { public static final int REASON_START_EXCEEDS_END = 2; /** The reason clipping failed. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason The reason clipping failed. */ public IllegalClippingException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java index 4e2070b043..5cbc5628d4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java @@ -472,8 +472,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { mediaSourceFactories = new HashMap<>(); } - @C.ContentType - public int[] getSupportedTypes() { + public @C.ContentType int[] getSupportedTypes() { ensureAllSuppliersAreLoaded(); return Ints.toArray(supportedTypes); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java index f3a6a04ed5..c5df2e15ea 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java @@ -28,7 +28,7 @@ import androidx.media3.common.util.UnstableApi; public final class MediaLoadData { /** The {@link DataType data type}. */ - @DataType public final int dataType; + public final @DataType int dataType; /** * One of the {@link TrackType track types}, which is a media track type if the data corresponds * to media of a specific type, or {@link C#TRACK_TYPE_UNKNOWN} otherwise. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java index af34e47388..af47342210 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java @@ -47,8 +47,7 @@ public interface MediaSourceFactory extends MediaSource.Factory { } @Override - @C.ContentType - public int[] getSupportedTypes() { + public @C.ContentType int[] getSupportedTypes() { throw new UnsupportedOperationException(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java index c9c589fb74..8822b24268 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java @@ -61,7 +61,7 @@ public final class MergingMediaSource extends CompositeMediaSource { public static final int REASON_PERIOD_COUNT_MISMATCH = 0; /** The reason the merge failed. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason The reason the merge failed. */ public IllegalMergeException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java index e5e74de76e..f20a57caeb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java @@ -131,7 +131,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SeekMap seekMap; private long durationUs; private boolean isLive; - @DataType private int dataType; + private @DataType int dataType; private boolean seenFirstTrackSelection; private boolean notifyDiscontinuity; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java index c783249821..d2857100fc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java @@ -87,7 +87,7 @@ public final class SilenceMediaSource extends BaseMediaSource { public static final String MEDIA_ID = "SilenceMediaSource"; private static final int SAMPLE_RATE_HZ = 44100; - @C.PcmEncoding private static final int PCM_ENCODING = C.ENCODING_PCM_16BIT; + private static final @C.PcmEncoding int PCM_ENCODING = C.ENCODING_PCM_16BIT; private static final int CHANNEL_COUNT = 2; private static final Format FORMAT = new Format.Builder() diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index 248b7648ec..a0821d571e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -847,8 +847,7 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource return positionUs; } - @SampleStream.ReadDataResult - public int readData( + public @SampleStream.ReadDataResult int readData( MediaPeriodImpl mediaPeriod, int streamIndex, FormatHolder formatHolder, @@ -1204,8 +1203,7 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource } @Override - @ReadDataResult - public int readData( + public @ReadDataResult int readData( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return mediaPeriod.sharedPeriod.readData( mediaPeriod, streamIndex, formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java index 09c157a9a7..7a2d8060ec 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java @@ -42,7 +42,7 @@ public abstract class Chunk implements Loadable { /** The {@link DataSpec} that defines the data to be loaded. */ public final DataSpec dataSpec; /** The {@link DataType data type} of the chunk. For reporting only. */ - @DataType public final int type; + public final @DataType int type; /** The format of the track to which this chunk belongs. */ public final Format trackFormat; /** @@ -50,7 +50,7 @@ public abstract class Chunk implements Loadable { * C#SELECTION_REASON_UNKNOWN} if the chunk does not belong to a track, or if the selection reason * is unknown. */ - @C.SelectionReason public final int trackSelectionReason; + public final @C.SelectionReason int trackSelectionReason; /** * Optional data associated with the selection of the track to which this chunk belongs. Null if * the chunk does not belong to a track, or if there is no associated track selection data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java index 1e4dc6014f..1b6a07874b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java @@ -540,8 +540,7 @@ public final class OutputConsumerAdapterV30 implements MediaParser.OutputConsume return new DrmInitData(schemeType, schemeDatas); } - @SelectionFlags - private static int getSelectionFlags(MediaFormat mediaFormat) { + private static @SelectionFlags int getSelectionFlags(MediaFormat mediaFormat) { int selectionFlags = 0; selectionFlags |= getFlag( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java index 4c668d3dc9..504b6f7843 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java @@ -63,7 +63,7 @@ public final class ExoplayerCuesDecoder implements SubtitleDecoder { private final SubtitleInputBuffer inputBuffer; private final Deque availableOutputBuffers; - @InputBufferState private int inputBufferState; + private @InputBufferState int inputBufferState; private boolean released; public ExoplayerCuesDecoder() { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java index e6dac32716..af8809966a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java @@ -94,7 +94,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { private boolean inputStreamEnded; private boolean outputStreamEnded; private boolean waitingForKeyFrame; - @ReplacementState private int decoderReplacementState; + private @ReplacementState int decoderReplacementState; @Nullable private Format streamFormat; @Nullable private SubtitleDecoder decoder; @Nullable private SubtitleInputBuffer nextInputBuffer; @@ -141,8 +141,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index e8dc20c7e3..037304cfe2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -123,7 +123,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private boolean allowAudioMixedChannelCountAdaptiveness; private boolean allowAudioMixedDecoderSupportAdaptiveness; // Text - @C.SelectionFlags private int disabledTextTrackSelectionFlags; + private @C.SelectionFlags int disabledTextTrackSelectionFlags; // General private boolean exceedRendererCapabilitiesIfNecessary; private boolean tunnelingEnabled; @@ -894,7 +894,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * Bitmask of selection flags that are disabled for text track selections. See {@link * C.SelectionFlags}. The default value is {@code 0} (i.e. no flags). */ - @C.SelectionFlags public final int disabledTextTrackSelectionFlags; + public final @C.SelectionFlags int disabledTextTrackSelectionFlags; /** Returns an instance configured with default values. */ public static Parameters getDefaults(Context context) { @@ -2255,8 +2255,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } /** Returns to what extent the track is {@link SelectionEligibility eligible for selection}. */ - @SelectionEligibility - public abstract int getSelectionEligibility(); + public abstract @SelectionEligibility int getSelectionEligibility(); /** * Returns whether this track is compatible for an adaptive selection with the specified other @@ -2308,7 +2307,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int preferredRoleFlagsScore; private final boolean hasMainOrNoRoleFlag; private final boolean allowMixedMimeTypes; - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean usesPrimaryDecoder; private final boolean usesHardwareAcceleration; private final int codecPreferenceScore; @@ -2375,8 +2374,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } @@ -2389,8 +2387,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { && this.usesHardwareAcceleration == otherTrack.usesHardwareAcceleration)); } - @SelectionEligibility - private int evaluateSelectionEligibility( + private @SelectionEligibility int evaluateSelectionEligibility( @Capabilities int rendererSupport, @AdaptiveSupport int requiredAdaptiveSupport) { if ((format.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) { // Ignore trick-play tracks for now. @@ -2502,7 +2499,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean isWithinConstraints; @Nullable private final String language; private final Parameters parameters; @@ -2594,8 +2591,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } @@ -2664,8 +2660,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { .result(); } - @SelectionEligibility - private int evaluateSelectionEligibility( + private @SelectionEligibility int evaluateSelectionEligibility( @Capabilities int rendererSupport, boolean hasMappedVideoTracks) { if (!isSupported(rendererSupport, parameters.exceedRendererCapabilitiesIfNecessary)) { return SELECTION_ELIGIBILITY_NO; @@ -2712,7 +2707,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean isWithinRendererCapabilities; private final boolean isDefault; private final boolean isForced; @@ -2777,8 +2772,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index 980a5a7749..d1836b7094 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -107,8 +107,8 @@ public abstract class MappingTrackSelector extends TrackSelector { private final String[] rendererNames; private final @C.TrackType int[] rendererTrackTypes; private final TrackGroupArray[] rendererTrackGroups; - @AdaptiveSupport private final int[] rendererMixedMimeTypeAdaptiveSupports; - @Capabilities private final int[][][] rendererFormatSupports; + private final @AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports; + private final @Capabilities int[][][] rendererFormatSupports; private final TrackGroupArray unmappedTrackGroups; /** @@ -181,8 +181,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param rendererIndex The renderer index. * @return The {@link RendererSupport}. */ - @RendererSupport - public int getRendererSupport(int rendererIndex) { + public @RendererSupport int getRendererSupport(int rendererIndex) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; @Capabilities int[][] rendererFormatSupport = rendererFormatSupports[rendererIndex]; for (@Capabilities int[] trackGroupFormatSupport : rendererFormatSupport) { @@ -217,8 +216,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackType The {@link C.TrackType track type}. * @return The {@link RendererSupport}. */ - @RendererSupport - public int getTypeSupport(@C.TrackType int trackType) { + public @RendererSupport int getTypeSupport(@C.TrackType int trackType) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; for (int i = 0; i < rendererCount; i++) { if (rendererTrackTypes[i] == trackType) { @@ -236,8 +234,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link Capabilities}. */ - @Capabilities - public int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { + public @Capabilities int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { return rendererFormatSupports[rendererIndex][groupIndex][trackIndex]; } @@ -249,8 +246,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link FormatSupport}. */ - @FormatSupport - public int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { + public @FormatSupport int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { return RendererCapabilities.getFormatSupport( getCapabilities(rendererIndex, groupIndex, trackIndex)); } @@ -272,8 +268,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer are included when determining support. * @return The {@link AdaptiveSupport}. */ - @AdaptiveSupport - public int getAdaptiveSupport( + public @AdaptiveSupport int getAdaptiveSupport( int rendererIndex, int groupIndex, boolean includeCapabilitiesExceededTracks) { int trackCount = rendererTrackGroups[rendererIndex].get(groupIndex).length; // Iterate over the tracks in the group, recording the indices of those to consider. @@ -299,8 +294,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param groupIndex The index of the track group. * @return The {@link AdaptiveSupport}. */ - @AdaptiveSupport - public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) { + public @AdaptiveSupport int getAdaptiveSupport( + int rendererIndex, int groupIndex, int[] trackIndices) { int handledTrackCount = 0; @AdaptiveSupport int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS; boolean multipleMimeTypes = false; @@ -540,9 +535,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @return An array containing {@link Capabilities} for each track in the group. * @throws ExoPlaybackException If an error occurs determining the format support. */ - @Capabilities - private static int[] getFormatSupport(RendererCapabilities rendererCapabilities, TrackGroup group) - throws ExoPlaybackException { + private static @Capabilities int[] getFormatSupport( + RendererCapabilities rendererCapabilities, TrackGroup group) throws ExoPlaybackException { @Capabilities int[] formatSupport = new int[group.length]; for (int i = 0; i < group.length; i++) { formatSupport[i] = rendererCapabilities.supportsFormat(group.getFormat(i)); @@ -559,8 +553,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer. * @throws ExoPlaybackException If an error occurs determining the adaptation support. */ - @AdaptiveSupport - private static int[] getMixedMimeTypeAdaptationSupports( + private static @AdaptiveSupport int[] getMixedMimeTypeAdaptationSupports( RendererCapabilities[] rendererCapabilities) throws ExoPlaybackException { @AdaptiveSupport int[] mixedMimeTypeAdaptationSupport = new int[rendererCapabilities.length]; for (int i = 0; i < mixedMimeTypeAdaptationSupport.length; i++) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java index 02c89e42a3..654a34d975 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java @@ -281,14 +281,14 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList private long sampleStartTimeMs; private long sampleBytesTransferred; - @C.NetworkType private int networkType; + private @C.NetworkType int networkType; private long totalElapsedTimeMs; private long totalBytesTransferred; private long bitrateEstimate; private long lastReportedBitrateEstimate; private boolean networkTypeOverrideSet; - @C.NetworkType private int networkTypeOverride; + private @C.NetworkType int networkTypeOverride; /** @deprecated Use {@link Builder} instead. */ @Deprecated diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java index fd40859f2d..40e5c13c2c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java @@ -130,7 +130,7 @@ public interface LoadErrorHandlingPolicy { /** A selected fallback option. */ final class FallbackSelection { /** The type of fallback. */ - @FallbackType public final int type; + public final @FallbackType int type; /** The duration for which the failing resource should be excluded, in milliseconds. */ public final long exclusionDurationMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java index 5e978e19c4..859f920713 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java @@ -104,7 +104,7 @@ public final class ParsingLoadable implements Loadable { private final StatsDataSource dataSource; private final Parser parser; - private volatile @Nullable T result; + @Nullable private volatile T result; /** * @param dataSource A {@link DataSource} to use when loading the data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java index 8d2144084f..2f247237d2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java @@ -121,7 +121,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { private DecoderInputBuffer inputBuffer; private VideoDecoderOutputBuffer outputBuffer; - @VideoOutputMode private int outputMode; + private @VideoOutputMode int outputMode; @Nullable private Object output; @Nullable private Surface outputSurface; @Nullable private VideoDecoderOutputBufferRenderer outputBufferRenderer; @@ -130,7 +130,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - @ReinitializationState private int decoderReinitializationState; + private @ReinitializationState int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean renderedFirstFrameAfterReset; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java index f225812bbb..a7819ddd25 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java @@ -105,8 +105,7 @@ public final class DummySurface extends Surface { } } - @SecureMode - private static int getSecureMode(Context context) { + private static @SecureMode int getSecureMode(Context context) { if (GlUtil.isProtectedContentExtensionSupported(context)) { if (GlUtil.isSurfacelessContextExtensionSupported()) { return SECURE_MODE_SURFACELESS_CONTEXT; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index d3aa4562ec..d1114f4ca3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -131,7 +131,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @Nullable private Surface surface; @Nullable private DummySurface dummySurface; private boolean haveReportedFirstFrameRenderedForCurrentSurface; - @C.VideoScalingMode private int scalingMode; + private @C.VideoScalingMode int scalingMode; private boolean renderedFirstFrameAfterReset; private boolean mayRenderFirstFrameAfterEnableIfNotStarted; private boolean renderedFirstFrameAfterEnable; @@ -342,8 +342,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java index d5646ca11c..2735852372 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java @@ -111,7 +111,7 @@ public final class VideoFrameReleaseHelper { private float surfacePlaybackFrameRate; private float playbackSpeed; - @C.VideoChangeFrameRateStrategy private int changeFrameRateStrategy; + private @C.VideoChangeFrameRateStrategy int changeFrameRateStrategy; private long vsyncDurationNs; private long vsyncOffsetNs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java index b1a222f49a..b372d03481 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java @@ -58,8 +58,7 @@ public final class CameraMotionRenderer extends BaseRenderer { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return MimeTypes.APPLICATION_CAMERA_MOTION.equals(format.sampleMimeType) ? RendererCapabilities.create(C.FORMAT_HANDLED) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java index 81444dd5b0..4b0acba57b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java @@ -58,7 +58,8 @@ import java.util.zip.Inflater; * @param stereoMode A {@link C.StereoMode} value. * @return The projection or null if the data can't be decoded. */ - public static @Nullable Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { + @Nullable + public static Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { ParsableByteArray input = new ParsableByteArray(projectionData); // MP4 containers include the proj box but webm containers do not. // Both containers use mshp. @@ -91,7 +92,8 @@ import java.util.zip.Inflater; return type == TYPE_PROJ; } - private static @Nullable ArrayList parseProj(ParsableByteArray input) { + @Nullable + private static ArrayList parseProj(ParsableByteArray input) { input.skipBytes(8); // size and type. int position = input.getPosition(); int limit = input.limit(); @@ -112,7 +114,8 @@ import java.util.zip.Inflater; return null; } - private static @Nullable ArrayList parseMshp(ParsableByteArray input) { + @Nullable + private static ArrayList parseMshp(ParsableByteArray input) { int version = input.readUnsignedByte(); if (version != 0) { return null; @@ -137,7 +140,8 @@ import java.util.zip.Inflater; } /** Parses MSHP data after the encoding_four_cc field. */ - private static @Nullable ArrayList parseRawMshpData(ParsableByteArray input) { + @Nullable + private static ArrayList parseRawMshpData(ParsableByteArray input) { ArrayList meshes = new ArrayList<>(); int position = input.getPosition(); int limit = input.limit(); @@ -160,7 +164,8 @@ import java.util.zip.Inflater; return meshes; } - private static @Nullable Mesh parseMesh(ParsableByteArray input) { + @Nullable + private static Mesh parseMesh(ParsableByteArray input) { // Read the coordinates. int coordinateCount = input.readInt(); if (coordinateCount > MAX_COORDINATE_COUNT) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java index b31cfa98ab..b2828a465e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java @@ -50,8 +50,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SurfaceTexture surfaceTexture; // Used by other threads only - @C.StereoMode private volatile int defaultStereoMode; - @C.StereoMode private int lastStereoMode; + private volatile @C.StereoMode int defaultStereoMode; + private @C.StereoMode int lastStereoMode; @Nullable private byte[] lastProjectionData; // Methods called on any thread. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index cc444ebb0f..0961f4492d 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -11488,7 +11488,7 @@ public final class ExoPlayerTest { private static final class PlayerStateGrabber extends PlayerRunnable { public boolean playWhenReady; - @Player.State public int playbackState; + public @Player.State int playbackState; @Nullable public Timeline timeline; @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java index dbf9c8dfc6..f0009b7ceb 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java @@ -71,8 +71,7 @@ public class DecoderAudioRendererTest { } @Override - @C.FormatSupport - protected int supportsFormatInternal(Format format) { + protected @C.FormatSupport int supportsFormatInternal(Format format) { return FORMAT_HANDLED; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java index 8916391b8b..59b41fdc0e 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java @@ -44,8 +44,7 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class PcmTest { @Parameterized.Parameter(0) - @C.PcmEncoding - public int encoding; + public @C.PcmEncoding int encoding; @Parameterized.Parameter(1) public int channelCount; @@ -216,8 +215,7 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class EncodedTest { @Parameterized.Parameter(0) - @C.Encoding - public int encoding; + public @C.Encoding int encoding; @Parameterized.Parameters(name = "{index}: encoding={0}") public static ImmutableList data() { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java index 5c85327510..a5514c7068 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java @@ -2416,7 +2416,7 @@ public final class DefaultTrackSelectorTest { private static final class FakeRendererCapabilities implements RendererCapabilities { private final int trackType; - @Capabilities private final int supportValue; + private final @Capabilities int supportValue; /** * Returns {@link FakeRendererCapabilities} that advertises adaptive support for all tracks of @@ -2456,16 +2456,14 @@ public final class DefaultTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? supportValue : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } @@ -2504,16 +2502,14 @@ public final class DefaultTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return format.id != null && formatToCapability.containsKey(format.id) ? formatToCapability.get(format.id) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java index 4885587d6b..32023b11e2 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java @@ -245,8 +245,7 @@ public final class MappingTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? RendererCapabilities.create( C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) @@ -254,8 +253,7 @@ public final class MappingTrackSelectorTest { } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java index c8d195f9b8..95a9a67b61 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java @@ -84,7 +84,7 @@ public final class DecoderVideoRendererTest { /* maxDroppedFramesToNotify= */ -1) { private final Phaser inputBuffersInCodecPhaser = new Phaser(); - @C.VideoOutputMode private int outputMode; + private @C.VideoOutputMode int outputMode; @Override public String getName() { @@ -92,8 +92,7 @@ public final class DecoderVideoRendererTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index b1df16d07a..d12739b2b4 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -111,8 +111,8 @@ public class MediaCodecVideoRendererTest { /* eventListener= */ eventListener, /* maxDroppedFramesToNotify= */ 1) { @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) { + protected @Capabilities int supportsFormat( + MediaCodecSelector mediaCodecSelector, Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index be20b5e054..23b9262cf0 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -937,7 +937,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; public final int[] adaptationSetIndices; public final @C.TrackType int trackType; - @TrackGroupCategory public final int trackGroupCategory; + public final @TrackGroupCategory int trackGroupCategory; public final int eventStreamGroupIndex; public final int primaryTrackGroupIndex; diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java index 9ca9fc2102..6ee1ab5d6c 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java @@ -1481,8 +1481,8 @@ public class DashManifestParser extends DefaultHandler // Selection flag parsing. - @C.SelectionFlags - protected int parseSelectionFlagsFromRoleDescriptors(List roleDescriptors) { + protected @C.SelectionFlags int parseSelectionFlagsFromRoleDescriptors( + List roleDescriptors) { @C.SelectionFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1493,8 +1493,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.SelectionFlags - protected int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { + protected @C.SelectionFlags int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1510,8 +1509,7 @@ public class DashManifestParser extends DefaultHandler // Role and Accessibility parsing. - @C.RoleFlags - protected int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { + protected @C.RoleFlags int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1522,8 +1520,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromAccessibilityDescriptors( + protected @C.RoleFlags int parseRoleFlagsFromAccessibilityDescriptors( List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { @@ -1538,8 +1535,8 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromProperties(List accessibilityDescriptors) { + protected @C.RoleFlags int parseRoleFlagsFromProperties( + List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { Descriptor descriptor = accessibilityDescriptors.get(i); @@ -1551,8 +1548,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { + protected @C.RoleFlags int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1587,8 +1583,7 @@ public class DashManifestParser extends DefaultHandler } } - @C.RoleFlags - protected int parseTvaAudioPurposeCsValue(@Nullable String value) { + protected @C.RoleFlags int parseTvaAudioPurposeCsValue(@Nullable String value) { if (value == null) { return 0; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java index 684bf120ed..aec437e327 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java @@ -62,7 +62,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { FileTypes.MP3, }; - @DefaultTsPayloadReaderFactory.Flags private final int payloadReaderFactoryFlags; + private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags; private final boolean exposeCea608WhenMissingDeclarations; /** diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index e054b113ca..b843ae48d6 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -303,8 +303,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * has been {@link #CHUNK_PUBLICATION_STATE_REMOVED removed} or is definitely {@link * #CHUNK_PUBLICATION_STATE_PUBLISHED published}. */ - @ChunkPublicationState - public int getChunkPublicationState(HlsMediaChunk mediaChunk) { + public @ChunkPublicationState int getChunkPublicationState(HlsMediaChunk mediaChunk) { if (mediaChunk.partIndex == C.INDEX_UNSET) { // Chunks based on full segments can't be removed and are always published. return CHUNK_PUBLICATION_STATE_PUBLISHED; diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java index c6a43c9eec..fb2444d4bb 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java @@ -398,7 +398,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { public static final int PLAYLIST_TYPE_EVENT = 2; /** The type of the playlist. See {@link PlaylistType}. */ - @PlaylistType public final int playlistType; + public final @PlaylistType int playlistType; /** * The start offset in microseconds from the beginning of the playlist, as defined by * #EXT-X-START, or {@link C#TIME_UNSET} if undefined. The value is guaranteed to be between 0 and diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index c1e48a068b..63850c72a0 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -1087,8 +1087,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variableDefinitions) { + private static @C.RoleFlags int parseRoleFlags( + String line, Map variableDefinitions) { String concatenatedCharacteristics = parseOptionalStringAttr(line, REGEX_CHARACTERISTICS, variableDefinitions); if (TextUtils.isEmpty(concatenatedCharacteristics)) { diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java index b55ec3b9ac..d4176e67fc 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java @@ -162,7 +162,7 @@ import java.util.Map; /** Whether IMA has sent an ad event to pause content since the last resume content event. */ private boolean imaPausedContent; /** The current ad playback state. */ - @ImaAdState private int imaAdState; + private @ImaAdState int imaAdState; /** The current ad media info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ @Nullable private AdMediaInfo imaAdMediaInfo; /** The current ad info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java index f9026648d2..d8a2345210 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java @@ -51,7 +51,7 @@ import java.util.Map; @Nullable private String authToken; @Nullable private String streamActivityMonitorId; private ImmutableMap adTagParameters; - @ContentType public int format = C.TYPE_HLS; + public @ContentType int format = C.TYPE_HLS; private int loadVideoTimeoutMs; /** Creates a new instance. */ @@ -270,7 +270,7 @@ import java.util.Map; @Nullable public final String contentUrl; @Nullable public final String authToken; @Nullable public final String streamActivityMonitorId; - @ContentType public int format = C.TYPE_HLS; + public @ContentType int format = C.TYPE_HLS; public final int loadVideoTimeoutMs; private ServerSideAdInsertionStreamRequest( diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java index 4d1fd431e7..6a9602c976 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java @@ -42,7 +42,7 @@ import androidx.media3.test.utils.StubExoPlayer; private final MediaItem mediaItem = MediaItem.fromUri("http://google.com/0"); private Timeline timeline; - @Player.State private int state; + private @Player.State int state; private boolean playWhenReady; private int periodIndex; private long positionMs; @@ -236,8 +236,7 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - @Player.State - public int getPlaybackState() { + public @Player.State int getPlaybackState() { return state; } @@ -247,8 +246,7 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { return REPEAT_MODE_OFF; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java index ecb9994f3f..0ecdbc9fe4 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java @@ -56,7 +56,7 @@ import java.security.NoSuchAlgorithmException; private static final String ALGORITHM = "MD5"; /** The authentication mechanism. */ - @AuthenticationMechanism public final int authenticationMechanism; + public final @AuthenticationMechanism int authenticationMechanism; /** The authentication realm. */ public final String realm; /** The nonce used in digest authentication; empty if using {@link #BASIC} authentication. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java index e8b3fe7867..84bcc4cd8d 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java @@ -141,7 +141,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private String sessionId; @Nullable private KeepAliveMonitor keepAliveMonitor; @Nullable private RtspAuthenticationInfo rtspAuthenticationInfo; - @RtspState private int rtspState; + private @RtspState int rtspState; private boolean hasUpdatedTimelineAndTracks; private boolean receivedAuthorizationRequest; private boolean hasPendingPauseRequest; @@ -204,8 +204,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } /** Returns the current {@link RtspState RTSP state}. */ - @RtspState - public int getState() { + public @RtspState int getState() { return rtspState; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java index b0ba5c4c5e..28084cdef6 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java @@ -775,8 +775,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return sampleQueue.isReady(/* loadingFinished= */ canceled); } - @ReadDataResult - public int read( + public @ReadDataResult int read( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return sampleQueue.read(formatHolder, buffer, readFlags, /* loadingFinished= */ canceled); } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java index dda52e8f98..a2d5970431 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java @@ -353,7 +353,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final List messageLines; - @ReadingState private int state; + private @ReadingState int state; private long messageBodyLength; /** Creates a new instance. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java index e05b4c6469..db1e4116ea 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java @@ -256,8 +256,7 @@ import java.util.regex.Pattern; } } - @RtspRequest.Method - private static int parseMethodString(String method) { + private static @RtspRequest.Method int parseMethodString(String method) { switch (method) { case "ANNOUNCE": return METHOD_ANNOUNCE; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java index 6d9e5f35e8..23cff58977 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java @@ -88,7 +88,7 @@ import java.lang.annotation.Target; /** The {@link Uri} to which this request is sent. */ public final Uri uri; /** The request method, as defined in {@link Method}. */ - @Method public final int method; + public final @Method int method; /** The headers of this request. */ public final RtspHeaders headers; /** The body of this RTSP message, or empty string if absent. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java index 6895010b75..659a28f70f 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java @@ -58,7 +58,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final RtpPayloadFormat payloadFormat; private @MonotonicNonNull TrackOutput trackOutput; - @C.BufferFlags private int bufferFlags; + private @C.BufferFlags int bufferFlags; private long firstReceivedTimestamp; private int previousSequenceNumber; @@ -296,8 +296,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /* divisor= */ MEDIA_CLOCK_FREQUENCY); } - @C.BufferFlags - private static int getBufferFlagsFromNalType(int nalType) { + private static @C.BufferFlags int getBufferFlagsFromNalType(int nalType) { return nalType == NAL_UNIT_TYPE_IDR ? C.BUFFER_FLAG_KEY_FRAME : 0; } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java index 1ff09dfdff..a8bcb676a6 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java @@ -423,7 +423,7 @@ public abstract class BinarySearchSeeker { new TimestampSearchResult(TYPE_NO_TIMESTAMP, C.TIME_UNSET, C.POSITION_UNSET); /** The type of the result. */ - @Type private final int type; + private final @Type int type; /** * When {@link #type} is {@link #TYPE_POSITION_OVERESTIMATED}, the {@link diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java index f5ee3432f1..5470d906a8 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java @@ -107,15 +107,15 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { private boolean constantBitrateSeekingEnabled; private boolean constantBitrateSeekingAlwaysEnabled; - @AdtsExtractor.Flags private int adtsFlags; - @AmrExtractor.Flags private int amrFlags; - @FlacExtractor.Flags private int flacFlags; - @MatroskaExtractor.Flags private int matroskaFlags; - @Mp4Extractor.Flags private int mp4Flags; - @FragmentedMp4Extractor.Flags private int fragmentedMp4Flags; - @Mp3Extractor.Flags private int mp3Flags; - @TsExtractor.Mode private int tsMode; - @DefaultTsPayloadReaderFactory.Flags private int tsFlags; + private @AdtsExtractor.Flags int adtsFlags; + private @AmrExtractor.Flags int amrFlags; + private @FlacExtractor.Flags int flacFlags; + private @MatroskaExtractor.Flags int matroskaFlags; + private @Mp4Extractor.Flags int mp4Flags; + private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; + private @Mp3Extractor.Flags int mp3Flags; + private @TsExtractor.Mode int tsMode; + private @DefaultTsPayloadReaderFactory.Flags int tsFlags; private int tsTimestampSearchBytes; public DefaultExtractorsFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java index 710e11a7ad..e479c27aac 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java @@ -40,7 +40,7 @@ public interface TrackOutput { final class CryptoData { /** The encryption mode used for the sample. */ - @C.CryptoMode public final int cryptoMode; + public final @C.CryptoMode int cryptoMode; /** The encryption key associated with the sample. Its contents must not be modified. */ public final byte[] encryptionKey; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java index 93f675fb5f..53f5db50ad 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java @@ -33,7 +33,7 @@ public final class TrueHdSampleRechunker { private boolean foundSyncframe; private int chunkSampleCount; private long chunkTimeUs; - @C.BufferFlags private int chunkFlags; + private @C.BufferFlags int chunkFlags; private int chunkSize; private int chunkOffset; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java index 700d5538f7..3d2cca8171 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java @@ -84,7 +84,7 @@ public final class JpegExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; - @State private int state; + private @State int state; private int marker; private int segmentLength; private long mp4StartPosition; @@ -128,8 +128,8 @@ public final class JpegExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { switch (state) { case STATE_READING_MARKER: readMarker(input); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 6442ad7993..5f4d246a9f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -431,7 +431,7 @@ public class MatroskaExtractor implements Extractor { private int[] blockSampleSizes; private int blockTrackNumber; private int blockTrackNumberLength; - @C.BufferFlags private int blockFlags; + private @C.BufferFlags int blockFlags; private int blockAdditionalId; private boolean blockHasReferenceBlock; @@ -531,8 +531,7 @@ public class MatroskaExtractor implements Extractor { * @see EbmlProcessor#getElementType(int) */ @CallSuper - @EbmlProcessor.ElementType - protected int getElementType(int id) { + protected @EbmlProcessor.ElementType int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: @@ -1908,8 +1907,7 @@ public class MatroskaExtractor implements Extractor { private final class InnerEbmlProcessor implements EbmlProcessor { @Override - @ElementType - public int getElementType(int id) { + public @ElementType int getElementType(int id) { return MatroskaExtractor.this.getElementType(id); } @@ -1981,16 +1979,16 @@ public class MatroskaExtractor implements Extractor { public int displayWidth = Format.NO_VALUE; public int displayHeight = Format.NO_VALUE; public int displayUnit = DISPLAY_UNIT_PIXELS; - @C.Projection public int projectionType = Format.NO_VALUE; + public @C.Projection int projectionType = Format.NO_VALUE; public float projectionPoseYaw = 0f; public float projectionPosePitch = 0f; public float projectionPoseRoll = 0f; public byte @MonotonicNonNull [] projectionData = null; - @C.StereoMode public int stereoMode = Format.NO_VALUE; + public @C.StereoMode int stereoMode = Format.NO_VALUE; public boolean hasColorInfo = false; - @C.ColorSpace public int colorSpace = Format.NO_VALUE; - @C.ColorTransfer public int colorTransfer = Format.NO_VALUE; - @C.ColorRange public int colorRange = Format.NO_VALUE; + public @C.ColorSpace int colorSpace = Format.NO_VALUE; + public @C.ColorTransfer int colorTransfer = Format.NO_VALUE; + public @C.ColorRange int colorRange = Format.NO_VALUE; public int maxContentLuminance = DEFAULT_MAX_CLL; public int maxFrameAverageLuminance = DEFAULT_MAX_FALL; public float primaryRChromaticityX = Format.NO_VALUE; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index 936c7be490..53ae30f9ee 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -142,7 +142,7 @@ public final class Mp3Extractor implements Extractor { private static final int SEEK_HEADER_VBRI = 0x56425249; private static final int SEEK_HEADER_UNSET = 0; - @Flags private final int flags; + private final @Flags int flags; private final long forcedFirstSampleTimestampUs; private final ParsableByteArray scratch; private final MpegAudioUtil.Header synchronizedHeader; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java index 985255a1b9..bd34ba5970 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java @@ -1910,7 +1910,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable public Format format; public int nalUnitLengthFieldLength; - @Track.Transformation public int requiredSampleTransformation; + public @Track.Transformation int requiredSampleTransformation; public StsdData(int numberOfEntries) { trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries]; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index dc83015d88..36284771d1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -132,7 +132,7 @@ public class FragmentedMp4Extractor implements Extractor { private static final int STATE_READING_SAMPLE_CONTINUE = 4; // Workarounds. - @Flags private final int flags; + private final @Flags int flags; @Nullable private final Track sideloadedTrack; // Sideloaded data. @@ -1689,8 +1689,7 @@ public class FragmentedMp4Extractor implements Extractor { } /** Returns the {@link C.BufferFlags} corresponding to the current sample. */ - @C.BufferFlags - public int getCurrentSampleFlags() { + public @C.BufferFlags int getCurrentSampleFlags() { int flags = !currentlyInFragment ? moovSampleTable.flags[currentSampleIndex] diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index b2b8bc74a3..e839cf1e9e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -141,7 +141,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { */ private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10 * 1024 * 1024; - @Flags private final int flags; + private final @Flags int flags; // Temporary arrays. private final ParsableByteArray nalStartCode; @@ -153,7 +153,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private final SefReader sefReader; private final List slowMotionMetadataEntries; - @State private int parserState; + private @State int parserState; private int atomType; private long atomSize; private int atomHeaderBytesRead; @@ -171,7 +171,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private long @MonotonicNonNull [][] accumulatedSampleSizes; private int firstVideoTrackIndex; private long durationUs; - @FileType private int fileType; + private @FileType int fileType; @Nullable private MotionPhotoMetadata motionPhotoMetadata; /** Creates a new extractor for unfragmented MP4 streams. */ @@ -438,8 +438,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { return seekRequired && parserState != STATE_READING_SAMPLE; } - @ReadResult - private int readSefData(ExtractorInput input, PositionHolder seekPosition) throws IOException { + private @ReadResult int readSefData(ExtractorInput input, PositionHolder seekPosition) + throws IOException { @ReadResult int result = sefReader.read(input, seekPosition, slowMotionMetadataEntries); if (result == RESULT_SEEK && seekPosition.position == 0) { enterReadingAtomHeaderState(); @@ -861,8 +861,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { * @param atomData The ftyp atom data. * @return The {@link FileType}. */ - @FileType - private static int processFtypAtom(ParsableByteArray atomData) { + private static @FileType int processFtypAtom(ParsableByteArray atomData) { atomData.setPosition(Atom.HEADER_SIZE); int majorBrand = atomData.readInt(); @FileType int fileType = brandToFileType(majorBrand); @@ -879,8 +878,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { return FILE_TYPE_MP4; } - @FileType - private static int brandToFileType(int brand) { + private static @FileType int brandToFileType(int brand) { switch (brand) { case BRAND_QUICKTIME: return FILE_TYPE_QUICKTIME; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java index 84170809eb..b5161fbf1e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java @@ -97,7 +97,7 @@ import java.util.List; private static final Splitter ASTERISK_SPLITTER = Splitter.on('*'); private final List dataReferences; - @State private int readerState; + private @State int readerState; private int tailLength; public SefReader() { @@ -110,8 +110,7 @@ import java.util.List; readerState = STATE_SHOULD_CHECK_FOR_SEF; } - @Extractor.ReadResult - public int read( + public @Extractor.ReadResult int read( ExtractorInput input, PositionHolder seekPosition, List slowMotionMetadataEntries) @@ -250,8 +249,7 @@ import java.util.List; return new SlowMotionData(segments); } - @DataType - private static int nameToDataType(String name) throws ParserException { + private static @DataType int nameToDataType(String name) throws ParserException { switch (name) { case "SlowMotion_Data": return TYPE_SLOW_MOTION_DATA; @@ -269,7 +267,7 @@ import java.util.List; } private static final class DataReference { - @DataType public final int dataType; + public final @DataType int dataType; public final long startOffset; public final int size; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java index 2259bbae86..adf6c8db59 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java @@ -69,7 +69,7 @@ public final class Track { * One of {@code TRANSFORMATION_*}. Defines the transformation to apply before outputting each * sample. */ - @Transformation public final int sampleTransformation; + public final @Transformation int sampleTransformation; /** Durations of edit list segments in the movie timescale. Null if there is no edit list. */ @Nullable public final long[] editListDurations; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java index 8791a4fe5c..6ef108cd30 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java @@ -79,8 +79,7 @@ public final class TrackEncryptionBox { schemeToCryptoMode(schemeType), keyId, defaultEncryptedBlocks, defaultClearBlocks); } - @C.CryptoMode - private static int schemeToCryptoMode(@Nullable String schemeType) { + private static @C.CryptoMode int schemeToCryptoMode(@Nullable String schemeType) { if (schemeType == null) { // If unknown, assume cenc. return C.CRYPTO_MODE_AES_CTR; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java index 46720375d1..df43d16ae5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java @@ -87,7 +87,7 @@ public class SubtitleExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; private @MonotonicNonNull TrackOutput trackOutput; private int bytesRead; - @State private int state; + private @State int state; private long seekTimeUs; /** diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java index 5c24fd14f0..3871ac6676 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java @@ -409,8 +409,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - @Cue.AnchorType - private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { + private static @Cue.AnchorType int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: @@ -432,8 +431,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - @Cue.AnchorType - private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { + private static @Cue.AnchorType int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java index 7bfd5785a5..08461769f7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java @@ -93,7 +93,7 @@ import java.util.regex.Pattern; public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; public final String name; - @SsaAlignment public final int alignment; + public final @SsaAlignment int alignment; @Nullable @ColorInt public final Integer primaryColor; public final float fontSize; public final boolean bold; @@ -158,8 +158,7 @@ import java.util.regex.Pattern; } } - @SsaAlignment - private static int parseAlignment(String alignmentStr) { + private static @SsaAlignment int parseAlignment(String alignmentStr) { try { @SsaAlignment int alignment = Integer.parseInt(alignmentStr.trim()); if (isValidAlignment(alignment)) { @@ -373,7 +372,7 @@ import java.util.regex.Pattern; /** Matches "\anx" and returns x in group 1 */ private static final Pattern ALIGNMENT_OVERRIDE_PATTERN = Pattern.compile("\\\\an(\\d+)"); - @SsaAlignment public final int alignment; + public final @SsaAlignment int alignment; @Nullable public final PointF position; private Overrides(@SsaAlignment int alignment, @Nullable PointF position) { @@ -451,8 +450,7 @@ import java.util.regex.Pattern; Float.parseFloat(Assertions.checkNotNull(y).trim())); } - @SsaAlignment - private static int parseAlignmentOverride(String braceContents) { + private static @SsaAlignment int parseAlignmentOverride(String braceContents) { Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents); return matcher.find() ? parseAlignment(Assertions.checkNotNull(matcher.group(1))) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java index a9e11e3642..42d34effc5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java @@ -97,13 +97,13 @@ import java.util.regex.Pattern; TtmlNode.ANNOTATION_POSITION_OUTSIDE); /** The text emphasis mark shape. */ - @MarkShape public final int markShape; + public final @MarkShape int markShape; /** The fill style of the text emphasis mark. */ - @TextEmphasisSpan.MarkFill public final int markFill; + public final @TextEmphasisSpan.MarkFill int markFill; /** The position of the text emphasis relative to the base text. */ - @Position public final int position; + public final @Position int position; private TextEmphasis( @MarkShape int markShape, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java index fe75015684..1bf821c72e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java @@ -23,13 +23,13 @@ import androidx.media3.common.text.Cue; public final String id; public final float position; public final float line; - @Cue.LineType public final int lineType; - @Cue.AnchorType public final int lineAnchor; + public final @Cue.LineType int lineType; + public final @Cue.AnchorType int lineAnchor; public final float width; public final float height; - @Cue.TextSizeType public final int textSizeType; + public final @Cue.TextSizeType int textSizeType; public final float textSize; - @Cue.VerticalType public final int verticalType; + public final @Cue.VerticalType int verticalType; public TtmlRegion(String id) { this( diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java index a2b71b0463..b706154a0f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java @@ -81,18 +81,18 @@ import java.lang.annotation.Target; private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - @OptionalBoolean private int linethrough; - @OptionalBoolean private int underline; - @OptionalBoolean private int bold; - @OptionalBoolean private int italic; - @FontSizeUnit private int fontSizeUnit; + private @OptionalBoolean int linethrough; + private @OptionalBoolean int underline; + private @OptionalBoolean int bold; + private @OptionalBoolean int italic; + private @FontSizeUnit int fontSizeUnit; private float fontSize; @Nullable private String id; - @RubyType private int rubyType; - @TextAnnotation.Position private int rubyPosition; + private @RubyType int rubyType; + private @TextAnnotation.Position int rubyPosition; @Nullable private Layout.Alignment textAlign; @Nullable private Layout.Alignment multiRowAlign; - @OptionalBoolean private int textCombine; + private @OptionalBoolean int textCombine; @Nullable private TextEmphasis textEmphasis; private float shearPercentage; @@ -114,8 +114,7 @@ import java.lang.annotation.Target; * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - @StyleFlags - public int getStyle() { + public @StyleFlags int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -292,8 +291,7 @@ import java.lang.annotation.Target; return this; } - @RubyType - public int getRubyType() { + public @RubyType int getRubyType() { return rubyType; } @@ -302,8 +300,7 @@ import java.lang.annotation.Target; return this; } - @TextAnnotation.Position - public int getRubyPosition() { + public @TextAnnotation.Position int getRubyPosition() { return rubyPosition; } @@ -357,8 +354,7 @@ import java.lang.annotation.Target; return this; } - @FontSizeUnit - public int getFontSizeUnit() { + public @FontSizeUnit int getFontSizeUnit() { return fontSizeUnit; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 29753e1d4f..77713c1672 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -97,13 +97,13 @@ public final class WebvttCssStyle { private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - @OptionalBoolean private int linethrough; - @OptionalBoolean private int underline; - @OptionalBoolean private int bold; - @OptionalBoolean private int italic; - @FontSizeUnit private int fontSizeUnit; + private @OptionalBoolean int linethrough; + private @OptionalBoolean int underline; + private @OptionalBoolean int bold; + private @OptionalBoolean int italic; + private @FontSizeUnit int fontSizeUnit; private float fontSize; - @TextAnnotation.Position private int rubyPosition; + private @TextAnnotation.Position int rubyPosition; private boolean combineUpright; public WebvttCssStyle() { @@ -186,8 +186,7 @@ public final class WebvttCssStyle { * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - @StyleFlags - public int getStyle() { + public @StyleFlags int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -276,8 +275,7 @@ public final class WebvttCssStyle { return this; } - @FontSizeUnit - public int getFontSizeUnit() { + public @FontSizeUnit int getFontSizeUnit() { return fontSizeUnit; } @@ -290,8 +288,7 @@ public final class WebvttCssStyle { return this; } - @TextAnnotation.Position - public int getRubyPosition() { + public @TextAnnotation.Position int getRubyPosition() { return rubyPosition; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java index a80d381502..456dd61680 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java @@ -406,8 +406,7 @@ public final class WebvttCueParser { } } - @Cue.AnchorType - private static int parseLineAnchor(String s) { + private static @Cue.AnchorType int parseLineAnchor(String s) { switch (s) { case "start": return Cue.ANCHOR_TYPE_START; @@ -431,8 +430,7 @@ public final class WebvttCueParser { builder.position = WebvttParserUtil.parsePercentage(s); } - @Cue.AnchorType - private static int parsePositionAnchor(String s) { + private static @Cue.AnchorType int parsePositionAnchor(String s) { switch (s) { case "line-left": case "start": @@ -449,8 +447,7 @@ public final class WebvttCueParser { } } - @Cue.VerticalType - private static int parseVerticalAttribute(String s) { + private static @Cue.VerticalType int parseVerticalAttribute(String s) { switch (s) { case "rl": return Cue.VERTICAL_TYPE_RL; @@ -462,8 +459,7 @@ public final class WebvttCueParser { } } - @TextAlignment - private static int parseTextAlignment(String s) { + private static @TextAlignment int parseTextAlignment(String s) { switch (s) { case "start": return TEXT_ALIGNMENT_START; @@ -611,8 +607,7 @@ public final class WebvttCueParser { } } - @TextAnnotation.Position - private static int getRubyPosition( + private static @TextAnnotation.Position int getRubyPosition( List styles, @Nullable String cueId, StartTag startTag) { List styleMatches = getApplicableStyles(styles, cueId, startTag); for (int i = 0; i < styleMatches.size(); i++) { @@ -624,8 +619,7 @@ public final class WebvttCueParser { return TextAnnotation.POSITION_UNKNOWN; } - @TextAnnotation.Position - private static int firstKnownRubyPosition( + private static @TextAnnotation.Position int firstKnownRubyPosition( @TextAnnotation.Position int position1, @TextAnnotation.Position int position2, @TextAnnotation.Position int position3) { @@ -770,16 +764,16 @@ public final class WebvttCueParser { public long startTimeUs; public long endTimeUs; public @MonotonicNonNull CharSequence text; - @TextAlignment public int textAlignment; + public @TextAlignment int textAlignment; public float line; // Equivalent to WebVTT's snap-to-lines flag: // https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag - @Cue.LineType public int lineType; - @Cue.AnchorType public int lineAnchor; + public @Cue.LineType int lineType; + public @Cue.AnchorType int lineAnchor; public float position; - @Cue.AnchorType public int positionAnchor; + public @Cue.AnchorType int positionAnchor; public float size; - @Cue.VerticalType public int verticalType; + public @Cue.VerticalType int verticalType; public WebvttCueInfoBuilder() { startTimeUs = 0; @@ -861,8 +855,7 @@ public final class WebvttCueParser { } // https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment - @Cue.AnchorType - private static int derivePositionAnchor(@TextAlignment int textAlignment) { + private static @Cue.AnchorType int derivePositionAnchor(@TextAlignment int textAlignment) { switch (textAlignment) { case TEXT_ALIGNMENT_LEFT: case TEXT_ALIGNMENT_START: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java index 527f32d965..1d80fbca08 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java @@ -62,7 +62,7 @@ public final class Ac3Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - @State private int state; + private @State int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java index 238b861ad7..95440cec23 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java @@ -60,7 +60,7 @@ public final class Ac4Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - @State private int state; + private @State int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java index 105059098f..11fa10f074 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -104,7 +104,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact private static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86; - @Flags private final int flags; + private final @Flags int flags; private final List closedCaptionFormats; public DefaultTsPayloadReaderFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index efa84e123f..11c81fef99 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -335,7 +335,7 @@ public final class H263Reader implements ElementaryStreamReader { private static final int STATE_WAIT_FOR_VOP_START = 4; private boolean isFilling; - @State private int state; + private @State int state; public int length; public int volStartPosition; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java index aa2a36b02b..ea033b979b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java @@ -118,7 +118,7 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_SIZE = TS_PACKET_SIZE * 50; private static final int SNIFF_TS_PACKET_COUNT = 5; - @Mode private final int mode; + private final @Mode int mode; private final int timestampSearchBytes; private final List timestampAdjusters; private final ParsableByteArray tsPacketBuffer; @@ -298,8 +298,8 @@ public final class TsExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { long inputLength = input.getLength(); if (tracksEnded) { boolean canReadDuration = inputLength != C.LENGTH_UNSET && mode != MODE_HLS; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java index 76461fa7cf..ca35a92b43 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java @@ -122,8 +122,8 @@ public final class WavExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { assertInitialized(); switch (state) { case STATE_READING_FILE_TYPE: @@ -227,8 +227,7 @@ public final class WavExtractor implements Extractor { state = STATE_READING_SAMPLE_DATA; } - @ReadResult - private int readSampleData(ExtractorInput input) throws IOException { + private @ReadResult int readSampleData(ExtractorInput input) throws IOException { Assertions.checkState(dataEndPosition != C.POSITION_UNSET); long bytesLeft = dataEndPosition - input.getPosition(); return Assertions.checkNotNull(outputWriter).sampleData(input, bytesLeft) diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java index 6597835a12..7fe5bb9284 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java @@ -178,8 +178,7 @@ public class DefaultEbmlReaderTest { private final List events = new ArrayList<>(); @Override - @EbmlProcessor.ElementType - public int getElementType(int id) { + public @EbmlProcessor.ElementType int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java index 11c65d7d1c..3837b1260a 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java @@ -42,7 +42,7 @@ public final class TtmlStyleTest { private static final String FONT_FAMILY = "serif"; @ColorInt private static final int FONT_COLOR = Color.WHITE; private static final float FONT_SIZE = 12.5f; - @TtmlStyle.FontSizeUnit private static final int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; + private static final @TtmlStyle.FontSizeUnit int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; @ColorInt private static final int BACKGROUND_COLOR = Color.BLACK; private static final int RUBY_TYPE = TtmlStyle.RUBY_TYPE_TEXT; private static final int RUBY_POSITION = TextAnnotation.POSITION_AFTER; diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java index 15d3566214..416365e414 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java @@ -82,7 +82,7 @@ public class MockPlayer implements Player { public int newIndex; public int currentPeriodIndex; public int currentMediaItemIndex; - @RepeatMode public int repeatMode; + public @RepeatMode int repeatMode; public boolean shuffleModeEnabled; public VideoSize videoSize; @Nullable public Surface surface; @@ -95,8 +95,8 @@ public class MockPlayer implements Player { public int deviceVolume; public boolean deviceMuted; public boolean playWhenReady; - @PlaybackSuppressionReason public int playbackSuppressionReason; - @State public int playbackState; + public @PlaybackSuppressionReason int playbackSuppressionReason; + public @State int playbackState; public boolean isPlaying; public boolean isLoading; public MediaMetadata mediaMetadata; @@ -591,14 +591,12 @@ public class MockPlayer implements Player { } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { return playbackSuppressionReason; } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { return playbackState; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java index edf7665e65..6aaf678a37 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java @@ -495,7 +495,7 @@ public abstract class Action { /** Calls {@link Player#setRepeatMode(int)}. */ public static final class SetRepeatMode extends Action { - @Player.RepeatMode private final int repeatMode; + private final @Player.RepeatMode int repeatMode; /** * @param tag A tag to use for logging. @@ -742,7 +742,7 @@ public abstract class Action { @Nullable private final Timeline expectedTimeline; private final boolean ignoreExpectedReason; - @Player.TimelineChangeReason private final int expectedReason; + private final @Player.TimelineChangeReason int expectedReason; /** * Creates action waiting for a timeline change for a given reason. @@ -909,7 +909,7 @@ public abstract class Action { */ public static final class WaitForPlaybackState extends Action { - @Player.State private final int targetPlaybackState; + private final @Player.State int targetPlaybackState; /** * @param tag A tag to use for logging. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java index 4da0ab6ab2..c10e0fa685 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java @@ -93,7 +93,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum private static final class DumpableConfiguration implements Dumper.Dumpable { - @C.PcmEncoding private final int inputPcmEncoding; + private final @C.PcmEncoding int inputPcmEncoding; private final int inputChannelCount; private final int inputSampleRate; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java index 743c6b1716..fa9c146a8e 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java @@ -47,7 +47,7 @@ public final class DownloadBuilder { @Nullable private String cacheKey; private byte[] customMetadata; - @Download.State private int state; + private @Download.State int state; private long startTimeMs; private long updateTimeMs; private long contentLength; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java index 7cab3c2d8a..7059c004ae 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java @@ -72,7 +72,7 @@ public class DumpFileAsserts { /** Write output to folder {@code /storage/emulated/0/Android/data} of device. */ private static final int WRITE_TO_DEVICE = 1 << 1; - @DumpFilesAction private static final int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; + private static final @DumpFilesAction int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; private DumpFileAsserts() {} diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java index 314726f85e..fca4296d6d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java @@ -398,8 +398,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return FakeCryptoConfig.TYPE; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java index 9c95fbaa25..e4c99ffa64 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java @@ -162,8 +162,7 @@ public class FakeRenderer extends BaseRenderer { } @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { int trackType = MimeTypes.getTrackType(format.sampleMimeType); return trackType != C.TRACK_TYPE_UNKNOWN && trackType == getTrackType() ? RendererCapabilities.create(C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java index d072791b45..d779b21109 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java @@ -295,7 +295,7 @@ public class FakeSampleStream implements SampleStream { private static class SampleInfo { public final byte[] data; - @C.BufferFlags public final int flags; + public final @C.BufferFlags int flags; public final long timeUs; public SampleInfo(byte[] data, @C.BufferFlags int flags, long timeUs) { diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java index ce2411f93a..b3373e1f68 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java @@ -62,14 +62,12 @@ public class StubPlayer extends BasePlayer { } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { throw new UnsupportedOperationException(); } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { throw new UnsupportedOperationException(); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java index d252878fee..63b20dfc7c 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java @@ -99,7 +99,7 @@ public class WebServerDispatcher extends Dispatcher { private byte @MonotonicNonNull [] data; private boolean supportsRangeRequests; private boolean resolvesToUnknownLength; - @GzipSupport private int gzipSupport; + private @GzipSupport int gzipSupport; /** Constructs an instance. */ public Builder() { @@ -187,7 +187,7 @@ public class WebServerDispatcher extends Dispatcher { private final byte[] data; private final boolean supportsRangeRequests; private final boolean resolvesToUnknownLength; - @GzipSupport private final int gzipSupport; + private final @GzipSupport int gzipSupport; private Resource( String path, @@ -223,8 +223,7 @@ public class WebServerDispatcher extends Dispatcher { } /** Returns the level of gzip support the server should provide for this resource. */ - @GzipSupport - public int getGzipSupport() { + public @GzipSupport int getGzipSupport() { return gzipSupport; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java index d5f16d72f2..9a30b04202 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java @@ -1115,7 +1115,7 @@ public final class SpannedSubject extends Subject { private static final class TextAndPosition { private final String text; - @TextAnnotation.Position private final int position; + private final @TextAnnotation.Position int position; private TextAndPosition(String text, int position) { this.text = text; @@ -1212,9 +1212,9 @@ public final class SpannedSubject extends Subject { private static final class MarkAndPosition { - @TextEmphasisSpan.MarkShape private final int markShape; - @TextEmphasisSpan.MarkFill private final int markFill; - @TextAnnotation.Position private final int position; + private final @TextEmphasisSpan.MarkShape int markShape; + private final @TextEmphasisSpan.MarkFill int markFill; + private final @TextAnnotation.Position int position; private MarkAndPosition( @TextEmphasisSpan.MarkShape int markShape, diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java index 1a79143a32..ff80c45666 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java @@ -44,7 +44,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen private final ConditionVariable initializedCondition; private final ConditionVariable idleCondition; - @Download.FailureReason private int failureReason; + private @Download.FailureReason int failureReason; public TestDownloadManagerListener(DownloadManager downloadManager) { this.downloadManager = downloadManager; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index ffc6bcf8ac..61755bc869 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -542,7 +542,7 @@ public final class Transformer { @Nullable private MuxerWrapper muxerWrapper; @Nullable private ExoPlayer player; - @ProgressState private int progressState; + private @ProgressState int progressState; private Transformer( Context context, @@ -743,8 +743,7 @@ public final class Transformer { * @return The {@link ProgressState}. * @throws IllegalStateException If this method is called from the wrong thread. */ - @ProgressState - public int getProgress(ProgressHolder progressHolder) { + public @ProgressState int getProgress(ProgressHolder progressHolder) { verifyApplicationThread(); if (progressState == PROGRESS_STATE_AVAILABLE) { Player player = checkNotNull(this.player); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index 94b398dcc1..d6e6c8b645 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -67,8 +67,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @return The {@link Capabilities} for this format. */ @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { return RendererCapabilities.create( MimeTypes.getTrackType(format.sampleMimeType) == getTrackType() ? C.FORMAT_HANDLED From 168914256dba29cf197a9c7624f4c72a18648476 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 10:19:41 +0000 Subject: [PATCH 148/251] Remove LegacyPlayerView reference from comment in proguard-rules.txt This class is deprecated (and will be removed from media3). #minor-release PiperOrigin-RevId: 426860018 --- libraries/ui/proguard-rules.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ui/proguard-rules.txt b/libraries/ui/proguard-rules.txt index 34ec787952..e60edc4649 100644 --- a/libraries/ui/proguard-rules.txt +++ b/libraries/ui/proguard-rules.txt @@ -1,6 +1,6 @@ # Proguard rules specific to the UI module. -# Constructor method accessed via reflection in PlayerView and LegacyPlayerView +# Constructor method accessed via reflection in PlayerView -dontnote androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView -keepclassmembers class androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView { (android.content.Context); From ce4225f864ea9fb361c49cec107ba9c99c30cab5 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 11:08:04 +0000 Subject: [PATCH 149/251] Fix some missed StyledPlayerView -> PlayerView rename sites PiperOrigin-RevId: 426868804 --- .../androidx/media3/ui/PlayerControlView.java | 44 ++++++++----------- .../java/androidx/media3/ui/PlayerView.java | 4 +- libraries/ui/src/main/res/values/attrs.xml | 2 +- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java index a3e869f4ce..ce3ba265d2 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java @@ -89,12 +89,12 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * A view for controlling {@link Player} instances. * - *

    A StyledPlayerControlView can be customized by setting attributes (or calling corresponding + *

    A PlayerControlView can be customized by setting attributes (or calling corresponding * methods), or overriding drawables. * *

    Attributes

    * - * The following attributes can be set on a StyledPlayerControlView when used in a layout XML file: + * The following attributes can be set on a PlayerControlView when used in a layout XML file: * *
      *
    • {@code show_timeout} - The time between the last user interaction and the controls @@ -160,13 +160,13 @@ import java.util.concurrent.CopyOnWriteArrayList; *
    • Default: {@code R.layout.exo_player_control_view} *
    *
  • All attributes that can be set on {@link DefaultTimeBar} can also be set on a - * StyledPlayerControlView, and will be propagated to the inflated {@link DefaultTimeBar} - * unless the layout is overridden to specify a custom {@code exo_progress} (see below). + * PlayerControlView, and will be propagated to the inflated {@link DefaultTimeBar} unless the + * layout is overridden to specify a custom {@code exo_progress} (see below). * * *

    Overriding drawables

    * - * The drawables used by StyledPlayerControlView (with its default layout file) can be overridden by + * The drawables used by PlayerControlView (with its default layout file) can be overridden by * drawables with the same names defined in your application. The drawables that can be overridden * are: * @@ -375,40 +375,32 @@ public class PlayerControlView extends FrameLayout { context .getTheme() .obtainStyledAttributes( - playbackAttrs, - R.styleable.StyledPlayerControlView, - defStyleAttr, - /* defStyleRes= */ 0); + playbackAttrs, R.styleable.PlayerControlView, defStyleAttr, /* defStyleRes= */ 0); try { controllerLayoutId = - a.getResourceId( - R.styleable.StyledPlayerControlView_controller_layout_id, controllerLayoutId); - showTimeoutMs = a.getInt(R.styleable.StyledPlayerControlView_show_timeout, showTimeoutMs); + a.getResourceId(R.styleable.PlayerControlView_controller_layout_id, controllerLayoutId); + showTimeoutMs = a.getInt(R.styleable.PlayerControlView_show_timeout, showTimeoutMs); repeatToggleModes = getRepeatToggleModes(a, repeatToggleModes); showRewindButton = - a.getBoolean(R.styleable.StyledPlayerControlView_show_rewind_button, showRewindButton); + a.getBoolean(R.styleable.PlayerControlView_show_rewind_button, showRewindButton); showFastForwardButton = a.getBoolean( - R.styleable.StyledPlayerControlView_show_fastforward_button, showFastForwardButton); + R.styleable.PlayerControlView_show_fastforward_button, showFastForwardButton); showPreviousButton = - a.getBoolean( - R.styleable.StyledPlayerControlView_show_previous_button, showPreviousButton); + a.getBoolean(R.styleable.PlayerControlView_show_previous_button, showPreviousButton); showNextButton = - a.getBoolean(R.styleable.StyledPlayerControlView_show_next_button, showNextButton); + a.getBoolean(R.styleable.PlayerControlView_show_next_button, showNextButton); showShuffleButton = - a.getBoolean( - R.styleable.StyledPlayerControlView_show_shuffle_button, showShuffleButton); + a.getBoolean(R.styleable.PlayerControlView_show_shuffle_button, showShuffleButton); showSubtitleButton = - a.getBoolean( - R.styleable.StyledPlayerControlView_show_subtitle_button, showSubtitleButton); - showVrButton = - a.getBoolean(R.styleable.StyledPlayerControlView_show_vr_button, showVrButton); + a.getBoolean(R.styleable.PlayerControlView_show_subtitle_button, showSubtitleButton); + showVrButton = a.getBoolean(R.styleable.PlayerControlView_show_vr_button, showVrButton); setTimeBarMinUpdateInterval( a.getInt( - R.styleable.StyledPlayerControlView_time_bar_min_update_interval, + R.styleable.PlayerControlView_time_bar_min_update_interval, timeBarMinUpdateIntervalMs)); animationEnabled = - a.getBoolean(R.styleable.StyledPlayerControlView_animation_enabled, animationEnabled); + a.getBoolean(R.styleable.PlayerControlView_animation_enabled, animationEnabled); } finally { a.recycle(); } @@ -1572,7 +1564,7 @@ public class PlayerControlView extends FrameLayout { @SuppressWarnings("ResourceType") private static @RepeatModeUtil.RepeatToggleModes int getRepeatToggleModes( TypedArray a, @RepeatModeUtil.RepeatToggleModes int defaultValue) { - return a.getInt(R.styleable.StyledPlayerControlView_repeat_toggle_modes, defaultValue); + return a.getInt(R.styleable.PlayerControlView_repeat_toggle_modes, defaultValue); } private final class ComponentListener diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java index 158ff83b85..ed6f7412f4 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java @@ -424,7 +424,7 @@ public class PlayerView extends FrameLayout implements AdViewProvider { if (customController != null) { this.controller = customController; } else if (controllerPlaceholder != null) { - // Propagate attrs as playbackAttrs so that StyledPlayerControlView's custom attributes are + // Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are // transferred, but standard attributes (e.g. background) are not. this.controller = new PlayerControlView(context, null, 0, attrs); controller.setId(R.id.exo_controller); @@ -1522,7 +1522,7 @@ public class PlayerView extends FrameLayout implements AdViewProvider { toggleControllerVisibility(); } - // StyledPlayerControlView.VisibilityListener implementation + // PlayerControlView.VisibilityListener implementation @Override public void onVisibilityChange(int visibility) { diff --git a/libraries/ui/src/main/res/values/attrs.xml b/libraries/ui/src/main/res/values/attrs.xml index 172b9acf0e..d944b28bf1 100644 --- a/libraries/ui/src/main/res/values/attrs.xml +++ b/libraries/ui/src/main/res/values/attrs.xml @@ -162,7 +162,7 @@ - + From 4e3d15be8751d3c450c360bfc04210a54537e6cc Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 11:08:51 +0000 Subject: [PATCH 150/251] Remove deprecated ActionFile and ActionFileUpgradeUtil #minor-release PiperOrigin-RevId: 426868933 --- .../androidx/media3/demo/main/DemoUtil.java | 31 -- .../media3/exoplayer/offline/ActionFile.java | 189 ----------- .../offline/ActionFileUpgradeUtil.java | 122 ------- .../exoplayer/offline/ActionFileTest.java | 133 -------- .../offline/ActionFileUpgradeUtilTest.java | 309 ------------------ .../offline/ProgressiveDownloaderTest.java | 3 +- ...n_file_for_download_index_upgrade_dash.exi | Bin 146 -> 0 bytes ...on_file_for_download_index_upgrade_hls.exi | Bin 146 -> 0 bytes ...for_download_index_upgrade_progressive.exi | Bin 140 -> 0 bytes ...ion_file_for_download_index_upgrade_ss.exi | Bin 154 -> 0 bytes .../offline/action_file_incomplete_header.exi | Bin 4 -> 0 bytes .../media/offline/action_file_no_data.exi | 0 .../media/offline/action_file_one_action.exi | Bin 69 -> 0 bytes .../media/offline/action_file_two_actions.exi | Bin 146 -> 0 bytes .../action_file_unsupported_version.exi | Bin 69 -> 0 bytes .../offline/action_file_zero_actions.exi | Bin 8 -> 0 bytes 16 files changed, 1 insertion(+), 786 deletions(-) delete mode 100644 libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFile.java delete mode 100644 libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtil.java delete mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileTest.java delete mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtilTest.java delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_dash.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_hls.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_progressive.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_ss.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_incomplete_header.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_no_data.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_one_action.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_two_actions.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_unsupported_version.exi delete mode 100644 libraries/test_data/src/test/assets/media/offline/action_file_zero_actions.exi diff --git a/demos/main/src/main/java/androidx/media3/demo/main/DemoUtil.java b/demos/main/src/main/java/androidx/media3/demo/main/DemoUtil.java index fc2999d8c8..2e3424697a 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/DemoUtil.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/DemoUtil.java @@ -16,7 +16,6 @@ package androidx.media3.demo.main; import android.content.Context; -import androidx.media3.common.util.Log; import androidx.media3.database.DatabaseProvider; import androidx.media3.database.StandaloneDatabaseProvider; import androidx.media3.datasource.DataSource; @@ -31,12 +30,9 @@ import androidx.media3.datasource.cronet.CronetDataSource; import androidx.media3.datasource.cronet.CronetUtil; import androidx.media3.exoplayer.DefaultRenderersFactory; import androidx.media3.exoplayer.RenderersFactory; -import androidx.media3.exoplayer.offline.ActionFileUpgradeUtil; -import androidx.media3.exoplayer.offline.DefaultDownloadIndex; import androidx.media3.exoplayer.offline.DownloadManager; import androidx.media3.exoplayer.offline.DownloadNotificationHelper; import java.io.File; -import java.io.IOException; import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; @@ -60,8 +56,6 @@ public final class DemoUtil { private static final boolean USE_CRONET_FOR_NETWORKING = true; private static final String TAG = "DemoUtil"; - private static final String DOWNLOAD_ACTION_FILE = "actions"; - private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions"; private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads"; private static DataSource.@MonotonicNonNull Factory dataSourceFactory; @@ -155,14 +149,6 @@ public final class DemoUtil { private static synchronized void ensureDownloadManagerInitialized(Context context) { if (downloadManager == null) { - DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context)); - upgradeActionFile( - context, DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false); - upgradeActionFile( - context, - DOWNLOAD_TRACKER_ACTION_FILE, - downloadIndex, - /* addNewDownloadsAsCompleted= */ true); downloadManager = new DownloadManager( context, @@ -175,23 +161,6 @@ public final class DemoUtil { } } - private static synchronized void upgradeActionFile( - Context context, - String fileName, - DefaultDownloadIndex downloadIndex, - boolean addNewDownloadsAsCompleted) { - try { - ActionFileUpgradeUtil.upgradeAndDelete( - new File(getDownloadDirectory(context), fileName), - /* downloadIdProvider= */ null, - downloadIndex, - /* deleteOnFailure= */ true, - addNewDownloadsAsCompleted); - } catch (IOException e) { - Log.e(TAG, "Failed to upgrade action file: " + fileName, e); - } - } - private static synchronized DatabaseProvider getDatabaseProvider(Context context) { if (databaseProvider == null) { databaseProvider = new StandaloneDatabaseProvider(context); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFile.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFile.java deleted file mode 100644 index ef52fac500..0000000000 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFile.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.offline; - -import android.net.Uri; -import androidx.annotation.Nullable; -import androidx.media3.common.MimeTypes; -import androidx.media3.common.StreamKey; -import androidx.media3.common.util.AtomicFile; -import androidx.media3.common.util.Util; -import androidx.media3.exoplayer.offline.DownloadRequest.UnsupportedRequestException; -import java.io.DataInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * Loads {@link DownloadRequest DownloadRequests} from legacy action files. - * - * @deprecated Legacy action files should be merged into download indices using {@link - * ActionFileUpgradeUtil}. - */ -@Deprecated -/* package */ final class ActionFile { - - private static final int VERSION = 0; - private static final String DOWNLOAD_TYPE_PROGRESSIVE = "progressive"; - private static final String DOWNLOAD_TYPE_DASH = "dash"; - private static final String DOWNLOAD_TYPE_HLS = "hls"; - private static final String DOWNLOAD_TYPE_SS = "ss"; - - private final AtomicFile atomicFile; - - /** - * @param actionFile The file from which {@link DownloadRequest DownloadRequests} will be loaded. - */ - public ActionFile(File actionFile) { - atomicFile = new AtomicFile(actionFile); - } - - /** Returns whether the file or its backup exists. */ - public boolean exists() { - return atomicFile.exists(); - } - - /** Deletes the action file and its backup. */ - public void delete() { - atomicFile.delete(); - } - - /** - * Loads {@link DownloadRequest DownloadRequests} from the file. - * - * @return The loaded {@link DownloadRequest DownloadRequests}, or an empty array if the file does - * not exist. - * @throws IOException If there is an error reading the file. - */ - public DownloadRequest[] load() throws IOException { - if (!exists()) { - return new DownloadRequest[0]; - } - @Nullable InputStream inputStream = null; - try { - inputStream = atomicFile.openRead(); - DataInputStream dataInputStream = new DataInputStream(inputStream); - int version = dataInputStream.readInt(); - if (version > VERSION) { - throw new IOException("Unsupported action file version: " + version); - } - int actionCount = dataInputStream.readInt(); - ArrayList actions = new ArrayList<>(); - for (int i = 0; i < actionCount; i++) { - try { - actions.add(readDownloadRequest(dataInputStream)); - } catch (UnsupportedRequestException e) { - // remove DownloadRequest is not supported. Ignore and continue loading rest. - } - } - return actions.toArray(new DownloadRequest[0]); - } finally { - Util.closeQuietly(inputStream); - } - } - - private static DownloadRequest readDownloadRequest(DataInputStream input) throws IOException { - String downloadType = input.readUTF(); - int version = input.readInt(); - - Uri uri = Uri.parse(input.readUTF()); - boolean isRemoveAction = input.readBoolean(); - - int dataLength = input.readInt(); - @Nullable byte[] data; - if (dataLength != 0) { - data = new byte[dataLength]; - input.readFully(data); - } else { - data = null; - } - - // Serialized version 0 progressive actions did not contain keys. - boolean isLegacyProgressive = version == 0 && DOWNLOAD_TYPE_PROGRESSIVE.equals(downloadType); - List keys = new ArrayList<>(); - if (!isLegacyProgressive) { - int keyCount = input.readInt(); - for (int i = 0; i < keyCount; i++) { - keys.add(readKey(downloadType, version, input)); - } - } - - // Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key. - boolean isLegacySegmented = - version < 2 - && (DOWNLOAD_TYPE_DASH.equals(downloadType) - || DOWNLOAD_TYPE_HLS.equals(downloadType) - || DOWNLOAD_TYPE_SS.equals(downloadType)); - @Nullable String customCacheKey = null; - if (!isLegacySegmented) { - customCacheKey = input.readBoolean() ? input.readUTF() : null; - } - - // Serialized version 0, 1 and 2 did not contain an id. We need to generate one. - String id = version < 3 ? generateDownloadId(uri, customCacheKey) : input.readUTF(); - - if (isRemoveAction) { - // Remove actions are not supported anymore. - throw new UnsupportedRequestException(); - } - - return new DownloadRequest.Builder(id, uri) - .setMimeType(inferMimeType(downloadType)) - .setStreamKeys(keys) - .setCustomCacheKey(customCacheKey) - .setData(data) - .build(); - } - - private static StreamKey readKey(String type, int version, DataInputStream input) - throws IOException { - int periodIndex; - int groupIndex; - int trackIndex; - - // Serialized version 0 HLS/SS actions did not contain a period index. - if ((DOWNLOAD_TYPE_HLS.equals(type) || DOWNLOAD_TYPE_SS.equals(type)) && version == 0) { - periodIndex = 0; - groupIndex = input.readInt(); - trackIndex = input.readInt(); - } else { - periodIndex = input.readInt(); - groupIndex = input.readInt(); - trackIndex = input.readInt(); - } - return new StreamKey(periodIndex, groupIndex, trackIndex); - } - - private static String inferMimeType(String downloadType) { - switch (downloadType) { - case DOWNLOAD_TYPE_DASH: - return MimeTypes.APPLICATION_MPD; - case DOWNLOAD_TYPE_HLS: - return MimeTypes.APPLICATION_M3U8; - case DOWNLOAD_TYPE_SS: - return MimeTypes.APPLICATION_SS; - case DOWNLOAD_TYPE_PROGRESSIVE: - default: - return MimeTypes.VIDEO_UNKNOWN; - } - } - - private static String generateDownloadId(Uri uri, @Nullable String customCacheKey) { - return customCacheKey != null ? customCacheKey : uri.toString(); - } -} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtil.java deleted file mode 100644 index d47237892a..0000000000 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtil.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.offline; - -import static androidx.media3.exoplayer.offline.Download.STATE_QUEUED; - -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; -import androidx.media3.common.C; -import androidx.media3.common.util.UnstableApi; -import java.io.File; -import java.io.IOException; - -/** Utility class for upgrading legacy action files into {@link DefaultDownloadIndex}. */ -@UnstableApi -public final class ActionFileUpgradeUtil { - - /** Provides download IDs during action file upgrade. */ - public interface DownloadIdProvider { - - /** - * Returns a download id for given request. - * - * @param downloadRequest The request for which an ID is required. - * @return A corresponding download ID. - */ - String getId(DownloadRequest downloadRequest); - } - - private ActionFileUpgradeUtil() {} - - /** - * Merges {@link DownloadRequest DownloadRequests} contained in a legacy action file into a {@link - * DefaultDownloadIndex}, deleting the action file if the merge is successful or if {@code - * deleteOnFailure} is {@code true}. - * - *

    This method must not be called while the {@link DefaultDownloadIndex} is being used by a - * {@link DownloadManager}. - * - *

    This method may be slow and shouldn't normally be called on the main thread. - * - * @param actionFilePath The action file path. - * @param downloadIdProvider A download ID provider, or {@code null}. If {@code null} then ID of - * each download will be its custom cache key if one is specified, or else its URL. - * @param downloadIndex The index into which the requests will be merged. - * @param deleteOnFailure Whether to delete the action file if the merge fails. - * @param addNewDownloadsAsCompleted Whether to add new downloads as completed. - * @throws IOException If an error occurs loading or merging the requests. - */ - @WorkerThread - @SuppressWarnings("deprecation") - public static void upgradeAndDelete( - File actionFilePath, - @Nullable DownloadIdProvider downloadIdProvider, - DefaultDownloadIndex downloadIndex, - boolean deleteOnFailure, - boolean addNewDownloadsAsCompleted) - throws IOException { - ActionFile actionFile = new ActionFile(actionFilePath); - if (actionFile.exists()) { - boolean success = false; - try { - long nowMs = System.currentTimeMillis(); - for (DownloadRequest request : actionFile.load()) { - if (downloadIdProvider != null) { - request = request.copyWithId(downloadIdProvider.getId(request)); - } - mergeRequest(request, downloadIndex, addNewDownloadsAsCompleted, nowMs); - } - success = true; - } finally { - if (success || deleteOnFailure) { - actionFile.delete(); - } - } - } - } - - /** - * Merges a {@link DownloadRequest} into a {@link DefaultDownloadIndex}. - * - * @param request The request to be merged. - * @param downloadIndex The index into which the request will be merged. - * @param addNewDownloadAsCompleted Whether to add new downloads as completed. - * @throws IOException If an error occurs merging the request. - */ - /* package */ static void mergeRequest( - DownloadRequest request, - DefaultDownloadIndex downloadIndex, - boolean addNewDownloadAsCompleted, - long nowMs) - throws IOException { - @Nullable Download download = downloadIndex.getDownload(request.id); - if (download != null) { - download = DownloadManager.mergeRequest(download, request, download.stopReason, nowMs); - } else { - download = - new Download( - request, - addNewDownloadAsCompleted ? Download.STATE_COMPLETED : STATE_QUEUED, - /* startTimeMs= */ nowMs, - /* updateTimeMs= */ nowMs, - /* contentLength= */ C.LENGTH_UNSET, - Download.STOP_REASON_NONE, - Download.FAILURE_REASON_NONE); - } - downloadIndex.putDownload(download); - } -} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileTest.java deleted file mode 100644 index 66c0d4c2da..0000000000 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.offline; - -import static com.google.common.truth.Truth.assertThat; - -import android.net.Uri; -import androidx.media3.common.MimeTypes; -import androidx.media3.common.util.Util; -import androidx.media3.test.utils.TestUtil; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link ActionFile}. */ -@SuppressWarnings("deprecation") -@RunWith(AndroidJUnit4.class) -public class ActionFileTest { - - private File tempFile; - private DownloadRequest expectedAction1; - private DownloadRequest expectedAction2; - - @Before - public void setUp() throws Exception { - tempFile = Util.createTempFile(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); - expectedAction1 = - buildExpectedRequest(Uri.parse("http://test1.uri"), TestUtil.buildTestData(16)); - expectedAction2 = - buildExpectedRequest(Uri.parse("http://test2.uri"), TestUtil.buildTestData(32)); - } - - @After - public void tearDown() throws Exception { - tempFile.delete(); - } - - @Test - public void loadNoDataThrowsIOException() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_no_data.exi"); - try { - actionFile.load(); - Assert.fail(); - } catch (IOException e) { - // Expected exception. - } - } - - @Test - public void loadIncompleteHeaderThrowsIOException() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_incomplete_header.exi"); - try { - actionFile.load(); - Assert.fail(); - } catch (IOException e) { - // Expected exception. - } - } - - @Test - public void loadZeroActions() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_zero_actions.exi"); - DownloadRequest[] actions = actionFile.load(); - assertThat(actions).isNotNull(); - assertThat(actions).hasLength(0); - } - - @Test - public void loadOneAction() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_one_action.exi"); - DownloadRequest[] actions = actionFile.load(); - assertThat(actions).hasLength(1); - assertThat(actions[0]).isEqualTo(expectedAction1); - } - - @Test - public void loadTwoActions() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_two_actions.exi"); - DownloadRequest[] actions = actionFile.load(); - assertThat(actions).hasLength(2); - assertThat(actions[0]).isEqualTo(expectedAction1); - assertThat(actions[1]).isEqualTo(expectedAction2); - } - - @Test - public void loadUnsupportedVersion() throws Exception { - ActionFile actionFile = getActionFile("media/offline/action_file_unsupported_version.exi"); - try { - actionFile.load(); - Assert.fail(); - } catch (IOException e) { - // Expected exception. - } - } - - private ActionFile getActionFile(String fileName) throws IOException { - // Copy the test data from the asset to where the ActionFile expects it to be. - byte[] actionFileBytes = - TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), fileName); - try (FileOutputStream output = new FileOutputStream(tempFile)) { - output.write(actionFileBytes); - } - // Load the action file. - return new ActionFile(tempFile); - } - - private static DownloadRequest buildExpectedRequest(Uri uri, byte[] data) { - return new DownloadRequest.Builder(/* id= */ uri.toString(), uri) - .setMimeType(MimeTypes.VIDEO_UNKNOWN) - .setData(data) - .build(); - } -} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtilTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtilTest.java deleted file mode 100644 index 7b91c9b647..0000000000 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ActionFileUpgradeUtilTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.offline; - -import static com.google.common.truth.Truth.assertThat; - -import android.net.Uri; -import androidx.media3.common.MimeTypes; -import androidx.media3.common.StreamKey; -import androidx.media3.common.util.Util; -import androidx.media3.database.StandaloneDatabaseProvider; -import androidx.media3.test.utils.TestUtil; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.common.collect.ImmutableList; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link ActionFileUpgradeUtil}. */ -@RunWith(AndroidJUnit4.class) -public class ActionFileUpgradeUtilTest { - - private static final long NOW_MS = 1234; - - private File tempFile; - private StandaloneDatabaseProvider databaseProvider; - private DefaultDownloadIndex downloadIndex; - - @Before - public void setUp() throws Exception { - tempFile = Util.createTempFile(ApplicationProvider.getApplicationContext(), "ExoPlayerTest"); - databaseProvider = new StandaloneDatabaseProvider(ApplicationProvider.getApplicationContext()); - downloadIndex = new DefaultDownloadIndex(databaseProvider); - } - - @After - public void tearDown() { - databaseProvider.close(); - tempFile.delete(); - } - - @Test - public void upgradeAndDelete_progressiveActionFile_createsDownloads() throws IOException { - byte[] actionFileBytes = - TestUtil.getByteArray( - ApplicationProvider.getApplicationContext(), - "media/offline/action_file_for_download_index_upgrade_progressive.exi"); - try (FileOutputStream output = new FileOutputStream(tempFile)) { - output.write(actionFileBytes); - } - DownloadRequest expectedRequest1 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/1/video.mp4", - Uri.parse("http://www.test.com/1/video.mp4")) - .setMimeType(MimeTypes.VIDEO_UNKNOWN) - .build(); - DownloadRequest expectedRequest2 = - new DownloadRequest.Builder( - /* id= */ "customCacheKey", Uri.parse("http://www.test.com/2/video.mp4")) - .setMimeType(MimeTypes.VIDEO_UNKNOWN) - .setCustomCacheKey("customCacheKey") - .setData(new byte[] {0, 1, 2, 3}) - .build(); - - ActionFileUpgradeUtil.upgradeAndDelete( - tempFile, - /* downloadIdProvider= */ null, - downloadIndex, - /* deleteOnFailure= */ true, - /* addNewDownloadsAsCompleted= */ false); - - assertThat(tempFile.exists()).isFalse(); - assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED); - assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED); - } - - @Test - public void upgradeAndDelete_dashActionFile_createsDownloads() throws IOException { - byte[] actionFileBytes = - TestUtil.getByteArray( - ApplicationProvider.getApplicationContext(), - "media/offline/action_file_for_download_index_upgrade_dash.exi"); - try (FileOutputStream output = new FileOutputStream(tempFile)) { - output.write(actionFileBytes); - } - DownloadRequest expectedRequest1 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/1/manifest.mpd", - Uri.parse("http://www.test.com/1/manifest.mpd")) - .setMimeType(MimeTypes.APPLICATION_MPD) - .build(); - DownloadRequest expectedRequest2 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/2/manifest.mpd", - Uri.parse("http://www.test.com/2/manifest.mpd")) - .setMimeType(MimeTypes.APPLICATION_MPD) - .setStreamKeys( - ImmutableList.of( - new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0), - new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1))) - .setData(new byte[] {0, 1, 2, 3}) - .build(); - - ActionFileUpgradeUtil.upgradeAndDelete( - tempFile, - /* downloadIdProvider= */ null, - downloadIndex, - /* deleteOnFailure= */ true, - /* addNewDownloadsAsCompleted= */ false); - - assertThat(tempFile.exists()).isFalse(); - assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED); - assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED); - } - - @Test - public void upgradeAndDelete_hlsActionFile_createsDownloads() throws IOException { - byte[] actionFileBytes = - TestUtil.getByteArray( - ApplicationProvider.getApplicationContext(), - "media/offline/action_file_for_download_index_upgrade_hls.exi"); - try (FileOutputStream output = new FileOutputStream(tempFile)) { - output.write(actionFileBytes); - } - DownloadRequest expectedRequest1 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/1/manifest.m3u8", - Uri.parse("http://www.test.com/1/manifest.m3u8")) - .setMimeType(MimeTypes.APPLICATION_M3U8) - .build(); - DownloadRequest expectedRequest2 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/2/manifest.m3u8", - Uri.parse("http://www.test.com/2/manifest.m3u8")) - .setMimeType(MimeTypes.APPLICATION_M3U8) - .setStreamKeys( - ImmutableList.of( - new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0), - new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1))) - .setData(new byte[] {0, 1, 2, 3}) - .build(); - - ActionFileUpgradeUtil.upgradeAndDelete( - tempFile, - /* downloadIdProvider= */ null, - downloadIndex, - /* deleteOnFailure= */ true, - /* addNewDownloadsAsCompleted= */ false); - - assertThat(tempFile.exists()).isFalse(); - assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED); - assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED); - } - - @Test - public void upgradeAndDelete_smoothStreamingActionFile_createsDownloads() throws IOException { - byte[] actionFileBytes = - TestUtil.getByteArray( - ApplicationProvider.getApplicationContext(), - "media/offline/action_file_for_download_index_upgrade_ss.exi"); - try (FileOutputStream output = new FileOutputStream(tempFile)) { - output.write(actionFileBytes); - } - DownloadRequest expectedRequest1 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/1/video.ism/manifest", - Uri.parse("http://www.test.com/1/video.ism/manifest")) - .setMimeType(MimeTypes.APPLICATION_SS) - .build(); - DownloadRequest expectedRequest2 = - new DownloadRequest.Builder( - /* id= */ "http://www.test.com/2/video.ism/manifest", - Uri.parse("http://www.test.com/2/video.ism/manifest")) - .setMimeType(MimeTypes.APPLICATION_SS) - .setStreamKeys( - ImmutableList.of( - new StreamKey(/* groupIndex= */ 0, /* trackIndex= */ 0), - new StreamKey(/* groupIndex= */ 1, /* trackIndex= */ 1))) - .setData(new byte[] {0, 1, 2, 3}) - .build(); - - ActionFileUpgradeUtil.upgradeAndDelete( - tempFile, - /* downloadIdProvider= */ null, - downloadIndex, - /* deleteOnFailure= */ true, - /* addNewDownloadsAsCompleted= */ false); - - assertThat(tempFile.exists()).isFalse(); - assertDownloadIndexContainsRequest(expectedRequest1, Download.STATE_QUEUED); - assertDownloadIndexContainsRequest(expectedRequest2, Download.STATE_QUEUED); - } - - @Test - public void mergeRequest_nonExistingDownload_createsNewDownload() throws IOException { - DownloadRequest request = - new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download")) - .setStreamKeys( - ImmutableList.of( - new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2), - new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5))) - .setKeySetId(new byte[] {1, 2, 3, 4}) - .setCustomCacheKey("key123") - .setData(new byte[] {1, 2, 3, 4}) - .build(); - - ActionFileUpgradeUtil.mergeRequest( - request, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); - - assertDownloadIndexContainsRequest(request, Download.STATE_QUEUED); - } - - @Test - public void mergeRequest_existingDownload_createsMergedDownload() throws IOException { - StreamKey streamKey1 = - new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5); - StreamKey streamKey2 = - new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2); - DownloadRequest request1 = - new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download1")) - .setStreamKeys(ImmutableList.of(streamKey1)) - .setKeySetId(new byte[] {1, 2, 3, 4}) - .setCustomCacheKey("key123") - .setData(new byte[] {1, 2, 3, 4}) - .build(); - DownloadRequest request2 = - new DownloadRequest.Builder(/* id= */ "id", Uri.parse("https://www.test.com/download2")) - .setMimeType(MimeTypes.APPLICATION_MP4) - .setStreamKeys(ImmutableList.of(streamKey2)) - .setKeySetId(new byte[] {5, 4, 3, 2, 1}) - .setCustomCacheKey("key345") - .setData(new byte[] {5, 4, 3, 2, 1}) - .build(); - - ActionFileUpgradeUtil.mergeRequest( - request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); - ActionFileUpgradeUtil.mergeRequest( - request2, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); - Download download = downloadIndex.getDownload(request2.id); - - assertThat(download).isNotNull(); - assertThat(download.request.mimeType).isEqualTo(MimeTypes.APPLICATION_MP4); - assertThat(download.request.customCacheKey).isEqualTo(request2.customCacheKey); - assertThat(download.request.data).isEqualTo(request2.data); - assertThat(download.request.uri).isEqualTo(request2.uri); - assertThat(download.request.streamKeys).containsExactly(streamKey1, streamKey2); - assertThat(download.request.keySetId).isEqualTo(request2.keySetId); - assertThat(download.state).isEqualTo(Download.STATE_QUEUED); - } - - @Test - public void mergeRequest_addNewDownloadAsCompleted() throws IOException { - StreamKey streamKey1 = - new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5); - StreamKey streamKey2 = - new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2); - DownloadRequest request1 = - new DownloadRequest.Builder(/* id= */ "id1", Uri.parse("https://www.test.com/download1")) - .setStreamKeys(ImmutableList.of(streamKey1)) - .setKeySetId(new byte[] {1, 2, 3, 4}) - .setCustomCacheKey("key123") - .setData(new byte[] {1, 2, 3, 4}) - .build(); - DownloadRequest request2 = - new DownloadRequest.Builder(/* id= */ "id2", Uri.parse("https://www.test.com/download2")) - .setStreamKeys(ImmutableList.of(streamKey2)) - .setKeySetId(new byte[] {5, 4, 3, 2, 1}) - .setCustomCacheKey("key456") - .setData(new byte[] {5, 4, 3, 2, 1}) - .build(); - ActionFileUpgradeUtil.mergeRequest( - request1, downloadIndex, /* addNewDownloadAsCompleted= */ false, NOW_MS); - - // Merging existing download, keeps it queued. - ActionFileUpgradeUtil.mergeRequest( - request1, downloadIndex, /* addNewDownloadAsCompleted= */ true, NOW_MS); - assertThat(downloadIndex.getDownload(request1.id).state).isEqualTo(Download.STATE_QUEUED); - - // New download is merged as completed. - ActionFileUpgradeUtil.mergeRequest( - request2, downloadIndex, /* addNewDownloadAsCompleted= */ true, NOW_MS); - assertThat(downloadIndex.getDownload(request2.id).state).isEqualTo(Download.STATE_COMPLETED); - } - - private void assertDownloadIndexContainsRequest(DownloadRequest request, int state) - throws IOException { - Download download = downloadIndex.getDownload(request.id); - assertThat(download.request).isEqualTo(request); - assertThat(download.state).isEqualTo(state); - } -} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ProgressiveDownloaderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ProgressiveDownloaderTest.java index ff4f25ae8a..ce9db0ae8e 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ProgressiveDownloaderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/offline/ProgressiveDownloaderTest.java @@ -41,8 +41,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -/** Unit tests for {@link ActionFile}. */ -@SuppressWarnings("deprecation") +/** Unit tests for {@link ProgressiveDownloader}. */ @RunWith(AndroidJUnit4.class) public class ProgressiveDownloaderTest { diff --git a/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_dash.exi b/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_dash.exi deleted file mode 100644 index 9c249a377eb4efe157bc08906206ff9dcb081842..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmZQz00SllmXyTe3@}?Mqokz3N?*Ucyj-s&wYWqtIX_q5P(L>@FEb6q%`HfQXabQ0 Yv>9P&V_{%qVg_ntLYM((F#=hP0B~O%cmMzZ diff --git a/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_hls.exi b/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_hls.exi deleted file mode 100644 index a6315a80ef4d4bfd0177ba698ca53e1caf07676e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmZQz00Sll=8T+TAd`_nIisYcz)D}gyu4hmB(=CiFF8L~-%vj{F)uR>#LYD>wSZ^> ak@&S4VQOPxU}RzjYGgu~0cSA+S&RTibQ|CR diff --git a/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_progressive.exi b/libraries/test_data/src/test/assets/media/offline/action_file_for_download_index_upgrade_progressive.exi deleted file mode 100644 index 477bd0815a5c80d19c275ee2440eb9de4d667185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 zcmZQz00Sll?t-HH^rF<_;>@yCu#kL4NlAf~zJ7Umxn4 kfeAz-k@}2K^|3H8GBGnU@FkZPm*nR@yCpb!&-Kt@SPft9{~NosM4p~9J|`1= diff --git a/libraries/test_data/src/test/assets/media/offline/action_file_two_actions.exi b/libraries/test_data/src/test/assets/media/offline/action_file_two_actions.exi deleted file mode 100644 index 35c9b35e1ee54b5ef04a1f11ed94e9c16ed7017c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmZQz00Sll?t-HH^rF<_;>@yCpb!&-Kt@SPft9{~NosM4p@q)ks6a`pW|dL*DcgNoe2-b#&8HMhi=683@Sp^2 F7y#d!EZqPA diff --git a/libraries/test_data/src/test/assets/media/offline/action_file_unsupported_version.exi b/libraries/test_data/src/test/assets/media/offline/action_file_unsupported_version.exi deleted file mode 100644 index 25e0dee842c29f0a3715408b7bfd8a91f7e4bc33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69 zcmZQzU|?hbVeW#W{Pd#K;^NG*RG9 From 99f952aae5034c5e2cdc9868c10e1d189ec59e03 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 11:16:52 +0000 Subject: [PATCH 151/251] Update final translations PiperOrigin-RevId: 426870226 --- .../session/src/main/res/values-sw/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/session/src/main/res/values-sw/strings.xml b/libraries/session/src/main/res/values-sw/strings.xml index 3844321f1e..25484e4f98 100755 --- a/libraries/session/src/main/res/values-sw/strings.xml +++ b/libraries/session/src/main/res/values-sw/strings.xml @@ -14,11 +14,11 @@ limitations under the License. --> - Now playing - Play - Pause - Seek to previous item - Seek to next item - Seek back - Seek forward + Inayocheza sasa + Cheza + Sitisha + Rudi kwenye maudhui yaliyotangulia + Nenda kwenye maudhui yanayofuata + Sogeza nyuma + Sogeza mbele From 9c8c0a59823e2a49451fbdf7040107c6cea43cc5 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 11:59:39 +0000 Subject: [PATCH 152/251] Remove deprecated EventListener PiperOrigin-RevId: 426876984 --- .../java/androidx/media3/cast/CastPlayer.java | 36 +- .../media3/common/ForwardingPlayer.java | 322 +++++---- .../java/androidx/media3/common/Player.java | 654 ++++++++---------- .../media3/common/ForwardingPlayerTest.java | 15 - .../androidx/media3/exoplayer/ExoPlayer.java | 26 - .../media3/exoplayer/ExoPlayerImpl.java | 116 ++-- .../media3/exoplayer/SimpleExoPlayer.java | 13 - .../media3/test/utils/StubExoPlayer.java | 11 - 8 files changed, 498 insertions(+), 695 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index cc026ba69a..347d27904e 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -137,7 +137,7 @@ public final class CastPlayer extends BasePlayer { private final SeekResultCallback seekResultCallback; // Listeners and notification. - private final ListenerSet listeners; + private final ListenerSet listeners; @Nullable private SessionAvailabilityListener sessionAvailabilityListener; // Internal state. @@ -280,41 +280,11 @@ public final class CastPlayer extends BasePlayer { @Override public void addListener(Listener listener) { - EventListener eventListener = listener; - addListener(eventListener); - } - - /** - * Registers a listener to receive events from the player. - * - *

    The listener's methods will be called on the thread associated with {@link - * #getApplicationLooper()}. - * - * @param listener The listener to register. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @Deprecated - @SuppressWarnings("deprecation") - public void addListener(EventListener listener) { listeners.add(listener); } @Override public void removeListener(Listener listener) { - EventListener eventListener = listener; - removeListener(eventListener); - } - - /** - * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will - * no longer receive events from the player. - * - * @param listener The listener to unregister. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @Deprecated - @SuppressWarnings("deprecation") - public void removeListener(EventListener listener) { listeners.remove(listener); } @@ -473,7 +443,7 @@ public final class CastPlayer extends BasePlayer { } updateAvailableCommandsAndNotifyIfChanged(); } else if (pendingSeekCount == 0) { - listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } listeners.flushEvents(); } @@ -1477,7 +1447,7 @@ public final class CastPlayer extends BasePlayer { currentWindowIndex = pendingSeekWindowIndex; pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekPositionMs = C.TIME_UNSET; - listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index c0ea8befcc..5e00fa0663 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -819,191 +819,159 @@ public class ForwardingPlayer implements Player { return player; } - @SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility. - private static class ForwardingEventListener implements EventListener { + private static final class ForwardingListener implements Listener { private final ForwardingPlayer forwardingPlayer; - private final EventListener eventListener; + private final Listener listener; - private ForwardingEventListener( - ForwardingPlayer forwardingPlayer, EventListener eventListener) { + public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { this.forwardingPlayer = forwardingPlayer; - this.eventListener = eventListener; - } - - @Override - public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { - eventListener.onTimelineChanged(timeline, reason); - } - - @Override - public void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - eventListener.onMediaItemTransition(mediaItem, reason); - } - - @Override - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - eventListener.onTracksChanged(trackGroups, trackSelections); - } - - @Override - public void onTracksInfoChanged(TracksInfo tracksInfo) { - eventListener.onTracksInfoChanged(tracksInfo); - } - - @Override - public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { - eventListener.onMediaMetadataChanged(mediaMetadata); - } - - @Override - public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { - eventListener.onPlaylistMetadataChanged(mediaMetadata); - } - - @Override - public void onIsLoadingChanged(boolean isLoading) { - eventListener.onIsLoadingChanged(isLoading); - } - - @Override - public void onLoadingChanged(boolean isLoading) { - eventListener.onIsLoadingChanged(isLoading); - } - - @Override - public void onAvailableCommandsChanged(Commands availableCommands) { - eventListener.onAvailableCommandsChanged(availableCommands); - } - - @Override - public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { - eventListener.onTrackSelectionParametersChanged(parameters); - } - - @Override - public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { - eventListener.onPlayerStateChanged(playWhenReady, playbackState); - } - - @Override - public void onPlaybackStateChanged(@State int playbackState) { - eventListener.onPlaybackStateChanged(playbackState); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - eventListener.onPlayWhenReadyChanged(playWhenReady, reason); - } - - @Override - public void onPlaybackSuppressionReasonChanged( - @PlayWhenReadyChangeReason int playbackSuppressionReason) { - eventListener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); - } - - @Override - public void onIsPlayingChanged(boolean isPlaying) { - eventListener.onIsPlayingChanged(isPlaying); - } - - @Override - public void onRepeatModeChanged(@RepeatMode int repeatMode) { - eventListener.onRepeatModeChanged(repeatMode); - } - - @Override - public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled); - } - - @Override - public void onPlayerError(PlaybackException error) { - eventListener.onPlayerError(error); - } - - @Override - public void onPlayerErrorChanged(@Nullable PlaybackException error) { - eventListener.onPlayerErrorChanged(error); - } - - @Override - public void onPositionDiscontinuity(@DiscontinuityReason int reason) { - eventListener.onPositionDiscontinuity(reason); - } - - @Override - public void onPositionDiscontinuity( - PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { - eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason); - } - - @Override - public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { - eventListener.onPlaybackParametersChanged(playbackParameters); - } - - @Override - public void onSeekBackIncrementChanged(long seekBackIncrementMs) { - eventListener.onSeekBackIncrementChanged(seekBackIncrementMs); - } - - @Override - public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { - eventListener.onSeekForwardIncrementChanged(seekForwardIncrementMs); - } - - @Override - public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { - eventListener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); - } - - @Override - public void onSeekProcessed() { - eventListener.onSeekProcessed(); + this.listener = listener; } @Override public void onEvents(Player player, Events events) { // Replace player with forwarding player. - eventListener.onEvents(forwardingPlayer, events); + listener.onEvents(forwardingPlayer, events); } @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ForwardingEventListener)) { - return false; - } - - ForwardingEventListener that = (ForwardingEventListener) o; - - if (!forwardingPlayer.equals(that.forwardingPlayer)) { - return false; - } - return eventListener.equals(that.eventListener); + public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { + listener.onTimelineChanged(timeline, reason); } @Override - public int hashCode() { - int result = forwardingPlayer.hashCode(); - result = 31 * result + eventListener.hashCode(); - return result; + public void onMediaItemTransition( + @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { + listener.onMediaItemTransition(mediaItem, reason); } - } - private static final class ForwardingListener extends ForwardingEventListener - implements Listener { + @Override + @SuppressWarnings("deprecation") + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + listener.onTracksChanged(trackGroups, trackSelections); + } - private final Listener listener; + @Override + public void onTracksInfoChanged(TracksInfo tracksInfo) { + listener.onTracksInfoChanged(tracksInfo); + } - public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { - super(forwardingPlayer, listener); - this.listener = listener; + @Override + public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { + listener.onMediaMetadataChanged(mediaMetadata); + } + + @Override + public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { + listener.onPlaylistMetadataChanged(mediaMetadata); + } + + @Override + public void onIsLoadingChanged(boolean isLoading) { + listener.onIsLoadingChanged(isLoading); + } + + @Override + @SuppressWarnings("deprecation") + public void onLoadingChanged(boolean isLoading) { + listener.onIsLoadingChanged(isLoading); + } + + @Override + public void onAvailableCommandsChanged(Commands availableCommands) { + listener.onAvailableCommandsChanged(availableCommands); + } + + @Override + public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { + listener.onTrackSelectionParametersChanged(parameters); + } + + @Override + @SuppressWarnings("deprecation") + public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { + listener.onPlayerStateChanged(playWhenReady, playbackState); + } + + @Override + public void onPlaybackStateChanged(@State int playbackState) { + listener.onPlaybackStateChanged(playbackState); + } + + @Override + public void onPlayWhenReadyChanged( + boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { + listener.onPlayWhenReadyChanged(playWhenReady, reason); + } + + @Override + public void onPlaybackSuppressionReasonChanged( + @PlayWhenReadyChangeReason int playbackSuppressionReason) { + listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + listener.onIsPlayingChanged(isPlaying); + } + + @Override + public void onRepeatModeChanged(@RepeatMode int repeatMode) { + listener.onRepeatModeChanged(repeatMode); + } + + @Override + public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { + listener.onShuffleModeEnabledChanged(shuffleModeEnabled); + } + + @Override + public void onPlayerError(PlaybackException error) { + listener.onPlayerError(error); + } + + @Override + public void onPlayerErrorChanged(@Nullable PlaybackException error) { + listener.onPlayerErrorChanged(error); + } + + @Override + @SuppressWarnings("deprecation") + public void onPositionDiscontinuity(@DiscontinuityReason int reason) { + listener.onPositionDiscontinuity(reason); + } + + @Override + public void onPositionDiscontinuity( + PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { + listener.onPositionDiscontinuity(oldPosition, newPosition, reason); + } + + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + listener.onPlaybackParametersChanged(playbackParameters); + } + + @Override + public void onSeekBackIncrementChanged(long seekBackIncrementMs) { + listener.onSeekBackIncrementChanged(seekBackIncrementMs); + } + + @Override + public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { + listener.onSeekForwardIncrementChanged(seekForwardIncrementMs); + } + + @Override + public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { + listener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); + } + + @Override + @SuppressWarnings("deprecation") + public void onSeekProcessed() { + listener.onSeekProcessed(); } @Override @@ -1060,5 +1028,27 @@ public class ForwardingPlayer implements Player { public void onDeviceVolumeChanged(int volume, boolean muted) { listener.onDeviceVolumeChanged(volume, muted); } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ForwardingListener)) { + return false; + } + ForwardingListener that = (ForwardingListener) o; + if (!forwardingPlayer.equals(that.forwardingPlayer)) { + return false; + } + return listener.equals(that.listener); + } + + @Override + public int hashCode() { + int result = forwardingPlayer.hashCode(); + result = 31 * result + listener.hashCode(); + return result; + } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 1e7ed137e3..7487910ebb 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -63,343 +63,6 @@ import java.util.List; */ public interface Player { - /** - * Listener of changes in player state. - * - *

    All methods have no-op default implementations to allow selective overrides. - * - *

    Listeners can choose to implement individual events (e.g. {@link - * #onIsPlayingChanged(boolean)}) or {@link #onEvents(Player, Events)}, which is called after one - * or more events occurred together. - * - * @deprecated Use {@link Player.Listener}. - */ - @UnstableApi - @Deprecated - interface EventListener { - - /** - * Called when the timeline has been refreshed. - * - *

    Note that the current {@link MediaItem} or playback position may change as a result of a - * timeline change. If playback can't continue smoothly because of this timeline change, a - * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be - * triggered. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param timeline The latest timeline. Never null, but may be empty. - * @param reason The {@link TimelineChangeReason} responsible for this timeline change. - */ - default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} - - /** - * Called when playback transitions to a media item or starts repeating a media item according - * to the current {@link #getRepeatMode() repeat mode}. - * - *

    Note that this callback is also called when the playlist becomes non-empty or empty as a - * consequence of a playlist change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. - * @param reason The reason for the transition. - */ - default void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} - - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param trackGroups The available tracks. Never null, but may be of length zero. - * @param trackSelections The selected tracks. Never null, but may contain null elements. A - * concrete implementation may include null elements if it has a fixed number of renderer - * components, wishes to report a TrackSelection for each of them, and has one or more - * renderer components that is not assigned any selected tracks. - * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. - */ - @UnstableApi - @Deprecated - default void onTracksChanged( - TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} - - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param tracksInfo The available tracks information. Never null, but may be of length zero. - */ - default void onTracksInfoChanged(TracksInfo tracksInfo) {} - - /** - * Called when the combined {@link MediaMetadata} changes. - * - *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} - * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track - * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in - * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from - * static or dynamic metadata. - * - *

    This method may be called multiple times in quick succession. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaMetadata The combined {@link MediaMetadata}. - */ - default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - - /** Called when the playlist {@link MediaMetadata} changes. */ - default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} - - /** - * Called when the player starts or stops loading the source. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isLoading Whether the source is currently being loaded. - */ - default void onIsLoadingChanged(boolean isLoading) {} - - /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ - @Deprecated - default void onLoadingChanged(boolean isLoading) {} - - /** - * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one - * {@link Command}. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param availableCommands The available {@link Commands}. - */ - default void onAvailableCommandsChanged(Commands availableCommands) {} - - /** - * Called when the value returned from {@link #getTrackSelectionParameters()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param parameters The new {@link TrackSelectionParameters}. - */ - default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} - - /** - * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link - * #onPlayWhenReadyChanged(boolean, int)} instead. - */ - @Deprecated - default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} - - /** - * Called when the value returned from {@link #getPlaybackState()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackState The new playback {@link State state}. - */ - default void onPlaybackStateChanged(@State int playbackState) {} - - /** - * Called when the value returned from {@link #getPlayWhenReady()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playWhenReady Whether playback will proceed when ready. - * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. - */ - default void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} - - /** - * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. - */ - default void onPlaybackSuppressionReasonChanged( - @PlaybackSuppressionReason int playbackSuppressionReason) {} - - /** - * Called when the value of {@link #isPlaying()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isPlaying Whether the player is playing. - */ - default void onIsPlayingChanged(boolean isPlaying) {} - - /** - * Called when the value of {@link #getRepeatMode()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param repeatMode The {@link RepeatMode} used for playback. - */ - default void onRepeatModeChanged(@RepeatMode int repeatMode) {} - - /** - * Called when the value of {@link #getShuffleModeEnabled()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. - */ - default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} - - /** - * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} - * immediately after this method is called. The player instance can still be used, and {@link - * #release()} must still be called on the player should it no longer be required. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The error. - */ - default void onPlayerError(PlaybackException error) {} - - /** - * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The new error, or null if the error is being cleared. - */ - default void onPlayerErrorChanged(@Nullable PlaybackException error) {} - - /** - * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. - */ - @Deprecated - default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} - - /** - * Called when a position discontinuity occurs. - * - *

    A position discontinuity occurs when the playing period changes, the playback position - * jumps within the period currently being played, or when the playing period has been skipped - * or removed. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param oldPosition The position before the discontinuity. - * @param newPosition The position after the discontinuity. - * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. - */ - default void onPositionDiscontinuity( - PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} - - /** - * Called when the current playback parameters change. The playback parameters may change due to - * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change - * them (for example, if audio playback switches to passthrough or offload mode, where speed - * adjustment is no longer possible). - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackParameters The playback parameters. - */ - default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} - - /** - * Called when the value of {@link #getSeekBackIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. - */ - default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} - - /** - * Called when the value of {@link #getSeekForwardIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. - */ - default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} - - /** - * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} - * seeks to the previous position, in milliseconds. - */ - default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} - - /** - * @deprecated Seeks are processed without delay. Listen to {@link - * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link - * #DISCONTINUITY_REASON_SEEK} instead. - */ - @Deprecated - default void onSeekProcessed() {} - - /** - * Called when one or more player states changed. - * - *

    State changes and events that happen within one {@link Looper} message queue iteration are - * reported together and only after all individual callbacks were triggered. - * - *

    Only state changes represented by {@link Event events} are reported through this method. - * - *

    Listeners should prefer this method over individual callbacks in the following cases: - * - *

      - *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for - * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, - * int)}). - *
    • They need access to the {@link Player} object to trigger further events (e.g. to call - * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). - *
    • They intend to use multiple state values together or in combination with {@link Player} - * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code - * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from - * within this method. - *
    • They are interested in events that logically happened together (e.g {@link - * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link - * #onMediaItemTransition(MediaItem, int)}). - *
    - * - * @param player The {@link Player} whose state changed. Use the getters to obtain the latest - * states. - * @param events The {@link Events} that happened in this iteration, indicating which player - * states changed. - */ - default void onEvents(Player player, Events events) {} - } - /** A set of {@link Event events}. */ final class Events { @@ -939,62 +602,332 @@ public interface Player { * *

    All methods have no-op default implementations to allow selective overrides. */ - interface Listener extends EventListener { + interface Listener { - @Override + /** + * Called when one or more player states changed. + * + *

    State changes and events that happen within one {@link Looper} message queue iteration are + * reported together and only after all individual callbacks were triggered. + * + *

    Only state changes represented by {@link Event events} are reported through this method. + * + *

    Listeners should prefer this method over individual callbacks in the following cases: + * + *

      + *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for + * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, + * int)}). + *
    • They need access to the {@link Player} object to trigger further events (e.g. to call + * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). + *
    • They intend to use multiple state values together or in combination with {@link Player} + * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code + * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from + * within this method. + *
    • They are interested in events that logically happened together (e.g {@link + * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link + * #onMediaItemTransition(MediaItem, int)}). + *
    + * + * @param player The {@link Player} whose state changed. Use the getters to obtain the latest + * states. + * @param events The {@link Events} that happened in this iteration, indicating which player + * states changed. + */ + default void onEvents(Player player, Events events) {} + + /** + * Called when the timeline has been refreshed. + * + *

    Note that the current {@link MediaItem} or playback position may change as a result of a + * timeline change. If playback can't continue smoothly because of this timeline change, a + * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be + * triggered. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param timeline The latest timeline. Never null, but may be empty. + * @param reason The {@link TimelineChangeReason} responsible for this timeline change. + */ default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} - @Override + /** + * Called when playback transitions to a media item or starts repeating a media item according + * to the current {@link #getRepeatMode() repeat mode}. + * + *

    Note that this callback is also called when the playlist becomes non-empty or empty as a + * consequence of a playlist change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. + * @param reason The reason for the transition. + */ default void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} - @Override + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param trackGroups The available tracks. Never null, but may be of length zero. + * @param trackSelections The selected tracks. Never null, but may contain null elements. A + * concrete implementation may include null elements if it has a fixed number of renderer + * components, wishes to report a TrackSelection for each of them, and has one or more + * renderer components that is not assigned any selected tracks. + * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. + */ + @UnstableApi + @Deprecated + default void onTracksChanged( + TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} + + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param tracksInfo The available tracks information. Never null, but may be of length zero. + */ default void onTracksInfoChanged(TracksInfo tracksInfo) {} - @Override + /** + * Called when the combined {@link MediaMetadata} changes. + * + *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} + * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track + * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in + * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from + * static or dynamic metadata. + * + *

    This method may be called multiple times in quick succession. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaMetadata The combined {@link MediaMetadata}. + */ + default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} + + /** Called when the playlist {@link MediaMetadata} changes. */ + default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} + + /** + * Called when the player starts or stops loading the source. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isLoading Whether the source is currently being loaded. + */ default void onIsLoadingChanged(boolean isLoading) {} - @Override + /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ + @Deprecated + @UnstableApi + default void onLoadingChanged(boolean isLoading) {} + + /** + * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one + * {@link Command}. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param availableCommands The available {@link Commands}. + */ default void onAvailableCommandsChanged(Commands availableCommands) {} - @Override + /** + * Called when the value returned from {@link #getTrackSelectionParameters()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param parameters The new {@link TrackSelectionParameters}. + */ + default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} + + /** + * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link + * #onPlayWhenReadyChanged(boolean, int)} instead. + */ + @Deprecated + @UnstableApi + default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} + + /** + * Called when the value returned from {@link #getPlaybackState()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackState The new playback {@link State state}. + */ default void onPlaybackStateChanged(@State int playbackState) {} - @Override + /** + * Called when the value returned from {@link #getPlayWhenReady()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playWhenReady Whether playback will proceed when ready. + * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. + */ default void onPlayWhenReadyChanged( boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} - @Override + /** + * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. + */ default void onPlaybackSuppressionReasonChanged( @PlaybackSuppressionReason int playbackSuppressionReason) {} - @Override + /** + * Called when the value of {@link #isPlaying()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isPlaying Whether the player is playing. + */ default void onIsPlayingChanged(boolean isPlaying) {} - @Override + /** + * Called when the value of {@link #getRepeatMode()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param repeatMode The {@link RepeatMode} used for playback. + */ default void onRepeatModeChanged(@RepeatMode int repeatMode) {} - @Override + /** + * Called when the value of {@link #getShuffleModeEnabled()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. + */ default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} - @Override + /** + * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} + * immediately after this method is called. The player instance can still be used, and {@link + * #release()} must still be called on the player should it no longer be required. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The error. + */ default void onPlayerError(PlaybackException error) {} - @Override + /** + * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The new error, or null if the error is being cleared. + */ default void onPlayerErrorChanged(@Nullable PlaybackException error) {} - @Override + /** + * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. + */ + @Deprecated + @UnstableApi + default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} + + /** + * Called when a position discontinuity occurs. + * + *

    A position discontinuity occurs when the playing period changes, the playback position + * jumps within the period currently being played, or when the playing period has been skipped + * or removed. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param oldPosition The position before the discontinuity. + * @param newPosition The position after the discontinuity. + * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. + */ default void onPositionDiscontinuity( PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} - @Override + /** + * Called when the current playback parameters change. The playback parameters may change due to + * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change + * them (for example, if audio playback switches to passthrough or offload mode, where speed + * adjustment is no longer possible). + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackParameters The playback parameters. + */ default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} - @Override + /** + * Called when the value of {@link #getSeekBackIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. + */ + default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} + + /** + * Called when the value of {@link #getSeekForwardIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. + */ default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} - @Override - default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} + /** + * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} + * seeks to the previous position, in milliseconds. + */ + default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} + + /** + * @deprecated Seeks are processed without delay. Listen to {@link + * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link + * #DISCONTINUITY_REASON_SEEK} instead. + */ + @Deprecated + @UnstableApi + default void onSeekProcessed() {} /** * Called when the audio session ID changes. @@ -1031,9 +964,6 @@ public interface Player { /** Called when the device volume or mute state changes. */ default void onDeviceVolumeChanged(int volume, boolean muted) {} - @Override - default void onEvents(Player player, Events events) {} - /** * Called each time there's a change in the size of the video being rendered. * @@ -1075,12 +1005,6 @@ public interface Player { */ @UnstableApi default void onMetadata(Metadata metadata) {} - - @Override - default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - - @Override - default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} } /** diff --git a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java index 2973bbb2c2..22b30a8b33 100644 --- a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java @@ -115,21 +115,6 @@ public class ForwardingPlayerTest { } } - @Test - @SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated type. - public void forwardingEventListener_overridesAllEventListenerMethods() throws Exception { - // Check with reflection that ForwardingListener overrides all Listener methods. - Class forwardingListenerClass = getInnerClass("ForwardingEventListener"); - List methods = getPublicMethods(Player.EventListener.class); - for (int i = 0; i < methods.size(); i++) { - Method method = methods.get(i); - assertThat( - forwardingListenerClass.getDeclaredMethod( - method.getName(), method.getParameterTypes())) - .isNotNull(); - } - } - @Test public void forwardingListener_overridesAllListenerMethods() throws Exception { // Check with reflection that ForwardingListener overrides all Listener methods. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index ab755cbc26..36d21c7ad8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -1081,32 +1081,6 @@ public interface ExoPlayer extends Player { @Deprecated DeviceComponent getDeviceComponent(); - /** - * Registers a listener to receive events from the player. - * - *

    The listener's methods will be called on the thread associated with {@link - * #getApplicationLooper()}. - * - * @param listener The listener to register. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @UnstableApi - @Deprecated - @SuppressWarnings("deprecation") - void addListener(EventListener listener); - - /** - * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will - * no longer receive events from the player. - * - * @param listener The listener to unregister. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @UnstableApi - @Deprecated - @SuppressWarnings("deprecation") - void removeListener(EventListener listener); - /** * Adds a listener to receive audio offload events. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index ab7c22607d..621ad87af9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -109,7 +109,6 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.Player.Commands; import androidx.media3.common.Player.DiscontinuityReason; -import androidx.media3.common.Player.EventListener; import androidx.media3.common.Player.Events; import androidx.media3.common.Player.Listener; import androidx.media3.common.Player.PlayWhenReadyChangeReason; @@ -191,9 +190,9 @@ import java.util.concurrent.TimeoutException; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; - @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners. - private final ListenerSet eventListeners; - + private final ListenerSet listeners; + // TODO(b/187152483): Remove this once all events are dispatched via ListenerSet. + private final CopyOnWriteArraySet listenerArraySet; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -208,7 +207,6 @@ import java.util.concurrent.TimeoutException; private final Clock clock; private final ComponentListener componentListener; private final FrameMetadataListener frameMetadataListener; - private final CopyOnWriteArraySet listeners; private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; private final StreamVolumeManager streamVolumeManager; @@ -294,7 +292,6 @@ import java.util.concurrent.TimeoutException; detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; componentListener = new ComponentListener(); frameMetadataListener = new FrameMetadataListener(); - listeners = new CopyOnWriteArraySet<>(); Handler eventHandler = new Handler(builder.looper); renderers = builder @@ -318,11 +315,12 @@ import java.util.concurrent.TimeoutException; this.applicationLooper = builder.looper; this.clock = builder.clock; this.wrappingPlayer = wrappingPlayer; - eventListeners = + listeners = new ListenerSet<>( applicationLooper, clock, (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); + listenerArraySet = new CopyOnWriteArraySet<>(); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -410,9 +408,9 @@ import java.util.concurrent.TimeoutException; currentCues = ImmutableList.of(); throwsWhenUsingWrongThread = true; - addEventListener(analyticsCollector); + listeners.add(analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - addEventListener(componentListener); + listeners.add(componentListener); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); @@ -488,18 +486,6 @@ import java.util.concurrent.TimeoutException; return clock; } - @SuppressWarnings("deprecation") // Register deprecated EventListener. - public void addEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.add(eventListener); - } - - @SuppressWarnings("deprecation") // Deregister deprecated EventListener. - public void removeEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.remove(eventListener); - } - public void addAudioOffloadListener(AudioOffloadListener listener) { // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); @@ -805,10 +791,10 @@ import java.util.concurrent.TimeoutException; if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; internalPlayer.setRepeatMode(repeatMode); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } @@ -822,11 +808,11 @@ import java.util.concurrent.TimeoutException; if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } @@ -1028,7 +1014,7 @@ import java.util.concurrent.TimeoutException; audioFocusManager.release(); if (!internalPlayer.release()) { // One of the renderers timed out releasing its resources. - eventListeners.sendEvent( + listeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( @@ -1036,7 +1022,7 @@ import java.util.concurrent.TimeoutException; new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), PlaybackException.ERROR_CODE_TIMEOUT))); } - eventListeners.release(); + listeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); @@ -1216,7 +1202,7 @@ import java.util.concurrent.TimeoutException; return; } trackSelector.setParameters(parameters); - eventListeners.queueEvent( + listeners.queueEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } @@ -1236,7 +1222,7 @@ import java.util.concurrent.TimeoutException; return; } mediaMetadata = newMediaMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } @@ -1252,7 +1238,7 @@ import java.util.concurrent.TimeoutException; return; } this.playlistMetadata = playlistMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_PLAYLIST_METADATA_CHANGED, listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } @@ -1409,7 +1395,7 @@ import java.util.concurrent.TimeoutException; streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); analyticsCollector.onAudioAttributesChanged(audioAttributes); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onAudioAttributesChanged(audioAttributes); } } @@ -1447,7 +1433,7 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); analyticsCollector.onAudioSessionIdChanged(audioSessionId); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onAudioSessionIdChanged(audioSessionId); } } @@ -1475,7 +1461,7 @@ import java.util.concurrent.TimeoutException; sendVolumeToRenderers(); analyticsCollector.onVolumeChanged(volume); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onVolumeChanged(volume); } } @@ -1607,14 +1593,14 @@ import java.util.concurrent.TimeoutException; // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.add(listener); - addEventListener(listener); + listenerArraySet.add(listener); } public void removeListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.remove(listener); - removeEventListener(listener); + listenerArraySet.remove(listener); } public void setHandleWakeLock(boolean handleWakeLock) { @@ -1815,7 +1801,7 @@ import java.util.concurrent.TimeoutException; mediaMetadata = newMediaMetadata; if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); } @@ -1824,7 +1810,7 @@ import java.util.concurrent.TimeoutException; getPreviousPositionInfo( positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_POSITION_DISCONTINUITY, listener -> { listener.onPositionDiscontinuity(positionDiscontinuityReason); @@ -1834,16 +1820,16 @@ import java.util.concurrent.TimeoutException; } if (mediaItemTransitioned) { @Nullable final MediaItem finalMediaItem = mediaItem; - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); if (newPlaybackInfo.playbackError != null) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); } @@ -1852,21 +1838,21 @@ import java.util.concurrent.TimeoutException; trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); } if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; - eventListeners.queueEvent( + listeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { listener.onLoadingChanged(newPlaybackInfo.isLoading); @@ -1875,19 +1861,19 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged( @@ -1895,27 +1881,27 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged( newPlaybackInfo.playbackSuppressionReason)); } if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); } if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); } if (seekProcessed) { - eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { for (AudioOffloadListener listener : audioOffloadListeners) { @@ -2072,7 +2058,7 @@ import java.util.concurrent.TimeoutException; Commands previousAvailableCommands = availableCommands; availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, listener -> listener.onAvailableCommandsChanged(availableCommands)); } @@ -2492,7 +2478,7 @@ import java.util.concurrent.TimeoutException; surfaceHeight = height; analyticsCollector.onSurfaceSizeChanged(width, height); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onSurfaceSizeChanged(width, height); } } @@ -2506,7 +2492,7 @@ import java.util.concurrent.TimeoutException; private void notifySkipSilenceEnabledChanged() { analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); } } @@ -2648,10 +2634,9 @@ import java.util.concurrent.TimeoutException; } } - // TODO(b/204189802): Remove self-listening to deprecated EventListener. - @SuppressWarnings("deprecation") private final class ComponentListener - implements VideoRendererEventListener, + implements Player.Listener, + VideoRendererEventListener, AudioRendererEventListener, TextOutput, MetadataOutput, @@ -2661,7 +2646,6 @@ import java.util.concurrent.TimeoutException; AudioFocusManager.PlayerControl, AudioBecomingNoisyManager.EventListener, StreamVolumeManager.Listener, - Player.EventListener, AudioOffloadListener { // VideoRendererEventListener implementation @@ -2696,7 +2680,7 @@ import java.util.concurrent.TimeoutException; ExoPlayerImpl.this.videoSize = videoSize; analyticsCollector.onVideoSizeChanged(videoSize); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onVideoSizeChanged(videoSize); } } @@ -2706,7 +2690,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); if (videoOutput == output) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onRenderedFirstFrame(); } } @@ -2803,7 +2787,7 @@ import java.util.concurrent.TimeoutException; public void onCues(List cues) { currentCues = cues; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listeners) { + for (Listener listeners : listenerArraySet) { listeners.onCues(cues); } } @@ -2815,7 +2799,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onMetadata(metadata); ExoPlayerImpl.this.onMetadata(metadata); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onMetadata(metadata); } } @@ -2911,7 +2895,7 @@ import java.util.concurrent.TimeoutException; if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); } } @@ -2920,12 +2904,12 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); } } - // Player.EventListener implementation. + // Player.Listener implementation. @Override public void onIsLoadingChanged(boolean isLoading) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 14540012df..53b308104f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -33,7 +33,6 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; import androidx.media3.common.PlaybackParameters; -import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroupArray; @@ -619,23 +618,11 @@ public class SimpleExoPlayer extends BasePlayer player.addListener(listener); } - @Deprecated - @Override - public void addListener(Player.EventListener listener) { - player.addEventListener(listener); - } - @Override public void removeListener(Listener listener) { player.removeListener(listener); } - @Deprecated - @Override - public void removeListener(Player.EventListener listener) { - player.removeEventListener(listener); - } - @Override public @State int getPlaybackState() { return player.getPlaybackState(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index 0f4cd5bdae..baa9047330 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -20,7 +20,6 @@ import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.Format; -import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; @@ -80,16 +79,6 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void addListener(Player.EventListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeListener(Player.EventListener listener) { - throw new UnsupportedOperationException(); - } - @Override public void addAudioOffloadListener(AudioOffloadListener listener) { throw new UnsupportedOperationException(); From e0bab55f12eb68073fa812c5580634d4efe32f7f Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 13:32:56 +0000 Subject: [PATCH 153/251] Add missing calls to AnalyticsCollector PiperOrigin-RevId: 426892085 --- .../src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 621ad87af9..5616e5f773 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -2786,6 +2786,7 @@ import java.util.concurrent.TimeoutException; @Override public void onCues(List cues) { currentCues = cues; + analyticsCollector.onCues(cues); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listeners : listenerArraySet) { listeners.onCues(cues); @@ -2894,6 +2895,7 @@ import java.util.concurrent.TimeoutException; DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; + analyticsCollector.onDeviceInfoChanged(deviceInfo); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); @@ -2903,6 +2905,7 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { + analyticsCollector.onDeviceVolumeChanged(streamVolume, streamMuted); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); From d50a43912e3246dac4f974adc4f616ea5dc2fb79 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 15:18:20 +0000 Subject: [PATCH 154/251] Remove deprecated ProgressiveMediaSource.Factory setters #minor-release PiperOrigin-RevId: 426909957 --- .../source/ProgressiveMediaSource.java | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaSource.java index 1714930513..d56fd4ca65 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaSource.java @@ -126,40 +126,6 @@ public final class ProgressiveMediaSource extends BaseMediaSource this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes; } - /** - * @deprecated Pass the {@link ExtractorsFactory} via {@link #Factory(DataSource.Factory, - * ExtractorsFactory)}. This is necessary so that proguard can treat the default extractors - * factory as unused. - */ - @Deprecated - public Factory setExtractorsFactory(@Nullable ExtractorsFactory extractorsFactory) { - this.progressiveMediaExtractorFactory = - playerId -> - new BundledExtractorsAdapter( - extractorsFactory != null ? extractorsFactory : new DefaultExtractorsFactory()); - return this; - } - - /** - * @deprecated Use {@link MediaItem.Builder#setCustomCacheKey(String)} and {@link - * #createMediaSource(MediaItem)} instead. - */ - @Deprecated - public Factory setCustomCacheKey(@Nullable String customCacheKey) { - this.customCacheKey = customCacheKey; - return this; - } - - /** - * @deprecated Use {@link MediaItem.Builder#setTag(Object)} and {@link - * #createMediaSource(MediaItem)} instead. - */ - @Deprecated - public Factory setTag(@Nullable Object tag) { - this.tag = tag; - return this; - } - /** * Sets the {@link LoadErrorHandlingPolicy}. The default value is created by calling {@link * DefaultLoadErrorHandlingPolicy#DefaultLoadErrorHandlingPolicy()}. From 1df5572dd28323d8a2f54bf1a29ce080b7700feb Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 7 Feb 2022 16:28:07 +0000 Subject: [PATCH 155/251] Update some text descriptions to ease translations. Some strings didn't mention the context in which they are used (for example as item in a list, or for accessibility). This makes it harder for translators to choose the most appropriate translation and grammar. Also fix repeat and shuffle mode button accessibility descriptions to indicate the action, not the current state. PiperOrigin-RevId: 426924163 --- .../session/src/main/res/values/strings.xml | 14 ++-- libraries/ui/src/main/res/values/strings.xml | 82 +++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/libraries/session/src/main/res/values/strings.xml b/libraries/session/src/main/res/values/strings.xml index 8cb062ac72..4b5b6c86e0 100644 --- a/libraries/session/src/main/res/values/strings.xml +++ b/libraries/session/src/main/res/values/strings.xml @@ -14,18 +14,18 @@ limitations under the License. --> - + Now playing - + Play - + Pause - + Seek to previous item - + Seek to next item - + Seek back - + Seek forward diff --git a/libraries/ui/src/main/res/values/strings.xml b/libraries/ui/src/main/res/values/strings.xml index 2b7a00d418..515767c8ef 100644 --- a/libraries/ui/src/main/res/values/strings.xml +++ b/libraries/ui/src/main/res/values/strings.xml @@ -14,63 +14,63 @@ limitations under the License. --> - + Show player controls - + Hide player controls - + Playback progress - + Settings - + Hide additional settings - + Show additional settings - + Enter fullscreen - + Exit fullscreen - + Previous - + Next - + Pause - + Play - + Stop - + Rewind - + Rewind %d second Rewind %d seconds - + Fast forward - + Fast forward %d second Fast forward %d seconds - - Repeat none - - Repeat one - - Repeat all - - Shuffle on - - Shuffle off - + + Current mode: Repeat none. Toggle repeat mode. + + Current mode: Repeat one. Toggle repeat mode. + + Current mode: Repeat all. Toggle repeat mode. + + Disable shuffle mode + + Enable shuffle mode + VR mode - + Disable subtitles - + Enable subtitles - + Speed @@ -98,27 +98,27 @@ None Auto - + Unknown %1$d × %2$d - + Mono - + Stereo - + Surround sound - + 5.1 surround sound - + 7.1 surround sound - + Alternate - + Supplementary - + Commentary - + CC %1$.2f Mbps From 59958d6a162f5b41d66fc9c6b53bb3982d48b763 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 7 Feb 2022 17:31:40 +0000 Subject: [PATCH 156/251] Remove deprecated DefaultRenderersFactory constructors #minor-release PiperOrigin-RevId: 426938026 --- .../exoplayer/DefaultRenderersFactory.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 013aab4365..451c0ee3a1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -112,33 +112,6 @@ public class DefaultRenderersFactory implements RenderersFactory { mediaCodecSelector = MediaCodecSelector.DEFAULT; } - /** - * @deprecated Use {@link #DefaultRenderersFactory(Context)} and {@link - * #setExtensionRendererMode(int)}. - */ - @Deprecated - @SuppressWarnings("deprecation") - public DefaultRenderersFactory( - Context context, @ExtensionRendererMode int extensionRendererMode) { - this(context, extensionRendererMode, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS); - } - - /** - * @deprecated Use {@link #DefaultRenderersFactory(Context)}, {@link - * #setExtensionRendererMode(int)} and {@link #setAllowedVideoJoiningTimeMs(long)}. - */ - @Deprecated - public DefaultRenderersFactory( - Context context, - @ExtensionRendererMode int extensionRendererMode, - long allowedVideoJoiningTimeMs) { - this.context = context; - this.extensionRendererMode = extensionRendererMode; - this.allowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs; - mediaCodecSelector = MediaCodecSelector.DEFAULT; - codecAdapterFactory = new DefaultMediaCodecAdapterFactory(); - } - /** * Sets the extension renderer mode, which determines if and how available extension renderers are * used. Note that extensions must be included in the application build for them to be considered From 403d92a4d4272ddb8cc01691e25cc6aa09e66b51 Mon Sep 17 00:00:00 2001 From: samrobinson Date: Mon, 7 Feb 2022 18:38:44 +0000 Subject: [PATCH 157/251] Output from the Transformer the average audio & video bitrates. PiperOrigin-RevId: 426956151 --- .../demo/transformer/TransformerActivity.java | 4 +- .../main/java/androidx/media3/common/C.java | 3 + .../media3/transformer/AndroidTestUtil.java | 80 ++++------- .../RepeatedTranscodeTransformationTest.java | 7 +- .../media3/transformer/MuxerWrapper.java | 30 ++++- .../transformer/TransformationResult.java | 124 ++++++++++++++++++ .../media3/transformer/Transformer.java | 24 +++- .../transformer/TransformerEndToEndTest.java | 39 +++++- .../transformer/TransformerTestRunner.java | 3 +- 9 files changed, 245 insertions(+), 69 deletions(-) create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/TransformationResult.java diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index 9e0ce52328..3b7ebb4304 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -42,6 +42,7 @@ import androidx.media3.exoplayer.util.DebugTextViewHelper; import androidx.media3.transformer.ProgressHolder; import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationRequest; +import androidx.media3.transformer.TransformationResult; import androidx.media3.transformer.Transformer; import androidx.media3.ui.AspectRatioFrameLayout; import androidx.media3.ui.PlayerView; @@ -223,7 +224,8 @@ public final class TransformerActivity extends AppCompatActivity { .addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem mediaItem) { + public void onTransformationCompleted( + MediaItem mediaItem, TransformationResult transformationResult) { TransformerActivity.this.onTransformationCompleted(filePath); } diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index ef48f66f6d..a83ca8798f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -69,6 +69,9 @@ public final class C { /** Represents an unset or unknown rate. */ public static final float RATE_UNSET = -Float.MAX_VALUE; + /** Represents an unset or unknown integer rate. */ + @UnstableApi public static final int RATE_UNSET_INT = Integer.MIN_VALUE + 1; + /** Represents an unset or unknown length. */ public static final int LENGTH_UNSET = -1; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 0a8c48c145..82f3c29f56 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -23,6 +23,7 @@ import android.content.Context; import android.net.Uri; import android.os.Build; import androidx.annotation.Nullable; +import androidx.media3.common.C; import androidx.media3.common.MediaItem; import androidx.test.platform.app.InstrumentationRegistry; import java.io.File; @@ -39,50 +40,6 @@ public final class AndroidTestUtil { public static final String REMOTE_MP4_10_SECONDS_URI_STRING = "https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4"; - /** Information about the result of successfully running a transformer. */ - public static final class TransformationResult { - - /** A builder for {@link TransformationResult} instances. */ - public static final class Builder { - private final String testId; - @Nullable private Long fileSizeBytes; - - public Builder(String testId) { - this.testId = testId; - } - - public Builder setFileSizeBytes(long fileSizeBytes) { - this.fileSizeBytes = fileSizeBytes; - return this; - } - - public TransformationResult build() { - return new TransformationResult(testId, fileSizeBytes); - } - } - - public final String testId; - @Nullable public final Long fileSizeBytes; - - private TransformationResult(String testId, @Nullable Long fileSizeBytes) { - this.testId = testId; - this.fileSizeBytes = fileSizeBytes; - } - - /** - * Returns all the analysis data from the test. - * - *

    If a value was not generated, it will not be part of the return value. - */ - public String getFormattedAnalysis() { - String analysis = "test=" + testId; - if (fileSizeBytes != null) { - analysis += ", fileSizeBytes=" + fileSizeBytes; - } - return analysis; - } - } - /** * Transforms the {@code uriString} with the {@link Transformer}. * @@ -98,6 +55,7 @@ public final class AndroidTestUtil { Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) throws Exception { AtomicReference<@NullableType Exception> exceptionReference = new AtomicReference<>(); + AtomicReference resultReference = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); Transformer testTransformer = @@ -106,7 +64,9 @@ public final class AndroidTestUtil { .addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem inputMediaItem) { + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult result) { + resultReference.set(result); countDownLatch.countDown(); } @@ -141,19 +101,21 @@ public final class AndroidTestUtil { } TransformationResult result = - new TransformationResult.Builder(testId).setFileSizeBytes(outputVideoFile.length()).build(); + resultReference.get().buildUpon().setFileSizeBytes(outputVideoFile.length()).build(); - writeTransformationResultToFile(context, result); + writeResultToFile(context, testId, result); return result; } - private static void writeTransformationResultToFile(Context context, TransformationResult result) + private static void writeResultToFile(Context context, String testId, TransformationResult result) throws IOException { - File analysisFile = - createExternalCacheFile(context, /* fileName= */ result.testId + "-result.txt"); + File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt"); try (FileWriter fileWriter = new FileWriter(analysisFile)) { String fileContents = - result.getFormattedAnalysis() + "test=" + + testId + + ", " + + getFormattedResult(result) + ", deviceFingerprint=" + Build.FINGERPRINT + ", deviceBrand=" @@ -166,6 +128,22 @@ public final class AndroidTestUtil { } } + /** Formats a {@link TransformationResult} into a comma separated String. */ + public static String getFormattedResult(TransformationResult result) { + String analysis = ""; + if (result.fileSizeBytes != C.LENGTH_UNSET) { + analysis += "fileSizeBytes=" + result.fileSizeBytes; + } + if (result.averageAudioBitrate != C.RATE_UNSET_INT) { + analysis += ", averageAudioBitrate=" + result.averageAudioBitrate; + } + if (result.averageVideoBitrate != C.RATE_UNSET_INT) { + analysis += ", averageVideoBitrate=" + result.averageVideoBitrate; + } + + return analysis; + } + private static File createExternalCacheFile(Context context, String fileName) throws IOException { File file = new File(context.getExternalCacheDir(), fileName); checkState(!file.exists() || file.delete(), "Could not delete file: " + file.getAbsolutePath()); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java index eb3feca46c..6de689a8df 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -24,6 +24,7 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Assertions; import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.TransformationRequest; +import androidx.media3.transformer.TransformationResult; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -57,7 +58,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, @@ -91,7 +92,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, @@ -122,7 +123,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i, 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 5f62af713c..364337b910 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MuxerWrapper.java @@ -48,6 +48,7 @@ import java.nio.ByteBuffer; private final Muxer.Factory muxerFactory; private final SparseIntArray trackTypeToIndex; private final SparseLongArray trackTypeToTimeUs; + private final SparseLongArray trackTypeToBytesWritten; private final String containerMimeType; private int trackCount; @@ -62,6 +63,7 @@ import java.nio.ByteBuffer; this.containerMimeType = containerMimeType; trackTypeToIndex = new SparseIntArray(); trackTypeToTimeUs = new SparseLongArray(); + trackTypeToBytesWritten = new SparseLongArray(); previousTrackType = C.TRACK_TYPE_NONE; } @@ -121,6 +123,7 @@ import java.nio.ByteBuffer; int trackIndex = muxer.addTrack(format); trackTypeToIndex.put(trackType, trackIndex); trackTypeToTimeUs.put(trackType, 0L); + trackTypeToBytesWritten.put(trackType, 0L); trackFormatCount++; if (trackFormatCount == trackCount) { isReady = true; @@ -154,8 +157,11 @@ import java.nio.ByteBuffer; return false; } - muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); + trackTypeToBytesWritten.put( + trackType, trackTypeToBytesWritten.get(trackType) + data.remaining()); trackTypeToTimeUs.put(trackType, presentationTimeUs); + + muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); previousTrackType = trackType; return true; } @@ -168,7 +174,6 @@ import java.nio.ByteBuffer; */ public void endTrack(@C.TrackType int trackType) { trackTypeToIndex.delete(trackType); - trackTypeToTimeUs.delete(trackType); } /** @@ -191,6 +196,25 @@ import java.nio.ByteBuffer; return trackCount; } + /** + * Returns the average bitrate of data written to the track of the provided {@code trackType}, or + * {@link C#RATE_UNSET_INT} if there is no track data. + */ + public int getTrackAverageBitrate(@C.TrackType int trackType) { + long trackDurationUs = trackTypeToTimeUs.get(trackType, /* valueIfKeyNotFound= */ -1); + long trackBytes = trackTypeToBytesWritten.get(trackType, /* valueIfKeyNotFound= */ -1); + if (trackDurationUs <= 0 || trackBytes <= 0) { + return C.RATE_UNSET_INT; + } + // The number of bytes written is not a timestamp, however this utility method provides + // overflow-safe multiplication & division. + return (int) + Util.scaleLargeTimestamp( + /* timestamp= */ trackBytes, + /* multiplier= */ C.BITS_PER_BYTE * C.MICROS_PER_SECOND, + /* divisor= */ trackDurationUs); + } + /** * Returns whether the muxer can write a sample of the given track type. * @@ -208,7 +232,7 @@ import java.nio.ByteBuffer; if (!isReady) { return false; } - if (trackTypeToTimeUs.size() == 1) { + if (trackTypeToIndex.size() == 1) { return true; } if (trackType != previousTrackType) { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationResult.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationResult.java new file mode 100644 index 0000000000..b5ece274a6 --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationResult.java @@ -0,0 +1,124 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static androidx.media3.common.util.Assertions.checkArgument; + +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.util.UnstableApi; + +/** Information about the result of a successful transformation. */ +@UnstableApi +public final class TransformationResult { + + /** A builder for {@link TransformationResult} instances. */ + public static final class Builder { + private long fileSizeBytes; + private int averageAudioBitrate; + private int averageVideoBitrate; + + public Builder() { + fileSizeBytes = C.LENGTH_UNSET; + averageAudioBitrate = C.RATE_UNSET_INT; + averageVideoBitrate = C.RATE_UNSET_INT; + } + + /** + * Sets the file size in bytes. + * + *

    Input must be positive or {@link C#LENGTH_UNSET}. + */ + public Builder setFileSizeBytes(long fileSizeBytes) { + checkArgument(fileSizeBytes > 0 || fileSizeBytes == C.LENGTH_UNSET); + this.fileSizeBytes = fileSizeBytes; + return this; + } + + /** + * Sets the average audio bitrate. + * + *

    Input must be positive or {@link C#RATE_UNSET_INT}. + */ + public Builder setAverageAudioBitrate(int averageAudioBitrate) { + checkArgument(averageAudioBitrate > 0 || averageAudioBitrate == C.RATE_UNSET_INT); + this.averageAudioBitrate = averageAudioBitrate; + return this; + } + + /** + * Sets the average video bitrate. + * + *

    Input must be positive or {@link C#RATE_UNSET_INT}. + */ + public Builder setAverageVideoBitrate(int averageVideoBitrate) { + checkArgument(averageVideoBitrate > 0 || averageVideoBitrate == C.RATE_UNSET_INT); + this.averageVideoBitrate = averageVideoBitrate; + return this; + } + + public TransformationResult build() { + return new TransformationResult(fileSizeBytes, averageAudioBitrate, averageVideoBitrate); + } + } + + /** The size of the file in bytes, or {@link C#LENGTH_UNSET} if unset or unknown. */ + public final long fileSizeBytes; + /** + * The average bitrate of the audio track data, or {@link C#RATE_UNSET_INT} if unset or unknown. + */ + public final int averageAudioBitrate; + /** + * The average bitrate of the video track data, or {@link C#RATE_UNSET_INT} if unset or unknown. + */ + public final int averageVideoBitrate; + + private TransformationResult( + long fileSizeBytes, int averageAudioBitrate, int averageVideoBitrate) { + this.fileSizeBytes = fileSizeBytes; + this.averageAudioBitrate = averageAudioBitrate; + this.averageVideoBitrate = averageVideoBitrate; + } + + public Builder buildUpon() { + return new Builder() + .setFileSizeBytes(fileSizeBytes) + .setAverageAudioBitrate(averageAudioBitrate) + .setAverageVideoBitrate(averageVideoBitrate); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TransformationResult)) { + return false; + } + TransformationResult result = (TransformationResult) o; + return fileSizeBytes == result.fileSizeBytes + && averageAudioBitrate == result.averageAudioBitrate + && averageVideoBitrate == result.averageVideoBitrate; + } + + @Override + public int hashCode() { + int result = (int) fileSizeBytes; + result = 31 * result + averageAudioBitrate; + result = 31 * result + averageVideoBitrate; + return result; + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 61755bc869..3b88fff41d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -445,12 +445,22 @@ public final class Transformer { /** A listener for the transformation events. */ public interface Listener { + /** + * @deprecated Use {@link #onTransformationCompleted(MediaItem, TransformationResult)} instead. + */ + @Deprecated + default void onTransformationCompleted(MediaItem inputMediaItem) {} + /** * Called when the transformation is completed successfully. * * @param inputMediaItem The {@link MediaItem} for which the transformation is completed. + * @param transformationResult The {@link TransformationResult} of the transformation. */ - default void onTransformationCompleted(MediaItem inputMediaItem) {} + default void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { + onTransformationCompleted(inputMediaItem); + } /** @deprecated Use {@link #onTransformationError(MediaItem, TransformationException)}. */ @Deprecated @@ -735,8 +745,9 @@ public final class Transformer { * Returns the current {@link ProgressState} and updates {@code progressHolder} with the current * progress if it is {@link #PROGRESS_STATE_AVAILABLE available}. * - *

    After a transformation {@link Listener#onTransformationCompleted(MediaItem) completes}, this - * method returns {@link #PROGRESS_STATE_NO_TRANSFORMATION}. + *

    After a transformation {@link Listener#onTransformationCompleted(MediaItem, + * TransformationResult) completes}, this method returns {@link + * #PROGRESS_STATE_NO_TRANSFORMATION}. * * @param progressHolder A {@link ProgressHolder}, updated to hold the percentage progress if * {@link #PROGRESS_STATE_AVAILABLE available}. @@ -952,9 +963,14 @@ public final class Transformer { /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onTransformationError(mediaItem, finalException)); } else { + TransformationResult result = + new TransformationResult.Builder() + .setAverageAudioBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO)) + .setAverageVideoBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO)) + .build(); listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, - listener -> listener.onTransformationCompleted(mediaItem)); + listener -> listener.onTransformationCompleted(mediaItem, result)); } listeners.flushEvents(); } diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java index e3178ad40b..773d457b41 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -22,6 +22,8 @@ import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE import static androidx.media3.transformer.Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -57,6 +59,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import org.checkerframework.checker.nullness.compatqual.NullableType; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -239,9 +242,9 @@ public final class TransformerEndToEndTest { transformer.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer); - verify(mockListener1, times(1)).onTransformationCompleted(mediaItem); - verify(mockListener2, times(1)).onTransformationCompleted(mediaItem); - verify(mockListener3, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener1, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener2, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener3, times(1)).onTransformationCompleted(eq(mediaItem), any()); } @Test @@ -313,9 +316,9 @@ public final class TransformerEndToEndTest { transformer2.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer2); - verify(mockListener1, times(1)).onTransformationCompleted(mediaItem); - verify(mockListener2, never()).onTransformationCompleted(mediaItem); - verify(mockListener3, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener1, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener2, never()).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener3, times(1)).onTransformationCompleted(eq(mediaItem), any()); } @Test @@ -333,6 +336,30 @@ public final class TransformerEndToEndTest { DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SEF_SLOW_MOTION)); } + @Test + public void startTransformation_completesWithValidBitrate() throws Exception { + AtomicReference<@NullableType TransformationResult> resultReference = new AtomicReference<>(); + Transformer.Listener listener = + new Transformer.Listener() { + @Override + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { + resultReference.set(transformationResult); + } + }; + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ true).addListener(listener).build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); + + transformer.startTransformation(mediaItem, outputPath); + TransformerTestRunner.runUntilCompleted(transformer); + + @Nullable TransformationResult result = resultReference.get(); + assertThat(result).isNotNull(); + assertThat(result.averageAudioBitrate).isGreaterThan(0); + assertThat(result.averageVideoBitrate).isGreaterThan(0); + } + @Test public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError() throws Exception { diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java index 03f4c020ea..ee933c702f 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerTestRunner.java @@ -78,7 +78,8 @@ public final class TransformerTestRunner { transformer.addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem inputMediaItem) { + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { transformationCompleted.set(true); } From bc118097d8532cfe7433f2b7909f7fff1b71ce04 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 21:04:05 +0000 Subject: [PATCH 158/251] Revert of e0bab55f12eb68073fa812c5580634d4efe32f7f PiperOrigin-RevId: 426994559 --- .../src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 5616e5f773..621ad87af9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -2786,7 +2786,6 @@ import java.util.concurrent.TimeoutException; @Override public void onCues(List cues) { currentCues = cues; - analyticsCollector.onCues(cues); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listeners : listenerArraySet) { listeners.onCues(cues); @@ -2895,7 +2894,6 @@ import java.util.concurrent.TimeoutException; DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; - analyticsCollector.onDeviceInfoChanged(deviceInfo); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); @@ -2905,7 +2903,6 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { - analyticsCollector.onDeviceVolumeChanged(streamVolume, streamMuted); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); From 631b107bda9351a79f111b01e9855b7257bb2bed Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 21:04:58 +0000 Subject: [PATCH 159/251] Revert of 9c8c0a59823e2a49451fbdf7040107c6cea43cc5 PiperOrigin-RevId: 426994820 --- .../java/androidx/media3/cast/CastPlayer.java | 36 +- .../media3/common/ForwardingPlayer.java | 136 ++-- .../java/androidx/media3/common/Player.java | 654 ++++++++++-------- .../media3/common/ForwardingPlayerTest.java | 15 + .../androidx/media3/exoplayer/ExoPlayer.java | 26 + .../media3/exoplayer/ExoPlayerImpl.java | 116 ++-- .../media3/exoplayer/SimpleExoPlayer.java | 13 + .../media3/test/utils/StubExoPlayer.java | 11 + 8 files changed, 602 insertions(+), 405 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 347d27904e..cc026ba69a 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -137,7 +137,7 @@ public final class CastPlayer extends BasePlayer { private final SeekResultCallback seekResultCallback; // Listeners and notification. - private final ListenerSet listeners; + private final ListenerSet listeners; @Nullable private SessionAvailabilityListener sessionAvailabilityListener; // Internal state. @@ -280,11 +280,41 @@ public final class CastPlayer extends BasePlayer { @Override public void addListener(Listener listener) { + EventListener eventListener = listener; + addListener(eventListener); + } + + /** + * Registers a listener to receive events from the player. + * + *

    The listener's methods will be called on the thread associated with {@link + * #getApplicationLooper()}. + * + * @param listener The listener to register. + * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. + */ + @Deprecated + @SuppressWarnings("deprecation") + public void addListener(EventListener listener) { listeners.add(listener); } @Override public void removeListener(Listener listener) { + EventListener eventListener = listener; + removeListener(eventListener); + } + + /** + * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will + * no longer receive events from the player. + * + * @param listener The listener to unregister. + * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. + */ + @Deprecated + @SuppressWarnings("deprecation") + public void removeListener(EventListener listener) { listeners.remove(listener); } @@ -443,7 +473,7 @@ public final class CastPlayer extends BasePlayer { } updateAvailableCommandsAndNotifyIfChanged(); } else if (pendingSeekCount == 0) { - listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } listeners.flushEvents(); } @@ -1447,7 +1477,7 @@ public final class CastPlayer extends BasePlayer { currentWindowIndex = pendingSeekWindowIndex; pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekPositionMs = C.TIME_UNSET; - listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); + listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index 5e00fa0663..c0ea8befcc 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -819,159 +819,191 @@ public class ForwardingPlayer implements Player { return player; } - private static final class ForwardingListener implements Listener { + @SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility. + private static class ForwardingEventListener implements EventListener { private final ForwardingPlayer forwardingPlayer; - private final Listener listener; + private final EventListener eventListener; - public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { + private ForwardingEventListener( + ForwardingPlayer forwardingPlayer, EventListener eventListener) { this.forwardingPlayer = forwardingPlayer; - this.listener = listener; - } - - @Override - public void onEvents(Player player, Events events) { - // Replace player with forwarding player. - listener.onEvents(forwardingPlayer, events); + this.eventListener = eventListener; } @Override public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { - listener.onTimelineChanged(timeline, reason); + eventListener.onTimelineChanged(timeline, reason); } @Override public void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - listener.onMediaItemTransition(mediaItem, reason); + eventListener.onMediaItemTransition(mediaItem, reason); } @Override - @SuppressWarnings("deprecation") public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - listener.onTracksChanged(trackGroups, trackSelections); + eventListener.onTracksChanged(trackGroups, trackSelections); } @Override public void onTracksInfoChanged(TracksInfo tracksInfo) { - listener.onTracksInfoChanged(tracksInfo); + eventListener.onTracksInfoChanged(tracksInfo); } @Override public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { - listener.onMediaMetadataChanged(mediaMetadata); + eventListener.onMediaMetadataChanged(mediaMetadata); } @Override public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { - listener.onPlaylistMetadataChanged(mediaMetadata); + eventListener.onPlaylistMetadataChanged(mediaMetadata); } @Override public void onIsLoadingChanged(boolean isLoading) { - listener.onIsLoadingChanged(isLoading); + eventListener.onIsLoadingChanged(isLoading); } @Override - @SuppressWarnings("deprecation") public void onLoadingChanged(boolean isLoading) { - listener.onIsLoadingChanged(isLoading); + eventListener.onIsLoadingChanged(isLoading); } @Override public void onAvailableCommandsChanged(Commands availableCommands) { - listener.onAvailableCommandsChanged(availableCommands); + eventListener.onAvailableCommandsChanged(availableCommands); } @Override public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { - listener.onTrackSelectionParametersChanged(parameters); + eventListener.onTrackSelectionParametersChanged(parameters); } @Override - @SuppressWarnings("deprecation") public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { - listener.onPlayerStateChanged(playWhenReady, playbackState); + eventListener.onPlayerStateChanged(playWhenReady, playbackState); } @Override public void onPlaybackStateChanged(@State int playbackState) { - listener.onPlaybackStateChanged(playbackState); + eventListener.onPlaybackStateChanged(playbackState); } @Override public void onPlayWhenReadyChanged( boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - listener.onPlayWhenReadyChanged(playWhenReady, reason); + eventListener.onPlayWhenReadyChanged(playWhenReady, reason); } @Override public void onPlaybackSuppressionReasonChanged( @PlayWhenReadyChangeReason int playbackSuppressionReason) { - listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); + eventListener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); } @Override public void onIsPlayingChanged(boolean isPlaying) { - listener.onIsPlayingChanged(isPlaying); + eventListener.onIsPlayingChanged(isPlaying); } @Override public void onRepeatModeChanged(@RepeatMode int repeatMode) { - listener.onRepeatModeChanged(repeatMode); + eventListener.onRepeatModeChanged(repeatMode); } @Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - listener.onShuffleModeEnabledChanged(shuffleModeEnabled); + eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled); } @Override public void onPlayerError(PlaybackException error) { - listener.onPlayerError(error); + eventListener.onPlayerError(error); } @Override public void onPlayerErrorChanged(@Nullable PlaybackException error) { - listener.onPlayerErrorChanged(error); + eventListener.onPlayerErrorChanged(error); } @Override - @SuppressWarnings("deprecation") public void onPositionDiscontinuity(@DiscontinuityReason int reason) { - listener.onPositionDiscontinuity(reason); + eventListener.onPositionDiscontinuity(reason); } @Override public void onPositionDiscontinuity( PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { - listener.onPositionDiscontinuity(oldPosition, newPosition, reason); + eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason); } @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { - listener.onPlaybackParametersChanged(playbackParameters); + eventListener.onPlaybackParametersChanged(playbackParameters); } @Override public void onSeekBackIncrementChanged(long seekBackIncrementMs) { - listener.onSeekBackIncrementChanged(seekBackIncrementMs); + eventListener.onSeekBackIncrementChanged(seekBackIncrementMs); } @Override public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { - listener.onSeekForwardIncrementChanged(seekForwardIncrementMs); + eventListener.onSeekForwardIncrementChanged(seekForwardIncrementMs); } @Override public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { - listener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); + eventListener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); } @Override - @SuppressWarnings("deprecation") public void onSeekProcessed() { - listener.onSeekProcessed(); + eventListener.onSeekProcessed(); + } + + @Override + public void onEvents(Player player, Events events) { + // Replace player with forwarding player. + eventListener.onEvents(forwardingPlayer, events); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ForwardingEventListener)) { + return false; + } + + ForwardingEventListener that = (ForwardingEventListener) o; + + if (!forwardingPlayer.equals(that.forwardingPlayer)) { + return false; + } + return eventListener.equals(that.eventListener); + } + + @Override + public int hashCode() { + int result = forwardingPlayer.hashCode(); + result = 31 * result + eventListener.hashCode(); + return result; + } + } + + private static final class ForwardingListener extends ForwardingEventListener + implements Listener { + + private final Listener listener; + + public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { + super(forwardingPlayer, listener); + this.listener = listener; } @Override @@ -1028,27 +1060,5 @@ public class ForwardingPlayer implements Player { public void onDeviceVolumeChanged(int volume, boolean muted) { listener.onDeviceVolumeChanged(volume, muted); } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ForwardingListener)) { - return false; - } - ForwardingListener that = (ForwardingListener) o; - if (!forwardingPlayer.equals(that.forwardingPlayer)) { - return false; - } - return listener.equals(that.listener); - } - - @Override - public int hashCode() { - int result = forwardingPlayer.hashCode(); - result = 31 * result + listener.hashCode(); - return result; - } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 7487910ebb..1e7ed137e3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -63,6 +63,343 @@ import java.util.List; */ public interface Player { + /** + * Listener of changes in player state. + * + *

    All methods have no-op default implementations to allow selective overrides. + * + *

    Listeners can choose to implement individual events (e.g. {@link + * #onIsPlayingChanged(boolean)}) or {@link #onEvents(Player, Events)}, which is called after one + * or more events occurred together. + * + * @deprecated Use {@link Player.Listener}. + */ + @UnstableApi + @Deprecated + interface EventListener { + + /** + * Called when the timeline has been refreshed. + * + *

    Note that the current {@link MediaItem} or playback position may change as a result of a + * timeline change. If playback can't continue smoothly because of this timeline change, a + * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be + * triggered. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param timeline The latest timeline. Never null, but may be empty. + * @param reason The {@link TimelineChangeReason} responsible for this timeline change. + */ + default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} + + /** + * Called when playback transitions to a media item or starts repeating a media item according + * to the current {@link #getRepeatMode() repeat mode}. + * + *

    Note that this callback is also called when the playlist becomes non-empty or empty as a + * consequence of a playlist change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. + * @param reason The reason for the transition. + */ + default void onMediaItemTransition( + @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} + + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param trackGroups The available tracks. Never null, but may be of length zero. + * @param trackSelections The selected tracks. Never null, but may contain null elements. A + * concrete implementation may include null elements if it has a fixed number of renderer + * components, wishes to report a TrackSelection for each of them, and has one or more + * renderer components that is not assigned any selected tracks. + * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. + */ + @UnstableApi + @Deprecated + default void onTracksChanged( + TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} + + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param tracksInfo The available tracks information. Never null, but may be of length zero. + */ + default void onTracksInfoChanged(TracksInfo tracksInfo) {} + + /** + * Called when the combined {@link MediaMetadata} changes. + * + *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} + * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track + * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in + * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from + * static or dynamic metadata. + * + *

    This method may be called multiple times in quick succession. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaMetadata The combined {@link MediaMetadata}. + */ + default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} + + /** Called when the playlist {@link MediaMetadata} changes. */ + default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} + + /** + * Called when the player starts or stops loading the source. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isLoading Whether the source is currently being loaded. + */ + default void onIsLoadingChanged(boolean isLoading) {} + + /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ + @Deprecated + default void onLoadingChanged(boolean isLoading) {} + + /** + * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one + * {@link Command}. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param availableCommands The available {@link Commands}. + */ + default void onAvailableCommandsChanged(Commands availableCommands) {} + + /** + * Called when the value returned from {@link #getTrackSelectionParameters()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param parameters The new {@link TrackSelectionParameters}. + */ + default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} + + /** + * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link + * #onPlayWhenReadyChanged(boolean, int)} instead. + */ + @Deprecated + default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} + + /** + * Called when the value returned from {@link #getPlaybackState()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackState The new playback {@link State state}. + */ + default void onPlaybackStateChanged(@State int playbackState) {} + + /** + * Called when the value returned from {@link #getPlayWhenReady()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playWhenReady Whether playback will proceed when ready. + * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. + */ + default void onPlayWhenReadyChanged( + boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} + + /** + * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. + */ + default void onPlaybackSuppressionReasonChanged( + @PlaybackSuppressionReason int playbackSuppressionReason) {} + + /** + * Called when the value of {@link #isPlaying()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isPlaying Whether the player is playing. + */ + default void onIsPlayingChanged(boolean isPlaying) {} + + /** + * Called when the value of {@link #getRepeatMode()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param repeatMode The {@link RepeatMode} used for playback. + */ + default void onRepeatModeChanged(@RepeatMode int repeatMode) {} + + /** + * Called when the value of {@link #getShuffleModeEnabled()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. + */ + default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} + + /** + * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} + * immediately after this method is called. The player instance can still be used, and {@link + * #release()} must still be called on the player should it no longer be required. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The error. + */ + default void onPlayerError(PlaybackException error) {} + + /** + * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The new error, or null if the error is being cleared. + */ + default void onPlayerErrorChanged(@Nullable PlaybackException error) {} + + /** + * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. + */ + @Deprecated + default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} + + /** + * Called when a position discontinuity occurs. + * + *

    A position discontinuity occurs when the playing period changes, the playback position + * jumps within the period currently being played, or when the playing period has been skipped + * or removed. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param oldPosition The position before the discontinuity. + * @param newPosition The position after the discontinuity. + * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. + */ + default void onPositionDiscontinuity( + PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} + + /** + * Called when the current playback parameters change. The playback parameters may change due to + * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change + * them (for example, if audio playback switches to passthrough or offload mode, where speed + * adjustment is no longer possible). + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackParameters The playback parameters. + */ + default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} + + /** + * Called when the value of {@link #getSeekBackIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. + */ + default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} + + /** + * Called when the value of {@link #getSeekForwardIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. + */ + default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} + + /** + * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} + * seeks to the previous position, in milliseconds. + */ + default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} + + /** + * @deprecated Seeks are processed without delay. Listen to {@link + * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link + * #DISCONTINUITY_REASON_SEEK} instead. + */ + @Deprecated + default void onSeekProcessed() {} + + /** + * Called when one or more player states changed. + * + *

    State changes and events that happen within one {@link Looper} message queue iteration are + * reported together and only after all individual callbacks were triggered. + * + *

    Only state changes represented by {@link Event events} are reported through this method. + * + *

    Listeners should prefer this method over individual callbacks in the following cases: + * + *

      + *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for + * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, + * int)}). + *
    • They need access to the {@link Player} object to trigger further events (e.g. to call + * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). + *
    • They intend to use multiple state values together or in combination with {@link Player} + * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code + * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from + * within this method. + *
    • They are interested in events that logically happened together (e.g {@link + * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link + * #onMediaItemTransition(MediaItem, int)}). + *
    + * + * @param player The {@link Player} whose state changed. Use the getters to obtain the latest + * states. + * @param events The {@link Events} that happened in this iteration, indicating which player + * states changed. + */ + default void onEvents(Player player, Events events) {} + } + /** A set of {@link Event events}. */ final class Events { @@ -602,332 +939,62 @@ public interface Player { * *

    All methods have no-op default implementations to allow selective overrides. */ - interface Listener { + interface Listener extends EventListener { - /** - * Called when one or more player states changed. - * - *

    State changes and events that happen within one {@link Looper} message queue iteration are - * reported together and only after all individual callbacks were triggered. - * - *

    Only state changes represented by {@link Event events} are reported through this method. - * - *

    Listeners should prefer this method over individual callbacks in the following cases: - * - *

      - *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for - * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, - * int)}). - *
    • They need access to the {@link Player} object to trigger further events (e.g. to call - * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). - *
    • They intend to use multiple state values together or in combination with {@link Player} - * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code - * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from - * within this method. - *
    • They are interested in events that logically happened together (e.g {@link - * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link - * #onMediaItemTransition(MediaItem, int)}). - *
    - * - * @param player The {@link Player} whose state changed. Use the getters to obtain the latest - * states. - * @param events The {@link Events} that happened in this iteration, indicating which player - * states changed. - */ - default void onEvents(Player player, Events events) {} - - /** - * Called when the timeline has been refreshed. - * - *

    Note that the current {@link MediaItem} or playback position may change as a result of a - * timeline change. If playback can't continue smoothly because of this timeline change, a - * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be - * triggered. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param timeline The latest timeline. Never null, but may be empty. - * @param reason The {@link TimelineChangeReason} responsible for this timeline change. - */ + @Override default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} - /** - * Called when playback transitions to a media item or starts repeating a media item according - * to the current {@link #getRepeatMode() repeat mode}. - * - *

    Note that this callback is also called when the playlist becomes non-empty or empty as a - * consequence of a playlist change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. - * @param reason The reason for the transition. - */ + @Override default void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param trackGroups The available tracks. Never null, but may be of length zero. - * @param trackSelections The selected tracks. Never null, but may contain null elements. A - * concrete implementation may include null elements if it has a fixed number of renderer - * components, wishes to report a TrackSelection for each of them, and has one or more - * renderer components that is not assigned any selected tracks. - * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. - */ - @UnstableApi - @Deprecated - default void onTracksChanged( - TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} - - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param tracksInfo The available tracks information. Never null, but may be of length zero. - */ + @Override default void onTracksInfoChanged(TracksInfo tracksInfo) {} - /** - * Called when the combined {@link MediaMetadata} changes. - * - *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} - * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track - * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in - * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from - * static or dynamic metadata. - * - *

    This method may be called multiple times in quick succession. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaMetadata The combined {@link MediaMetadata}. - */ - default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - - /** Called when the playlist {@link MediaMetadata} changes. */ - default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} - - /** - * Called when the player starts or stops loading the source. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isLoading Whether the source is currently being loaded. - */ + @Override default void onIsLoadingChanged(boolean isLoading) {} - /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ - @Deprecated - @UnstableApi - default void onLoadingChanged(boolean isLoading) {} - - /** - * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one - * {@link Command}. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param availableCommands The available {@link Commands}. - */ + @Override default void onAvailableCommandsChanged(Commands availableCommands) {} - /** - * Called when the value returned from {@link #getTrackSelectionParameters()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param parameters The new {@link TrackSelectionParameters}. - */ - default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} - - /** - * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link - * #onPlayWhenReadyChanged(boolean, int)} instead. - */ - @Deprecated - @UnstableApi - default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} - - /** - * Called when the value returned from {@link #getPlaybackState()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackState The new playback {@link State state}. - */ + @Override default void onPlaybackStateChanged(@State int playbackState) {} - /** - * Called when the value returned from {@link #getPlayWhenReady()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playWhenReady Whether playback will proceed when ready. - * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. - */ + @Override default void onPlayWhenReadyChanged( boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} - /** - * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. - */ + @Override default void onPlaybackSuppressionReasonChanged( @PlaybackSuppressionReason int playbackSuppressionReason) {} - /** - * Called when the value of {@link #isPlaying()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isPlaying Whether the player is playing. - */ + @Override default void onIsPlayingChanged(boolean isPlaying) {} - /** - * Called when the value of {@link #getRepeatMode()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param repeatMode The {@link RepeatMode} used for playback. - */ + @Override default void onRepeatModeChanged(@RepeatMode int repeatMode) {} - /** - * Called when the value of {@link #getShuffleModeEnabled()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. - */ + @Override default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} - /** - * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} - * immediately after this method is called. The player instance can still be used, and {@link - * #release()} must still be called on the player should it no longer be required. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The error. - */ + @Override default void onPlayerError(PlaybackException error) {} - /** - * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The new error, or null if the error is being cleared. - */ + @Override default void onPlayerErrorChanged(@Nullable PlaybackException error) {} - /** - * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. - */ - @Deprecated - @UnstableApi - default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} - - /** - * Called when a position discontinuity occurs. - * - *

    A position discontinuity occurs when the playing period changes, the playback position - * jumps within the period currently being played, or when the playing period has been skipped - * or removed. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param oldPosition The position before the discontinuity. - * @param newPosition The position after the discontinuity. - * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. - */ + @Override default void onPositionDiscontinuity( PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} - /** - * Called when the current playback parameters change. The playback parameters may change due to - * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change - * them (for example, if audio playback switches to passthrough or offload mode, where speed - * adjustment is no longer possible). - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackParameters The playback parameters. - */ + @Override default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} - /** - * Called when the value of {@link #getSeekBackIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. - */ - default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} - - /** - * Called when the value of {@link #getSeekForwardIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. - */ + @Override default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} - /** - * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} - * seeks to the previous position, in milliseconds. - */ - default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} - - /** - * @deprecated Seeks are processed without delay. Listen to {@link - * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link - * #DISCONTINUITY_REASON_SEEK} instead. - */ - @Deprecated - @UnstableApi - default void onSeekProcessed() {} + @Override + default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} /** * Called when the audio session ID changes. @@ -964,6 +1031,9 @@ public interface Player { /** Called when the device volume or mute state changes. */ default void onDeviceVolumeChanged(int volume, boolean muted) {} + @Override + default void onEvents(Player player, Events events) {} + /** * Called each time there's a change in the size of the video being rendered. * @@ -1005,6 +1075,12 @@ public interface Player { */ @UnstableApi default void onMetadata(Metadata metadata) {} + + @Override + default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} + + @Override + default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} } /** diff --git a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java index 22b30a8b33..2973bbb2c2 100644 --- a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java @@ -115,6 +115,21 @@ public class ForwardingPlayerTest { } } + @Test + @SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated type. + public void forwardingEventListener_overridesAllEventListenerMethods() throws Exception { + // Check with reflection that ForwardingListener overrides all Listener methods. + Class forwardingListenerClass = getInnerClass("ForwardingEventListener"); + List methods = getPublicMethods(Player.EventListener.class); + for (int i = 0; i < methods.size(); i++) { + Method method = methods.get(i); + assertThat( + forwardingListenerClass.getDeclaredMethod( + method.getName(), method.getParameterTypes())) + .isNotNull(); + } + } + @Test public void forwardingListener_overridesAllListenerMethods() throws Exception { // Check with reflection that ForwardingListener overrides all Listener methods. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 36d21c7ad8..ab755cbc26 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -1081,6 +1081,32 @@ public interface ExoPlayer extends Player { @Deprecated DeviceComponent getDeviceComponent(); + /** + * Registers a listener to receive events from the player. + * + *

    The listener's methods will be called on the thread associated with {@link + * #getApplicationLooper()}. + * + * @param listener The listener to register. + * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. + */ + @UnstableApi + @Deprecated + @SuppressWarnings("deprecation") + void addListener(EventListener listener); + + /** + * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will + * no longer receive events from the player. + * + * @param listener The listener to unregister. + * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. + */ + @UnstableApi + @Deprecated + @SuppressWarnings("deprecation") + void removeListener(EventListener listener); + /** * Adds a listener to receive audio offload events. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 621ad87af9..ab7c22607d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -109,6 +109,7 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.Player.Commands; import androidx.media3.common.Player.DiscontinuityReason; +import androidx.media3.common.Player.EventListener; import androidx.media3.common.Player.Events; import androidx.media3.common.Player.Listener; import androidx.media3.common.Player.PlayWhenReadyChangeReason; @@ -190,9 +191,9 @@ import java.util.concurrent.TimeoutException; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; - private final ListenerSet listeners; - // TODO(b/187152483): Remove this once all events are dispatched via ListenerSet. - private final CopyOnWriteArraySet listenerArraySet; + @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners. + private final ListenerSet eventListeners; + private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -207,6 +208,7 @@ import java.util.concurrent.TimeoutException; private final Clock clock; private final ComponentListener componentListener; private final FrameMetadataListener frameMetadataListener; + private final CopyOnWriteArraySet listeners; private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; private final StreamVolumeManager streamVolumeManager; @@ -292,6 +294,7 @@ import java.util.concurrent.TimeoutException; detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; componentListener = new ComponentListener(); frameMetadataListener = new FrameMetadataListener(); + listeners = new CopyOnWriteArraySet<>(); Handler eventHandler = new Handler(builder.looper); renderers = builder @@ -315,12 +318,11 @@ import java.util.concurrent.TimeoutException; this.applicationLooper = builder.looper; this.clock = builder.clock; this.wrappingPlayer = wrappingPlayer; - listeners = + eventListeners = new ListenerSet<>( applicationLooper, clock, (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); - listenerArraySet = new CopyOnWriteArraySet<>(); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -408,9 +410,9 @@ import java.util.concurrent.TimeoutException; currentCues = ImmutableList.of(); throwsWhenUsingWrongThread = true; - listeners.add(analyticsCollector); + addEventListener(analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - listeners.add(componentListener); + addEventListener(componentListener); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); @@ -486,6 +488,18 @@ import java.util.concurrent.TimeoutException; return clock; } + @SuppressWarnings("deprecation") // Register deprecated EventListener. + public void addEventListener(Player.EventListener eventListener) { + // Don't verify application thread. We allow calls to this method from any thread. + eventListeners.add(eventListener); + } + + @SuppressWarnings("deprecation") // Deregister deprecated EventListener. + public void removeEventListener(Player.EventListener eventListener) { + // Don't verify application thread. We allow calls to this method from any thread. + eventListeners.remove(eventListener); + } + public void addAudioOffloadListener(AudioOffloadListener listener) { // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); @@ -791,10 +805,10 @@ import java.util.concurrent.TimeoutException; if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; internalPlayer.setRepeatMode(repeatMode); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); } } @@ -808,11 +822,11 @@ import java.util.concurrent.TimeoutException; if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); } } @@ -1014,7 +1028,7 @@ import java.util.concurrent.TimeoutException; audioFocusManager.release(); if (!internalPlayer.release()) { // One of the renderers timed out releasing its resources. - listeners.sendEvent( + eventListeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( @@ -1022,7 +1036,7 @@ import java.util.concurrent.TimeoutException; new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), PlaybackException.ERROR_CODE_TIMEOUT))); } - listeners.release(); + eventListeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); @@ -1202,7 +1216,7 @@ import java.util.concurrent.TimeoutException; return; } trackSelector.setParameters(parameters); - listeners.queueEvent( + eventListeners.queueEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } @@ -1222,7 +1236,7 @@ import java.util.concurrent.TimeoutException; return; } mediaMetadata = newMediaMetadata; - listeners.sendEvent( + eventListeners.sendEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } @@ -1238,7 +1252,7 @@ import java.util.concurrent.TimeoutException; return; } this.playlistMetadata = playlistMetadata; - listeners.sendEvent( + eventListeners.sendEvent( EVENT_PLAYLIST_METADATA_CHANGED, listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } @@ -1395,7 +1409,7 @@ import java.util.concurrent.TimeoutException; streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); analyticsCollector.onAudioAttributesChanged(audioAttributes); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onAudioAttributesChanged(audioAttributes); } } @@ -1433,7 +1447,7 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); analyticsCollector.onAudioSessionIdChanged(audioSessionId); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onAudioSessionIdChanged(audioSessionId); } } @@ -1461,7 +1475,7 @@ import java.util.concurrent.TimeoutException; sendVolumeToRenderers(); analyticsCollector.onVolumeChanged(volume); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onVolumeChanged(volume); } } @@ -1593,14 +1607,14 @@ import java.util.concurrent.TimeoutException; // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.add(listener); - listenerArraySet.add(listener); + addEventListener(listener); } public void removeListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.remove(listener); - listenerArraySet.remove(listener); + removeEventListener(listener); } public void setHandleWakeLock(boolean handleWakeLock) { @@ -1801,7 +1815,7 @@ import java.util.concurrent.TimeoutException; mediaMetadata = newMediaMetadata; if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); } @@ -1810,7 +1824,7 @@ import java.util.concurrent.TimeoutException; getPreviousPositionInfo( positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_POSITION_DISCONTINUITY, listener -> { listener.onPositionDiscontinuity(positionDiscontinuityReason); @@ -1820,16 +1834,16 @@ import java.util.concurrent.TimeoutException; } if (mediaItemTransitioned) { @Nullable final MediaItem finalMediaItem = mediaItem; - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); if (newPlaybackInfo.playbackError != null) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); } @@ -1838,21 +1852,21 @@ import java.util.concurrent.TimeoutException; trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); } if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; - listeners.queueEvent( + eventListeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { listener.onLoadingChanged(newPlaybackInfo.isLoading); @@ -1861,19 +1875,19 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - listeners.queueEvent( + eventListeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged( @@ -1881,27 +1895,27 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged( newPlaybackInfo.playbackSuppressionReason)); } if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); } if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); } if (seekProcessed) { - listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); + eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { for (AudioOffloadListener listener : audioOffloadListeners) { @@ -2058,7 +2072,7 @@ import java.util.concurrent.TimeoutException; Commands previousAvailableCommands = availableCommands; availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, listener -> listener.onAvailableCommandsChanged(availableCommands)); } @@ -2478,7 +2492,7 @@ import java.util.concurrent.TimeoutException; surfaceHeight = height; analyticsCollector.onSurfaceSizeChanged(width, height); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onSurfaceSizeChanged(width, height); } } @@ -2492,7 +2506,7 @@ import java.util.concurrent.TimeoutException; private void notifySkipSilenceEnabledChanged() { analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); } } @@ -2634,9 +2648,10 @@ import java.util.concurrent.TimeoutException; } } + // TODO(b/204189802): Remove self-listening to deprecated EventListener. + @SuppressWarnings("deprecation") private final class ComponentListener - implements Player.Listener, - VideoRendererEventListener, + implements VideoRendererEventListener, AudioRendererEventListener, TextOutput, MetadataOutput, @@ -2646,6 +2661,7 @@ import java.util.concurrent.TimeoutException; AudioFocusManager.PlayerControl, AudioBecomingNoisyManager.EventListener, StreamVolumeManager.Listener, + Player.EventListener, AudioOffloadListener { // VideoRendererEventListener implementation @@ -2680,7 +2696,7 @@ import java.util.concurrent.TimeoutException; ExoPlayerImpl.this.videoSize = videoSize; analyticsCollector.onVideoSizeChanged(videoSize); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onVideoSizeChanged(videoSize); } } @@ -2690,7 +2706,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); if (videoOutput == output) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onRenderedFirstFrame(); } } @@ -2787,7 +2803,7 @@ import java.util.concurrent.TimeoutException; public void onCues(List cues) { currentCues = cues; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listenerArraySet) { + for (Listener listeners : listeners) { listeners.onCues(cues); } } @@ -2799,7 +2815,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onMetadata(metadata); ExoPlayerImpl.this.onMetadata(metadata); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onMetadata(metadata); } } @@ -2895,7 +2911,7 @@ import java.util.concurrent.TimeoutException; if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onDeviceInfoChanged(deviceInfo); } } @@ -2904,12 +2920,12 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { + for (Listener listener : listeners) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); } } - // Player.Listener implementation. + // Player.EventListener implementation. @Override public void onIsLoadingChanged(boolean isLoading) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 53b308104f..14540012df 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -33,6 +33,7 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroupArray; @@ -618,11 +619,23 @@ public class SimpleExoPlayer extends BasePlayer player.addListener(listener); } + @Deprecated + @Override + public void addListener(Player.EventListener listener) { + player.addEventListener(listener); + } + @Override public void removeListener(Listener listener) { player.removeListener(listener); } + @Deprecated + @Override + public void removeListener(Player.EventListener listener) { + player.removeEventListener(listener); + } + @Override public @State int getPlaybackState() { return player.getPlaybackState(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index baa9047330..0f4cd5bdae 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -20,6 +20,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.Format; +import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; @@ -79,6 +80,16 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public void addListener(Player.EventListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeListener(Player.EventListener listener) { + throw new UnsupportedOperationException(); + } + @Override public void addAudioOffloadListener(AudioOffloadListener listener) { throw new UnsupportedOperationException(); From 1f7174e731e1e9ee67c3da3d57d84fed720986c4 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 21:12:44 +0000 Subject: [PATCH 160/251] Revert of 87420e5f9bd1671cd3068185f7358f5faba53e71 PiperOrigin-RevId: 426996878 --- .../java/androidx/media3/cast/CastPlayer.java | 14 +++++--- .../media3/common/AdPlaybackState.java | 2 +- .../androidx/media3/common/BasePlayer.java | 3 +- .../main/java/androidx/media3/common/C.java | 4 +-- .../androidx/media3/common/ColorInfo.java | 13 +++---- .../androidx/media3/common/DrmInitData.java | 3 +- .../androidx/media3/common/FileTypes.java | 10 +++--- .../java/androidx/media3/common/Format.java | 12 +++---- .../androidx/media3/common/HeartRating.java | 2 +- .../androidx/media3/common/MediaMetadata.java | 4 +-- .../androidx/media3/common/MimeTypes.java | 6 ++-- .../media3/common/PercentageRating.java | 2 +- .../java/androidx/media3/common/Player.java | 3 +- .../androidx/media3/common/StarRating.java | 2 +- .../androidx/media3/common/ThumbRating.java | 2 +- .../androidx/media3/common/TrackGroup.java | 3 +- .../androidx/media3/common/TracksInfo.java | 8 +++-- .../java/androidx/media3/common/text/Cue.java | 17 +++++---- .../androidx/media3/common/text/RubySpan.java | 2 +- .../media3/common/text/TextEmphasisSpan.java | 6 ++-- .../common/util/NetworkTypeObserver.java | 12 ++++--- .../androidx/media3/common/util/Util.java | 36 ++++++++++--------- .../media3/common/util/XmlPullParserUtil.java | 7 ++-- .../datasource/DataSourceException.java | 2 +- .../androidx/media3/datasource/DataSpec.java | 8 ++--- .../media3/datasource/HttpDataSource.java | 6 ++-- .../datasource/cache/CacheDataSource.java | 2 +- .../java/androidx/media3/decoder/Buffer.java | 2 +- .../androidx/media3/decoder/CryptoInfo.java | 2 +- .../media3/decoder/DecoderInputBuffer.java | 2 +- .../decoder/VideoDecoderOutputBuffer.java | 2 +- .../media3/decoder/av1/Gav1Decoder.java | 2 +- .../decoder/av1/Libgav1VideoRenderer.java | 3 +- .../decoder/ffmpeg/FfmpegAudioDecoder.java | 5 +-- .../decoder/ffmpeg/FfmpegAudioRenderer.java | 6 ++-- .../decoder/ffmpeg/FfmpegVideoRenderer.java | 3 +- .../decoder/flac/LibflacAudioRenderer.java | 3 +- .../decoder/opus/LibopusAudioRenderer.java | 3 +- .../decoder/vp9/LibvpxVideoRenderer.java | 3 +- .../media3/decoder/vp9/VpxDecoder.java | 2 +- .../exoplayer/StreamVolumeManagerTest.java | 2 +- .../media3/exoplayer/AudioFocusManager.java | 9 ++--- .../media3/exoplayer/BaseRenderer.java | 6 ++-- .../exoplayer/DecoderReuseEvaluation.java | 4 +-- .../exoplayer/DefaultRenderersFactory.java | 2 +- .../exoplayer/ExoPlaybackException.java | 4 +-- .../media3/exoplayer/ExoPlayerImpl.java | 33 ++++++++++------- .../exoplayer/ExoPlayerImplInternal.java | 9 +++-- .../media3/exoplayer/ExoTimeoutException.java | 2 +- .../media3/exoplayer/NoSampleRenderer.java | 6 ++-- .../media3/exoplayer/PlaybackInfo.java | 4 +-- .../exoplayer/RendererCapabilities.java | 25 ++++++++----- .../media3/exoplayer/SimpleExoPlayer.java | 12 ++++--- .../media3/exoplayer/StreamVolumeManager.java | 2 +- .../analytics/AnalyticsListener.java | 3 +- .../analytics/MediaMetricsListener.java | 4 +-- .../analytics/PlaybackStatsListener.java | 2 +- .../exoplayer/audio/AudioProcessor.java | 2 +- .../exoplayer/audio/DecoderAudioRenderer.java | 11 +++--- .../exoplayer/audio/DefaultAudioSink.java | 15 ++++---- .../exoplayer/audio/ForwardingAudioSink.java | 3 +- .../audio/MediaCodecAudioRenderer.java | 3 +- .../audio/SilenceSkippingAudioProcessor.java | 2 +- .../exoplayer/audio/SpatializerDelegate.java | 3 +- .../exoplayer/audio/TeeAudioProcessor.java | 2 +- .../audio/TrimmingAudioProcessor.java | 2 +- .../exoplayer/drm/DefaultDrmSession.java | 3 +- .../drm/DefaultDrmSessionManager.java | 5 +-- .../media3/exoplayer/drm/DrmSession.java | 2 +- .../exoplayer/drm/DrmSessionManager.java | 3 +- .../media3/exoplayer/drm/DrmUtil.java | 7 ++-- .../exoplayer/drm/DummyExoMediaDrm.java | 3 +- .../media3/exoplayer/drm/ExoMediaDrm.java | 5 +-- .../exoplayer/drm/FrameworkMediaDrm.java | 3 +- .../drm/UnsupportedDrmException.java | 2 +- .../media3/exoplayer/drm/WidevineUtil.java | 3 +- .../AsynchronousMediaCodecAdapter.java | 2 +- .../DefaultMediaCodecAdapterFactory.java | 2 +- .../mediacodec/MediaCodecRenderer.java | 19 +++++----- .../exoplayer/metadata/MetadataRenderer.java | 3 +- .../media3/exoplayer/offline/Download.java | 4 +-- .../exoplayer/offline/DownloadHelper.java | 3 +- .../exoplayer/offline/DownloadManager.java | 5 +-- .../exoplayer/scheduler/Requirements.java | 11 +++--- .../scheduler/RequirementsWatcher.java | 5 +-- .../exoplayer/source/ClippingMediaSource.java | 2 +- .../source/DefaultMediaSourceFactory.java | 3 +- .../exoplayer/source/MediaLoadData.java | 2 +- .../exoplayer/source/MediaSourceFactory.java | 3 +- .../exoplayer/source/MergingMediaSource.java | 2 +- .../source/ProgressiveMediaPeriod.java | 2 +- .../exoplayer/source/SilenceMediaSource.java | 2 +- .../ads/ServerSideAdInsertionMediaSource.java | 6 ++-- .../media3/exoplayer/source/chunk/Chunk.java | 4 +-- .../mediaparser/OutputConsumerAdapterV30.java | 3 +- .../exoplayer/text/ExoplayerCuesDecoder.java | 2 +- .../media3/exoplayer/text/TextRenderer.java | 5 +-- .../trackselection/DefaultTrackSelector.java | 28 +++++++++------ .../trackselection/MappingTrackSelector.java | 31 +++++++++------- .../upstream/DefaultBandwidthMeter.java | 4 +-- .../upstream/LoadErrorHandlingPolicy.java | 2 +- .../exoplayer/upstream/ParsingLoadable.java | 2 +- .../exoplayer/video/DecoderVideoRenderer.java | 4 +-- .../media3/exoplayer/video/DummySurface.java | 3 +- .../video/MediaCodecVideoRenderer.java | 5 +-- .../video/VideoFrameReleaseHelper.java | 2 +- .../video/spherical/CameraMotionRenderer.java | 3 +- .../video/spherical/ProjectionDecoder.java | 15 +++----- .../video/spherical/SceneRenderer.java | 4 +-- .../media3/exoplayer/ExoPlayerTest.java | 2 +- .../audio/DecoderAudioRendererTest.java | 3 +- ...faultAudioTrackBufferSizeProviderTest.java | 6 ++-- .../DefaultTrackSelectorTest.java | 14 +++++--- .../MappingTrackSelectorTest.java | 6 ++-- .../video/DecoderVideoRendererTest.java | 5 +-- .../video/MediaCodecVideoRendererTest.java | 4 +-- .../exoplayer/dash/DashMediaPeriod.java | 2 +- .../dash/manifest/DashManifestParser.java | 23 +++++++----- .../hls/DefaultHlsExtractorFactory.java | 2 +- .../media3/exoplayer/hls/HlsChunkSource.java | 3 +- .../hls/playlist/HlsMediaPlaylist.java | 2 +- .../hls/playlist/HlsPlaylistParser.java | 7 ++-- .../media3/exoplayer/ima/AdTagLoader.java | 2 +- .../ServerSideAdInsertionStreamRequest.java | 4 +-- .../media3/exoplayer/ima/FakeExoPlayer.java | 8 +++-- .../rtsp/RtspAuthenticationInfo.java | 2 +- .../media3/exoplayer/rtsp/RtspClient.java | 5 +-- .../exoplayer/rtsp/RtspMediaPeriod.java | 3 +- .../exoplayer/rtsp/RtspMessageChannel.java | 2 +- .../exoplayer/rtsp/RtspMessageUtil.java | 3 +- .../media3/exoplayer/rtsp/RtspRequest.java | 2 +- .../exoplayer/rtsp/reader/RtpH264Reader.java | 5 +-- .../media3/extractor/BinarySearchSeeker.java | 2 +- .../extractor/DefaultExtractorsFactory.java | 18 +++++----- .../media3/extractor/TrackOutput.java | 2 +- .../extractor/TrueHdSampleRechunker.java | 2 +- .../media3/extractor/jpeg/JpegExtractor.java | 6 ++-- .../extractor/mkv/MatroskaExtractor.java | 18 +++++----- .../media3/extractor/mp3/Mp3Extractor.java | 2 +- .../media3/extractor/mp4/AtomParsers.java | 2 +- .../extractor/mp4/FragmentedMp4Extractor.java | 5 +-- .../media3/extractor/mp4/Mp4Extractor.java | 16 +++++---- .../media3/extractor/mp4/SefReader.java | 10 +++--- .../androidx/media3/extractor/mp4/Track.java | 2 +- .../extractor/mp4/TrackEncryptionBox.java | 3 +- .../extractor/text/SubtitleExtractor.java | 2 +- .../media3/extractor/text/ssa/SsaDecoder.java | 6 ++-- .../media3/extractor/text/ssa/SsaStyle.java | 10 +++--- .../extractor/text/ttml/TextEmphasis.java | 6 ++-- .../extractor/text/ttml/TtmlRegion.java | 8 ++--- .../media3/extractor/text/ttml/TtmlStyle.java | 28 ++++++++------- .../extractor/text/webvtt/WebvttCssStyle.java | 21 ++++++----- .../text/webvtt/WebvttCueParser.java | 31 +++++++++------- .../media3/extractor/ts/Ac3Reader.java | 2 +- .../media3/extractor/ts/Ac4Reader.java | 2 +- .../ts/DefaultTsPayloadReaderFactory.java | 2 +- .../media3/extractor/ts/H263Reader.java | 2 +- .../media3/extractor/ts/TsExtractor.java | 6 ++-- .../media3/extractor/wav/WavExtractor.java | 7 ++-- .../extractor/mkv/DefaultEbmlReaderTest.java | 3 +- .../extractor/text/ttml/TtmlStyleTest.java | 2 +- .../androidx/media3/session/MockPlayer.java | 12 ++++--- .../androidx/media3/test/utils/Action.java | 6 ++-- .../media3/test/utils/CapturingAudioSink.java | 2 +- .../media3/test/utils/DownloadBuilder.java | 2 +- .../media3/test/utils/DumpFileAsserts.java | 2 +- .../media3/test/utils/FakeExoMediaDrm.java | 3 +- .../media3/test/utils/FakeRenderer.java | 3 +- .../media3/test/utils/FakeSampleStream.java | 2 +- .../media3/test/utils/StubPlayer.java | 6 ++-- .../test/utils/WebServerDispatcher.java | 7 ++-- .../test/utils/truth/SpannedSubject.java | 8 ++--- .../TestDownloadManagerListener.java | 2 +- .../media3/transformer/Transformer.java | 5 +-- .../transformer/TransformerBaseRenderer.java | 3 +- 175 files changed, 595 insertions(+), 439 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index cc026ba69a..4d54065d0e 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -150,7 +150,7 @@ public final class CastPlayer extends BasePlayer { private TrackSelectionArray currentTrackSelection; private TracksInfo currentTracksInfo; private Commands availableCommands; - private @Player.State int playbackState; + @Player.State private int playbackState; private int currentWindowIndex; private long lastReportedPositionMs; private int pendingSeekCount; @@ -387,12 +387,14 @@ public final class CastPlayer extends BasePlayer { } @Override - public @Player.State int getPlaybackState() { + @Player.State + public int getPlaybackState() { return playbackState; } @Override - public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { return Player.PLAYBACK_SUPPRESSION_REASON_NONE; } @@ -572,7 +574,8 @@ public final class CastPlayer extends BasePlayer { } @Override - public @RepeatMode int getRepeatMode() { + @RepeatMode + public int getRepeatMode() { return repeatMode.value; } @@ -1289,7 +1292,8 @@ public final class CastPlayer extends BasePlayer { * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link * Player.RepeatMode}. */ - private static @RepeatMode int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { + @RepeatMode + private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { MediaStatus mediaStatus = remoteMediaClient.getMediaStatus(); if (mediaStatus == null) { // No media session active, yet. diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index b713f32ef3..3b900b60e2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -67,7 +67,7 @@ public final class AdPlaybackState implements Bundleable { /** The URI of each ad in the ad group. */ public final @NullableType Uri[] uris; /** The state of each ad in the ad group. */ - public final @AdState int[] states; + @AdState public final int[] states; /** The durations of each ad in the ad group, in microseconds. */ public final long[] durationsUs; /** diff --git a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java index d782dcb0e6..f79df0ff90 100644 --- a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java @@ -384,7 +384,8 @@ public abstract class BasePlayer implements Player { : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); } - private @RepeatMode int getRepeatModeForNavigation() { + @RepeatMode + private int getRepeatModeForNavigation() { @RepeatMode int repeatMode = getRepeatMode(); return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; } diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index a83ca8798f..486e00a36b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -1250,8 +1250,8 @@ public final class C { replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)", imports = {"androidx.media3.common.util.Util"}) @Deprecated - public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( - int mediaDrmErrorCode) { + @PlaybackException.ErrorCode + public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode); } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java index 829262bb88..fb27d2d18c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java @@ -38,7 +38,8 @@ public final class ColorInfo implements Bundleable { * made. */ @Pure - public static @C.ColorSpace int isoColorPrimariesToColorSpace(int isoColorPrimaries) { + @C.ColorSpace + public static int isoColorPrimariesToColorSpace(int isoColorPrimaries) { switch (isoColorPrimaries) { case 1: return C.COLOR_SPACE_BT709; @@ -60,8 +61,8 @@ public final class ColorInfo implements Bundleable { * mapping can be made. */ @Pure - public static @C.ColorTransfer int isoTransferCharacteristicsToColorTransfer( - int isoTransferCharacteristics) { + @C.ColorTransfer + public static int isoTransferCharacteristicsToColorTransfer(int isoTransferCharacteristics) { switch (isoTransferCharacteristics) { case 1: // BT.709. case 6: // SMPTE 170M. @@ -80,20 +81,20 @@ public final class ColorInfo implements Bundleable { * The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link * C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown. */ - public final @C.ColorSpace int colorSpace; + @C.ColorSpace public final int colorSpace; /** * The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link * C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown. */ - public final @C.ColorRange int colorRange; + @C.ColorRange public final int colorRange; /** * The color transfer characteristics of the video. Valid values are {@link C#COLOR_TRANSFER_HLG}, * {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link Format#NO_VALUE} if * unknown. */ - public final @C.ColorTransfer int colorTransfer; + @C.ColorTransfer public final int colorTransfer; /** HdrStaticInfo as defined in CTA-861.3, or null if none specified. */ @Nullable public final byte[] hdrStaticInfo; diff --git a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java index 2d9e19caab..639fb7ee91 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java +++ b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java @@ -52,8 +52,7 @@ public final class DrmInitData implements Comparator, Parcelable { * @param mediaData DRM session acquisition data obtained from the media. * @return A {@link DrmInitData} obtained from merging a media manifest and a media stream. */ - @Nullable - public static DrmInitData createSessionCreationData( + public static @Nullable DrmInitData createSessionCreationData( @Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) { ArrayList result = new ArrayList<>(); String schemeType = null; diff --git a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java index cc5fd41dee..50a988f980 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java @@ -114,8 +114,8 @@ public final class FileTypes { private FileTypes() {} /** Returns the {@link Type} corresponding to the response headers provided. */ - public static @FileTypes.Type int inferFileTypeFromResponseHeaders( - Map> responseHeaders) { + @FileTypes.Type + public static int inferFileTypeFromResponseHeaders(Map> responseHeaders) { @Nullable List contentTypes = responseHeaders.get(HEADER_CONTENT_TYPE); @Nullable String mimeType = contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.get(0); @@ -127,7 +127,8 @@ public final class FileTypes { * *

    Returns {@link #UNKNOWN} if the mime type is {@code null}. */ - public static @FileTypes.Type int inferFileTypeFromMimeType(@Nullable String mimeType) { + @FileTypes.Type + public static int inferFileTypeFromMimeType(@Nullable String mimeType) { if (mimeType == null) { return FileTypes.UNKNOWN; } @@ -177,7 +178,8 @@ public final class FileTypes { } /** Returns the {@link Type} corresponding to the {@link Uri} provided. */ - public static @FileTypes.Type int inferFileTypeFromUri(Uri uri) { + @FileTypes.Type + public static int inferFileTypeFromUri(Uri uri) { @Nullable String filename = uri.getLastPathSegment(); if (filename == null) { return FileTypes.UNKNOWN; diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index 773b9fab37..dd6460c9cf 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -156,14 +156,14 @@ public final class Format implements Bundleable { private int rotationDegrees; private float pixelWidthHeightRatio; @Nullable private byte[] projectionData; - private @C.StereoMode int stereoMode; + @C.StereoMode private int stereoMode; @Nullable private ColorInfo colorInfo; // Audio specific. private int channelCount; private int sampleRate; - private @C.PcmEncoding int pcmEncoding; + @C.PcmEncoding private int pcmEncoding; private int encoderDelay; private int encoderPadding; @@ -173,7 +173,7 @@ public final class Format implements Bundleable { // Provided by the source. - private @C.CryptoType int cryptoType; + @C.CryptoType private int cryptoType; /** Creates a new instance with default values. */ public Builder() { @@ -727,7 +727,7 @@ public final class Format implements Bundleable { * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}. */ - @UnstableApi public final @C.StereoMode int stereoMode; + @UnstableApi @C.StereoMode public final int stereoMode; /** The color metadata associated with the video, or null if not applicable. */ @UnstableApi @Nullable public final ColorInfo colorInfo; @@ -738,7 +738,7 @@ public final class Format implements Bundleable { /** The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. */ public final int sampleRate; /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ - @UnstableApi public final @C.PcmEncoding int pcmEncoding; + @UnstableApi @C.PcmEncoding public final int pcmEncoding; /** * The number of frames to trim from the start of the decoded audio stream, or 0 if not * applicable. @@ -762,7 +762,7 @@ public final class Format implements Bundleable { * {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that * the samples are encrypted using an unsupported crypto type. */ - @UnstableApi public final @C.CryptoType int cryptoType; + @UnstableApi @C.CryptoType public final int cryptoType; // Lazily initialized hashcode. private int hashCode; diff --git a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java index 08a6b405f3..4a7117764c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java @@ -79,7 +79,7 @@ public final class HeartRating extends Rating { // Bundleable implementation. - private static final @RatingType int TYPE = RATING_TYPE_HEART; + @RatingType private static final int TYPE = RATING_TYPE_HEART; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index 4d257fe7d4..7a847c24e2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -56,11 +56,11 @@ public final class MediaMetadata implements Bundleable { @Nullable private Rating userRating; @Nullable private Rating overallRating; @Nullable private byte[] artworkData; - @Nullable private @PictureType Integer artworkDataType; + @Nullable @PictureType private Integer artworkDataType; @Nullable private Uri artworkUri; @Nullable private Integer trackNumber; @Nullable private Integer totalTrackCount; - @Nullable private @FolderType Integer folderType; + @Nullable @FolderType private Integer folderType; @Nullable private Boolean isPlayable; @Nullable private Integer recordingYear; @Nullable private Integer recordingMonth; diff --git a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java index 2c6973d1e1..e1958db3e1 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java @@ -552,7 +552,8 @@ public final class MimeTypes { * @return The corresponding {@link C.Encoding}, or {@link C#ENCODING_INVALID}. */ @UnstableApi - public static @C.Encoding int getEncoding(String mimeType, @Nullable String codec) { + @C.Encoding + public static int getEncoding(String mimeType, @Nullable String codec) { switch (mimeType) { case MimeTypes.AUDIO_MPEG: return C.ENCODING_MP3; @@ -727,7 +728,8 @@ public final class MimeTypes { } /** Returns the encoding for {@link #audioObjectTypeIndication}. */ - public @C.Encoding int getEncoding() { + @C.Encoding + public int getEncoding() { // See AUDIO_OBJECT_TYPE_AAC_* constants in AacUtil. switch (audioObjectTypeIndication) { case 2: diff --git a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java index afc20a1687..f110efba16 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java @@ -77,7 +77,7 @@ public final class PercentageRating extends Rating { // Bundleable implementation. - private static final @RatingType int TYPE = RATING_TYPE_PERCENTAGE; + @RatingType private static final int TYPE = RATING_TYPE_PERCENTAGE; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 1e7ed137e3..aed681f729 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -686,7 +686,8 @@ public interface Player { @UnstableApi public static final class Builder { - private static final @Command int[] SUPPORTED_COMMANDS = { + @Command + private static final int[] SUPPORTED_COMMANDS = { COMMAND_PLAY_PAUSE, COMMAND_PREPARE, COMMAND_STOP, diff --git a/libraries/common/src/main/java/androidx/media3/common/StarRating.java b/libraries/common/src/main/java/androidx/media3/common/StarRating.java index 2c38f7cb75..c6547dde33 100644 --- a/libraries/common/src/main/java/androidx/media3/common/StarRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/StarRating.java @@ -103,7 +103,7 @@ public final class StarRating extends Rating { // Bundleable implementation. - private static final @RatingType int TYPE = RATING_TYPE_STAR; + @RatingType private static final int TYPE = RATING_TYPE_STAR; private static final int MAX_STARS_DEFAULT = 5; @Documented diff --git a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java index cd4ad73473..3c68986b98 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java @@ -76,7 +76,7 @@ public final class ThumbRating extends Rating { // Bundleable implementation. - private static final @RatingType int TYPE = RATING_TYPE_THUMB; + @RatingType private static final int TYPE = RATING_TYPE_THUMB; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java index ad0bb81c23..f0c85adfb8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java @@ -207,7 +207,8 @@ public final class TrackGroup implements Bundleable { return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language; } - private static @C.RoleFlags int normalizeRoleFlags(@C.RoleFlags int roleFlags) { + @C.RoleFlags + private static int normalizeRoleFlags(@C.RoleFlags int roleFlags) { // Treat trick-play and non-trick-play formats as compatible. return roleFlags | C.ROLE_FLAG_TRICK_PLAY; } diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 437f7dda44..066a92152e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -45,7 +45,7 @@ public final class TracksInfo implements Bundleable { */ public static final class TrackGroupInfo implements Bundleable { private final TrackGroup trackGroup; - private final @C.FormatSupport int[] trackSupport; + @C.FormatSupport private final int[] trackSupport; private final @C.TrackType int trackType; private final boolean[] trackSelected; @@ -83,7 +83,8 @@ public final class TracksInfo implements Bundleable { * @return The {@link C.FormatSupport} of the track. */ @UnstableApi - public @C.FormatSupport int getTrackSupport(int trackIndex) { + @C.FormatSupport + public int getTrackSupport(int trackIndex) { return trackSupport[trackIndex]; } @@ -228,7 +229,8 @@ public final class TracksInfo implements Bundleable { fromNullableBundle( TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP))); checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup - final @C.FormatSupport int[] trackSupport = + @C.FormatSupport + final int[] trackSupport = MoreObjects.firstNonNull( bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]); @C.TrackType diff --git a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java index 6b9d9ded28..d5f04a3540 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java @@ -569,7 +569,7 @@ public final class Cue implements Bundleable { @Nullable private Alignment textAlignment; @Nullable private Alignment multiRowAlignment; private float line; - private @LineType int lineType; + @LineType private int lineType; private @AnchorType int lineAnchor; private float position; private @AnchorType int positionAnchor; @@ -730,7 +730,8 @@ public final class Cue implements Bundleable { * @see Cue#lineType */ @Pure - public @LineType int getLineType() { + @LineType + public int getLineType() { return lineType; } @@ -750,7 +751,8 @@ public final class Cue implements Bundleable { * @see Cue#lineAnchor */ @Pure - public @AnchorType int getLineAnchor() { + @AnchorType + public int getLineAnchor() { return lineAnchor; } @@ -792,7 +794,8 @@ public final class Cue implements Bundleable { * @see Cue#positionAnchor */ @Pure - public @AnchorType int getPositionAnchor() { + @AnchorType + public int getPositionAnchor() { return positionAnchor; } @@ -814,7 +817,8 @@ public final class Cue implements Bundleable { * @see Cue#textSizeType */ @Pure - public @TextSizeType int getTextSizeType() { + @TextSizeType + public int getTextSizeType() { return textSizeType; } @@ -932,7 +936,8 @@ public final class Cue implements Bundleable { * @see Cue#verticalType */ @Pure - public @VerticalType int getVerticalType() { + @VerticalType + public int getVerticalType() { return verticalType; } diff --git a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java index 482cb7a92d..9c6ba1275e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java @@ -39,7 +39,7 @@ public final class RubySpan implements LanguageFeatureSpan { public final String rubyText; /** The position of the ruby text relative to the base text. */ - public final @TextAnnotation.Position int position; + @TextAnnotation.Position public final int position; public RubySpan(String rubyText, @TextAnnotation.Position int position) { this.rubyText = rubyText; diff --git a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java index 11595c4f84..68d6e236e5 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java @@ -83,13 +83,13 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan { public static final int MARK_FILL_OPEN = 2; /** The mark shape used for text emphasis. */ - public @MarkShape int markShape; + @MarkShape public int markShape; /** The mark fill for the text emphasis mark. */ - public @MarkShape int markFill; + @MarkShape public int markFill; /** The position of the text emphasis relative to the base text. */ - public final @TextAnnotation.Position int position; + @TextAnnotation.Position public final int position; public TextEmphasisSpan( @MarkShape int shape, @MarkFill int fill, @TextAnnotation.Position int position) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java index 9609cff496..ceefb88e0f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java @@ -85,7 +85,8 @@ public final class NetworkTypeObserver { private final Object networkTypeLock; @GuardedBy("networkTypeLock") - private @C.NetworkType int networkType; + @C.NetworkType + private int networkType; /** * Returns a network type observer instance. @@ -131,7 +132,8 @@ public final class NetworkTypeObserver { } /** Returns the current network type. */ - public @C.NetworkType int getNetworkType() { + @C.NetworkType + public int getNetworkType() { synchronized (networkTypeLock) { return networkType; } @@ -162,7 +164,8 @@ public final class NetworkTypeObserver { } } - private static @C.NetworkType int getNetworkTypeFromConnectivityManager(Context context) { + @C.NetworkType + private static int getNetworkTypeFromConnectivityManager(Context context) { NetworkInfo networkInfo; @Nullable ConnectivityManager connectivityManager = @@ -195,7 +198,8 @@ public final class NetworkTypeObserver { } } - private static @C.NetworkType int getMobileNetworkType(NetworkInfo networkInfo) { + @C.NetworkType + private static int getMobileNetworkType(NetworkInfo networkInfo) { switch (networkInfo.getSubtype()) { case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_GPRS: 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 d45d1d35dc..5e1976f65c 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 @@ -1560,7 +1560,8 @@ public final class Util { * C#ENCODING_PCM_16BIT}, {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. If * the bit depth is unsupported then {@link C#ENCODING_INVALID} is returned. */ - public static @C.PcmEncoding int getPcmEncoding(int bitDepth) { + @C.PcmEncoding + public static int getPcmEncoding(int bitDepth) { switch (bitDepth) { case 8: return C.ENCODING_PCM_8BIT; @@ -1670,7 +1671,8 @@ public final class Util { } /** Returns the {@link C.AudioUsage} corresponding to the specified {@link C.StreamType}. */ - public static @C.AudioUsage int getAudioUsageForStreamType(@C.StreamType int streamType) { + @C.AudioUsage + public static int getAudioUsageForStreamType(@C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: return C.USAGE_ALARM; @@ -1691,8 +1693,8 @@ public final class Util { } /** Returns the {@link C.AudioContentType} corresponding to the specified {@link C.StreamType}. */ - public static @C.AudioContentType int getAudioContentTypeForStreamType( - @C.StreamType int streamType) { + @C.AudioContentType + public static int getAudioContentTypeForStreamType(@C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: case C.STREAM_TYPE_DTMF: @@ -1709,7 +1711,8 @@ public final class Util { } /** Returns the {@link C.StreamType} corresponding to the specified {@link C.AudioUsage}. */ - public static @C.StreamType int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { + @C.StreamType + public static int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { switch (usage) { case C.USAGE_MEDIA: case C.USAGE_GAME: @@ -1759,8 +1762,7 @@ public final class Util { * "clearkey"}. * @return The derived {@link UUID}, or {@code null} if one could not be derived. */ - @Nullable - public static UUID getDrmUuid(String drmScheme) { + public static @Nullable UUID getDrmUuid(String drmScheme) { switch (Ascii.toLowerCase(drmScheme)) { case "widevine": return C.WIDEVINE_UUID; @@ -1782,8 +1784,8 @@ public final class Util { * MediaDrm.ErrorCodes} value. Returns {@link PlaybackException#ERROR_CODE_DRM_SYSTEM_ERROR} if * the provided error code isn't recognised. */ - public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( - int mediaDrmErrorCode) { + @PlaybackException.ErrorCode + public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { switch (mediaDrmErrorCode) { case MediaDrm.ErrorCodes.ERROR_PROVISIONING_CONFIG: case MediaDrm.ErrorCodes.ERROR_PROVISIONING_PARSE: @@ -1819,7 +1821,8 @@ public final class Util { * @param overrideExtension If not null, used to infer the type. * @return The content type. */ - public static @ContentType int inferContentType(Uri uri, @Nullable String overrideExtension) { + @ContentType + public static int inferContentType(Uri uri, @Nullable String overrideExtension) { return TextUtils.isEmpty(overrideExtension) ? inferContentType(uri) : inferContentType("." + overrideExtension); @@ -1831,7 +1834,8 @@ public final class Util { * @param uri The {@link Uri}. * @return The content type. */ - public static @ContentType int inferContentType(Uri uri) { + @ContentType + public static int inferContentType(Uri uri) { @Nullable String scheme = uri.getScheme(); if (scheme != null && Ascii.equalsIgnoreCase("rtsp", scheme)) { return C.TYPE_RTSP; @@ -1847,7 +1851,8 @@ public final class Util { * @param fileName Name of the file. It can include the path of the file. * @return The content type. */ - public static @ContentType int inferContentType(String fileName) { + @ContentType + public static int inferContentType(String fileName) { fileName = Ascii.toLowerCase(fileName); if (fileName.endsWith(".mpd")) { return C.TYPE_DASH; @@ -1876,8 +1881,8 @@ public final class Util { * @param mimeType If MIME type, or {@code null}. * @return The content type. */ - public static @ContentType int inferContentTypeForUriAndMimeType( - Uri uri, @Nullable String mimeType) { + @ContentType + public static int inferContentTypeForUriAndMimeType(Uri uri, @Nullable String mimeType) { if (mimeType == null) { return Util.inferContentType(uri); } @@ -2027,8 +2032,7 @@ public final class Util { * @return The original value of the file name before it was escaped, or null if the escaped * fileName seems invalid. */ - @Nullable - public static String unescapeFileName(String fileName) { + public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java index 3266ee427f..46794813e8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java @@ -93,8 +93,7 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - @Nullable - public static String getAttributeValue(XmlPullParser xpp, String attributeName) { + public static @Nullable String getAttributeValue(XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (xpp.getAttributeName(i).equals(attributeName)) { @@ -113,8 +112,8 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - @Nullable - public static String getAttributeValueIgnorePrefix(XmlPullParser xpp, String attributeName) { + public static @Nullable String getAttributeValueIgnorePrefix( + XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (stripPrefix(xpp.getAttributeName(i)).equals(attributeName)) { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java index fa14682255..5ea554ab78 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java @@ -57,7 +57,7 @@ public class DataSourceException extends IOException { * The reason of this {@link DataSourceException}, should be one of the {@code ERROR_CODE_IO_*} in * {@link PlaybackException.ErrorCode}. */ - public final @PlaybackException.ErrorCode int reason; + @PlaybackException.ErrorCode public final int reason; /** * Constructs a DataSourceException. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java index 646b5f274a..9927ec238d 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java @@ -49,13 +49,13 @@ public final class DataSpec { @Nullable private Uri uri; private long uriPositionOffset; - private @HttpMethod int httpMethod; + @HttpMethod private int httpMethod; @Nullable private byte[] httpBody; private Map httpRequestHeaders; private long position; private long length; @Nullable private String key; - private @Flags int flags; + @Flags private int flags; @Nullable private Object customData; /** Creates a new instance with default values. */ @@ -330,7 +330,7 @@ public final class DataSpec { * The HTTP method to use when requesting the data. This value will be ignored by non-HTTP {@link * DataSource} implementations. */ - public final @HttpMethod int httpMethod; + @HttpMethod public final int httpMethod; /** * The HTTP request body, null otherwise. If the body is non-null, then {@code httpBody.length} @@ -382,7 +382,7 @@ public final class DataSpec { @Nullable public final String key; /** Request {@link Flags flags}. */ - public final @Flags int flags; + @Flags public final int flags; /** * Application specific data. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java index 967a00b49c..4352008b78 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java @@ -233,7 +233,7 @@ public interface HttpDataSource extends DataSource { /** The {@link DataSpec} associated with the current connection. */ public final DataSpec dataSpec; - public final @Type int type; + @Type public final int type; /** * @deprecated Use {@link #HttpDataSourceException(DataSpec, int, int) @@ -349,8 +349,8 @@ public interface HttpDataSource extends DataSource { this.type = type; } - private static @PlaybackException.ErrorCode int assignErrorCode( - @PlaybackException.ErrorCode int errorCode, @Type int type) { + @PlaybackException.ErrorCode + private static int assignErrorCode(@PlaybackException.ErrorCode int errorCode, @Type int type) { return errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED && type == TYPE_OPEN ? PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED : errorCode; diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java index 4eae94a1ee..d39cceb0f0 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java @@ -72,7 +72,7 @@ public final class CacheDataSource implements DataSource { @Nullable private DataSource.Factory upstreamDataSourceFactory; @Nullable private PriorityTaskManager upstreamPriorityTaskManager; private int upstreamPriority; - private @CacheDataSource.Flags int flags; + @CacheDataSource.Flags private int flags; @Nullable private CacheDataSource.EventListener eventListener; public Factory() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java index abbdba9c9b..26101921d9 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java @@ -22,7 +22,7 @@ import androidx.media3.common.util.UnstableApi; @UnstableApi public abstract class Buffer { - private @C.BufferFlags int flags; + @C.BufferFlags private int flags; /** Clears the buffer. */ public void clear() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java index 4663b38788..93443e28a5 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java @@ -48,7 +48,7 @@ public final class CryptoInfo { * * @see android.media.MediaCodec.CryptoInfo#mode */ - public @C.CryptoMode int mode; + @C.CryptoMode public int mode; /** * The number of leading unencrypted bytes in each sub-sample. If null, all bytes are treated as * encrypted and {@link #numBytesOfEncryptedData} must be specified. diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java index 265e2b0685..05a0164493 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java @@ -111,7 +111,7 @@ public class DecoderInputBuffer extends Buffer { */ @Nullable public ByteBuffer supplementalData; - private final @BufferReplacementMode int bufferReplacementMode; + @BufferReplacementMode private final int bufferReplacementMode; private final int paddingSize; /** Returns a new instance that's not able to hold any data. */ diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java index b0fb72294b..ad71e2d066 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java @@ -34,7 +34,7 @@ public class VideoDecoderOutputBuffer extends DecoderOutputBuffer { public int decoderPrivate; /** Output mode. */ - public @C.VideoOutputMode int mode; + @C.VideoOutputMode public int mode; /** RGB buffer for RGB mode. */ @Nullable public ByteBuffer data; diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java index fb54007a84..27b602caef 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java @@ -41,7 +41,7 @@ public final class Gav1Decoder private final long gav1DecoderContext; - private volatile @C.VideoOutputMode int outputMode; + @C.VideoOutputMode private volatile int outputMode; /** * Creates a Gav1Decoder. diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java index 835c505b1f..7df78d3f1d 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java @@ -128,7 +128,8 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer { } @Override - public final @Capabilities int supportsFormat(Format format) { + @Capabilities + public final int supportsFormat(Format format) { if (!MimeTypes.VIDEO_AV1.equalsIgnoreCase(format.sampleMimeType) || !Gav1Library.isAvailable()) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java index 0e07287400..aa24f3176b 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java @@ -41,7 +41,7 @@ import java.util.List; private final String codecName; @Nullable private final byte[] extraData; - private final @C.PcmEncoding int encoding; + @C.PcmEncoding private final int encoding; private final int outputBufferSize; private long nativeContext; // May be reassigned on resetting the codec. @@ -158,7 +158,8 @@ import java.util.List; } /** Returns the encoding of output audio. */ - public @C.PcmEncoding int getEncoding() { + @C.PcmEncoding + public int getEncoding() { return encoding; } diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java index f9119e5e43..047ab542dd 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java @@ -90,7 +90,8 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { } @Override - protected @C.FormatSupport int supportsFormatInternal(Format format) { + @C.FormatSupport + protected int supportsFormatInternal(Format format) { boolean drmIsSupported = OpusLibrary.supportsCryptoType(format.cryptoType); if (!OpusLibrary.isAvailable() || !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) { diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java index 3f8130c1ad..0cbc8eb5ab 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java @@ -126,7 +126,8 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer { } @Override - public final @Capabilities int supportsFormat(Format format) { + @Capabilities + public final int supportsFormat(Format format) { if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java index 259ac2a544..6771056f7b 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java @@ -49,7 +49,7 @@ public final class VpxDecoder @Nullable private ByteBuffer lastSupplementalData; - private volatile @C.VideoOutputMode int outputMode; + @C.VideoOutputMode private volatile int outputMode; /** * Creates a VP9 decoder. diff --git a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java index df1ea2f4a0..404611de21 100644 --- a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java +++ b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java @@ -273,7 +273,7 @@ public class StreamVolumeManagerTest { private static class TestListener implements StreamVolumeManager.Listener { - private @C.StreamType int lastStreamType; + @C.StreamType private int lastStreamType; private int lastStreamVolume; private boolean lastStreamVolumeMuted; public final CountDownLatch onStreamVolumeChangedLatch; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index e07c434534..68203349aa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -139,7 +139,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private PlayerControl playerControl; @Nullable private AudioAttributes audioAttributes; - private @AudioFocusState int audioFocusState; + @AudioFocusState private int audioFocusState; private @AudioFocusGain int focusGainToRequest; private float volumeMultiplier = VOLUME_MULTIPLIER_DEFAULT; @@ -193,8 +193,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param playbackState The desired playback state. * @return A {@link PlayerCommand} to execute on the player. */ - public @PlayerCommand int updateAudioFocus( - boolean playWhenReady, @Player.State int playbackState) { + @PlayerCommand + public int updateAudioFocus(boolean playWhenReady, @Player.State int playbackState) { if (shouldAbandonAudioFocusIfHeld(playbackState)) { abandonAudioFocusIfHeld(); return playWhenReady ? PLAYER_COMMAND_PLAY_WHEN_READY : PLAYER_COMMAND_DO_NOT_PLAY; @@ -222,7 +222,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return playbackState == Player.STATE_IDLE || focusGainToRequest != AUDIOFOCUS_GAIN; } - private @PlayerCommand int requestAudioFocus() { + @PlayerCommand + private int requestAudioFocus() { if (audioFocusState == AUDIO_FOCUS_STATE_HAVE_FOCUS) { return PLAYER_COMMAND_PLAY_WHEN_READY; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java index db410d3a8d..f2667947d9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java @@ -200,7 +200,8 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // RendererCapabilities implementation. @Override - public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + @AdaptiveSupport + public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } @@ -423,7 +424,8 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { * the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and * flags are populated if this exception is thrown, but the read position is not advanced. */ - protected final @ReadDataResult int readSource( + @ReadDataResult + protected final int readSource( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { @ReadDataResult int result = Assertions.checkNotNull(stream).readData(formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java index 142555e985..0257b4af1d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java @@ -125,13 +125,13 @@ public final class DecoderReuseEvaluation { public final Format newFormat; /** The {@link DecoderReuseResult result} of the evaluation. */ - public final @DecoderReuseResult int result; + @DecoderReuseResult public final int result; /** * {@link DecoderDiscardReasons Reasons} why the decoder cannot be reused. Always {@code 0} if * reuse is possible. May also be {code 0} if reuse is not possible for an unspecified reason. */ - public final @DecoderDiscardReasons int discardReasons; + @DecoderDiscardReasons public final int discardReasons; /** * @param decoderName The name of the decoder. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 451c0ee3a1..f11ec4d5b8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -95,7 +95,7 @@ public class DefaultRenderersFactory implements RenderersFactory { private final Context context; private final DefaultMediaCodecAdapterFactory codecAdapterFactory; - private @ExtensionRendererMode int extensionRendererMode; + @ExtensionRendererMode private int extensionRendererMode; private long allowedVideoJoiningTimeMs; private boolean enableDecoderFallback; private MediaCodecSelector mediaCodecSelector; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java index 52bf89b365..2a2b03ab0f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java @@ -85,7 +85,7 @@ public final class ExoPlaybackException extends PlaybackException { @UnstableApi public static final int TYPE_REMOTE = 3; /** The {@link Type} of the playback failure. */ - @UnstableApi public final @Type int type; + @UnstableApi @Type public final int type; /** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */ @UnstableApi @Nullable public final String rendererName; @@ -104,7 +104,7 @@ public final class ExoPlaybackException extends PlaybackException { * renderer for {@link #rendererFormat}. If {@link #rendererFormat} is null, this is {@link * C#FORMAT_HANDLED}. */ - @UnstableApi public final @FormatSupport int rendererFormatSupport; + @UnstableApi @FormatSupport public final int rendererFormatSupport; /** The {@link MediaPeriodId} of the media associated with this error, or null if undetermined. */ @UnstableApi @Nullable public final MediaPeriodId mediaPeriodId; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index ab7c22607d..e076dad419 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -216,12 +216,12 @@ import java.util.concurrent.TimeoutException; private final WifiLockManager wifiLockManager; private final long detachSurfaceTimeoutMs; - private @RepeatMode int repeatMode; + @RepeatMode private int repeatMode; private boolean shuffleModeEnabled; private int pendingOperationAcks; - private @DiscontinuityReason int pendingDiscontinuityReason; + @DiscontinuityReason private int pendingDiscontinuityReason; private boolean pendingDiscontinuity; - private @PlayWhenReadyChangeReason int pendingPlayWhenReadyChangeReason; + @PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason; private boolean foregroundMode; private SeekParameters seekParameters; private ShuffleOrder shuffleOrder; @@ -238,8 +238,8 @@ import java.util.concurrent.TimeoutException; @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; private boolean surfaceHolderSurfaceIsVideoOutput; @Nullable private TextureView textureView; - private @C.VideoScalingMode int videoScalingMode; - private @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy; + @C.VideoScalingMode private int videoScalingMode; + @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; private int surfaceWidth; private int surfaceHeight; @Nullable private DecoderCounters videoDecoderCounters; @@ -515,12 +515,14 @@ import java.util.concurrent.TimeoutException; return availableCommands; } - public @State int getPlaybackState() { + @State + public int getPlaybackState() { verifyApplicationThread(); return playbackInfo.playbackState; } - public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } @@ -812,7 +814,8 @@ import java.util.concurrent.TimeoutException; } } - public @RepeatMode int getRepeatMode() { + @RepeatMode + public int getRepeatMode() { verifyApplicationThread(); return repeatMode; } @@ -1268,7 +1271,8 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); } - public @C.VideoScalingMode int getVideoScalingMode() { + @C.VideoScalingMode + public int getVideoScalingMode() { return videoScalingMode; } @@ -1283,7 +1287,8 @@ import java.util.concurrent.TimeoutException; TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); } - public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { + @C.VideoChangeFrameRateStrategy + public int getVideoChangeFrameRateStrategy() { return videoChangeFrameRateStrategy; } @@ -2963,13 +2968,15 @@ import java.util.concurrent.TimeoutException; private static final class FrameMetadataListener implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { - public static final @MessageType int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = + @MessageType + public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; - public static final @MessageType int MSG_SET_CAMERA_MOTION_LISTENER = + @MessageType + public static final int MSG_SET_CAMERA_MOTION_LISTENER = Renderer.MSG_SET_CAMERA_MOTION_LISTENER; - public static final @MessageType int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; + @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; @Nullable private CameraMotionListener cameraMotionListener; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 1bbed758c9..6096159fbc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -93,9 +93,9 @@ import java.util.concurrent.atomic.AtomicBoolean; public PlaybackInfo playbackInfo; public int operationAcks; public boolean positionDiscontinuity; - public @DiscontinuityReason int discontinuityReason; + @DiscontinuityReason public int discontinuityReason; public boolean hasPlayWhenReadyChangeReason; - public @PlayWhenReadyChangeReason int playWhenReadyChangeReason; + @PlayWhenReadyChangeReason public int playWhenReadyChangeReason; public PlaybackInfoUpdate(PlaybackInfo playbackInfo) { this.playbackInfo = playbackInfo; @@ -216,7 +216,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private boolean pendingPauseAtEndOfPeriod; private boolean isRebuffering; private boolean shouldContinueLoading; - private @Player.RepeatMode int repeatMode; + @Player.RepeatMode private int repeatMode; private boolean shuffleModeEnabled; private boolean foregroundMode; private boolean requestForRendererSleep; @@ -2943,8 +2943,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return The uid in the new timeline of the first subsequent period, or null if no such period * was found. */ - /* package */ @Nullable - static Object resolveSubsequentPeriod( + /* package */ static @Nullable Object resolveSubsequentPeriod( Timeline.Window window, Timeline.Period period, @Player.RepeatMode int repeatMode, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java index 06dd0f9ff7..cc1ca1404e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java @@ -62,7 +62,7 @@ public final class ExoTimeoutException extends RuntimeException { public static final int TIMEOUT_OPERATION_DETACH_SURFACE = 3; /** The operation on the ExoPlayer playback thread that timed out. */ - public final @TimeoutOperation int timeoutOperation; + @TimeoutOperation public final int timeoutOperation; /** * Creates the timeout exception. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java index c9cf750d62..95999b5f75 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java @@ -169,12 +169,14 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities // RendererCapabilities implementation. @Override - public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { + @Capabilities + public int supportsFormat(Format format) throws ExoPlaybackException { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + @AdaptiveSupport + public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java index 6dab072666..68fb854178 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java @@ -55,7 +55,7 @@ import java.util.List; /** The start position after a reported position discontinuity, in microseconds. */ public final long discontinuityStartPositionUs; /** The current playback state. One of the {@link Player}.STATE_ constants. */ - public final @Player.State int playbackState; + @Player.State public final int playbackState; /** The current playback error, or null if this is not an error state. */ @Nullable public final ExoPlaybackException playbackError; /** Whether the player is currently loading. */ @@ -71,7 +71,7 @@ import java.util.List; /** Whether playback should proceed when {@link #playbackState} == {@link Player#STATE_READY}. */ public final boolean playWhenReady; /** Reason why playback is suppressed even though {@link #playWhenReady} is {@code true}. */ - public final @PlaybackSuppressionReason int playbackSuppressionReason; + @PlaybackSuppressionReason public final int playbackSuppressionReason; /** The playback parameters. */ public final PlaybackParameters playbackParameters; /** Whether offload scheduling is enabled for the main player loop. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index a51f245cc3..755b2c77b7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -191,7 +191,8 @@ public interface RendererCapabilities { * @return The combined {@link Capabilities} of the given {@link C.FormatSupport}, {@link * #ADAPTIVE_NOT_SUPPORTED} and {@link #TUNNELING_NOT_SUPPORTED}. */ - static @Capabilities int create(@C.FormatSupport int formatSupport) { + @Capabilities + static int create(@C.FormatSupport int formatSupport) { return create(formatSupport, ADAPTIVE_NOT_SUPPORTED, TUNNELING_NOT_SUPPORTED); } @@ -207,7 +208,8 @@ public interface RendererCapabilities { * @param tunnelingSupport The {@link TunnelingSupport}. * @return The combined {@link Capabilities}. */ - static @Capabilities int create( + @Capabilities + static int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport) { @@ -233,7 +235,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @Capabilities int create( + @Capabilities + static int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport, @@ -254,7 +257,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @C.FormatSupport int getFormatSupport(@Capabilities int supportFlags) { + @C.FormatSupport + static int getFormatSupport(@Capabilities int supportFlags) { return supportFlags & FORMAT_SUPPORT_MASK; } @@ -266,7 +270,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @AdaptiveSupport int getAdaptiveSupport(@Capabilities int supportFlags) { + @AdaptiveSupport + static int getAdaptiveSupport(@Capabilities int supportFlags) { return supportFlags & ADAPTIVE_SUPPORT_MASK; } @@ -278,7 +283,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @TunnelingSupport int getTunnelingSupport(@Capabilities int supportFlags) { + @TunnelingSupport + static int getTunnelingSupport(@Capabilities int supportFlags) { return supportFlags & TUNNELING_SUPPORT_MASK; } @@ -290,8 +296,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @HardwareAccelerationSupport int getHardwareAccelerationSupport( - @Capabilities int supportFlags) { + @HardwareAccelerationSupport + static int getHardwareAccelerationSupport(@Capabilities int supportFlags) { return supportFlags & HARDWARE_ACCELERATION_SUPPORT_MASK; } @@ -303,7 +309,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - static @DecoderSupport int getDecoderSupport(@Capabilities int supportFlags) { + @DecoderSupport + static int getDecoderSupport(@Capabilities int supportFlags) { return supportFlags & MODE_SUPPORT_MASK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 14540012df..bffbe5e453 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -398,7 +398,8 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public @C.VideoScalingMode int getVideoScalingMode() { + @C.VideoScalingMode + public int getVideoScalingMode() { return player.getVideoScalingMode(); } @@ -409,7 +410,8 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { + @C.VideoChangeFrameRateStrategy + public int getVideoChangeFrameRateStrategy() { return player.getVideoChangeFrameRateStrategy(); } @@ -637,12 +639,14 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public @State int getPlaybackState() { + @State + public int getPlaybackState() { return player.getPlaybackState(); } @Override - public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { return player.getPlaybackSuppressionReason(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java index c5a8230154..43eb76a1a1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java @@ -55,7 +55,7 @@ import androidx.media3.common.util.Util; private final AudioManager audioManager; @Nullable private VolumeChangeReceiver receiver; - private @C.StreamType int streamType; + @C.StreamType private int streamType; private int volume; private boolean muted; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 54b631389e..59d8b333bc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -150,7 +150,8 @@ public interface AnalyticsListener { * @param index The index. Must be between 0 (inclusive) and {@link #size()} (exclusive). * @return The {@link EventFlags event} at the given index. */ - public @EventFlags int get(int index) { + @EventFlags + public int get(int index) { return flags.get(index); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java index 50d07ce7ca..419e168b5c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java @@ -120,7 +120,7 @@ public final class MediaMetricsListener @Nullable private String activeSessionId; @Nullable private PlaybackMetrics.Builder metricsBuilder; - private @Player.DiscontinuityReason int discontinuityReason; + @Player.DiscontinuityReason private int discontinuityReason; private int currentPlaybackState; private int currentNetworkType; @Nullable private PlaybackException pendingPlayerError; @@ -887,7 +887,7 @@ public final class MediaMetricsListener private static final class PendingFormatUpdate { public final Format format; - public final @C.SelectionReason int selectionReason; + @C.SelectionReason public final int selectionReason; public final String sessionId; public PendingFormatUpdate( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java index 76ff331955..cb5ff1aa94 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java @@ -84,7 +84,7 @@ public final class PlaybackStatsListener @Nullable private String discontinuityFromSession; private long discontinuityFromPositionMs; - private @Player.DiscontinuityReason int discontinuityReason; + @Player.DiscontinuityReason private int discontinuityReason; private int droppedFrames; @Nullable private Exception nonFatalException; private long bandwidthTimeMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java index ed7b23813c..de6c34e119 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java @@ -45,7 +45,7 @@ public interface AudioProcessor { /** The number of interleaved channels. */ public final int channelCount; /** The type of linear PCM encoding. */ - public final @C.PcmEncoding int encoding; + @C.PcmEncoding public final int encoding; /** The number of bytes used to represent one audio frame. */ public final int bytesPerFrame; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 87c3666605..2f9ac4accf 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -138,7 +138,7 @@ public abstract class DecoderAudioRenderer< @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - private @ReinitializationState int decoderReinitializationState; + @ReinitializationState private int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean audioTrackNeedsConfigure; @@ -228,7 +228,8 @@ public abstract class DecoderAudioRenderer< } @Override - public final @Capabilities int supportsFormat(Format format) { + @Capabilities + public final int supportsFormat(Format format) { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @@ -247,7 +248,8 @@ public abstract class DecoderAudioRenderer< * @param format The format, which has an audio {@link Format#sampleMimeType}. * @return The {@link C.FormatSupport} for this {@link Format}. */ - protected abstract @C.FormatSupport int supportsFormatInternal(Format format); + @C.FormatSupport + protected abstract int supportsFormatInternal(Format format); /** * Returns whether the renderer's {@link AudioSink} supports a given {@link Format}. @@ -264,7 +266,8 @@ public abstract class DecoderAudioRenderer< * * @see AudioSink#getFormatSupport(Format) (Format) */ - protected final @SinkFormatSupport int getSinkFormatSupport(Format format) { + @SinkFormatSupport + protected final int getSinkFormatSupport(Format format) { return audioSink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index 7186ca79a5..d9859a99e6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -482,8 +482,8 @@ public final class DefaultAudioSink implements AudioSink { private final AudioTrackPositionTracker audioTrackPositionTracker; private final ArrayDeque mediaPositionParametersCheckpoints; private final boolean enableAudioTrackPlaybackParams; - private final @OffloadMode int offloadMode; - private @MonotonicNonNull StreamEventCallbackV29 offloadStreamEventCallbackV29; + @OffloadMode private final int offloadMode; + @MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29; private final PendingExceptionHolder initializationExceptionPendingExceptionHolder; private final PendingExceptionHolder writeExceptionPendingExceptionHolder; @@ -492,7 +492,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private PlayerId playerId; @Nullable private Listener listener; @Nullable private Configuration pendingConfiguration; - private @MonotonicNonNull Configuration configuration; + @MonotonicNonNull private Configuration configuration; @Nullable private AudioTrack audioTrack; private AudioAttributes audioAttributes; @@ -518,7 +518,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private ByteBuffer inputBuffer; private int inputBufferAccessUnitCount; @Nullable private ByteBuffer outputBuffer; - private @MonotonicNonNull byte[] preV21OutputBuffer; + @MonotonicNonNull private byte[] preV21OutputBuffer; private int preV21OutputBufferOffset; private int drainingAudioProcessorIndex; private boolean handledEndOfStream; @@ -661,7 +661,8 @@ public final class DefaultAudioSink implements AudioSink { } @Override - public @SinkFormatSupport int getFormatSupport(Format format) { + @SinkFormatSupport + public int getFormatSupport(Format format) { if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) { if (!Util.isEncodingLinearPcm(format.pcmEncoding)) { Log.w(TAG, "Invalid PCM encoding: " + format.pcmEncoding); @@ -2135,11 +2136,11 @@ public final class DefaultAudioSink implements AudioSink { public final Format inputFormat; public final int inputPcmFrameSize; - public final @OutputMode int outputMode; + @OutputMode public final int outputMode; public final int outputPcmFrameSize; public final int outputSampleRate; public final int outputChannelConfig; - public final @C.Encoding int outputEncoding; + @C.Encoding public final int outputEncoding; public final int bufferSize; public final AudioProcessor[] availableAudioProcessors; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java index 06fdf5afd1..eda2b250de 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java @@ -50,7 +50,8 @@ public class ForwardingAudioSink implements AudioSink { } @Override - public @SinkFormatSupport int getFormatSupport(Format format) { + @SinkFormatSupport + public int getFormatSupport(Format format) { return sink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 6c929efada..172d0a3ca8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -286,7 +286,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override - protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + @Capabilities + protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java index 2a0ef051ab..1390cc0aff 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java @@ -88,7 +88,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor { */ private byte[] paddingBuffer; - private @State int state; + @State private int state; private int maybeSilenceBufferSize; private int paddingSize; private boolean hasOutputNoise; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java index 0c1556012c..d2778905b5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java @@ -129,7 +129,8 @@ import java.util.concurrent.Executor; } /** Delegates to Spatializer.getImmersiveAudioLevel() */ - public @ImmersiveAudioLevel int getImmersiveAudioLevel() { + @ImmersiveAudioLevel + public int getImmersiveAudioLevel() { try { return (int) Util.castNonNull(getImmersiveAudioLevel.invoke(spatializer)); } catch (IllegalAccessException | InvocationTargetException e) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java index aa9c5e87e9..8e9c8d63e0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java @@ -128,7 +128,7 @@ public final class TeeAudioProcessor extends BaseAudioProcessor { private int sampleRateHz; private int channelCount; - private @C.PcmEncoding int encoding; + @C.PcmEncoding private int encoding; @Nullable private RandomAccessFile randomAccessFile; private int counter; private int bytesWritten; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java index 9e734f5230..a3d1173418 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java @@ -25,7 +25,7 @@ import java.nio.ByteBuffer; /** Audio processor for trimming samples from the start/end of data. */ /* package */ final class TrimmingAudioProcessor extends BaseAudioProcessor { - private static final @C.PcmEncoding int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; + @C.PcmEncoding private static final int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; private int trimStartFrames; private int trimEndFrames; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java index c64eb57dab..44dc7d3a05 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java @@ -254,7 +254,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // DrmSession implementation. @Override - public final @DrmSession.State int getState() { + @DrmSession.State + public final int getState() { return state; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java index adcb450e28..1a3c8ebcf9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java @@ -312,7 +312,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { @Nullable private byte[] offlineLicenseKeySetId; private @MonotonicNonNull PlayerId playerId; - /* package */ @Nullable volatile MediaDrmHandler mediaDrmHandler; + /* package */ volatile @Nullable MediaDrmHandler mediaDrmHandler; /** * @param uuid The UUID of the drm scheme. @@ -590,7 +590,8 @@ public class DefaultDrmSessionManager implements DrmSessionManager { } @Override - public @C.CryptoType int getCryptoType(Format format) { + @C.CryptoType + public int getCryptoType(Format format) { @C.CryptoType int cryptoType = checkNotNull(exoMediaDrm).getCryptoType(); if (format.drmInitData == null) { int trackType = MimeTypes.getTrackType(format.sampleMimeType); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java index 968a3d207f..73fcb430ee 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java @@ -65,7 +65,7 @@ public interface DrmSession { class DrmSessionException extends IOException { /** The {@link PlaybackException.ErrorCode} that corresponds to the failure. */ - public final @PlaybackException.ErrorCode int errorCode; + @PlaybackException.ErrorCode public final int errorCode; public DrmSessionException(Throwable cause, @PlaybackException.ErrorCode int errorCode) { super(cause); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java index 0fab623522..a1b1bae06e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java @@ -66,7 +66,8 @@ public interface DrmSessionManager { } @Override - public @C.CryptoType int getCryptoType(Format format) { + @C.CryptoType + public int getCryptoType(Format format) { return format.drmInitData != null ? C.CRYPTO_TYPE_UNSUPPORTED : C.CRYPTO_TYPE_NONE; } }; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java index a47fb682bb..ec43643855 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java @@ -72,7 +72,8 @@ public final class DrmUtil { * @return The {@link PlaybackException.ErrorCode} that corresponds to the given DRM-related * exception. */ - public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException( + @PlaybackException.ErrorCode + public static int getErrorCodeForMediaDrmException( Exception exception, @ErrorSource int errorSource) { if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) { return Api21.mediaDrmStateExceptionToErrorCode(exception); @@ -127,8 +128,8 @@ public final class DrmUtil { } @DoNotInline - public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode( - Throwable throwable) { + @PlaybackException.ErrorCode + public static int mediaDrmStateExceptionToErrorCode(Throwable throwable) { @Nullable String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo(); int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java index 58efd2f082..89afc0603f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java @@ -153,7 +153,8 @@ public final class DummyExoMediaDrm implements ExoMediaDrm { } @Override - public @C.CryptoType int getCryptoType() { + @C.CryptoType + public int getCryptoType() { return C.CRYPTO_TYPE_UNSUPPORTED; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java index ba537890c8..5557e00392 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java @@ -267,7 +267,7 @@ public interface ExoMediaDrm { private final byte[] data; private final String licenseServerUrl; - private final @RequestType int requestType; + @RequestType private final int requestType; /** * Creates an instance with {@link #REQUEST_TYPE_UNKNOWN}. @@ -307,7 +307,8 @@ public interface ExoMediaDrm { * request does not specify a type. Note that when using a platform {@link MediaDrm} instance, * key requests only specify a type on API levels 23 and above. */ - public @RequestType int getRequestType() { + @RequestType + public int getRequestType() { return requestType; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java index 10fda5099a..00bde4ce26 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java @@ -342,7 +342,8 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { } @Override - public @C.CryptoType int getCryptoType() { + @C.CryptoType + public int getCryptoType() { return C.CRYPTO_TYPE_FRAMEWORK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java index 9b360d2776..f42dde626b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java @@ -49,7 +49,7 @@ public final class UnsupportedDrmException extends Exception { public static final int REASON_INSTANTIATION_ERROR = 2; /** Either {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ - public final @Reason int reason; + @Reason public final int reason; /** @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ public UnsupportedDrmException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java index 2815f8c796..74224d1c21 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java @@ -39,8 +39,7 @@ public final class WidevineUtil { * @return A {@link Pair} consisting of the remaining license and playback durations in seconds, * or null if called before the session has been opened or after it's been released. */ - @Nullable - public static Pair getLicenseDurationRemainingSec(DrmSession drmSession) { + public static @Nullable Pair getLicenseDurationRemainingSec(DrmSession drmSession) { Map keyStatus = drmSession.queryKeyStatus(); if (keyStatus == null) { return null; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index e013c4b990..e4f465c46e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -141,7 +141,7 @@ import java.nio.ByteBuffer; private final boolean synchronizeCodecInteractionsWithQueueing; private final boolean enableImmediateCodecStartAfterFlush; private boolean codecReleased; - private @State int state; + @State private int state; private AsynchronousMediaCodecAdapter( MediaCodec codec, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index 03e5aafd44..d77786a0e7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -53,7 +53,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private static final String TAG = "DMCodecAdapterFactory"; - private @Mode int asynchronousMode; + @Mode private int asynchronousMode; private boolean enableSynchronizeCodecInteractionsWithQueueing; private boolean enableImmediateCodecStartAfterFlush; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index 6d328d506a..be578bf02b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -327,7 +327,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Nullable private ArrayDeque availableCodecInfos; @Nullable private DecoderInitializationException preferredDecoderInitializationException; @Nullable private MediaCodecInfo codecInfo; - private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; + @AdaptationWorkaroundMode private int codecAdaptationWorkaroundMode; private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsFlushWorkaround; private boolean codecNeedsSosFlushWorkaround; @@ -349,9 +349,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean bypassSampleBufferPending; private boolean bypassDrainAndReinitialize; private boolean codecReconfigured; - private @ReconfigurationState int codecReconfigurationState; - private @DrainState int codecDrainState; - private @DrainAction int codecDrainAction; + @ReconfigurationState private int codecReconfigurationState; + @DrainState private int codecDrainState; + @DrainAction private int codecDrainAction; private boolean codecReceivedBuffers; private boolean codecReceivedEos; private boolean codecHasOutputMediaFormat; @@ -437,12 +437,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } @Override - public final @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { + @AdaptiveSupport + public final int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_NOT_SEAMLESS; } @Override - public final @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { + @Capabilities + public final int supportsFormat(Format format) throws ExoPlaybackException { try { return supportsFormat(mediaCodecSelector, format); } catch (DecoderQueryException e) { @@ -458,8 +460,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * @return The {@link Capabilities} for this {@link Format}. * @throws DecoderQueryException If there was an error querying decoders. */ - protected abstract @Capabilities int supportsFormat( - MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException; + @Capabilities + protected abstract int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + throws DecoderQueryException; /** * Returns a list of decoders that can decode media in the specified format, in priority order. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java index 4aca3b8164..3c60048f18 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java @@ -94,7 +94,8 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java index 6eae3298d3..9594592d6d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java @@ -99,7 +99,7 @@ public final class Download { /** The download request. */ public final DownloadRequest request; /** The state of the download. */ - public final @State int state; + @State public final int state; /** The first time when download entry is created. */ public final long startTimeMs; /** The last update time. */ @@ -112,7 +112,7 @@ public final class Download { * If {@link #state} is {@link #STATE_FAILED} then this is the cause, otherwise {@link * #FAILURE_REASON_NONE}. */ - public final @FailureReason int failureReason; + @FailureReason public final int failureReason; /* package */ final DownloadProgress progress; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java index 0d1ffa7937..0ba6bc8aa4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java @@ -1100,7 +1100,8 @@ public final class DownloadHelper { } @Override - public @C.SelectionReason int getSelectionReason() { + @C.SelectionReason + public int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java index 2679e634f2..e08e0d5bb2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java @@ -351,7 +351,8 @@ public final class DownloadManager { * * @return The not met {@link Requirements.RequirementFlags}, or 0 if all requirements are met. */ - public @Requirements.RequirementFlags int getNotMetRequirements() { + @Requirements.RequirementFlags + public int getNotMetRequirements() { return notMetRequirements; } @@ -704,7 +705,7 @@ public final class DownloadManager { private final ArrayList downloads; private final HashMap activeTasks; - private @Requirements.RequirementFlags int notMetRequirements; + @Requirements.RequirementFlags private int notMetRequirements; private boolean downloadsPaused; private int maxParallelDownloads; private int minRetryCount; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index 4bdd389fbc..34d435bb47 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -74,7 +74,7 @@ public final class Requirements implements Parcelable { */ public static final int DEVICE_STORAGE_NOT_LOW = 1 << 4; - private final @RequirementFlags int requirements; + @RequirementFlags private final int requirements; /** @param requirements A combination of requirement flags. */ public Requirements(@RequirementFlags int requirements) { @@ -86,7 +86,8 @@ public final class Requirements implements Parcelable { } /** Returns the requirements. */ - public @RequirementFlags int getRequirements() { + @RequirementFlags + public int getRequirements() { return requirements; } @@ -143,7 +144,8 @@ public final class Requirements implements Parcelable { * @param context Any context. * @return The requirements that are not met, or 0. */ - public @RequirementFlags int getNotMetRequirements(Context context) { + @RequirementFlags + public int getNotMetRequirements(Context context) { @RequirementFlags int notMetRequirements = getNotMetNetworkRequirements(context); if (isChargingRequired() && !isDeviceCharging(context)) { notMetRequirements |= DEVICE_CHARGING; @@ -157,7 +159,8 @@ public final class Requirements implements Parcelable { return notMetRequirements; } - private @RequirementFlags int getNotMetNetworkRequirements(Context context) { + @RequirementFlags + private int getNotMetNetworkRequirements(Context context) { if (!isNetworkRequired()) { return 0; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java index c7ae614e05..ca2380cd42 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java @@ -62,7 +62,7 @@ public final class RequirementsWatcher { @Nullable private DeviceStatusChangeReceiver receiver; - private @Requirements.RequirementFlags int notMetRequirements; + @Requirements.RequirementFlags private int notMetRequirements; @Nullable private NetworkCallback networkCallback; /** @@ -83,7 +83,8 @@ public final class RequirementsWatcher { * * @return Initial {@link Requirements.RequirementFlags RequirementFlags} that are not met, or 0. */ - public @Requirements.RequirementFlags int start() { + @Requirements.RequirementFlags + public int start() { notMetRequirements = requirements.getNotMetRequirements(context); IntentFilter filter = new IntentFilter(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java index 6a734a752b..4263da5d62 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java @@ -63,7 +63,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { public static final int REASON_START_EXCEEDS_END = 2; /** The reason clipping failed. */ - public final @Reason int reason; + @Reason public final int reason; /** @param reason The reason clipping failed. */ public IllegalClippingException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java index 5cbc5628d4..4e2070b043 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java @@ -472,7 +472,8 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { mediaSourceFactories = new HashMap<>(); } - public @C.ContentType int[] getSupportedTypes() { + @C.ContentType + public int[] getSupportedTypes() { ensureAllSuppliersAreLoaded(); return Ints.toArray(supportedTypes); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java index c5df2e15ea..f3a6a04ed5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java @@ -28,7 +28,7 @@ import androidx.media3.common.util.UnstableApi; public final class MediaLoadData { /** The {@link DataType data type}. */ - public final @DataType int dataType; + @DataType public final int dataType; /** * One of the {@link TrackType track types}, which is a media track type if the data corresponds * to media of a specific type, or {@link C#TRACK_TYPE_UNKNOWN} otherwise. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java index af47342210..af34e47388 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java @@ -47,7 +47,8 @@ public interface MediaSourceFactory extends MediaSource.Factory { } @Override - public @C.ContentType int[] getSupportedTypes() { + @C.ContentType + public int[] getSupportedTypes() { throw new UnsupportedOperationException(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java index 8822b24268..c9c589fb74 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java @@ -61,7 +61,7 @@ public final class MergingMediaSource extends CompositeMediaSource { public static final int REASON_PERIOD_COUNT_MISMATCH = 0; /** The reason the merge failed. */ - public final @Reason int reason; + @Reason public final int reason; /** @param reason The reason the merge failed. */ public IllegalMergeException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java index f20a57caeb..e5e74de76e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java @@ -131,7 +131,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SeekMap seekMap; private long durationUs; private boolean isLive; - private @DataType int dataType; + @DataType private int dataType; private boolean seenFirstTrackSelection; private boolean notifyDiscontinuity; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java index d2857100fc..c783249821 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java @@ -87,7 +87,7 @@ public final class SilenceMediaSource extends BaseMediaSource { public static final String MEDIA_ID = "SilenceMediaSource"; private static final int SAMPLE_RATE_HZ = 44100; - private static final @C.PcmEncoding int PCM_ENCODING = C.ENCODING_PCM_16BIT; + @C.PcmEncoding private static final int PCM_ENCODING = C.ENCODING_PCM_16BIT; private static final int CHANNEL_COUNT = 2; private static final Format FORMAT = new Format.Builder() diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index a0821d571e..248b7648ec 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -847,7 +847,8 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource return positionUs; } - public @SampleStream.ReadDataResult int readData( + @SampleStream.ReadDataResult + public int readData( MediaPeriodImpl mediaPeriod, int streamIndex, FormatHolder formatHolder, @@ -1203,7 +1204,8 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource } @Override - public @ReadDataResult int readData( + @ReadDataResult + public int readData( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return mediaPeriod.sharedPeriod.readData( mediaPeriod, streamIndex, formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java index 7a2d8060ec..09c157a9a7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java @@ -42,7 +42,7 @@ public abstract class Chunk implements Loadable { /** The {@link DataSpec} that defines the data to be loaded. */ public final DataSpec dataSpec; /** The {@link DataType data type} of the chunk. For reporting only. */ - public final @DataType int type; + @DataType public final int type; /** The format of the track to which this chunk belongs. */ public final Format trackFormat; /** @@ -50,7 +50,7 @@ public abstract class Chunk implements Loadable { * C#SELECTION_REASON_UNKNOWN} if the chunk does not belong to a track, or if the selection reason * is unknown. */ - public final @C.SelectionReason int trackSelectionReason; + @C.SelectionReason public final int trackSelectionReason; /** * Optional data associated with the selection of the track to which this chunk belongs. Null if * the chunk does not belong to a track, or if there is no associated track selection data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java index 1b6a07874b..1e4dc6014f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java @@ -540,7 +540,8 @@ public final class OutputConsumerAdapterV30 implements MediaParser.OutputConsume return new DrmInitData(schemeType, schemeDatas); } - private static @SelectionFlags int getSelectionFlags(MediaFormat mediaFormat) { + @SelectionFlags + private static int getSelectionFlags(MediaFormat mediaFormat) { int selectionFlags = 0; selectionFlags |= getFlag( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java index 504b6f7843..4c668d3dc9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java @@ -63,7 +63,7 @@ public final class ExoplayerCuesDecoder implements SubtitleDecoder { private final SubtitleInputBuffer inputBuffer; private final Deque availableOutputBuffers; - private @InputBufferState int inputBufferState; + @InputBufferState private int inputBufferState; private boolean released; public ExoplayerCuesDecoder() { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java index af8809966a..e6dac32716 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java @@ -94,7 +94,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { private boolean inputStreamEnded; private boolean outputStreamEnded; private boolean waitingForKeyFrame; - private @ReplacementState int decoderReplacementState; + @ReplacementState private int decoderReplacementState; @Nullable private Format streamFormat; @Nullable private SubtitleDecoder decoder; @Nullable private SubtitleInputBuffer nextInputBuffer; @@ -141,7 +141,8 @@ public final class TextRenderer extends BaseRenderer implements Callback { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 037304cfe2..e8dc20c7e3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -123,7 +123,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private boolean allowAudioMixedChannelCountAdaptiveness; private boolean allowAudioMixedDecoderSupportAdaptiveness; // Text - private @C.SelectionFlags int disabledTextTrackSelectionFlags; + @C.SelectionFlags private int disabledTextTrackSelectionFlags; // General private boolean exceedRendererCapabilitiesIfNecessary; private boolean tunnelingEnabled; @@ -894,7 +894,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * Bitmask of selection flags that are disabled for text track selections. See {@link * C.SelectionFlags}. The default value is {@code 0} (i.e. no flags). */ - public final @C.SelectionFlags int disabledTextTrackSelectionFlags; + @C.SelectionFlags public final int disabledTextTrackSelectionFlags; /** Returns an instance configured with default values. */ public static Parameters getDefaults(Context context) { @@ -2255,7 +2255,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } /** Returns to what extent the track is {@link SelectionEligibility eligible for selection}. */ - public abstract @SelectionEligibility int getSelectionEligibility(); + @SelectionEligibility + public abstract int getSelectionEligibility(); /** * Returns whether this track is compatible for an adaptive selection with the specified other @@ -2307,7 +2308,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int preferredRoleFlagsScore; private final boolean hasMainOrNoRoleFlag; private final boolean allowMixedMimeTypes; - private final @SelectionEligibility int selectionEligibility; + @SelectionEligibility private final int selectionEligibility; private final boolean usesPrimaryDecoder; private final boolean usesHardwareAcceleration; private final int codecPreferenceScore; @@ -2374,7 +2375,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - public @SelectionEligibility int getSelectionEligibility() { + @SelectionEligibility + public int getSelectionEligibility() { return selectionEligibility; } @@ -2387,7 +2389,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { && this.usesHardwareAcceleration == otherTrack.usesHardwareAcceleration)); } - private @SelectionEligibility int evaluateSelectionEligibility( + @SelectionEligibility + private int evaluateSelectionEligibility( @Capabilities int rendererSupport, @AdaptiveSupport int requiredAdaptiveSupport) { if ((format.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) { // Ignore trick-play tracks for now. @@ -2499,7 +2502,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - private final @SelectionEligibility int selectionEligibility; + @SelectionEligibility private final int selectionEligibility; private final boolean isWithinConstraints; @Nullable private final String language; private final Parameters parameters; @@ -2591,7 +2594,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - public @SelectionEligibility int getSelectionEligibility() { + @SelectionEligibility + public int getSelectionEligibility() { return selectionEligibility; } @@ -2660,7 +2664,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { .result(); } - private @SelectionEligibility int evaluateSelectionEligibility( + @SelectionEligibility + private int evaluateSelectionEligibility( @Capabilities int rendererSupport, boolean hasMappedVideoTracks) { if (!isSupported(rendererSupport, parameters.exceedRendererCapabilitiesIfNecessary)) { return SELECTION_ELIGIBILITY_NO; @@ -2707,7 +2712,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - private final @SelectionEligibility int selectionEligibility; + @SelectionEligibility private final int selectionEligibility; private final boolean isWithinRendererCapabilities; private final boolean isDefault; private final boolean isForced; @@ -2772,7 +2777,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - public @SelectionEligibility int getSelectionEligibility() { + @SelectionEligibility + public int getSelectionEligibility() { return selectionEligibility; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index d1836b7094..980a5a7749 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -107,8 +107,8 @@ public abstract class MappingTrackSelector extends TrackSelector { private final String[] rendererNames; private final @C.TrackType int[] rendererTrackTypes; private final TrackGroupArray[] rendererTrackGroups; - private final @AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports; - private final @Capabilities int[][][] rendererFormatSupports; + @AdaptiveSupport private final int[] rendererMixedMimeTypeAdaptiveSupports; + @Capabilities private final int[][][] rendererFormatSupports; private final TrackGroupArray unmappedTrackGroups; /** @@ -181,7 +181,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param rendererIndex The renderer index. * @return The {@link RendererSupport}. */ - public @RendererSupport int getRendererSupport(int rendererIndex) { + @RendererSupport + public int getRendererSupport(int rendererIndex) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; @Capabilities int[][] rendererFormatSupport = rendererFormatSupports[rendererIndex]; for (@Capabilities int[] trackGroupFormatSupport : rendererFormatSupport) { @@ -216,7 +217,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackType The {@link C.TrackType track type}. * @return The {@link RendererSupport}. */ - public @RendererSupport int getTypeSupport(@C.TrackType int trackType) { + @RendererSupport + public int getTypeSupport(@C.TrackType int trackType) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; for (int i = 0; i < rendererCount; i++) { if (rendererTrackTypes[i] == trackType) { @@ -234,7 +236,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link Capabilities}. */ - public @Capabilities int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { + @Capabilities + public int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { return rendererFormatSupports[rendererIndex][groupIndex][trackIndex]; } @@ -246,7 +249,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link FormatSupport}. */ - public @FormatSupport int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { + @FormatSupport + public int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { return RendererCapabilities.getFormatSupport( getCapabilities(rendererIndex, groupIndex, trackIndex)); } @@ -268,7 +272,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer are included when determining support. * @return The {@link AdaptiveSupport}. */ - public @AdaptiveSupport int getAdaptiveSupport( + @AdaptiveSupport + public int getAdaptiveSupport( int rendererIndex, int groupIndex, boolean includeCapabilitiesExceededTracks) { int trackCount = rendererTrackGroups[rendererIndex].get(groupIndex).length; // Iterate over the tracks in the group, recording the indices of those to consider. @@ -294,8 +299,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param groupIndex The index of the track group. * @return The {@link AdaptiveSupport}. */ - public @AdaptiveSupport int getAdaptiveSupport( - int rendererIndex, int groupIndex, int[] trackIndices) { + @AdaptiveSupport + public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) { int handledTrackCount = 0; @AdaptiveSupport int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS; boolean multipleMimeTypes = false; @@ -535,8 +540,9 @@ public abstract class MappingTrackSelector extends TrackSelector { * @return An array containing {@link Capabilities} for each track in the group. * @throws ExoPlaybackException If an error occurs determining the format support. */ - private static @Capabilities int[] getFormatSupport( - RendererCapabilities rendererCapabilities, TrackGroup group) throws ExoPlaybackException { + @Capabilities + private static int[] getFormatSupport(RendererCapabilities rendererCapabilities, TrackGroup group) + throws ExoPlaybackException { @Capabilities int[] formatSupport = new int[group.length]; for (int i = 0; i < group.length; i++) { formatSupport[i] = rendererCapabilities.supportsFormat(group.getFormat(i)); @@ -553,7 +559,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer. * @throws ExoPlaybackException If an error occurs determining the adaptation support. */ - private static @AdaptiveSupport int[] getMixedMimeTypeAdaptationSupports( + @AdaptiveSupport + private static int[] getMixedMimeTypeAdaptationSupports( RendererCapabilities[] rendererCapabilities) throws ExoPlaybackException { @AdaptiveSupport int[] mixedMimeTypeAdaptationSupport = new int[rendererCapabilities.length]; for (int i = 0; i < mixedMimeTypeAdaptationSupport.length; i++) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java index 654a34d975..02c89e42a3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java @@ -281,14 +281,14 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList private long sampleStartTimeMs; private long sampleBytesTransferred; - private @C.NetworkType int networkType; + @C.NetworkType private int networkType; private long totalElapsedTimeMs; private long totalBytesTransferred; private long bitrateEstimate; private long lastReportedBitrateEstimate; private boolean networkTypeOverrideSet; - private @C.NetworkType int networkTypeOverride; + @C.NetworkType private int networkTypeOverride; /** @deprecated Use {@link Builder} instead. */ @Deprecated diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java index 40e5c13c2c..fd40859f2d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java @@ -130,7 +130,7 @@ public interface LoadErrorHandlingPolicy { /** A selected fallback option. */ final class FallbackSelection { /** The type of fallback. */ - public final @FallbackType int type; + @FallbackType public final int type; /** The duration for which the failing resource should be excluded, in milliseconds. */ public final long exclusionDurationMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java index 859f920713..5e978e19c4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java @@ -104,7 +104,7 @@ public final class ParsingLoadable implements Loadable { private final StatsDataSource dataSource; private final Parser parser; - @Nullable private volatile T result; + private volatile @Nullable T result; /** * @param dataSource A {@link DataSource} to use when loading the data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java index 2f247237d2..8d2144084f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java @@ -121,7 +121,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { private DecoderInputBuffer inputBuffer; private VideoDecoderOutputBuffer outputBuffer; - private @VideoOutputMode int outputMode; + @VideoOutputMode private int outputMode; @Nullable private Object output; @Nullable private Surface outputSurface; @Nullable private VideoDecoderOutputBufferRenderer outputBufferRenderer; @@ -130,7 +130,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - private @ReinitializationState int decoderReinitializationState; + @ReinitializationState private int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean renderedFirstFrameAfterReset; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java index a7819ddd25..f225812bbb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java @@ -105,7 +105,8 @@ public final class DummySurface extends Surface { } } - private static @SecureMode int getSecureMode(Context context) { + @SecureMode + private static int getSecureMode(Context context) { if (GlUtil.isProtectedContentExtensionSupported(context)) { if (GlUtil.isSurfacelessContextExtensionSupported()) { return SECURE_MODE_SURFACELESS_CONTEXT; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index d1114f4ca3..d3aa4562ec 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -131,7 +131,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @Nullable private Surface surface; @Nullable private DummySurface dummySurface; private boolean haveReportedFirstFrameRenderedForCurrentSurface; - private @C.VideoScalingMode int scalingMode; + @C.VideoScalingMode private int scalingMode; private boolean renderedFirstFrameAfterReset; private boolean mayRenderFirstFrameAfterEnableIfNotStarted; private boolean renderedFirstFrameAfterEnable; @@ -342,7 +342,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } @Override - protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + @Capabilities + protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java index 2735852372..d5646ca11c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java @@ -111,7 +111,7 @@ public final class VideoFrameReleaseHelper { private float surfacePlaybackFrameRate; private float playbackSpeed; - private @C.VideoChangeFrameRateStrategy int changeFrameRateStrategy; + @C.VideoChangeFrameRateStrategy private int changeFrameRateStrategy; private long vsyncDurationNs; private long vsyncOffsetNs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java index b372d03481..b1a222f49a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java @@ -58,7 +58,8 @@ public final class CameraMotionRenderer extends BaseRenderer { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { return MimeTypes.APPLICATION_CAMERA_MOTION.equals(format.sampleMimeType) ? RendererCapabilities.create(C.FORMAT_HANDLED) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java index 4b0acba57b..81444dd5b0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java @@ -58,8 +58,7 @@ import java.util.zip.Inflater; * @param stereoMode A {@link C.StereoMode} value. * @return The projection or null if the data can't be decoded. */ - @Nullable - public static Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { + public static @Nullable Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { ParsableByteArray input = new ParsableByteArray(projectionData); // MP4 containers include the proj box but webm containers do not. // Both containers use mshp. @@ -92,8 +91,7 @@ import java.util.zip.Inflater; return type == TYPE_PROJ; } - @Nullable - private static ArrayList parseProj(ParsableByteArray input) { + private static @Nullable ArrayList parseProj(ParsableByteArray input) { input.skipBytes(8); // size and type. int position = input.getPosition(); int limit = input.limit(); @@ -114,8 +112,7 @@ import java.util.zip.Inflater; return null; } - @Nullable - private static ArrayList parseMshp(ParsableByteArray input) { + private static @Nullable ArrayList parseMshp(ParsableByteArray input) { int version = input.readUnsignedByte(); if (version != 0) { return null; @@ -140,8 +137,7 @@ import java.util.zip.Inflater; } /** Parses MSHP data after the encoding_four_cc field. */ - @Nullable - private static ArrayList parseRawMshpData(ParsableByteArray input) { + private static @Nullable ArrayList parseRawMshpData(ParsableByteArray input) { ArrayList meshes = new ArrayList<>(); int position = input.getPosition(); int limit = input.limit(); @@ -164,8 +160,7 @@ import java.util.zip.Inflater; return meshes; } - @Nullable - private static Mesh parseMesh(ParsableByteArray input) { + private static @Nullable Mesh parseMesh(ParsableByteArray input) { // Read the coordinates. int coordinateCount = input.readInt(); if (coordinateCount > MAX_COORDINATE_COUNT) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java index b2828a465e..b31cfa98ab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java @@ -50,8 +50,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SurfaceTexture surfaceTexture; // Used by other threads only - private volatile @C.StereoMode int defaultStereoMode; - private @C.StereoMode int lastStereoMode; + @C.StereoMode private volatile int defaultStereoMode; + @C.StereoMode private int lastStereoMode; @Nullable private byte[] lastProjectionData; // Methods called on any thread. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 0961f4492d..cc444ebb0f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -11488,7 +11488,7 @@ public final class ExoPlayerTest { private static final class PlayerStateGrabber extends PlayerRunnable { public boolean playWhenReady; - public @Player.State int playbackState; + @Player.State public int playbackState; @Nullable public Timeline timeline; @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java index f0009b7ceb..dbf9c8dfc6 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java @@ -71,7 +71,8 @@ public class DecoderAudioRendererTest { } @Override - protected @C.FormatSupport int supportsFormatInternal(Format format) { + @C.FormatSupport + protected int supportsFormatInternal(Format format) { return FORMAT_HANDLED; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java index 59b41fdc0e..8916391b8b 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java @@ -44,7 +44,8 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class PcmTest { @Parameterized.Parameter(0) - public @C.PcmEncoding int encoding; + @C.PcmEncoding + public int encoding; @Parameterized.Parameter(1) public int channelCount; @@ -215,7 +216,8 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class EncodedTest { @Parameterized.Parameter(0) - public @C.Encoding int encoding; + @C.Encoding + public int encoding; @Parameterized.Parameters(name = "{index}: encoding={0}") public static ImmutableList data() { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java index a5514c7068..5c85327510 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java @@ -2416,7 +2416,7 @@ public final class DefaultTrackSelectorTest { private static final class FakeRendererCapabilities implements RendererCapabilities { private final int trackType; - private final @Capabilities int supportValue; + @Capabilities private final int supportValue; /** * Returns {@link FakeRendererCapabilities} that advertises adaptive support for all tracks of @@ -2456,14 +2456,16 @@ public final class DefaultTrackSelectorTest { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? supportValue : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { + @AdaptiveSupport + public int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } @@ -2502,14 +2504,16 @@ public final class DefaultTrackSelectorTest { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { return format.id != null && formatToCapability.containsKey(format.id) ? formatToCapability.get(format.id) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { + @AdaptiveSupport + public int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java index 32023b11e2..4885587d6b 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java @@ -245,7 +245,8 @@ public final class MappingTrackSelectorTest { } @Override - public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { + @Capabilities + public int supportsFormat(Format format) throws ExoPlaybackException { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? RendererCapabilities.create( C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) @@ -253,7 +254,8 @@ public final class MappingTrackSelectorTest { } @Override - public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + @AdaptiveSupport + public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java index 95a9a67b61..c8d195f9b8 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java @@ -84,7 +84,7 @@ public final class DecoderVideoRendererTest { /* maxDroppedFramesToNotify= */ -1) { private final Phaser inputBuffersInCodecPhaser = new Phaser(); - private @C.VideoOutputMode int outputMode; + @C.VideoOutputMode private int outputMode; @Override public String getName() { @@ -92,7 +92,8 @@ public final class DecoderVideoRendererTest { } @Override - public @Capabilities int supportsFormat(Format format) { + @Capabilities + public int supportsFormat(Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index d12739b2b4..b1df16d07a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -111,8 +111,8 @@ public class MediaCodecVideoRendererTest { /* eventListener= */ eventListener, /* maxDroppedFramesToNotify= */ 1) { @Override - protected @Capabilities int supportsFormat( - MediaCodecSelector mediaCodecSelector, Format format) { + @Capabilities + protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index 23b9262cf0..be20b5e054 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -937,7 +937,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; public final int[] adaptationSetIndices; public final @C.TrackType int trackType; - public final @TrackGroupCategory int trackGroupCategory; + @TrackGroupCategory public final int trackGroupCategory; public final int eventStreamGroupIndex; public final int primaryTrackGroupIndex; diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java index 6ee1ab5d6c..9ca9fc2102 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java @@ -1481,8 +1481,8 @@ public class DashManifestParser extends DefaultHandler // Selection flag parsing. - protected @C.SelectionFlags int parseSelectionFlagsFromRoleDescriptors( - List roleDescriptors) { + @C.SelectionFlags + protected int parseSelectionFlagsFromRoleDescriptors(List roleDescriptors) { @C.SelectionFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1493,7 +1493,8 @@ public class DashManifestParser extends DefaultHandler return result; } - protected @C.SelectionFlags int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { + @C.SelectionFlags + protected int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1509,7 +1510,8 @@ public class DashManifestParser extends DefaultHandler // Role and Accessibility parsing. - protected @C.RoleFlags int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { + @C.RoleFlags + protected int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1520,7 +1522,8 @@ public class DashManifestParser extends DefaultHandler return result; } - protected @C.RoleFlags int parseRoleFlagsFromAccessibilityDescriptors( + @C.RoleFlags + protected int parseRoleFlagsFromAccessibilityDescriptors( List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { @@ -1535,8 +1538,8 @@ public class DashManifestParser extends DefaultHandler return result; } - protected @C.RoleFlags int parseRoleFlagsFromProperties( - List accessibilityDescriptors) { + @C.RoleFlags + protected int parseRoleFlagsFromProperties(List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { Descriptor descriptor = accessibilityDescriptors.get(i); @@ -1548,7 +1551,8 @@ public class DashManifestParser extends DefaultHandler return result; } - protected @C.RoleFlags int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { + @C.RoleFlags + protected int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1583,7 +1587,8 @@ public class DashManifestParser extends DefaultHandler } } - protected @C.RoleFlags int parseTvaAudioPurposeCsValue(@Nullable String value) { + @C.RoleFlags + protected int parseTvaAudioPurposeCsValue(@Nullable String value) { if (value == null) { return 0; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java index aec437e327..684bf120ed 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java @@ -62,7 +62,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { FileTypes.MP3, }; - private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags; + @DefaultTsPayloadReaderFactory.Flags private final int payloadReaderFactoryFlags; private final boolean exposeCea608WhenMissingDeclarations; /** diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index b843ae48d6..e054b113ca 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -303,7 +303,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * has been {@link #CHUNK_PUBLICATION_STATE_REMOVED removed} or is definitely {@link * #CHUNK_PUBLICATION_STATE_PUBLISHED published}. */ - public @ChunkPublicationState int getChunkPublicationState(HlsMediaChunk mediaChunk) { + @ChunkPublicationState + public int getChunkPublicationState(HlsMediaChunk mediaChunk) { if (mediaChunk.partIndex == C.INDEX_UNSET) { // Chunks based on full segments can't be removed and are always published. return CHUNK_PUBLICATION_STATE_PUBLISHED; diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java index fb2444d4bb..c6a43c9eec 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java @@ -398,7 +398,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { public static final int PLAYLIST_TYPE_EVENT = 2; /** The type of the playlist. See {@link PlaylistType}. */ - public final @PlaylistType int playlistType; + @PlaylistType public final int playlistType; /** * The start offset in microseconds from the beginning of the playlist, as defined by * #EXT-X-START, or {@link C#TIME_UNSET} if undefined. The value is guaranteed to be between 0 and diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index 63850c72a0..c1e48a068b 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -1087,7 +1087,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variableDefinitions) { + @C.RoleFlags + private static int parseRoleFlags(String line, Map variableDefinitions) { String concatenatedCharacteristics = parseOptionalStringAttr(line, REGEX_CHARACTERISTICS, variableDefinitions); if (TextUtils.isEmpty(concatenatedCharacteristics)) { diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java index d4176e67fc..b55ec3b9ac 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java @@ -162,7 +162,7 @@ import java.util.Map; /** Whether IMA has sent an ad event to pause content since the last resume content event. */ private boolean imaPausedContent; /** The current ad playback state. */ - private @ImaAdState int imaAdState; + @ImaAdState private int imaAdState; /** The current ad media info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ @Nullable private AdMediaInfo imaAdMediaInfo; /** The current ad info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java index d8a2345210..f9026648d2 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java @@ -51,7 +51,7 @@ import java.util.Map; @Nullable private String authToken; @Nullable private String streamActivityMonitorId; private ImmutableMap adTagParameters; - public @ContentType int format = C.TYPE_HLS; + @ContentType public int format = C.TYPE_HLS; private int loadVideoTimeoutMs; /** Creates a new instance. */ @@ -270,7 +270,7 @@ import java.util.Map; @Nullable public final String contentUrl; @Nullable public final String authToken; @Nullable public final String streamActivityMonitorId; - public @ContentType int format = C.TYPE_HLS; + @ContentType public int format = C.TYPE_HLS; public final int loadVideoTimeoutMs; private ServerSideAdInsertionStreamRequest( diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java index 6a9602c976..4d1fd431e7 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java @@ -42,7 +42,7 @@ import androidx.media3.test.utils.StubExoPlayer; private final MediaItem mediaItem = MediaItem.fromUri("http://google.com/0"); private Timeline timeline; - private @Player.State int state; + @Player.State private int state; private boolean playWhenReady; private int periodIndex; private long positionMs; @@ -236,7 +236,8 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - public @Player.State int getPlaybackState() { + @Player.State + public int getPlaybackState() { return state; } @@ -246,7 +247,8 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - public @RepeatMode int getRepeatMode() { + @RepeatMode + public int getRepeatMode() { return REPEAT_MODE_OFF; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java index 0ecdbc9fe4..ecb9994f3f 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java @@ -56,7 +56,7 @@ import java.security.NoSuchAlgorithmException; private static final String ALGORITHM = "MD5"; /** The authentication mechanism. */ - public final @AuthenticationMechanism int authenticationMechanism; + @AuthenticationMechanism public final int authenticationMechanism; /** The authentication realm. */ public final String realm; /** The nonce used in digest authentication; empty if using {@link #BASIC} authentication. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java index 84bcc4cd8d..e8b3fe7867 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java @@ -141,7 +141,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private String sessionId; @Nullable private KeepAliveMonitor keepAliveMonitor; @Nullable private RtspAuthenticationInfo rtspAuthenticationInfo; - private @RtspState int rtspState; + @RtspState private int rtspState; private boolean hasUpdatedTimelineAndTracks; private boolean receivedAuthorizationRequest; private boolean hasPendingPauseRequest; @@ -204,7 +204,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } /** Returns the current {@link RtspState RTSP state}. */ - public @RtspState int getState() { + @RtspState + public int getState() { return rtspState; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java index 28084cdef6..b0ba5c4c5e 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java @@ -775,7 +775,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return sampleQueue.isReady(/* loadingFinished= */ canceled); } - public @ReadDataResult int read( + @ReadDataResult + public int read( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return sampleQueue.read(formatHolder, buffer, readFlags, /* loadingFinished= */ canceled); } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java index a2d5970431..dda52e8f98 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java @@ -353,7 +353,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final List messageLines; - private @ReadingState int state; + @ReadingState private int state; private long messageBodyLength; /** Creates a new instance. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java index db1e4116ea..e05b4c6469 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java @@ -256,7 +256,8 @@ import java.util.regex.Pattern; } } - private static @RtspRequest.Method int parseMethodString(String method) { + @RtspRequest.Method + private static int parseMethodString(String method) { switch (method) { case "ANNOUNCE": return METHOD_ANNOUNCE; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java index 23cff58977..6d9e5f35e8 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java @@ -88,7 +88,7 @@ import java.lang.annotation.Target; /** The {@link Uri} to which this request is sent. */ public final Uri uri; /** The request method, as defined in {@link Method}. */ - public final @Method int method; + @Method public final int method; /** The headers of this request. */ public final RtspHeaders headers; /** The body of this RTSP message, or empty string if absent. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java index 659a28f70f..6895010b75 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java @@ -58,7 +58,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final RtpPayloadFormat payloadFormat; private @MonotonicNonNull TrackOutput trackOutput; - private @C.BufferFlags int bufferFlags; + @C.BufferFlags private int bufferFlags; private long firstReceivedTimestamp; private int previousSequenceNumber; @@ -296,7 +296,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /* divisor= */ MEDIA_CLOCK_FREQUENCY); } - private static @C.BufferFlags int getBufferFlagsFromNalType(int nalType) { + @C.BufferFlags + private static int getBufferFlagsFromNalType(int nalType) { return nalType == NAL_UNIT_TYPE_IDR ? C.BUFFER_FLAG_KEY_FRAME : 0; } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java index a8bcb676a6..1ff09dfdff 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java @@ -423,7 +423,7 @@ public abstract class BinarySearchSeeker { new TimestampSearchResult(TYPE_NO_TIMESTAMP, C.TIME_UNSET, C.POSITION_UNSET); /** The type of the result. */ - private final @Type int type; + @Type private final int type; /** * When {@link #type} is {@link #TYPE_POSITION_OVERESTIMATED}, the {@link diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java index 5470d906a8..f5ee3432f1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java @@ -107,15 +107,15 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { private boolean constantBitrateSeekingEnabled; private boolean constantBitrateSeekingAlwaysEnabled; - private @AdtsExtractor.Flags int adtsFlags; - private @AmrExtractor.Flags int amrFlags; - private @FlacExtractor.Flags int flacFlags; - private @MatroskaExtractor.Flags int matroskaFlags; - private @Mp4Extractor.Flags int mp4Flags; - private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; - private @Mp3Extractor.Flags int mp3Flags; - private @TsExtractor.Mode int tsMode; - private @DefaultTsPayloadReaderFactory.Flags int tsFlags; + @AdtsExtractor.Flags private int adtsFlags; + @AmrExtractor.Flags private int amrFlags; + @FlacExtractor.Flags private int flacFlags; + @MatroskaExtractor.Flags private int matroskaFlags; + @Mp4Extractor.Flags private int mp4Flags; + @FragmentedMp4Extractor.Flags private int fragmentedMp4Flags; + @Mp3Extractor.Flags private int mp3Flags; + @TsExtractor.Mode private int tsMode; + @DefaultTsPayloadReaderFactory.Flags private int tsFlags; private int tsTimestampSearchBytes; public DefaultExtractorsFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java index e479c27aac..710e11a7ad 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java @@ -40,7 +40,7 @@ public interface TrackOutput { final class CryptoData { /** The encryption mode used for the sample. */ - public final @C.CryptoMode int cryptoMode; + @C.CryptoMode public final int cryptoMode; /** The encryption key associated with the sample. Its contents must not be modified. */ public final byte[] encryptionKey; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java index 53f5db50ad..93f675fb5f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java @@ -33,7 +33,7 @@ public final class TrueHdSampleRechunker { private boolean foundSyncframe; private int chunkSampleCount; private long chunkTimeUs; - private @C.BufferFlags int chunkFlags; + @C.BufferFlags private int chunkFlags; private int chunkSize; private int chunkOffset; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java index 3d2cca8171..700d5538f7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java @@ -84,7 +84,7 @@ public final class JpegExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; - private @State int state; + @State private int state; private int marker; private int segmentLength; private long mp4StartPosition; @@ -128,8 +128,8 @@ public final class JpegExtractor implements Extractor { } @Override - public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) - throws IOException { + @ReadResult + public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { switch (state) { case STATE_READING_MARKER: readMarker(input); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 5f4d246a9f..6442ad7993 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -431,7 +431,7 @@ public class MatroskaExtractor implements Extractor { private int[] blockSampleSizes; private int blockTrackNumber; private int blockTrackNumberLength; - private @C.BufferFlags int blockFlags; + @C.BufferFlags private int blockFlags; private int blockAdditionalId; private boolean blockHasReferenceBlock; @@ -531,7 +531,8 @@ public class MatroskaExtractor implements Extractor { * @see EbmlProcessor#getElementType(int) */ @CallSuper - protected @EbmlProcessor.ElementType int getElementType(int id) { + @EbmlProcessor.ElementType + protected int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: @@ -1907,7 +1908,8 @@ public class MatroskaExtractor implements Extractor { private final class InnerEbmlProcessor implements EbmlProcessor { @Override - public @ElementType int getElementType(int id) { + @ElementType + public int getElementType(int id) { return MatroskaExtractor.this.getElementType(id); } @@ -1979,16 +1981,16 @@ public class MatroskaExtractor implements Extractor { public int displayWidth = Format.NO_VALUE; public int displayHeight = Format.NO_VALUE; public int displayUnit = DISPLAY_UNIT_PIXELS; - public @C.Projection int projectionType = Format.NO_VALUE; + @C.Projection public int projectionType = Format.NO_VALUE; public float projectionPoseYaw = 0f; public float projectionPosePitch = 0f; public float projectionPoseRoll = 0f; public byte @MonotonicNonNull [] projectionData = null; - public @C.StereoMode int stereoMode = Format.NO_VALUE; + @C.StereoMode public int stereoMode = Format.NO_VALUE; public boolean hasColorInfo = false; - public @C.ColorSpace int colorSpace = Format.NO_VALUE; - public @C.ColorTransfer int colorTransfer = Format.NO_VALUE; - public @C.ColorRange int colorRange = Format.NO_VALUE; + @C.ColorSpace public int colorSpace = Format.NO_VALUE; + @C.ColorTransfer public int colorTransfer = Format.NO_VALUE; + @C.ColorRange public int colorRange = Format.NO_VALUE; public int maxContentLuminance = DEFAULT_MAX_CLL; public int maxFrameAverageLuminance = DEFAULT_MAX_FALL; public float primaryRChromaticityX = Format.NO_VALUE; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index 53ae30f9ee..936c7be490 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -142,7 +142,7 @@ public final class Mp3Extractor implements Extractor { private static final int SEEK_HEADER_VBRI = 0x56425249; private static final int SEEK_HEADER_UNSET = 0; - private final @Flags int flags; + @Flags private final int flags; private final long forcedFirstSampleTimestampUs; private final ParsableByteArray scratch; private final MpegAudioUtil.Header synchronizedHeader; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java index bd34ba5970..985255a1b9 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java @@ -1910,7 +1910,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable public Format format; public int nalUnitLengthFieldLength; - public @Track.Transformation int requiredSampleTransformation; + @Track.Transformation public int requiredSampleTransformation; public StsdData(int numberOfEntries) { trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries]; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 36284771d1..dc83015d88 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -132,7 +132,7 @@ public class FragmentedMp4Extractor implements Extractor { private static final int STATE_READING_SAMPLE_CONTINUE = 4; // Workarounds. - private final @Flags int flags; + @Flags private final int flags; @Nullable private final Track sideloadedTrack; // Sideloaded data. @@ -1689,7 +1689,8 @@ public class FragmentedMp4Extractor implements Extractor { } /** Returns the {@link C.BufferFlags} corresponding to the current sample. */ - public @C.BufferFlags int getCurrentSampleFlags() { + @C.BufferFlags + public int getCurrentSampleFlags() { int flags = !currentlyInFragment ? moovSampleTable.flags[currentSampleIndex] diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index e839cf1e9e..b2b8bc74a3 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -141,7 +141,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { */ private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10 * 1024 * 1024; - private final @Flags int flags; + @Flags private final int flags; // Temporary arrays. private final ParsableByteArray nalStartCode; @@ -153,7 +153,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private final SefReader sefReader; private final List slowMotionMetadataEntries; - private @State int parserState; + @State private int parserState; private int atomType; private long atomSize; private int atomHeaderBytesRead; @@ -171,7 +171,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private long @MonotonicNonNull [][] accumulatedSampleSizes; private int firstVideoTrackIndex; private long durationUs; - private @FileType int fileType; + @FileType private int fileType; @Nullable private MotionPhotoMetadata motionPhotoMetadata; /** Creates a new extractor for unfragmented MP4 streams. */ @@ -438,8 +438,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { return seekRequired && parserState != STATE_READING_SAMPLE; } - private @ReadResult int readSefData(ExtractorInput input, PositionHolder seekPosition) - throws IOException { + @ReadResult + private int readSefData(ExtractorInput input, PositionHolder seekPosition) throws IOException { @ReadResult int result = sefReader.read(input, seekPosition, slowMotionMetadataEntries); if (result == RESULT_SEEK && seekPosition.position == 0) { enterReadingAtomHeaderState(); @@ -861,7 +861,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { * @param atomData The ftyp atom data. * @return The {@link FileType}. */ - private static @FileType int processFtypAtom(ParsableByteArray atomData) { + @FileType + private static int processFtypAtom(ParsableByteArray atomData) { atomData.setPosition(Atom.HEADER_SIZE); int majorBrand = atomData.readInt(); @FileType int fileType = brandToFileType(majorBrand); @@ -878,7 +879,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { return FILE_TYPE_MP4; } - private static @FileType int brandToFileType(int brand) { + @FileType + private static int brandToFileType(int brand) { switch (brand) { case BRAND_QUICKTIME: return FILE_TYPE_QUICKTIME; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java index b5161fbf1e..84170809eb 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java @@ -97,7 +97,7 @@ import java.util.List; private static final Splitter ASTERISK_SPLITTER = Splitter.on('*'); private final List dataReferences; - private @State int readerState; + @State private int readerState; private int tailLength; public SefReader() { @@ -110,7 +110,8 @@ import java.util.List; readerState = STATE_SHOULD_CHECK_FOR_SEF; } - public @Extractor.ReadResult int read( + @Extractor.ReadResult + public int read( ExtractorInput input, PositionHolder seekPosition, List slowMotionMetadataEntries) @@ -249,7 +250,8 @@ import java.util.List; return new SlowMotionData(segments); } - private static @DataType int nameToDataType(String name) throws ParserException { + @DataType + private static int nameToDataType(String name) throws ParserException { switch (name) { case "SlowMotion_Data": return TYPE_SLOW_MOTION_DATA; @@ -267,7 +269,7 @@ import java.util.List; } private static final class DataReference { - public final @DataType int dataType; + @DataType public final int dataType; public final long startOffset; public final int size; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java index adf6c8db59..2259bbae86 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java @@ -69,7 +69,7 @@ public final class Track { * One of {@code TRANSFORMATION_*}. Defines the transformation to apply before outputting each * sample. */ - public final @Transformation int sampleTransformation; + @Transformation public final int sampleTransformation; /** Durations of edit list segments in the movie timescale. Null if there is no edit list. */ @Nullable public final long[] editListDurations; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java index 6ef108cd30..8791a4fe5c 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java @@ -79,7 +79,8 @@ public final class TrackEncryptionBox { schemeToCryptoMode(schemeType), keyId, defaultEncryptedBlocks, defaultClearBlocks); } - private static @C.CryptoMode int schemeToCryptoMode(@Nullable String schemeType) { + @C.CryptoMode + private static int schemeToCryptoMode(@Nullable String schemeType) { if (schemeType == null) { // If unknown, assume cenc. return C.CRYPTO_MODE_AES_CTR; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java index df43d16ae5..46720375d1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java @@ -87,7 +87,7 @@ public class SubtitleExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; private @MonotonicNonNull TrackOutput trackOutput; private int bytesRead; - private @State int state; + @State private int state; private long seekTimeUs; /** diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java index 3871ac6676..5c24fd14f0 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java @@ -409,7 +409,8 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - private static @Cue.AnchorType int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { + @Cue.AnchorType + private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: @@ -431,7 +432,8 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - private static @Cue.AnchorType int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { + @Cue.AnchorType + private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java index 08461769f7..7bfd5785a5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java @@ -93,7 +93,7 @@ import java.util.regex.Pattern; public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; public final String name; - public final @SsaAlignment int alignment; + @SsaAlignment public final int alignment; @Nullable @ColorInt public final Integer primaryColor; public final float fontSize; public final boolean bold; @@ -158,7 +158,8 @@ import java.util.regex.Pattern; } } - private static @SsaAlignment int parseAlignment(String alignmentStr) { + @SsaAlignment + private static int parseAlignment(String alignmentStr) { try { @SsaAlignment int alignment = Integer.parseInt(alignmentStr.trim()); if (isValidAlignment(alignment)) { @@ -372,7 +373,7 @@ import java.util.regex.Pattern; /** Matches "\anx" and returns x in group 1 */ private static final Pattern ALIGNMENT_OVERRIDE_PATTERN = Pattern.compile("\\\\an(\\d+)"); - public final @SsaAlignment int alignment; + @SsaAlignment public final int alignment; @Nullable public final PointF position; private Overrides(@SsaAlignment int alignment, @Nullable PointF position) { @@ -450,7 +451,8 @@ import java.util.regex.Pattern; Float.parseFloat(Assertions.checkNotNull(y).trim())); } - private static @SsaAlignment int parseAlignmentOverride(String braceContents) { + @SsaAlignment + private static int parseAlignmentOverride(String braceContents) { Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents); return matcher.find() ? parseAlignment(Assertions.checkNotNull(matcher.group(1))) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java index 42d34effc5..a9e11e3642 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java @@ -97,13 +97,13 @@ import java.util.regex.Pattern; TtmlNode.ANNOTATION_POSITION_OUTSIDE); /** The text emphasis mark shape. */ - public final @MarkShape int markShape; + @MarkShape public final int markShape; /** The fill style of the text emphasis mark. */ - public final @TextEmphasisSpan.MarkFill int markFill; + @TextEmphasisSpan.MarkFill public final int markFill; /** The position of the text emphasis relative to the base text. */ - public final @Position int position; + @Position public final int position; private TextEmphasis( @MarkShape int markShape, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java index 1bf821c72e..fe75015684 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java @@ -23,13 +23,13 @@ import androidx.media3.common.text.Cue; public final String id; public final float position; public final float line; - public final @Cue.LineType int lineType; - public final @Cue.AnchorType int lineAnchor; + @Cue.LineType public final int lineType; + @Cue.AnchorType public final int lineAnchor; public final float width; public final float height; - public final @Cue.TextSizeType int textSizeType; + @Cue.TextSizeType public final int textSizeType; public final float textSize; - public final @Cue.VerticalType int verticalType; + @Cue.VerticalType public final int verticalType; public TtmlRegion(String id) { this( diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java index b706154a0f..a2b71b0463 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java @@ -81,18 +81,18 @@ import java.lang.annotation.Target; private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - private @OptionalBoolean int linethrough; - private @OptionalBoolean int underline; - private @OptionalBoolean int bold; - private @OptionalBoolean int italic; - private @FontSizeUnit int fontSizeUnit; + @OptionalBoolean private int linethrough; + @OptionalBoolean private int underline; + @OptionalBoolean private int bold; + @OptionalBoolean private int italic; + @FontSizeUnit private int fontSizeUnit; private float fontSize; @Nullable private String id; - private @RubyType int rubyType; - private @TextAnnotation.Position int rubyPosition; + @RubyType private int rubyType; + @TextAnnotation.Position private int rubyPosition; @Nullable private Layout.Alignment textAlign; @Nullable private Layout.Alignment multiRowAlign; - private @OptionalBoolean int textCombine; + @OptionalBoolean private int textCombine; @Nullable private TextEmphasis textEmphasis; private float shearPercentage; @@ -114,7 +114,8 @@ import java.lang.annotation.Target; * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - public @StyleFlags int getStyle() { + @StyleFlags + public int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -291,7 +292,8 @@ import java.lang.annotation.Target; return this; } - public @RubyType int getRubyType() { + @RubyType + public int getRubyType() { return rubyType; } @@ -300,7 +302,8 @@ import java.lang.annotation.Target; return this; } - public @TextAnnotation.Position int getRubyPosition() { + @TextAnnotation.Position + public int getRubyPosition() { return rubyPosition; } @@ -354,7 +357,8 @@ import java.lang.annotation.Target; return this; } - public @FontSizeUnit int getFontSizeUnit() { + @FontSizeUnit + public int getFontSizeUnit() { return fontSizeUnit; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 77713c1672..29753e1d4f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -97,13 +97,13 @@ public final class WebvttCssStyle { private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - private @OptionalBoolean int linethrough; - private @OptionalBoolean int underline; - private @OptionalBoolean int bold; - private @OptionalBoolean int italic; - private @FontSizeUnit int fontSizeUnit; + @OptionalBoolean private int linethrough; + @OptionalBoolean private int underline; + @OptionalBoolean private int bold; + @OptionalBoolean private int italic; + @FontSizeUnit private int fontSizeUnit; private float fontSize; - private @TextAnnotation.Position int rubyPosition; + @TextAnnotation.Position private int rubyPosition; private boolean combineUpright; public WebvttCssStyle() { @@ -186,7 +186,8 @@ public final class WebvttCssStyle { * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - public @StyleFlags int getStyle() { + @StyleFlags + public int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -275,7 +276,8 @@ public final class WebvttCssStyle { return this; } - public @FontSizeUnit int getFontSizeUnit() { + @FontSizeUnit + public int getFontSizeUnit() { return fontSizeUnit; } @@ -288,7 +290,8 @@ public final class WebvttCssStyle { return this; } - public @TextAnnotation.Position int getRubyPosition() { + @TextAnnotation.Position + public int getRubyPosition() { return rubyPosition; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java index 456dd61680..a80d381502 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java @@ -406,7 +406,8 @@ public final class WebvttCueParser { } } - private static @Cue.AnchorType int parseLineAnchor(String s) { + @Cue.AnchorType + private static int parseLineAnchor(String s) { switch (s) { case "start": return Cue.ANCHOR_TYPE_START; @@ -430,7 +431,8 @@ public final class WebvttCueParser { builder.position = WebvttParserUtil.parsePercentage(s); } - private static @Cue.AnchorType int parsePositionAnchor(String s) { + @Cue.AnchorType + private static int parsePositionAnchor(String s) { switch (s) { case "line-left": case "start": @@ -447,7 +449,8 @@ public final class WebvttCueParser { } } - private static @Cue.VerticalType int parseVerticalAttribute(String s) { + @Cue.VerticalType + private static int parseVerticalAttribute(String s) { switch (s) { case "rl": return Cue.VERTICAL_TYPE_RL; @@ -459,7 +462,8 @@ public final class WebvttCueParser { } } - private static @TextAlignment int parseTextAlignment(String s) { + @TextAlignment + private static int parseTextAlignment(String s) { switch (s) { case "start": return TEXT_ALIGNMENT_START; @@ -607,7 +611,8 @@ public final class WebvttCueParser { } } - private static @TextAnnotation.Position int getRubyPosition( + @TextAnnotation.Position + private static int getRubyPosition( List styles, @Nullable String cueId, StartTag startTag) { List styleMatches = getApplicableStyles(styles, cueId, startTag); for (int i = 0; i < styleMatches.size(); i++) { @@ -619,7 +624,8 @@ public final class WebvttCueParser { return TextAnnotation.POSITION_UNKNOWN; } - private static @TextAnnotation.Position int firstKnownRubyPosition( + @TextAnnotation.Position + private static int firstKnownRubyPosition( @TextAnnotation.Position int position1, @TextAnnotation.Position int position2, @TextAnnotation.Position int position3) { @@ -764,16 +770,16 @@ public final class WebvttCueParser { public long startTimeUs; public long endTimeUs; public @MonotonicNonNull CharSequence text; - public @TextAlignment int textAlignment; + @TextAlignment public int textAlignment; public float line; // Equivalent to WebVTT's snap-to-lines flag: // https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag - public @Cue.LineType int lineType; - public @Cue.AnchorType int lineAnchor; + @Cue.LineType public int lineType; + @Cue.AnchorType public int lineAnchor; public float position; - public @Cue.AnchorType int positionAnchor; + @Cue.AnchorType public int positionAnchor; public float size; - public @Cue.VerticalType int verticalType; + @Cue.VerticalType public int verticalType; public WebvttCueInfoBuilder() { startTimeUs = 0; @@ -855,7 +861,8 @@ public final class WebvttCueParser { } // https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment - private static @Cue.AnchorType int derivePositionAnchor(@TextAlignment int textAlignment) { + @Cue.AnchorType + private static int derivePositionAnchor(@TextAlignment int textAlignment) { switch (textAlignment) { case TEXT_ALIGNMENT_LEFT: case TEXT_ALIGNMENT_START: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java index 1d80fbca08..527f32d965 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java @@ -62,7 +62,7 @@ public final class Ac3Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - private @State int state; + @State private int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java index 95440cec23..238b861ad7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java @@ -60,7 +60,7 @@ public final class Ac4Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - private @State int state; + @State private int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java index 11fa10f074..105059098f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -104,7 +104,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact private static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86; - private final @Flags int flags; + @Flags private final int flags; private final List closedCaptionFormats; public DefaultTsPayloadReaderFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index 11c81fef99..efa84e123f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -335,7 +335,7 @@ public final class H263Reader implements ElementaryStreamReader { private static final int STATE_WAIT_FOR_VOP_START = 4; private boolean isFilling; - private @State int state; + @State private int state; public int length; public int volStartPosition; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java index ea033b979b..aa2a36b02b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java @@ -118,7 +118,7 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_SIZE = TS_PACKET_SIZE * 50; private static final int SNIFF_TS_PACKET_COUNT = 5; - private final @Mode int mode; + @Mode private final int mode; private final int timestampSearchBytes; private final List timestampAdjusters; private final ParsableByteArray tsPacketBuffer; @@ -298,8 +298,8 @@ public final class TsExtractor implements Extractor { } @Override - public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) - throws IOException { + @ReadResult + public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { long inputLength = input.getLength(); if (tracksEnded) { boolean canReadDuration = inputLength != C.LENGTH_UNSET && mode != MODE_HLS; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java index ca35a92b43..76461fa7cf 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java @@ -122,8 +122,8 @@ public final class WavExtractor implements Extractor { } @Override - public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) - throws IOException { + @ReadResult + public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { assertInitialized(); switch (state) { case STATE_READING_FILE_TYPE: @@ -227,7 +227,8 @@ public final class WavExtractor implements Extractor { state = STATE_READING_SAMPLE_DATA; } - private @ReadResult int readSampleData(ExtractorInput input) throws IOException { + @ReadResult + private int readSampleData(ExtractorInput input) throws IOException { Assertions.checkState(dataEndPosition != C.POSITION_UNSET); long bytesLeft = dataEndPosition - input.getPosition(); return Assertions.checkNotNull(outputWriter).sampleData(input, bytesLeft) diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java index 7fe5bb9284..6597835a12 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java @@ -178,7 +178,8 @@ public class DefaultEbmlReaderTest { private final List events = new ArrayList<>(); @Override - public @EbmlProcessor.ElementType int getElementType(int id) { + @EbmlProcessor.ElementType + public int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java index 3837b1260a..11c65d7d1c 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java @@ -42,7 +42,7 @@ public final class TtmlStyleTest { private static final String FONT_FAMILY = "serif"; @ColorInt private static final int FONT_COLOR = Color.WHITE; private static final float FONT_SIZE = 12.5f; - private static final @TtmlStyle.FontSizeUnit int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; + @TtmlStyle.FontSizeUnit private static final int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; @ColorInt private static final int BACKGROUND_COLOR = Color.BLACK; private static final int RUBY_TYPE = TtmlStyle.RUBY_TYPE_TEXT; private static final int RUBY_POSITION = TextAnnotation.POSITION_AFTER; diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java index 416365e414..15d3566214 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java @@ -82,7 +82,7 @@ public class MockPlayer implements Player { public int newIndex; public int currentPeriodIndex; public int currentMediaItemIndex; - public @RepeatMode int repeatMode; + @RepeatMode public int repeatMode; public boolean shuffleModeEnabled; public VideoSize videoSize; @Nullable public Surface surface; @@ -95,8 +95,8 @@ public class MockPlayer implements Player { public int deviceVolume; public boolean deviceMuted; public boolean playWhenReady; - public @PlaybackSuppressionReason int playbackSuppressionReason; - public @State int playbackState; + @PlaybackSuppressionReason public int playbackSuppressionReason; + @State public int playbackState; public boolean isPlaying; public boolean isLoading; public MediaMetadata mediaMetadata; @@ -591,12 +591,14 @@ public class MockPlayer implements Player { } @Override - public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { return playbackSuppressionReason; } @Override - public @State int getPlaybackState() { + @State + public int getPlaybackState() { return playbackState; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java index 6aaf678a37..edf7665e65 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java @@ -495,7 +495,7 @@ public abstract class Action { /** Calls {@link Player#setRepeatMode(int)}. */ public static final class SetRepeatMode extends Action { - private final @Player.RepeatMode int repeatMode; + @Player.RepeatMode private final int repeatMode; /** * @param tag A tag to use for logging. @@ -742,7 +742,7 @@ public abstract class Action { @Nullable private final Timeline expectedTimeline; private final boolean ignoreExpectedReason; - private final @Player.TimelineChangeReason int expectedReason; + @Player.TimelineChangeReason private final int expectedReason; /** * Creates action waiting for a timeline change for a given reason. @@ -909,7 +909,7 @@ public abstract class Action { */ public static final class WaitForPlaybackState extends Action { - private final @Player.State int targetPlaybackState; + @Player.State private final int targetPlaybackState; /** * @param tag A tag to use for logging. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java index c10e0fa685..4da0ab6ab2 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java @@ -93,7 +93,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum private static final class DumpableConfiguration implements Dumper.Dumpable { - private final @C.PcmEncoding int inputPcmEncoding; + @C.PcmEncoding private final int inputPcmEncoding; private final int inputChannelCount; private final int inputSampleRate; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java index fa9c146a8e..743c6b1716 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java @@ -47,7 +47,7 @@ public final class DownloadBuilder { @Nullable private String cacheKey; private byte[] customMetadata; - private @Download.State int state; + @Download.State private int state; private long startTimeMs; private long updateTimeMs; private long contentLength; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java index 7059c004ae..7cab3c2d8a 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java @@ -72,7 +72,7 @@ public class DumpFileAsserts { /** Write output to folder {@code /storage/emulated/0/Android/data} of device. */ private static final int WRITE_TO_DEVICE = 1 << 1; - private static final @DumpFilesAction int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; + @DumpFilesAction private static final int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; private DumpFileAsserts() {} diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java index fca4296d6d..314726f85e 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java @@ -398,7 +398,8 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { } @Override - public @C.CryptoType int getCryptoType() { + @C.CryptoType + public int getCryptoType() { return FakeCryptoConfig.TYPE; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java index e4c99ffa64..9c95fbaa25 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java @@ -162,7 +162,8 @@ public class FakeRenderer extends BaseRenderer { } @Override - public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { + @Capabilities + public int supportsFormat(Format format) throws ExoPlaybackException { int trackType = MimeTypes.getTrackType(format.sampleMimeType); return trackType != C.TRACK_TYPE_UNKNOWN && trackType == getTrackType() ? RendererCapabilities.create(C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java index d779b21109..d072791b45 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java @@ -295,7 +295,7 @@ public class FakeSampleStream implements SampleStream { private static class SampleInfo { public final byte[] data; - public final @C.BufferFlags int flags; + @C.BufferFlags public final int flags; public final long timeUs; public SampleInfo(byte[] data, @C.BufferFlags int flags, long timeUs) { diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java index b3373e1f68..ce2411f93a 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java @@ -62,12 +62,14 @@ public class StubPlayer extends BasePlayer { } @Override - public @State int getPlaybackState() { + @State + public int getPlaybackState() { throw new UnsupportedOperationException(); } @Override - public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { + @PlaybackSuppressionReason + public int getPlaybackSuppressionReason() { throw new UnsupportedOperationException(); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java index 63b20dfc7c..d252878fee 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java @@ -99,7 +99,7 @@ public class WebServerDispatcher extends Dispatcher { private byte @MonotonicNonNull [] data; private boolean supportsRangeRequests; private boolean resolvesToUnknownLength; - private @GzipSupport int gzipSupport; + @GzipSupport private int gzipSupport; /** Constructs an instance. */ public Builder() { @@ -187,7 +187,7 @@ public class WebServerDispatcher extends Dispatcher { private final byte[] data; private final boolean supportsRangeRequests; private final boolean resolvesToUnknownLength; - private final @GzipSupport int gzipSupport; + @GzipSupport private final int gzipSupport; private Resource( String path, @@ -223,7 +223,8 @@ public class WebServerDispatcher extends Dispatcher { } /** Returns the level of gzip support the server should provide for this resource. */ - public @GzipSupport int getGzipSupport() { + @GzipSupport + public int getGzipSupport() { return gzipSupport; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java index 9a30b04202..d5f16d72f2 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java @@ -1115,7 +1115,7 @@ public final class SpannedSubject extends Subject { private static final class TextAndPosition { private final String text; - private final @TextAnnotation.Position int position; + @TextAnnotation.Position private final int position; private TextAndPosition(String text, int position) { this.text = text; @@ -1212,9 +1212,9 @@ public final class SpannedSubject extends Subject { private static final class MarkAndPosition { - private final @TextEmphasisSpan.MarkShape int markShape; - private final @TextEmphasisSpan.MarkFill int markFill; - private final @TextAnnotation.Position int position; + @TextEmphasisSpan.MarkShape private final int markShape; + @TextEmphasisSpan.MarkFill private final int markFill; + @TextAnnotation.Position private final int position; private MarkAndPosition( @TextEmphasisSpan.MarkShape int markShape, diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java index ff80c45666..1a79143a32 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java @@ -44,7 +44,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen private final ConditionVariable initializedCondition; private final ConditionVariable idleCondition; - private @Download.FailureReason int failureReason; + @Download.FailureReason private int failureReason; public TestDownloadManagerListener(DownloadManager downloadManager) { this.downloadManager = downloadManager; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 3b88fff41d..45037656a9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -552,7 +552,7 @@ public final class Transformer { @Nullable private MuxerWrapper muxerWrapper; @Nullable private ExoPlayer player; - private @ProgressState int progressState; + @ProgressState private int progressState; private Transformer( Context context, @@ -754,7 +754,8 @@ public final class Transformer { * @return The {@link ProgressState}. * @throws IllegalStateException If this method is called from the wrong thread. */ - public @ProgressState int getProgress(ProgressHolder progressHolder) { + @ProgressState + public int getProgress(ProgressHolder progressHolder) { verifyApplicationThread(); if (progressState == PROGRESS_STATE_AVAILABLE) { Player player = checkNotNull(this.player); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index d6e6c8b645..94b398dcc1 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -67,7 +67,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @return The {@link Capabilities} for this format. */ @Override - public final @Capabilities int supportsFormat(Format format) { + @Capabilities + public final int supportsFormat(Format format) { return RendererCapabilities.create( MimeTypes.getTrackType(format.sampleMimeType) == getTrackType() ? C.FORMAT_HANDLED From c2cb22a05687d36d842c7049753a7c8ab34d5053 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 21:14:14 +0000 Subject: [PATCH 161/251] Rollback of https://github.com/androidx/media/commit/1521e50307bb74983ecef1fc2ddf5f996f27468b *** Original commit *** Wire up MediaMetricsListener and add configuration to disable it. The listener will automatically forward diagnostics info to the Android platform. ExoPlayer.Builder gets a new setter that allows to disable this feature if required. #minor-release *** PiperOrigin-RevId: 426997342 --- .../androidx/media3/exoplayer/ExoPlayer.java | 25 ------------------- .../media3/exoplayer/ExoPlayerImpl.java | 21 +++------------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index ab755cbc26..7269e6c6c5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -400,7 +400,6 @@ public interface ExoPlayer extends Player { /* package */ long releaseTimeoutMs; /* package */ long detachSurfaceTimeoutMs; /* package */ boolean pauseAtEndOfMediaItems; - /* package */ boolean usePlatformDiagnostics; /* package */ boolean buildCalled; /** @@ -441,7 +440,6 @@ public interface ExoPlayer extends Player { *

  • {@code releaseTimeoutMs}: {@link #DEFAULT_RELEASE_TIMEOUT_MS} *
  • {@code detachSurfaceTimeoutMs}: {@link #DEFAULT_DETACH_SURFACE_TIMEOUT_MS} *
  • {@code pauseAtEndOfMediaItems}: {@code false} - *
  • {@code usePlatformDiagnostics}: {@code true} *
  • {@link Clock}: {@link Clock#DEFAULT} * * @@ -594,7 +592,6 @@ public interface ExoPlayer extends Player { clock = Clock.DEFAULT; releaseTimeoutMs = DEFAULT_RELEASE_TIMEOUT_MS; detachSurfaceTimeoutMs = DEFAULT_DETACH_SURFACE_TIMEOUT_MS; - usePlatformDiagnostics = true; } /** @@ -974,28 +971,6 @@ public interface ExoPlayer extends Player { return this; } - /** - * Sets whether the player reports diagnostics data to the Android platform. - * - *

    If enabled, the player will use the {@link android.media.metrics.MediaMetricsManager} to - * create a {@link android.media.metrics.PlaybackSession} and forward playback events and - * performance data to this session. This helps to provide system performance and debugging - * information for media playback on the device. This data may also be collected by Google if sharing usage and diagnostics - * data is enabled by the user of the device. - * - * @param usePlatformDiagnostics Whether the player reports diagnostics data to the Android - * platform. - * @return This builder. - * @throws IllegalStateException If {@link #build()} has already been called. - */ - @UnstableApi - public Builder setUsePlatformDiagnostics(boolean usePlatformDiagnostics) { - checkState(!buildCalled); - this.usePlatformDiagnostics = usePlatformDiagnostics; - return this; - } - /** * Sets the {@link Clock} that will be used by the player. Should only be set for testing * purposes. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index e076dad419..6cfdc10e35 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -139,7 +139,6 @@ import androidx.media3.exoplayer.PlayerMessage.Target; import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; -import androidx.media3.exoplayer.analytics.MediaMetricsListener; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.audio.AudioRendererEventListener; import androidx.media3.exoplayer.metadata.MetadataOutput; @@ -372,11 +371,7 @@ import java.util.concurrent.TimeoutException; playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); - PlayerId playerId = - Util.SDK_INT < 31 - ? new PlayerId() - : Api31.registerMediaMetricsListener( - applicationContext, /* player= */ this, builder.usePlatformDiagnostics); + PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); internalPlayer = new ExoPlayerImplInternal( renderers, @@ -3062,17 +3057,9 @@ import java.util.concurrent.TimeoutException; private Api31() {} @DoNotInline - public static PlayerId registerMediaMetricsListener( - Context context, ExoPlayerImpl player, boolean usePlatformDiagnostics) { - @Nullable MediaMetricsListener listener = MediaMetricsListener.create(context); - if (listener == null) { - Log.w(TAG, "MediaMetricsService unavailable."); - return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); - } - if (usePlatformDiagnostics) { - player.addAnalyticsListener(listener); - } - return new PlayerId(listener.getLogSessionId()); + public static PlayerId createPlayerId() { + // TODO: Create a MediaMetricsListener and obtain LogSessionId from it. + return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); } } } From fab5dfa156d96c7b6ad21e7b4801de188de58dc5 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 21:15:51 +0000 Subject: [PATCH 162/251] Rollback of https://github.com/androidx/media/commit/d93b0093aeaa860011ad447710a15dc800df7c95 *** Original commit *** Move SimpleExoPlayer logic into ExoPlayerImpl This makes SimpleExoPlayer a simple forwarding wrapper which can be removed in the future. The changes are all purely mechanical with none of the potential further simplifications made yet. The only exceptions are name clashes where either EPI or SEP was calling a method in one of the classes and both classes had different implementations for the same method name. In these cases we needed to disambiguate between the two different implementations (e *** PiperOrigin-RevId: 426997821 --- .../media3/exoplayer/ExoPlayerImpl.java | 1657 ++--------------- .../media3/exoplayer/SimpleExoPlayer.java | 1258 ++++++++++++- 2 files changed, 1379 insertions(+), 1536 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 6cfdc10e35..823f3ffd1a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -15,31 +15,20 @@ */ package androidx.media3.exoplayer; -import static androidx.media3.common.C.TRACK_TYPE_AUDIO; -import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION; -import static androidx.media3.common.C.TRACK_TYPE_VIDEO; -import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; -import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; -import static androidx.media3.common.Player.COMMAND_GET_TEXT; import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; import static androidx.media3.common.Player.COMMAND_GET_TRACK_INFOS; -import static androidx.media3.common.Player.COMMAND_GET_VOLUME; import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; import static androidx.media3.common.Player.COMMAND_PREPARE; import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA; import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; import static androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS; -import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; -import static androidx.media3.common.Player.COMMAND_SET_VOLUME; import static androidx.media3.common.Player.COMMAND_STOP; import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; @@ -53,7 +42,6 @@ import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIS import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_SEEK; import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; -import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS; import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; import static androidx.media3.common.Player.STATE_BUFFERING; import static androidx.media3.common.Player.STATE_ENDED; @@ -63,41 +51,18 @@ import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.castNonNull; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_ATTRIBUTES; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_SESSION_ID; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUX_EFFECT_INFO; -import static androidx.media3.exoplayer.Renderer.MSG_SET_CAMERA_MOTION_LISTENER; -import static androidx.media3.exoplayer.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY; -import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE; -import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME; import static java.lang.Math.max; import static java.lang.Math.min; import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.media.AudioFormat; -import android.media.AudioTrack; -import android.media.MediaFormat; import android.media.metrics.LogSessionId; import android.os.Handler; import android.os.Looper; import android.util.Pair; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.media3.common.AudioAttributes; -import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.C; -import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.IllegalSeekPositionException; import androidx.media3.common.MediaItem; @@ -118,49 +83,34 @@ import androidx.media3.common.Player.PositionInfo; import androidx.media3.common.Player.RepeatMode; import androidx.media3.common.Player.State; import androidx.media3.common.Player.TimelineChangeReason; -import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelectionArray; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; -import androidx.media3.common.VideoSize; -import androidx.media3.common.text.Cue; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Clock; -import androidx.media3.common.util.ConditionVariable; import androidx.media3.common.util.HandlerWrapper; import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.ExoPlayer.AudioOffloadListener; import androidx.media3.exoplayer.PlayerMessage.Target; -import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; -import androidx.media3.exoplayer.analytics.AnalyticsListener; import androidx.media3.exoplayer.analytics.PlayerId; -import androidx.media3.exoplayer.audio.AudioRendererEventListener; -import androidx.media3.exoplayer.metadata.MetadataOutput; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.ShuffleOrder; -import androidx.media3.exoplayer.text.TextOutput; import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.trackselection.TrackSelector; import androidx.media3.exoplayer.trackselection.TrackSelectorResult; import androidx.media3.exoplayer.upstream.BandwidthMeter; -import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; -import androidx.media3.exoplayer.video.VideoFrameMetadataListener; -import androidx.media3.exoplayer.video.VideoRendererEventListener; -import androidx.media3.exoplayer.video.spherical.CameraMotionListener; -import androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeoutException; /** A helper class for the {@link SimpleExoPlayer} implementation of {@link ExoPlayer}. */ /* package */ final class ExoPlayerImpl { @@ -181,18 +131,13 @@ import java.util.concurrent.TimeoutException; /* package */ final TrackSelectorResult emptyTrackSelectorResult; /* package */ final Commands permanentAvailableCommands; - private final ConditionVariable constructorFinished; - private final Context applicationContext; private final Player wrappingPlayer; private final Renderer[] renderers; private final TrackSelector trackSelector; private final HandlerWrapper playbackInfoUpdateHandler; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; - - @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners. - private final ListenerSet eventListeners; - + private final ListenerSet listeners; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -205,15 +150,6 @@ import java.util.concurrent.TimeoutException; private final long seekBackIncrementMs; private final long seekForwardIncrementMs; private final Clock clock; - private final ComponentListener componentListener; - private final FrameMetadataListener frameMetadataListener; - private final CopyOnWriteArraySet listeners; - private final AudioBecomingNoisyManager audioBecomingNoisyManager; - private final AudioFocusManager audioFocusManager; - private final StreamVolumeManager streamVolumeManager; - private final WakeLockManager wakeLockManager; - private final WifiLockManager wifiLockManager; - private final long detachSurfaceTimeoutMs; @RepeatMode private int repeatMode; private boolean shuffleModeEnabled; @@ -228,35 +164,6 @@ import java.util.concurrent.TimeoutException; private Commands availableCommands; private MediaMetadata mediaMetadata; private MediaMetadata playlistMetadata; - @Nullable private Format videoFormat; - @Nullable private Format audioFormat; - @Nullable private AudioTrack keepSessionIdAudioTrack; - @Nullable private Object videoOutput; - @Nullable private Surface ownedSurface; - @Nullable private SurfaceHolder surfaceHolder; - @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; - private boolean surfaceHolderSurfaceIsVideoOutput; - @Nullable private TextureView textureView; - @C.VideoScalingMode private int videoScalingMode; - @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; - private int surfaceWidth; - private int surfaceHeight; - @Nullable private DecoderCounters videoDecoderCounters; - @Nullable private DecoderCounters audioDecoderCounters; - private int audioSessionId; - private AudioAttributes audioAttributes; - private float volume; - private boolean skipSilenceEnabled; - private List currentCues; - @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; - @Nullable private CameraMotionListener cameraMotionListener; - private boolean throwsWhenUsingWrongThread; - private boolean hasNotifiedFullWrongThreadWarning; - @Nullable private PriorityTaskManager priorityTaskManager; - private boolean isPriorityTaskManagerRegistered; - private boolean playerReleased; - private DeviceInfo deviceInfo; - private VideoSize videoSize; // MediaMetadata built from static (TrackGroup Format) and dynamic (onMetadata(Metadata)) metadata // sources. @@ -270,178 +177,145 @@ import java.util.concurrent.TimeoutException; private int maskingPeriodIndex; private long maskingWindowPositionMs; + /** + * Constructs an instance. Must be called from a thread that has an associated {@link Looper}. + * + * @param renderers The {@link Renderer}s. + * @param trackSelector The {@link TrackSelector}. + * @param mediaSourceFactory The {@link MediaSource.Factory}. + * @param loadControl The {@link LoadControl}. + * @param bandwidthMeter The {@link BandwidthMeter}. + * @param analyticsCollector The {@link AnalyticsCollector}. + * @param useLazyPreparation Whether playlist items are prepared lazily. If false, all manifest + * loads and other initial preparation steps happen immediately. If true, these initial + * preparations are triggered only when the player starts buffering the media. + * @param seekParameters The {@link SeekParameters}. + * @param seekBackIncrementMs The seek back increment in milliseconds. + * @param seekForwardIncrementMs The seek forward increment in milliseconds. + * @param livePlaybackSpeedControl The {@link LivePlaybackSpeedControl}. + * @param releaseTimeoutMs The timeout for calls to {@link #release()} in milliseconds. + * @param pauseAtEndOfMediaItems Whether to pause playback at the end of each media item. + * @param clock The {@link Clock}. + * @param applicationLooper The {@link Looper} that must be used for all calls to the player and + * which is used to call listeners on. + * @param wrappingPlayer The {@link Player} using this class. + * @param additionalPermanentAvailableCommands The {@link Commands} that are permanently available + * in the wrapping player but that are not in this player. + */ @SuppressLint("HandlerLeak") - public ExoPlayerImpl(ExoPlayer.Builder builder, Player wrappingPlayer) { - constructorFinished = new ConditionVariable(); - try { - Log.i( - TAG, - "Init " - + Integer.toHexString(System.identityHashCode(this)) - + " [" - + MediaLibraryInfo.VERSION_SLASHY - + "] [" - + Util.DEVICE_DEBUG_INFO - + "]"); - applicationContext = builder.context.getApplicationContext(); - analyticsCollector = builder.analyticsCollectorSupplier.get(); - priorityTaskManager = builder.priorityTaskManager; - audioAttributes = builder.audioAttributes; - videoScalingMode = builder.videoScalingMode; - videoChangeFrameRateStrategy = builder.videoChangeFrameRateStrategy; - skipSilenceEnabled = builder.skipSilenceEnabled; - detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; - componentListener = new ComponentListener(); - frameMetadataListener = new FrameMetadataListener(); - listeners = new CopyOnWriteArraySet<>(); - Handler eventHandler = new Handler(builder.looper); - renderers = - builder - .renderersFactorySupplier - .get() - .createRenderers( - eventHandler, - componentListener, - componentListener, - componentListener, - componentListener); - checkState(renderers.length > 0); - this.trackSelector = builder.trackSelectorSupplier.get(); - this.mediaSourceFactory = builder.mediaSourceFactorySupplier.get(); - this.bandwidthMeter = builder.bandwidthMeterSupplier.get(); - this.useLazyPreparation = builder.useLazyPreparation; - this.seekParameters = builder.seekParameters; - this.seekBackIncrementMs = builder.seekBackIncrementMs; - this.seekForwardIncrementMs = builder.seekForwardIncrementMs; - this.pauseAtEndOfMediaItems = builder.pauseAtEndOfMediaItems; - this.applicationLooper = builder.looper; - this.clock = builder.clock; - this.wrappingPlayer = wrappingPlayer; - eventListeners = - new ListenerSet<>( - applicationLooper, - clock, - (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); - audioOffloadListeners = new CopyOnWriteArraySet<>(); - mediaSourceHolderSnapshots = new ArrayList<>(); - shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); - emptyTrackSelectorResult = - new TrackSelectorResult( - new RendererConfiguration[renderers.length], - new ExoTrackSelection[renderers.length], - TracksInfo.EMPTY, - /* info= */ null); - period = new Timeline.Period(); - window = new Timeline.Window(); - permanentAvailableCommands = - new Commands.Builder() - .addAll( - COMMAND_PLAY_PAUSE, - COMMAND_PREPARE, - COMMAND_STOP, - COMMAND_SET_SPEED_AND_PITCH, - COMMAND_SET_SHUFFLE_MODE, - COMMAND_SET_REPEAT_MODE, - COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_GET_TIMELINE, - COMMAND_GET_MEDIA_ITEMS_METADATA, - COMMAND_SET_MEDIA_ITEMS_METADATA, - COMMAND_CHANGE_MEDIA_ITEMS, - COMMAND_GET_TRACK_INFOS, - COMMAND_GET_AUDIO_ATTRIBUTES, - COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, - COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_SET_VIDEO_SURFACE, - COMMAND_GET_TEXT) - .addIf( - COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) - .build(); - availableCommands = - new Commands.Builder() - .addAll(permanentAvailableCommands) - .add(COMMAND_SEEK_TO_DEFAULT_POSITION) - .add(COMMAND_SEEK_TO_MEDIA_ITEM) - .build(); - playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); - playbackInfoUpdateListener = - playbackInfoUpdate -> - playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); - playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); - analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); - PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); - internalPlayer = - new ExoPlayerImplInternal( - renderers, - trackSelector, - emptyTrackSelectorResult, - builder.loadControlSupplier.get(), - bandwidthMeter, - repeatMode, - shuffleModeEnabled, - analyticsCollector, - seekParameters, - builder.livePlaybackSpeedControl, - builder.releaseTimeoutMs, - pauseAtEndOfMediaItems, - applicationLooper, - clock, - playbackInfoUpdateListener, - playerId); - - volume = 1; - repeatMode = Player.REPEAT_MODE_OFF; - mediaMetadata = MediaMetadata.EMPTY; - playlistMetadata = MediaMetadata.EMPTY; - staticAndDynamicMediaMetadata = MediaMetadata.EMPTY; - maskingWindowIndex = C.INDEX_UNSET; - if (Util.SDK_INT < 21) { - audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); - } else { - audioSessionId = Util.generateAudioSessionIdV21(applicationContext); - } - currentCues = ImmutableList.of(); - throwsWhenUsingWrongThread = true; - - addEventListener(analyticsCollector); - bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - addEventListener(componentListener); - addAudioOffloadListener(componentListener); - if (builder.foregroundModeTimeoutMs > 0) { - experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); - } - - audioBecomingNoisyManager = - new AudioBecomingNoisyManager(builder.context, eventHandler, componentListener); - audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); - audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); - audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); - streamVolumeManager = - new StreamVolumeManager(builder.context, eventHandler, componentListener); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - wakeLockManager = new WakeLockManager(builder.context); - wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); - wifiLockManager = new WifiLockManager(builder.context); - wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); - deviceInfo = createDeviceInfo(streamVolumeManager); - videoSize = VideoSize.UNKNOWN; - - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_VIDEO_FRAME_METADATA_LISTENER, frameMetadataListener); - sendRendererMessage( - TRACK_TYPE_CAMERA_MOTION, MSG_SET_CAMERA_MOTION_LISTENER, frameMetadataListener); - } finally { - constructorFinished.open(); - } + public ExoPlayerImpl( + Renderer[] renderers, + TrackSelector trackSelector, + MediaSource.Factory mediaSourceFactory, + LoadControl loadControl, + BandwidthMeter bandwidthMeter, + AnalyticsCollector analyticsCollector, + boolean useLazyPreparation, + SeekParameters seekParameters, + long seekBackIncrementMs, + long seekForwardIncrementMs, + LivePlaybackSpeedControl livePlaybackSpeedControl, + long releaseTimeoutMs, + boolean pauseAtEndOfMediaItems, + Clock clock, + Looper applicationLooper, + Player wrappingPlayer, + Commands additionalPermanentAvailableCommands) { + Log.i( + TAG, + "Init " + + Integer.toHexString(System.identityHashCode(this)) + + " [" + + MediaLibraryInfo.VERSION_SLASHY + + "] [" + + Util.DEVICE_DEBUG_INFO + + "]"); + checkState(renderers.length > 0); + this.renderers = checkNotNull(renderers); + this.trackSelector = checkNotNull(trackSelector); + this.mediaSourceFactory = mediaSourceFactory; + this.bandwidthMeter = bandwidthMeter; + this.analyticsCollector = analyticsCollector; + this.useLazyPreparation = useLazyPreparation; + this.seekParameters = seekParameters; + this.seekBackIncrementMs = seekBackIncrementMs; + this.seekForwardIncrementMs = seekForwardIncrementMs; + this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems; + this.applicationLooper = applicationLooper; + this.clock = clock; + this.wrappingPlayer = wrappingPlayer; + repeatMode = Player.REPEAT_MODE_OFF; + listeners = + new ListenerSet<>( + applicationLooper, + clock, + (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); + audioOffloadListeners = new CopyOnWriteArraySet<>(); + mediaSourceHolderSnapshots = new ArrayList<>(); + shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); + emptyTrackSelectorResult = + new TrackSelectorResult( + new RendererConfiguration[renderers.length], + new ExoTrackSelection[renderers.length], + TracksInfo.EMPTY, + /* info= */ null); + period = new Timeline.Period(); + window = new Timeline.Window(); + permanentAvailableCommands = + new Commands.Builder() + .addAll( + COMMAND_PLAY_PAUSE, + COMMAND_PREPARE, + COMMAND_STOP, + COMMAND_SET_SPEED_AND_PITCH, + COMMAND_SET_SHUFFLE_MODE, + COMMAND_SET_REPEAT_MODE, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_TIMELINE, + COMMAND_GET_MEDIA_ITEMS_METADATA, + COMMAND_SET_MEDIA_ITEMS_METADATA, + COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_TRACK_INFOS) + .addIf(COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) + .addAll(additionalPermanentAvailableCommands) + .build(); + availableCommands = + new Commands.Builder() + .addAll(permanentAvailableCommands) + .add(COMMAND_SEEK_TO_DEFAULT_POSITION) + .add(COMMAND_SEEK_TO_MEDIA_ITEM) + .build(); + mediaMetadata = MediaMetadata.EMPTY; + playlistMetadata = MediaMetadata.EMPTY; + staticAndDynamicMediaMetadata = MediaMetadata.EMPTY; + maskingWindowIndex = C.INDEX_UNSET; + playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); + playbackInfoUpdateListener = + playbackInfoUpdate -> + playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); + playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); + analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); + addListener(analyticsCollector); + bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); + PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); + internalPlayer = + new ExoPlayerImplInternal( + renderers, + trackSelector, + emptyTrackSelectorResult, + loadControl, + bandwidthMeter, + repeatMode, + shuffleModeEnabled, + analyticsCollector, + seekParameters, + livePlaybackSpeedControl, + releaseTimeoutMs, + pauseAtEndOfMediaItems, + applicationLooper, + clock, + playbackInfoUpdateListener, + playerId); } /** @@ -459,72 +333,63 @@ import java.util.concurrent.TimeoutException; } public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) { - verifyApplicationThread(); internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); } public boolean experimentalIsSleepingForOffload() { - verifyApplicationThread(); return playbackInfo.sleepingForOffload; } public Looper getPlaybackLooper() { - // Don't verify application thread. We allow calls to this method from any thread. return internalPlayer.getPlaybackLooper(); } public Looper getApplicationLooper() { - // Don't verify application thread. We allow calls to this method from any thread. return applicationLooper; } public Clock getClock() { - // Don't verify application thread. We allow calls to this method from any thread. return clock; } + public void addListener(Listener listener) { + addEventListener(listener); + } + @SuppressWarnings("deprecation") // Register deprecated EventListener. public void addEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.add(eventListener); + listeners.add(eventListener); } @SuppressWarnings("deprecation") // Deregister deprecated EventListener. public void removeEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.remove(eventListener); + listeners.remove(eventListener); } public void addAudioOffloadListener(AudioOffloadListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); } public void removeAudioOffloadListener(AudioOffloadListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.remove(listener); } public Commands getAvailableCommands() { - verifyApplicationThread(); return availableCommands; } @State public int getPlaybackState() { - verifyApplicationThread(); return playbackInfo.playbackState; } @PlaybackSuppressionReason public int getPlaybackSuppressionReason() { - verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } @Nullable public ExoPlaybackException getPlayerError() { - verifyApplicationThread(); return playbackInfo.playbackError; } @@ -535,12 +400,6 @@ import java.util.concurrent.TimeoutException; } public void prepare() { - verifyApplicationThread(); - boolean playWhenReady = getPlayWhenReady(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, Player.STATE_BUFFERING); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); if (playbackInfo.playbackState != Player.STATE_IDLE) { return; } @@ -570,7 +429,6 @@ import java.util.concurrent.TimeoutException; */ @Deprecated public void prepare(MediaSource mediaSource) { - verifyApplicationThread(); setMediaSource(mediaSource); prepare(); } @@ -581,44 +439,36 @@ import java.util.concurrent.TimeoutException; */ @Deprecated public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { - verifyApplicationThread(); setMediaSource(mediaSource, resetPosition); prepare(); } public void setMediaItems(List mediaItems, boolean resetPosition) { - verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), resetPosition); } public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { - verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), startIndex, startPositionMs); } public void setMediaSource(MediaSource mediaSource) { - verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource)); } public void setMediaSource(MediaSource mediaSource, long startPositionMs) { - verifyApplicationThread(); setMediaSources( Collections.singletonList(mediaSource), /* startWindowIndex= */ 0, startPositionMs); } public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { - verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource), resetPosition); } public void setMediaSources(List mediaSources) { - verifyApplicationThread(); setMediaSources(mediaSources, /* resetPosition= */ true); } public void setMediaSources(List mediaSources, boolean resetPosition) { - verifyApplicationThread(); setMediaSourcesInternal( mediaSources, /* startWindowIndex= */ C.INDEX_UNSET, @@ -628,34 +478,28 @@ import java.util.concurrent.TimeoutException; public void setMediaSources( List mediaSources, int startWindowIndex, long startPositionMs) { - verifyApplicationThread(); setMediaSourcesInternal( mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false); } public void addMediaItems(int index, List mediaItems) { - verifyApplicationThread(); index = min(index, mediaSourceHolderSnapshots.size()); addMediaSources(index, createMediaSources(mediaItems)); } public void addMediaSource(MediaSource mediaSource) { - verifyApplicationThread(); addMediaSources(Collections.singletonList(mediaSource)); } public void addMediaSource(int index, MediaSource mediaSource) { - verifyApplicationThread(); addMediaSources(index, Collections.singletonList(mediaSource)); } public void addMediaSources(List mediaSources) { - verifyApplicationThread(); addMediaSources(/* index= */ mediaSourceHolderSnapshots.size(), mediaSources); } public void addMediaSources(int index, List mediaSources) { - verifyApplicationThread(); Assertions.checkArgument(index >= 0); Timeline oldTimeline = getCurrentTimeline(); pendingOperationAcks++; @@ -679,7 +523,6 @@ import java.util.concurrent.TimeoutException; } public void removeMediaItems(int fromIndex, int toIndex) { - verifyApplicationThread(); toIndex = min(toIndex, mediaSourceHolderSnapshots.size()); PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); boolean positionDiscontinuity = @@ -696,7 +539,6 @@ import java.util.concurrent.TimeoutException; } public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) { - verifyApplicationThread(); Assertions.checkArgument( fromIndex >= 0 && fromIndex <= toIndex @@ -725,7 +567,6 @@ import java.util.concurrent.TimeoutException; } public void setShuffleOrder(ShuffleOrder shuffleOrder) { - verifyApplicationThread(); Timeline timeline = createMaskingTimeline(); PlaybackInfo newPlaybackInfo = maskTimelineAndPosition( @@ -748,7 +589,6 @@ import java.util.concurrent.TimeoutException; } public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { - verifyApplicationThread(); if (this.pauseAtEndOfMediaItems == pauseAtEndOfMediaItems) { return; } @@ -757,18 +597,9 @@ import java.util.concurrent.TimeoutException; } public boolean getPauseAtEndOfMediaItems() { - verifyApplicationThread(); return pauseAtEndOfMediaItems; } - public void setPlayWhenReady(boolean playWhenReady) { - verifyApplicationThread(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); - } - public void setPlayWhenReady( boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason, @@ -793,54 +624,46 @@ import java.util.concurrent.TimeoutException; } public boolean getPlayWhenReady() { - verifyApplicationThread(); return playbackInfo.playWhenReady; } public void setRepeatMode(@RepeatMode int repeatMode) { - verifyApplicationThread(); if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; internalPlayer.setRepeatMode(repeatMode); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } @RepeatMode public int getRepeatMode() { - verifyApplicationThread(); return repeatMode; } public void setShuffleModeEnabled(boolean shuffleModeEnabled) { - verifyApplicationThread(); if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } public boolean getShuffleModeEnabled() { - verifyApplicationThread(); return shuffleModeEnabled; } public boolean isLoading() { - verifyApplicationThread(); return playbackInfo.isLoading; } public void seekTo(int mediaItemIndex, long positionMs) { - verifyApplicationThread(); - analyticsCollector.notifySeekStarted(); Timeline timeline = playbackInfo.timeline; if (mediaItemIndex < 0 || (!timeline.isEmpty() && mediaItemIndex >= timeline.getWindowCount())) { @@ -881,22 +704,18 @@ import java.util.concurrent.TimeoutException; } public long getSeekBackIncrement() { - verifyApplicationThread(); return seekBackIncrementMs; } public long getSeekForwardIncrement() { - verifyApplicationThread(); return seekForwardIncrementMs; } public long getMaxSeekToPreviousPosition() { - verifyApplicationThread(); return C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS; } public void setPlaybackParameters(PlaybackParameters playbackParameters) { - verifyApplicationThread(); if (playbackParameters == null) { playbackParameters = PlaybackParameters.DEFAULT; } @@ -918,12 +737,10 @@ import java.util.concurrent.TimeoutException; } public PlaybackParameters getPlaybackParameters() { - verifyApplicationThread(); return playbackInfo.playbackParameters; } public void setSeekParameters(@Nullable SeekParameters seekParameters) { - verifyApplicationThread(); if (seekParameters == null) { seekParameters = SeekParameters.DEFAULT; } @@ -934,12 +751,10 @@ import java.util.concurrent.TimeoutException; } public SeekParameters getSeekParameters() { - verifyApplicationThread(); return seekParameters; } public void setForegroundMode(boolean foregroundMode) { - verifyApplicationThread(); if (this.foregroundMode != foregroundMode) { this.foregroundMode = foregroundMode; if (!internalPlayer.setForegroundMode(foregroundMode)) { @@ -953,15 +768,8 @@ import java.util.concurrent.TimeoutException; } } - public void stop() { - stop(/* reset= */ false); - } - public void stop(boolean reset) { - verifyApplicationThread(); - audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); stop(reset, /* error= */ null); - currentCues = ImmutableList.of(); } /** @@ -1014,19 +822,9 @@ import java.util.concurrent.TimeoutException; + "] [" + MediaLibraryInfo.registeredModules() + "]"); - verifyApplicationThread(); - if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) { - keepSessionIdAudioTrack.release(); - keepSessionIdAudioTrack = null; - } - audioBecomingNoisyManager.setEnabled(false); - streamVolumeManager.release(); - wakeLockManager.setStayAwake(false); - wifiLockManager.setStayAwake(false); - audioFocusManager.release(); if (!internalPlayer.release()) { // One of the renderers timed out releasing its resources. - eventListeners.sendEvent( + listeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( @@ -1034,34 +832,26 @@ import java.util.concurrent.TimeoutException; new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), PlaybackException.ERROR_CODE_TIMEOUT))); } - eventListeners.release(); + listeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId); playbackInfo.bufferedPositionUs = playbackInfo.positionUs; playbackInfo.totalBufferedDurationUs = 0; - analyticsCollector.release(); - removeSurfaceCallbacks(); - if (ownedSurface != null) { - ownedSurface.release(); - ownedSurface = null; - } - if (isPriorityTaskManagerRegistered) { - checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = false; - } - currentCues = ImmutableList.of(); - playerReleased = true; } public PlayerMessage createMessage(Target target) { - verifyApplicationThread(); - return createMessageInternal(target); + return new PlayerMessage( + internalPlayer, + target, + playbackInfo.timeline, + getCurrentMediaItemIndex(), + clock, + internalPlayer.getPlaybackLooper()); } public int getCurrentPeriodIndex() { - verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { return maskingPeriodIndex; } else { @@ -1070,13 +860,11 @@ import java.util.concurrent.TimeoutException; } public int getCurrentMediaItemIndex() { - verifyApplicationThread(); int currentWindowIndex = getCurrentWindowIndexInternal(); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } public long getDuration() { - verifyApplicationThread(); if (isPlayingAd()) { MediaPeriodId periodId = playbackInfo.periodId; playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period); @@ -1094,12 +882,10 @@ import java.util.concurrent.TimeoutException; } public long getCurrentPosition() { - verifyApplicationThread(); return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } public long getBufferedPosition() { - verifyApplicationThread(); if (isPlayingAd()) { return playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId) ? Util.usToMs(playbackInfo.bufferedPositionUs) @@ -1109,27 +895,22 @@ import java.util.concurrent.TimeoutException; } public long getTotalBufferedDuration() { - verifyApplicationThread(); return Util.usToMs(playbackInfo.totalBufferedDurationUs); } public boolean isPlayingAd() { - verifyApplicationThread(); return playbackInfo.periodId.isAd(); } public int getCurrentAdGroupIndex() { - verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; } public int getCurrentAdIndexInAdGroup() { - verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; } public long getContentPosition() { - verifyApplicationThread(); if (isPlayingAd()) { playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); return playbackInfo.requestedContentPositionUs == C.TIME_UNSET @@ -1144,7 +925,6 @@ import java.util.concurrent.TimeoutException; } public long getContentBufferedPosition() { - verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { return maskingWindowPositionMs; } @@ -1168,63 +948,53 @@ import java.util.concurrent.TimeoutException; } public int getRendererCount() { - verifyApplicationThread(); return renderers.length; } public @C.TrackType int getRendererType(int index) { - verifyApplicationThread(); return renderers[index].getTrackType(); } public Renderer getRenderer(int index) { - verifyApplicationThread(); return renderers[index]; } public TrackSelector getTrackSelector() { - verifyApplicationThread(); return trackSelector; } public TrackGroupArray getCurrentTrackGroups() { - verifyApplicationThread(); return playbackInfo.trackGroups; } public TrackSelectionArray getCurrentTrackSelections() { - verifyApplicationThread(); return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections); } public TracksInfo getCurrentTracksInfo() { - verifyApplicationThread(); return playbackInfo.trackSelectorResult.tracksInfo; } public TrackSelectionParameters getTrackSelectionParameters() { - verifyApplicationThread(); return trackSelector.getParameters(); } public void setTrackSelectionParameters(TrackSelectionParameters parameters) { - verifyApplicationThread(); if (!trackSelector.isSetParametersSupported() || parameters.equals(trackSelector.getParameters())) { return; } trackSelector.setParameters(parameters); - eventListeners.queueEvent( + listeners.queueEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } public MediaMetadata getMediaMetadata() { - verifyApplicationThread(); return mediaMetadata; } - private void onMetadata(Metadata metadata) { + public void onMetadata(Metadata metadata) { staticAndDynamicMediaMetadata = staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build(); @@ -1234,452 +1004,29 @@ import java.util.concurrent.TimeoutException; return; } mediaMetadata = newMediaMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } public MediaMetadata getPlaylistMetadata() { - verifyApplicationThread(); return playlistMetadata; } public void setPlaylistMetadata(MediaMetadata playlistMetadata) { - verifyApplicationThread(); checkNotNull(playlistMetadata); if (playlistMetadata.equals(this.playlistMetadata)) { return; } this.playlistMetadata = playlistMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_PLAYLIST_METADATA_CHANGED, listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } public Timeline getCurrentTimeline() { - verifyApplicationThread(); return playbackInfo.timeline; } - public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { - verifyApplicationThread(); - this.videoScalingMode = videoScalingMode; - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); - } - - @C.VideoScalingMode - public int getVideoScalingMode() { - return videoScalingMode; - } - - public void setVideoChangeFrameRateStrategy( - @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { - verifyApplicationThread(); - if (this.videoChangeFrameRateStrategy == videoChangeFrameRateStrategy) { - return; - } - this.videoChangeFrameRateStrategy = videoChangeFrameRateStrategy; - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); - } - - @C.VideoChangeFrameRateStrategy - public int getVideoChangeFrameRateStrategy() { - return videoChangeFrameRateStrategy; - } - - public VideoSize getVideoSize() { - return videoSize; - } - - public void clearVideoSurface() { - verifyApplicationThread(); - removeSurfaceCallbacks(); - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - - public void clearVideoSurface(@Nullable Surface surface) { - verifyApplicationThread(); - if (surface != null && surface == videoOutput) { - clearVideoSurface(); - } - } - - public void setVideoSurface(@Nullable Surface surface) { - verifyApplicationThread(); - removeSurfaceCallbacks(); - setVideoOutputInternal(surface); - int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; - maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); - } - - public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - verifyApplicationThread(); - if (surfaceHolder == null) { - clearVideoSurface(); - } else { - removeSurfaceCallbacks(); - this.surfaceHolderSurfaceIsVideoOutput = true; - this.surfaceHolder = surfaceHolder; - surfaceHolder.addCallback(componentListener); - Surface surface = surfaceHolder.getSurface(); - if (surface != null && surface.isValid()) { - setVideoOutputInternal(surface); - Rect surfaceSize = surfaceHolder.getSurfaceFrame(); - maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); - } else { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - } - } - - public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - verifyApplicationThread(); - if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { - clearVideoSurface(); - } - } - - public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { - verifyApplicationThread(); - if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { - removeSurfaceCallbacks(); - setVideoOutputInternal(surfaceView); - setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); - } else if (surfaceView instanceof SphericalGLSurfaceView) { - removeSurfaceCallbacks(); - sphericalGLSurfaceView = (SphericalGLSurfaceView) surfaceView; - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) - .setPayload(sphericalGLSurfaceView) - .send(); - sphericalGLSurfaceView.addVideoSurfaceListener(componentListener); - setVideoOutputInternal(sphericalGLSurfaceView.getVideoSurface()); - setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); - } else { - setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); - } - } - - public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { - verifyApplicationThread(); - clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); - } - - public void setVideoTextureView(@Nullable TextureView textureView) { - verifyApplicationThread(); - if (textureView == null) { - clearVideoSurface(); - } else { - removeSurfaceCallbacks(); - this.textureView = textureView; - if (textureView.getSurfaceTextureListener() != null) { - Log.w(TAG, "Replacing existing SurfaceTextureListener."); - } - textureView.setSurfaceTextureListener(componentListener); - @Nullable - SurfaceTexture surfaceTexture = - textureView.isAvailable() ? textureView.getSurfaceTexture() : null; - if (surfaceTexture == null) { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } else { - setSurfaceTextureInternal(surfaceTexture); - maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight()); - } - } - } - - public void clearVideoTextureView(@Nullable TextureView textureView) { - verifyApplicationThread(); - if (textureView != null && textureView == this.textureView) { - clearVideoSurface(); - } - } - - public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { - verifyApplicationThread(); - if (playerReleased) { - return; - } - if (!Util.areEqual(this.audioAttributes, audioAttributes)) { - this.audioAttributes = audioAttributes; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - analyticsCollector.onAudioAttributesChanged(audioAttributes); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onAudioAttributesChanged(audioAttributes); - } - } - - audioFocusManager.setAudioAttributes(handleAudioFocus ? audioAttributes : null); - boolean playWhenReady = getPlayWhenReady(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); - } - - public AudioAttributes getAudioAttributes() { - return audioAttributes; - } - - public void setAudioSessionId(int audioSessionId) { - verifyApplicationThread(); - if (this.audioSessionId == audioSessionId) { - return; - } - if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { - if (Util.SDK_INT < 21) { - audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); - } else { - audioSessionId = Util.generateAudioSessionIdV21(applicationContext); - } - } else if (Util.SDK_INT < 21) { - // We need to re-initialize keepSessionIdAudioTrack to make sure the session is kept alive for - // as long as the player is using it. - initializeKeepSessionIdAudioTrack(audioSessionId); - } - this.audioSessionId = audioSessionId; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - analyticsCollector.onAudioSessionIdChanged(audioSessionId); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onAudioSessionIdChanged(audioSessionId); - } - } - - public int getAudioSessionId() { - return audioSessionId; - } - - public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { - verifyApplicationThread(); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUX_EFFECT_INFO, auxEffectInfo); - } - - public void clearAuxEffectInfo() { - setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); - } - - public void setVolume(float volume) { - verifyApplicationThread(); - volume = Util.constrainValue(volume, /* min= */ 0, /* max= */ 1); - if (this.volume == volume) { - return; - } - this.volume = volume; - sendVolumeToRenderers(); - analyticsCollector.onVolumeChanged(volume); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onVolumeChanged(volume); - } - } - - public float getVolume() { - return volume; - } - - public boolean getSkipSilenceEnabled() { - return skipSilenceEnabled; - } - - public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { - verifyApplicationThread(); - if (this.skipSilenceEnabled == skipSilenceEnabled) { - return; - } - this.skipSilenceEnabled = skipSilenceEnabled; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); - notifySkipSilenceEnabledChanged(); - } - - public AnalyticsCollector getAnalyticsCollector() { - return analyticsCollector; - } - - public void addAnalyticsListener(AnalyticsListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); - analyticsCollector.addListener(listener); - } - - public void removeAnalyticsListener(AnalyticsListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - analyticsCollector.removeListener(listener); - } - - public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { - verifyApplicationThread(); - if (playerReleased) { - return; - } - audioBecomingNoisyManager.setEnabled(handleAudioBecomingNoisy); - } - - public void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) { - verifyApplicationThread(); - if (Util.areEqual(this.priorityTaskManager, priorityTaskManager)) { - return; - } - if (isPriorityTaskManagerRegistered) { - checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); - } - if (priorityTaskManager != null && isLoading()) { - priorityTaskManager.add(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = true; - } else { - isPriorityTaskManagerRegistered = false; - } - this.priorityTaskManager = priorityTaskManager; - } - - @Nullable - public Format getVideoFormat() { - return videoFormat; - } - - @Nullable - public Format getAudioFormat() { - return audioFormat; - } - - @Nullable - public DecoderCounters getVideoDecoderCounters() { - return videoDecoderCounters; - } - - @Nullable - public DecoderCounters getAudioDecoderCounters() { - return audioDecoderCounters; - } - - public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - verifyApplicationThread(); - videoFrameMetadataListener = listener; - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) - .setPayload(listener) - .send(); - } - - public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - verifyApplicationThread(); - if (videoFrameMetadataListener != listener) { - return; - } - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) - .setPayload(null) - .send(); - } - - public void setCameraMotionListener(CameraMotionListener listener) { - verifyApplicationThread(); - cameraMotionListener = listener; - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) - .setPayload(listener) - .send(); - } - - public void clearCameraMotionListener(CameraMotionListener listener) { - verifyApplicationThread(); - if (cameraMotionListener != listener) { - return; - } - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) - .setPayload(null) - .send(); - } - - public List getCurrentCues() { - verifyApplicationThread(); - return currentCues; - } - - public void addListener(Listener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); - listeners.add(listener); - addEventListener(listener); - } - - public void removeListener(Listener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); - listeners.remove(listener); - removeEventListener(listener); - } - - public void setHandleWakeLock(boolean handleWakeLock) { - setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); - } - - public void setWakeMode(@C.WakeMode int wakeMode) { - verifyApplicationThread(); - switch (wakeMode) { - case C.WAKE_MODE_NONE: - wakeLockManager.setEnabled(false); - wifiLockManager.setEnabled(false); - break; - case C.WAKE_MODE_LOCAL: - wakeLockManager.setEnabled(true); - wifiLockManager.setEnabled(false); - break; - case C.WAKE_MODE_NETWORK: - wakeLockManager.setEnabled(true); - wifiLockManager.setEnabled(true); - break; - default: - break; - } - } - - public DeviceInfo getDeviceInfo() { - verifyApplicationThread(); - return deviceInfo; - } - - public int getDeviceVolume() { - verifyApplicationThread(); - return streamVolumeManager.getVolume(); - } - - public boolean isDeviceMuted() { - verifyApplicationThread(); - return streamVolumeManager.isMuted(); - } - - public void setDeviceVolume(int volume) { - verifyApplicationThread(); - streamVolumeManager.setVolume(volume); - } - - public void increaseDeviceVolume() { - verifyApplicationThread(); - streamVolumeManager.increaseVolume(); - } - - public void decreaseDeviceVolume() { - verifyApplicationThread(); - streamVolumeManager.decreaseVolume(); - } - - public void setDeviceMuted(boolean muted) { - verifyApplicationThread(); - streamVolumeManager.setMuted(muted); - } - - /* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) { - this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread; - } - private int getCurrentWindowIndexInternal() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; @@ -1815,7 +1162,7 @@ import java.util.concurrent.TimeoutException; mediaMetadata = newMediaMetadata; if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); } @@ -1824,7 +1171,7 @@ import java.util.concurrent.TimeoutException; getPreviousPositionInfo( positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_POSITION_DISCONTINUITY, listener -> { listener.onPositionDiscontinuity(positionDiscontinuityReason); @@ -1834,16 +1181,16 @@ import java.util.concurrent.TimeoutException; } if (mediaItemTransitioned) { @Nullable final MediaItem finalMediaItem = mediaItem; - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); if (newPlaybackInfo.playbackError != null) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); } @@ -1852,21 +1199,21 @@ import java.util.concurrent.TimeoutException; trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); } if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; - eventListeners.queueEvent( + listeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { listener.onLoadingChanged(newPlaybackInfo.isLoading); @@ -1875,19 +1222,19 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged( @@ -1895,27 +1242,27 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged( newPlaybackInfo.playbackSuppressionReason)); } if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); } if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); } if (seekProcessed) { - eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { for (AudioOffloadListener listener : audioOffloadListeners) { @@ -2072,7 +1419,7 @@ import java.util.concurrent.TimeoutException; Commands previousAvailableCommands = availableCommands; availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, listener -> listener.onAvailableCommandsChanged(availableCommands)); } @@ -2363,17 +1710,6 @@ import java.util.concurrent.TimeoutException; return positionUs; } - private PlayerMessage createMessageInternal(Target target) { - int currentWindowIndex = getCurrentWindowIndexInternal(); - return new PlayerMessage( - internalPlayer, - target, - playbackInfo.timeline, - currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex, - clock, - internalPlayer.getPlaybackLooper()); - } - /** * Builds a {@link MediaMetadata} from the main sources. * @@ -2391,235 +1727,6 @@ import java.util.concurrent.TimeoutException; return staticAndDynamicMediaMetadata.buildUpon().populate(mediaItem.mediaMetadata).build(); } - private void removeSurfaceCallbacks() { - if (sphericalGLSurfaceView != null) { - createMessageInternal(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) - .setPayload(null) - .send(); - sphericalGLSurfaceView.removeVideoSurfaceListener(componentListener); - sphericalGLSurfaceView = null; - } - if (textureView != null) { - if (textureView.getSurfaceTextureListener() != componentListener) { - Log.w(TAG, "SurfaceTextureListener already unset or replaced."); - } else { - textureView.setSurfaceTextureListener(null); - } - textureView = null; - } - if (surfaceHolder != null) { - surfaceHolder.removeCallback(componentListener); - surfaceHolder = null; - } - } - - private void setSurfaceTextureInternal(SurfaceTexture surfaceTexture) { - Surface surface = new Surface(surfaceTexture); - setVideoOutputInternal(surface); - ownedSurface = surface; - } - - private void setVideoOutputInternal(@Nullable Object videoOutput) { - // Note: We don't turn this method into a no-op if the output is being replaced with itself so - // as to ensure onRenderedFirstFrame callbacks are still called in this case. - List messages = new ArrayList<>(); - for (Renderer renderer : renderers) { - if (renderer.getTrackType() == TRACK_TYPE_VIDEO) { - messages.add( - createMessageInternal(renderer) - .setType(MSG_SET_VIDEO_OUTPUT) - .setPayload(videoOutput) - .send()); - } - } - boolean messageDeliveryTimedOut = false; - if (this.videoOutput != null && this.videoOutput != videoOutput) { - // We're replacing an output. Block to ensure that this output will not be accessed by the - // renderers after this method returns. - try { - for (PlayerMessage message : messages) { - message.blockUntilDelivered(detachSurfaceTimeoutMs); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (TimeoutException e) { - messageDeliveryTimedOut = true; - } - if (this.videoOutput == ownedSurface) { - // We're replacing a surface that we are responsible for releasing. - ownedSurface.release(); - ownedSurface = null; - } - } - this.videoOutput = videoOutput; - if (messageDeliveryTimedOut) { - stop( - /* reset= */ false, - ExoPlaybackException.createForUnexpected( - new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE), - PlaybackException.ERROR_CODE_TIMEOUT)); - } - } - - /** - * Sets the holder of the surface that will be displayed to the user, but which should - * not be the output for video renderers. This case occurs when video frames need to be - * rendered to an intermediate surface (which is not the one held by the provided holder). - * - * @param nonVideoOutputSurfaceHolder The holder of the surface that will eventually be displayed - * to the user. - */ - private void setNonVideoOutputSurfaceHolderInternal(SurfaceHolder nonVideoOutputSurfaceHolder) { - // Although we won't use the view's surface directly as the video output, still use the holder - // to query the surface size, to be informed in changes to the size via componentListener, and - // for equality checking in clearVideoSurfaceHolder. - surfaceHolderSurfaceIsVideoOutput = false; - surfaceHolder = nonVideoOutputSurfaceHolder; - surfaceHolder.addCallback(componentListener); - Surface surface = surfaceHolder.getSurface(); - if (surface != null && surface.isValid()) { - Rect surfaceSize = surfaceHolder.getSurfaceFrame(); - maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); - } else { - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - } - - private void maybeNotifySurfaceSizeChanged(int width, int height) { - if (width != surfaceWidth || height != surfaceHeight) { - surfaceWidth = width; - surfaceHeight = height; - analyticsCollector.onSurfaceSizeChanged(width, height); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onSurfaceSizeChanged(width, height); - } - } - } - - private void sendVolumeToRenderers() { - float scaledVolume = volume * audioFocusManager.getVolumeMultiplier(); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_VOLUME, scaledVolume); - } - - private void notifySkipSilenceEnabledChanged() { - analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); - } - } - - private void updatePlayWhenReady( - boolean playWhenReady, - @AudioFocusManager.PlayerCommand int playerCommand, - @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason) { - playWhenReady = playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY; - @PlaybackSuppressionReason - int playbackSuppressionReason = - playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY - ? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS - : Player.PLAYBACK_SUPPRESSION_REASON_NONE; - setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason); - } - - private void updateWakeAndWifiLock() { - @State int playbackState = getPlaybackState(); - switch (playbackState) { - case Player.STATE_READY: - case Player.STATE_BUFFERING: - boolean isSleeping = experimentalIsSleepingForOffload(); - wakeLockManager.setStayAwake(getPlayWhenReady() && !isSleeping); - // The wifi lock is not released while sleeping to avoid interrupting downloads. - wifiLockManager.setStayAwake(getPlayWhenReady()); - break; - case Player.STATE_ENDED: - case Player.STATE_IDLE: - wakeLockManager.setStayAwake(false); - wifiLockManager.setStayAwake(false); - break; - default: - throw new IllegalStateException(); - } - } - - private void verifyApplicationThread() { - // The constructor may be executed on a background thread. Wait with accessing the player from - // the app thread until the constructor finished executing. - constructorFinished.blockUninterruptible(); - if (Thread.currentThread() != getApplicationLooper().getThread()) { - String message = - Util.formatInvariant( - "Player is accessed on the wrong thread.\n" - + "Current thread: '%s'\n" - + "Expected thread: '%s'\n" - + "See https://exoplayer.dev/issues/player-accessed-on-wrong-thread", - Thread.currentThread().getName(), getApplicationLooper().getThread().getName()); - if (throwsWhenUsingWrongThread) { - throw new IllegalStateException(message); - } - Log.w(TAG, message, hasNotifiedFullWrongThreadWarning ? null : new IllegalStateException()); - hasNotifiedFullWrongThreadWarning = true; - } - } - - private void sendRendererMessage( - @C.TrackType int trackType, int messageType, @Nullable Object payload) { - for (Renderer renderer : renderers) { - if (renderer.getTrackType() == trackType) { - createMessageInternal(renderer).setType(messageType).setPayload(payload).send(); - } - } - } - - /** - * Initializes {@link #keepSessionIdAudioTrack} to keep an audio session ID alive. If the audio - * session ID is {@link C#AUDIO_SESSION_ID_UNSET} then a new audio session ID is generated. - * - *

    Use of this method is only required on API level 21 and earlier. - * - * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} to generate a - * new one. - * @return The audio session ID. - */ - private int initializeKeepSessionIdAudioTrack(int audioSessionId) { - if (keepSessionIdAudioTrack != null - && keepSessionIdAudioTrack.getAudioSessionId() != audioSessionId) { - keepSessionIdAudioTrack.release(); - keepSessionIdAudioTrack = null; - } - if (keepSessionIdAudioTrack == null) { - int sampleRate = 4000; // Minimum sample rate supported by the platform. - int channelConfig = AudioFormat.CHANNEL_OUT_MONO; - @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT; - int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback. - keepSessionIdAudioTrack = - new AudioTrack( - C.STREAM_TYPE_DEFAULT, - sampleRate, - channelConfig, - encoding, - bufferSize, - AudioTrack.MODE_STATIC, - audioSessionId); - } - return keepSessionIdAudioTrack.getAudioSessionId(); - } - - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { - return new DeviceInfo( - DeviceInfo.PLAYBACK_TYPE_LOCAL, - streamVolumeManager.getMinVolume(), - streamVolumeManager.getMaxVolume()); - } - - private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) { - return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY - ? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS - : PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; - } - private static boolean isPlaying(PlaybackInfo playbackInfo) { return playbackInfo.playbackState == Player.STATE_READY && playbackInfo.playWhenReady @@ -2648,410 +1755,6 @@ import java.util.concurrent.TimeoutException; } } - // TODO(b/204189802): Remove self-listening to deprecated EventListener. - @SuppressWarnings("deprecation") - private final class ComponentListener - implements VideoRendererEventListener, - AudioRendererEventListener, - TextOutput, - MetadataOutput, - SurfaceHolder.Callback, - TextureView.SurfaceTextureListener, - SphericalGLSurfaceView.VideoSurfaceListener, - AudioFocusManager.PlayerControl, - AudioBecomingNoisyManager.EventListener, - StreamVolumeManager.Listener, - Player.EventListener, - AudioOffloadListener { - - // VideoRendererEventListener implementation - - @Override - public void onVideoEnabled(DecoderCounters counters) { - videoDecoderCounters = counters; - analyticsCollector.onVideoEnabled(counters); - } - - @Override - public void onVideoDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - analyticsCollector.onVideoDecoderInitialized( - decoderName, initializedTimestampMs, initializationDurationMs); - } - - @Override - public void onVideoInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - videoFormat = format; - analyticsCollector.onVideoInputFormatChanged(format, decoderReuseEvaluation); - } - - @Override - public void onDroppedFrames(int count, long elapsed) { - analyticsCollector.onDroppedFrames(count, elapsed); - } - - @Override - public void onVideoSizeChanged(VideoSize videoSize) { - ExoPlayerImpl.this.videoSize = videoSize; - analyticsCollector.onVideoSizeChanged(videoSize); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onVideoSizeChanged(videoSize); - } - } - - @Override - public void onRenderedFirstFrame(Object output, long renderTimeMs) { - analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); - if (videoOutput == output) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onRenderedFirstFrame(); - } - } - } - - @Override - public void onVideoDecoderReleased(String decoderName) { - analyticsCollector.onVideoDecoderReleased(decoderName); - } - - @Override - public void onVideoDisabled(DecoderCounters counters) { - analyticsCollector.onVideoDisabled(counters); - videoFormat = null; - videoDecoderCounters = null; - } - - @Override - public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { - analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount); - } - - @Override - public void onVideoCodecError(Exception videoCodecError) { - analyticsCollector.onVideoCodecError(videoCodecError); - } - - // AudioRendererEventListener implementation - - @Override - public void onAudioEnabled(DecoderCounters counters) { - audioDecoderCounters = counters; - analyticsCollector.onAudioEnabled(counters); - } - - @Override - public void onAudioDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - analyticsCollector.onAudioDecoderInitialized( - decoderName, initializedTimestampMs, initializationDurationMs); - } - - @Override - public void onAudioInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - audioFormat = format; - analyticsCollector.onAudioInputFormatChanged(format, decoderReuseEvaluation); - } - - @Override - public void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { - analyticsCollector.onAudioPositionAdvancing(playoutStartSystemTimeMs); - } - - @Override - public void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - analyticsCollector.onAudioUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - } - - @Override - public void onAudioDecoderReleased(String decoderName) { - analyticsCollector.onAudioDecoderReleased(decoderName); - } - - @Override - public void onAudioDisabled(DecoderCounters counters) { - analyticsCollector.onAudioDisabled(counters); - audioFormat = null; - audioDecoderCounters = null; - } - - @Override - public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { - if (ExoPlayerImpl.this.skipSilenceEnabled == skipSilenceEnabled) { - return; - } - ExoPlayerImpl.this.skipSilenceEnabled = skipSilenceEnabled; - notifySkipSilenceEnabledChanged(); - } - - @Override - public void onAudioSinkError(Exception audioSinkError) { - analyticsCollector.onAudioSinkError(audioSinkError); - } - - @Override - public void onAudioCodecError(Exception audioCodecError) { - analyticsCollector.onAudioCodecError(audioCodecError); - } - - // TextOutput implementation - - @Override - public void onCues(List cues) { - currentCues = cues; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listeners) { - listeners.onCues(cues); - } - } - - // MetadataOutput implementation - - @Override - public void onMetadata(Metadata metadata) { - analyticsCollector.onMetadata(metadata); - ExoPlayerImpl.this.onMetadata(metadata); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onMetadata(metadata); - } - } - - // SurfaceHolder.Callback implementation - - @Override - public void surfaceCreated(SurfaceHolder holder) { - if (surfaceHolderSurfaceIsVideoOutput) { - setVideoOutputInternal(holder.getSurface()); - } - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - if (surfaceHolderSurfaceIsVideoOutput) { - setVideoOutputInternal(/* videoOutput= */ null); - } - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - - // TextureView.SurfaceTextureListener implementation - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { - setSurfaceTextureInternal(surfaceTexture); - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - return true; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { - // Do nothing. - } - - // SphericalGLSurfaceView.VideoSurfaceListener - - @Override - public void onVideoSurfaceCreated(Surface surface) { - setVideoOutputInternal(surface); - } - - @Override - public void onVideoSurfaceDestroyed(Surface surface) { - setVideoOutputInternal(/* videoOutput= */ null); - } - - // AudioFocusManager.PlayerControl implementation - - @Override - public void setVolumeMultiplier(float volumeMultiplier) { - sendVolumeToRenderers(); - } - - @Override - public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) { - boolean playWhenReady = getPlayWhenReady(); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); - } - - // AudioBecomingNoisyManager.EventListener implementation. - - @Override - public void onAudioBecomingNoisy() { - updatePlayWhenReady( - /* playWhenReady= */ false, - AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY, - Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY); - } - - // StreamVolumeManager.Listener implementation. - - @Override - public void onStreamTypeChanged(@C.StreamType int streamType) { - DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); - if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { - ExoPlayerImpl.this.deviceInfo = deviceInfo; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onDeviceInfoChanged(deviceInfo); - } - } - } - - @Override - public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onDeviceVolumeChanged(streamVolume, streamMuted); - } - } - - // Player.EventListener implementation. - - @Override - public void onIsLoadingChanged(boolean isLoading) { - if (priorityTaskManager != null) { - if (isLoading && !isPriorityTaskManagerRegistered) { - priorityTaskManager.add(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = true; - } else if (!isLoading && isPriorityTaskManagerRegistered) { - priorityTaskManager.remove(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = false; - } - } - } - - @Override - public void onPlaybackStateChanged(@State int playbackState) { - updateWakeAndWifiLock(); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - updateWakeAndWifiLock(); - } - - // Player.AudioOffloadListener implementation. - - @Override - public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { - updateWakeAndWifiLock(); - } - } - - /** Listeners that are called on the playback thread. */ - private static final class FrameMetadataListener - implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { - - @MessageType - public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = - Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; - - @MessageType - public static final int MSG_SET_CAMERA_MOTION_LISTENER = - Renderer.MSG_SET_CAMERA_MOTION_LISTENER; - - @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; - - @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; - @Nullable private CameraMotionListener cameraMotionListener; - @Nullable private VideoFrameMetadataListener internalVideoFrameMetadataListener; - @Nullable private CameraMotionListener internalCameraMotionListener; - - @Override - public void handleMessage(@MessageType int messageType, @Nullable Object message) { - switch (messageType) { - case MSG_SET_VIDEO_FRAME_METADATA_LISTENER: - videoFrameMetadataListener = (VideoFrameMetadataListener) message; - break; - case MSG_SET_CAMERA_MOTION_LISTENER: - cameraMotionListener = (CameraMotionListener) message; - break; - case MSG_SET_SPHERICAL_SURFACE_VIEW: - @Nullable SphericalGLSurfaceView surfaceView = (SphericalGLSurfaceView) message; - if (surfaceView == null) { - internalVideoFrameMetadataListener = null; - internalCameraMotionListener = null; - } else { - internalVideoFrameMetadataListener = surfaceView.getVideoFrameMetadataListener(); - internalCameraMotionListener = surfaceView.getCameraMotionListener(); - } - break; - case Renderer.MSG_SET_AUDIO_ATTRIBUTES: - case Renderer.MSG_SET_AUDIO_SESSION_ID: - case Renderer.MSG_SET_AUX_EFFECT_INFO: - case Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY: - case Renderer.MSG_SET_SCALING_MODE: - case Renderer.MSG_SET_SKIP_SILENCE_ENABLED: - case Renderer.MSG_SET_VIDEO_OUTPUT: - case Renderer.MSG_SET_VOLUME: - case Renderer.MSG_SET_WAKEUP_LISTENER: - default: - break; - } - } - - // VideoFrameMetadataListener - - @Override - public void onVideoFrameAboutToBeRendered( - long presentationTimeUs, - long releaseTimeNs, - Format format, - @Nullable MediaFormat mediaFormat) { - if (internalVideoFrameMetadataListener != null) { - internalVideoFrameMetadataListener.onVideoFrameAboutToBeRendered( - presentationTimeUs, releaseTimeNs, format, mediaFormat); - } - if (videoFrameMetadataListener != null) { - videoFrameMetadataListener.onVideoFrameAboutToBeRendered( - presentationTimeUs, releaseTimeNs, format, mediaFormat); - } - } - - // CameraMotionListener - - @Override - public void onCameraMotion(long timeUs, float[] rotation) { - if (internalCameraMotionListener != null) { - internalCameraMotionListener.onCameraMotion(timeUs, rotation); - } - if (cameraMotionListener != null) { - cameraMotionListener.onCameraMotion(timeUs, rotation); - } - } - - @Override - public void onCameraMotionReset() { - if (internalCameraMotionListener != null) { - internalCameraMotionListener.onCameraMotionReset(); - } - if (cameraMotionListener != null) { - cameraMotionListener.onCameraMotionReset(); - } - } - } - @RequiresApi(31) private static final class Api31 { private Api31() {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index bffbe5e453..bc9f029e28 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -15,7 +15,28 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.C.TRACK_TYPE_AUDIO; +import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION; +import static androidx.media3.common.C.TRACK_TYPE_VIDEO; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_ATTRIBUTES; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_SESSION_ID; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUX_EFFECT_INFO; +import static androidx.media3.exoplayer.Renderer.MSG_SET_CAMERA_MOTION_LISTENER; +import static androidx.media3.exoplayer.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY; +import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE; +import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME; + import android.content.Context; +import android.graphics.Rect; +import android.graphics.SurfaceTexture; +import android.media.AudioFormat; +import android.media.AudioTrack; +import android.media.MediaFormat; +import android.os.Handler; import android.os.Looper; import android.view.Surface; import android.view.SurfaceHolder; @@ -32,6 +53,8 @@ import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; +import androidx.media3.common.Metadata; +import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; @@ -43,18 +66,32 @@ import androidx.media3.common.TracksInfo; import androidx.media3.common.VideoSize; import androidx.media3.common.text.Cue; import androidx.media3.common.util.Clock; +import androidx.media3.common.util.ConditionVariable; +import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; +import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; +import androidx.media3.exoplayer.audio.AudioRendererEventListener; +import androidx.media3.exoplayer.metadata.MetadataOutput; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.ShuffleOrder; +import androidx.media3.exoplayer.text.TextOutput; import androidx.media3.exoplayer.trackselection.TrackSelector; import androidx.media3.exoplayer.upstream.BandwidthMeter; +import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; import androidx.media3.exoplayer.video.VideoFrameMetadataListener; +import androidx.media3.exoplayer.video.VideoRendererEventListener; import androidx.media3.exoplayer.video.spherical.CameraMotionListener; +import androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView; import androidx.media3.extractor.ExtractorsFactory; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.TimeoutException; /** @deprecated Use {@link ExoPlayer} instead. */ @UnstableApi @@ -319,7 +356,52 @@ public class SimpleExoPlayer extends BasePlayer } } + private static final String TAG = "SimpleExoPlayer"; + + private final Renderer[] renderers; + private final ConditionVariable constructorFinished; + private final Context applicationContext; private final ExoPlayerImpl player; + private final ComponentListener componentListener; + private final FrameMetadataListener frameMetadataListener; + private final CopyOnWriteArraySet listeners; + private final AnalyticsCollector analyticsCollector; + private final AudioBecomingNoisyManager audioBecomingNoisyManager; + private final AudioFocusManager audioFocusManager; + private final StreamVolumeManager streamVolumeManager; + private final WakeLockManager wakeLockManager; + private final WifiLockManager wifiLockManager; + private final long detachSurfaceTimeoutMs; + + @Nullable private Format videoFormat; + @Nullable private Format audioFormat; + @Nullable private AudioTrack keepSessionIdAudioTrack; + @Nullable private Object videoOutput; + @Nullable private Surface ownedSurface; + @Nullable private SurfaceHolder surfaceHolder; + @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; + private boolean surfaceHolderSurfaceIsVideoOutput; + @Nullable private TextureView textureView; + @C.VideoScalingMode private int videoScalingMode; + @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; + private int surfaceWidth; + private int surfaceHeight; + @Nullable private DecoderCounters videoDecoderCounters; + @Nullable private DecoderCounters audioDecoderCounters; + private int audioSessionId; + private AudioAttributes audioAttributes; + private float volume; + private boolean skipSilenceEnabled; + private List currentCues; + @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; + @Nullable private CameraMotionListener cameraMotionListener; + private boolean throwsWhenUsingWrongThread; + private boolean hasNotifiedFullWrongThreadWarning; + @Nullable private PriorityTaskManager priorityTaskManager; + private boolean isPriorityTaskManagerRegistered; + private boolean playerReleased; + private DeviceInfo deviceInfo; + private VideoSize videoSize; /** @deprecated Use the {@link ExoPlayer.Builder}. */ @Deprecated @@ -355,16 +437,119 @@ public class SimpleExoPlayer extends BasePlayer /** @param builder The {@link ExoPlayer.Builder} to obtain all construction parameters. */ /* package */ SimpleExoPlayer(ExoPlayer.Builder builder) { - player = new ExoPlayerImpl(builder, /* wrappingPlayer= */ this); + constructorFinished = new ConditionVariable(); + try { + applicationContext = builder.context.getApplicationContext(); + analyticsCollector = builder.analyticsCollectorSupplier.get(); + priorityTaskManager = builder.priorityTaskManager; + audioAttributes = builder.audioAttributes; + videoScalingMode = builder.videoScalingMode; + videoChangeFrameRateStrategy = builder.videoChangeFrameRateStrategy; + skipSilenceEnabled = builder.skipSilenceEnabled; + detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; + componentListener = new ComponentListener(); + frameMetadataListener = new FrameMetadataListener(); + listeners = new CopyOnWriteArraySet<>(); + Handler eventHandler = new Handler(builder.looper); + renderers = + builder + .renderersFactorySupplier + .get() + .createRenderers( + eventHandler, + componentListener, + componentListener, + componentListener, + componentListener); + + // Set initial values. + volume = 1; + if (Util.SDK_INT < 21) { + audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); + } else { + audioSessionId = Util.generateAudioSessionIdV21(applicationContext); + } + currentCues = Collections.emptyList(); + throwsWhenUsingWrongThread = true; + + // Build the player and associated objects. + Commands additionalPermanentAvailableCommands = + new Commands.Builder() + .addAll( + COMMAND_GET_AUDIO_ATTRIBUTES, + COMMAND_GET_VOLUME, + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_SET_VIDEO_SURFACE, + COMMAND_GET_TEXT) + .build(); + player = + new ExoPlayerImpl( + renderers, + builder.trackSelectorSupplier.get(), + builder.mediaSourceFactorySupplier.get(), + builder.loadControlSupplier.get(), + builder.bandwidthMeterSupplier.get(), + analyticsCollector, + builder.useLazyPreparation, + builder.seekParameters, + builder.seekBackIncrementMs, + builder.seekForwardIncrementMs, + builder.livePlaybackSpeedControl, + builder.releaseTimeoutMs, + builder.pauseAtEndOfMediaItems, + builder.clock, + builder.looper, + /* wrappingPlayer= */ this, + additionalPermanentAvailableCommands); + player.addEventListener(componentListener); + player.addAudioOffloadListener(componentListener); + if (builder.foregroundModeTimeoutMs > 0) { + player.experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); + } + + audioBecomingNoisyManager = + new AudioBecomingNoisyManager(builder.context, eventHandler, componentListener); + audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); + audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); + audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); + streamVolumeManager = + new StreamVolumeManager(builder.context, eventHandler, componentListener); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + wakeLockManager = new WakeLockManager(builder.context); + wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); + wifiLockManager = new WifiLockManager(builder.context); + wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); + deviceInfo = createDeviceInfo(streamVolumeManager); + videoSize = VideoSize.UNKNOWN; + + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_VIDEO_FRAME_METADATA_LISTENER, frameMetadataListener); + sendRendererMessage( + TRACK_TYPE_CAMERA_MOTION, MSG_SET_CAMERA_MOTION_LISTENER, frameMetadataListener); + } finally { + constructorFinished.open(); + } } @Override public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) { + verifyApplicationThread(); player.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); } @Override public boolean experimentalIsSleepingForOffload() { + verifyApplicationThread(); return player.experimentalIsSleepingForOffload(); } @@ -394,209 +579,400 @@ public class SimpleExoPlayer extends BasePlayer @Override public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { - player.setVideoScalingMode(videoScalingMode); + verifyApplicationThread(); + this.videoScalingMode = videoScalingMode; + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); } @Override @C.VideoScalingMode public int getVideoScalingMode() { - return player.getVideoScalingMode(); + return videoScalingMode; } @Override public void setVideoChangeFrameRateStrategy( @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { - player.setVideoChangeFrameRateStrategy(videoChangeFrameRateStrategy); + verifyApplicationThread(); + if (this.videoChangeFrameRateStrategy == videoChangeFrameRateStrategy) { + return; + } + this.videoChangeFrameRateStrategy = videoChangeFrameRateStrategy; + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); } @Override @C.VideoChangeFrameRateStrategy public int getVideoChangeFrameRateStrategy() { - return player.getVideoChangeFrameRateStrategy(); + return videoChangeFrameRateStrategy; } @Override public VideoSize getVideoSize() { - return player.getVideoSize(); + return videoSize; } @Override public void clearVideoSurface() { - player.clearVideoSurface(); + verifyApplicationThread(); + removeSurfaceCallbacks(); + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } @Override public void clearVideoSurface(@Nullable Surface surface) { - player.clearVideoSurface(surface); + verifyApplicationThread(); + if (surface != null && surface == videoOutput) { + clearVideoSurface(); + } } @Override public void setVideoSurface(@Nullable Surface surface) { - player.setVideoSurface(surface); + verifyApplicationThread(); + removeSurfaceCallbacks(); + setVideoOutputInternal(surface); + int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; + maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); } @Override public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - player.setVideoSurfaceHolder(surfaceHolder); + verifyApplicationThread(); + if (surfaceHolder == null) { + clearVideoSurface(); + } else { + removeSurfaceCallbacks(); + this.surfaceHolderSurfaceIsVideoOutput = true; + this.surfaceHolder = surfaceHolder; + surfaceHolder.addCallback(componentListener); + Surface surface = surfaceHolder.getSurface(); + if (surface != null && surface.isValid()) { + setVideoOutputInternal(surface); + Rect surfaceSize = surfaceHolder.getSurfaceFrame(); + maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); + } else { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + } } @Override public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - player.clearVideoSurfaceHolder(surfaceHolder); + verifyApplicationThread(); + if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { + clearVideoSurface(); + } } @Override public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { - player.setVideoSurfaceView(surfaceView); + verifyApplicationThread(); + if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { + removeSurfaceCallbacks(); + setVideoOutputInternal(surfaceView); + setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); + } else if (surfaceView instanceof SphericalGLSurfaceView) { + removeSurfaceCallbacks(); + sphericalGLSurfaceView = (SphericalGLSurfaceView) surfaceView; + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) + .setPayload(sphericalGLSurfaceView) + .send(); + sphericalGLSurfaceView.addVideoSurfaceListener(componentListener); + setVideoOutputInternal(sphericalGLSurfaceView.getVideoSurface()); + setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); + } else { + setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); + } } @Override public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { - player.clearVideoSurfaceView(surfaceView); + verifyApplicationThread(); + clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); } @Override public void setVideoTextureView(@Nullable TextureView textureView) { - player.setVideoTextureView(textureView); + verifyApplicationThread(); + if (textureView == null) { + clearVideoSurface(); + } else { + removeSurfaceCallbacks(); + this.textureView = textureView; + if (textureView.getSurfaceTextureListener() != null) { + Log.w(TAG, "Replacing existing SurfaceTextureListener."); + } + textureView.setSurfaceTextureListener(componentListener); + @Nullable + SurfaceTexture surfaceTexture = + textureView.isAvailable() ? textureView.getSurfaceTexture() : null; + if (surfaceTexture == null) { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } else { + setSurfaceTextureInternal(surfaceTexture); + maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight()); + } + } } @Override public void clearVideoTextureView(@Nullable TextureView textureView) { - player.clearVideoTextureView(textureView); + verifyApplicationThread(); + if (textureView != null && textureView == this.textureView) { + clearVideoSurface(); + } } @Override public void addAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. player.addAudioOffloadListener(listener); } @Override public void removeAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. player.removeAudioOffloadListener(listener); } @Override public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { - player.setAudioAttributes(audioAttributes, handleAudioFocus); + verifyApplicationThread(); + if (playerReleased) { + return; + } + if (!Util.areEqual(this.audioAttributes, audioAttributes)) { + this.audioAttributes = audioAttributes; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + analyticsCollector.onAudioAttributesChanged(audioAttributes); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onAudioAttributesChanged(audioAttributes); + } + } + + audioFocusManager.setAudioAttributes(handleAudioFocus ? audioAttributes : null); + boolean playWhenReady = getPlayWhenReady(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); } @Override public AudioAttributes getAudioAttributes() { - return player.getAudioAttributes(); + return audioAttributes; } @Override public void setAudioSessionId(int audioSessionId) { - player.setAudioSessionId(audioSessionId); + verifyApplicationThread(); + if (this.audioSessionId == audioSessionId) { + return; + } + if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { + if (Util.SDK_INT < 21) { + audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); + } else { + audioSessionId = Util.generateAudioSessionIdV21(applicationContext); + } + } else if (Util.SDK_INT < 21) { + // We need to re-initialize keepSessionIdAudioTrack to make sure the session is kept alive for + // as long as the player is using it. + initializeKeepSessionIdAudioTrack(audioSessionId); + } + this.audioSessionId = audioSessionId; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + analyticsCollector.onAudioSessionIdChanged(audioSessionId); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onAudioSessionIdChanged(audioSessionId); + } } @Override public int getAudioSessionId() { - return player.getAudioSessionId(); + return audioSessionId; } @Override public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { - player.setAuxEffectInfo(auxEffectInfo); + verifyApplicationThread(); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUX_EFFECT_INFO, auxEffectInfo); } @Override public void clearAuxEffectInfo() { - player.clearAuxEffectInfo(); + setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); } @Override public void setVolume(float volume) { - player.setVolume(volume); + verifyApplicationThread(); + volume = Util.constrainValue(volume, /* min= */ 0, /* max= */ 1); + if (this.volume == volume) { + return; + } + this.volume = volume; + sendVolumeToRenderers(); + analyticsCollector.onVolumeChanged(volume); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onVolumeChanged(volume); + } } @Override public float getVolume() { - return player.getVolume(); + return volume; } @Override public boolean getSkipSilenceEnabled() { - return player.getSkipSilenceEnabled(); + return skipSilenceEnabled; } @Override public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { - player.setSkipSilenceEnabled(skipSilenceEnabled); + verifyApplicationThread(); + if (this.skipSilenceEnabled == skipSilenceEnabled) { + return; + } + this.skipSilenceEnabled = skipSilenceEnabled; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); + notifySkipSilenceEnabledChanged(); } @Override public AnalyticsCollector getAnalyticsCollector() { - return player.getAnalyticsCollector(); + return analyticsCollector; } @Override public void addAnalyticsListener(AnalyticsListener listener) { - player.addAnalyticsListener(listener); + // Don't verify application thread. We allow calls to this method from any thread. + checkNotNull(listener); + analyticsCollector.addListener(listener); } @Override public void removeAnalyticsListener(AnalyticsListener listener) { - player.removeAnalyticsListener(listener); + // Don't verify application thread. We allow calls to this method from any thread. + analyticsCollector.removeListener(listener); } @Override public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { - player.setHandleAudioBecomingNoisy(handleAudioBecomingNoisy); + verifyApplicationThread(); + if (playerReleased) { + return; + } + audioBecomingNoisyManager.setEnabled(handleAudioBecomingNoisy); } @Override public void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) { - player.setPriorityTaskManager(priorityTaskManager); + verifyApplicationThread(); + if (Util.areEqual(this.priorityTaskManager, priorityTaskManager)) { + return; + } + if (isPriorityTaskManagerRegistered) { + checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + } + if (priorityTaskManager != null && isLoading()) { + priorityTaskManager.add(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = true; + } else { + isPriorityTaskManagerRegistered = false; + } + this.priorityTaskManager = priorityTaskManager; } @Override @Nullable public Format getVideoFormat() { - return player.getVideoFormat(); + return videoFormat; } @Override @Nullable public Format getAudioFormat() { - return player.getAudioFormat(); + return audioFormat; } @Override @Nullable public DecoderCounters getVideoDecoderCounters() { - return player.getVideoDecoderCounters(); + return videoDecoderCounters; } @Override @Nullable public DecoderCounters getAudioDecoderCounters() { - return player.getAudioDecoderCounters(); + return audioDecoderCounters; } @Override public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - player.setVideoFrameMetadataListener(listener); + verifyApplicationThread(); + videoFrameMetadataListener = listener; + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) + .setPayload(listener) + .send(); } @Override public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - player.clearVideoFrameMetadataListener(listener); + verifyApplicationThread(); + if (videoFrameMetadataListener != listener) { + return; + } + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) + .setPayload(null) + .send(); } @Override public void setCameraMotionListener(CameraMotionListener listener) { - player.setCameraMotionListener(listener); + verifyApplicationThread(); + cameraMotionListener = listener; + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) + .setPayload(listener) + .send(); } @Override public void clearCameraMotionListener(CameraMotionListener listener) { - player.clearCameraMotionListener(listener); + verifyApplicationThread(); + if (cameraMotionListener != listener) { + return; + } + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) + .setPayload(null) + .send(); } @Override public List getCurrentCues() { - return player.getCurrentCues(); + verifyApplicationThread(); + return currentCues; } // ExoPlayer implementation @@ -618,59 +994,78 @@ public class SimpleExoPlayer extends BasePlayer @Override public void addListener(Listener listener) { - player.addListener(listener); + checkNotNull(listener); + listeners.add(listener); + EventListener eventListener = listener; + addListener(eventListener); } @Deprecated @Override public void addListener(Player.EventListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + checkNotNull(listener); player.addEventListener(listener); } @Override public void removeListener(Listener listener) { - player.removeListener(listener); + checkNotNull(listener); + listeners.remove(listener); + EventListener eventListener = listener; + removeListener(eventListener); } @Deprecated @Override public void removeListener(Player.EventListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. player.removeEventListener(listener); } @Override @State public int getPlaybackState() { + verifyApplicationThread(); return player.getPlaybackState(); } @Override @PlaybackSuppressionReason public int getPlaybackSuppressionReason() { + verifyApplicationThread(); return player.getPlaybackSuppressionReason(); } @Override @Nullable public ExoPlaybackException getPlayerError() { + verifyApplicationThread(); return player.getPlayerError(); } /** @deprecated Use {@link #prepare()} instead. */ @Deprecated @Override - @SuppressWarnings("deprecation") // Calling deprecated method. public void retry() { - player.retry(); + verifyApplicationThread(); + prepare(); } @Override public Commands getAvailableCommands() { + verifyApplicationThread(); return player.getAvailableCommands(); } @Override public void prepare() { + verifyApplicationThread(); + boolean playWhenReady = getPlayWhenReady(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, Player.STATE_BUFFERING); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); player.prepare(); } @@ -679,9 +1074,9 @@ public class SimpleExoPlayer extends BasePlayer */ @Deprecated @Override - @SuppressWarnings("deprecation") // Forwarding deprecated method. + @SuppressWarnings("deprecation") public void prepare(MediaSource mediaSource) { - player.prepare(mediaSource); + prepare(mediaSource, /* resetPosition= */ true, /* resetState= */ true); } /** @@ -690,245 +1085,319 @@ public class SimpleExoPlayer extends BasePlayer */ @Deprecated @Override - @SuppressWarnings("deprecation") // Forwarding deprecated method. public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { - player.prepare(mediaSource, resetPosition, resetState); + verifyApplicationThread(); + setMediaSources(Collections.singletonList(mediaSource), resetPosition); + prepare(); } @Override public void setMediaItems(List mediaItems, boolean resetPosition) { + verifyApplicationThread(); player.setMediaItems(mediaItems, resetPosition); } @Override public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { + verifyApplicationThread(); player.setMediaItems(mediaItems, startIndex, startPositionMs); } @Override public void setMediaSources(List mediaSources) { + verifyApplicationThread(); player.setMediaSources(mediaSources); } @Override public void setMediaSources(List mediaSources, boolean resetPosition) { + verifyApplicationThread(); player.setMediaSources(mediaSources, resetPosition); } @Override public void setMediaSources( List mediaSources, int startMediaItemIndex, long startPositionMs) { + verifyApplicationThread(); player.setMediaSources(mediaSources, startMediaItemIndex, startPositionMs); } @Override public void setMediaSource(MediaSource mediaSource) { + verifyApplicationThread(); player.setMediaSource(mediaSource); } @Override public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { + verifyApplicationThread(); player.setMediaSource(mediaSource, resetPosition); } @Override public void setMediaSource(MediaSource mediaSource, long startPositionMs) { + verifyApplicationThread(); player.setMediaSource(mediaSource, startPositionMs); } @Override public void addMediaItems(int index, List mediaItems) { + verifyApplicationThread(); player.addMediaItems(index, mediaItems); } @Override public void addMediaSource(MediaSource mediaSource) { + verifyApplicationThread(); player.addMediaSource(mediaSource); } @Override public void addMediaSource(int index, MediaSource mediaSource) { + verifyApplicationThread(); player.addMediaSource(index, mediaSource); } @Override public void addMediaSources(List mediaSources) { + verifyApplicationThread(); player.addMediaSources(mediaSources); } @Override public void addMediaSources(int index, List mediaSources) { + verifyApplicationThread(); player.addMediaSources(index, mediaSources); } @Override public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { + verifyApplicationThread(); player.moveMediaItems(fromIndex, toIndex, newIndex); } @Override public void removeMediaItems(int fromIndex, int toIndex) { + verifyApplicationThread(); player.removeMediaItems(fromIndex, toIndex); } @Override public void setShuffleOrder(ShuffleOrder shuffleOrder) { + verifyApplicationThread(); player.setShuffleOrder(shuffleOrder); } @Override public void setPlayWhenReady(boolean playWhenReady) { - player.setPlayWhenReady(playWhenReady); + verifyApplicationThread(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); } @Override public boolean getPlayWhenReady() { + verifyApplicationThread(); return player.getPlayWhenReady(); } @Override public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { + verifyApplicationThread(); player.setPauseAtEndOfMediaItems(pauseAtEndOfMediaItems); } @Override public boolean getPauseAtEndOfMediaItems() { + verifyApplicationThread(); return player.getPauseAtEndOfMediaItems(); } @Override public @RepeatMode int getRepeatMode() { + verifyApplicationThread(); return player.getRepeatMode(); } @Override public void setRepeatMode(@RepeatMode int repeatMode) { + verifyApplicationThread(); player.setRepeatMode(repeatMode); } @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { + verifyApplicationThread(); player.setShuffleModeEnabled(shuffleModeEnabled); } @Override public boolean getShuffleModeEnabled() { + verifyApplicationThread(); return player.getShuffleModeEnabled(); } @Override public boolean isLoading() { + verifyApplicationThread(); return player.isLoading(); } @Override public void seekTo(int mediaItemIndex, long positionMs) { + verifyApplicationThread(); + analyticsCollector.notifySeekStarted(); player.seekTo(mediaItemIndex, positionMs); } @Override public long getSeekBackIncrement() { + verifyApplicationThread(); return player.getSeekBackIncrement(); } @Override public long getSeekForwardIncrement() { + verifyApplicationThread(); return player.getSeekForwardIncrement(); } @Override public long getMaxSeekToPreviousPosition() { + verifyApplicationThread(); return player.getMaxSeekToPreviousPosition(); } @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { + verifyApplicationThread(); player.setPlaybackParameters(playbackParameters); } @Override public PlaybackParameters getPlaybackParameters() { + verifyApplicationThread(); return player.getPlaybackParameters(); } @Override public void setSeekParameters(@Nullable SeekParameters seekParameters) { + verifyApplicationThread(); player.setSeekParameters(seekParameters); } @Override public SeekParameters getSeekParameters() { + verifyApplicationThread(); return player.getSeekParameters(); } @Override public void setForegroundMode(boolean foregroundMode) { + verifyApplicationThread(); player.setForegroundMode(foregroundMode); } @Override public void stop() { - player.stop(); + stop(/* reset= */ false); } @Deprecated @Override public void stop(boolean reset) { + verifyApplicationThread(); + audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); player.stop(reset); + currentCues = Collections.emptyList(); } @Override public void release() { + verifyApplicationThread(); + if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) { + keepSessionIdAudioTrack.release(); + keepSessionIdAudioTrack = null; + } + audioBecomingNoisyManager.setEnabled(false); + streamVolumeManager.release(); + wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); + audioFocusManager.release(); player.release(); + analyticsCollector.release(); + removeSurfaceCallbacks(); + if (ownedSurface != null) { + ownedSurface.release(); + ownedSurface = null; + } + if (isPriorityTaskManagerRegistered) { + checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = false; + } + currentCues = Collections.emptyList(); + playerReleased = true; } @Override public PlayerMessage createMessage(PlayerMessage.Target target) { + verifyApplicationThread(); return player.createMessage(target); } @Override public int getRendererCount() { + verifyApplicationThread(); return player.getRendererCount(); } @Override public @C.TrackType int getRendererType(int index) { + verifyApplicationThread(); return player.getRendererType(index); } @Override public Renderer getRenderer(int index) { + verifyApplicationThread(); return player.getRenderer(index); } @Override public TrackSelector getTrackSelector() { + verifyApplicationThread(); return player.getTrackSelector(); } @Override public TrackGroupArray getCurrentTrackGroups() { + verifyApplicationThread(); return player.getCurrentTrackGroups(); } @Override public TrackSelectionArray getCurrentTrackSelections() { + verifyApplicationThread(); return player.getCurrentTrackSelections(); } @Override public TracksInfo getCurrentTracksInfo() { + verifyApplicationThread(); return player.getCurrentTracksInfo(); } @Override public TrackSelectionParameters getTrackSelectionParameters() { + verifyApplicationThread(); return player.getTrackSelectionParameters(); } @Override public void setTrackSelectionParameters(TrackSelectionParameters parameters) { + verifyApplicationThread(); player.setTrackSelectionParameters(parameters); } @@ -949,111 +1418,782 @@ public class SimpleExoPlayer extends BasePlayer @Override public Timeline getCurrentTimeline() { + verifyApplicationThread(); return player.getCurrentTimeline(); } @Override public int getCurrentPeriodIndex() { + verifyApplicationThread(); return player.getCurrentPeriodIndex(); } @Override public int getCurrentMediaItemIndex() { + verifyApplicationThread(); return player.getCurrentMediaItemIndex(); } @Override public long getDuration() { + verifyApplicationThread(); return player.getDuration(); } @Override public long getCurrentPosition() { + verifyApplicationThread(); return player.getCurrentPosition(); } @Override public long getBufferedPosition() { + verifyApplicationThread(); return player.getBufferedPosition(); } @Override public long getTotalBufferedDuration() { + verifyApplicationThread(); return player.getTotalBufferedDuration(); } @Override public boolean isPlayingAd() { + verifyApplicationThread(); return player.isPlayingAd(); } @Override public int getCurrentAdGroupIndex() { + verifyApplicationThread(); return player.getCurrentAdGroupIndex(); } @Override public int getCurrentAdIndexInAdGroup() { + verifyApplicationThread(); return player.getCurrentAdIndexInAdGroup(); } @Override public long getContentPosition() { + verifyApplicationThread(); return player.getContentPosition(); } @Override public long getContentBufferedPosition() { + verifyApplicationThread(); return player.getContentBufferedPosition(); } @Deprecated @Override public void setHandleWakeLock(boolean handleWakeLock) { - player.setHandleWakeLock(handleWakeLock); + setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); } @Override public void setWakeMode(@C.WakeMode int wakeMode) { - player.setWakeMode(wakeMode); + verifyApplicationThread(); + switch (wakeMode) { + case C.WAKE_MODE_NONE: + wakeLockManager.setEnabled(false); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_LOCAL: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_NETWORK: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(true); + break; + default: + break; + } } @Override public DeviceInfo getDeviceInfo() { - return player.getDeviceInfo(); + verifyApplicationThread(); + return deviceInfo; } @Override public int getDeviceVolume() { - return player.getDeviceVolume(); + verifyApplicationThread(); + return streamVolumeManager.getVolume(); } @Override public boolean isDeviceMuted() { - return player.isDeviceMuted(); + verifyApplicationThread(); + return streamVolumeManager.isMuted(); } @Override public void setDeviceVolume(int volume) { - player.setDeviceVolume(volume); + verifyApplicationThread(); + streamVolumeManager.setVolume(volume); } @Override public void increaseDeviceVolume() { - player.increaseDeviceVolume(); + verifyApplicationThread(); + streamVolumeManager.increaseVolume(); } @Override public void decreaseDeviceVolume() { - player.decreaseDeviceVolume(); + verifyApplicationThread(); + streamVolumeManager.decreaseVolume(); } @Override public void setDeviceMuted(boolean muted) { - player.setDeviceMuted(muted); + verifyApplicationThread(); + streamVolumeManager.setMuted(muted); } /* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) { - player.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread); + this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread; + } + + // Internal methods. + + private void removeSurfaceCallbacks() { + if (sphericalGLSurfaceView != null) { + player + .createMessage(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) + .setPayload(null) + .send(); + sphericalGLSurfaceView.removeVideoSurfaceListener(componentListener); + sphericalGLSurfaceView = null; + } + if (textureView != null) { + if (textureView.getSurfaceTextureListener() != componentListener) { + Log.w(TAG, "SurfaceTextureListener already unset or replaced."); + } else { + textureView.setSurfaceTextureListener(null); + } + textureView = null; + } + if (surfaceHolder != null) { + surfaceHolder.removeCallback(componentListener); + surfaceHolder = null; + } + } + + private void setSurfaceTextureInternal(SurfaceTexture surfaceTexture) { + Surface surface = new Surface(surfaceTexture); + setVideoOutputInternal(surface); + ownedSurface = surface; + } + + private void setVideoOutputInternal(@Nullable Object videoOutput) { + // Note: We don't turn this method into a no-op if the output is being replaced with itself so + // as to ensure onRenderedFirstFrame callbacks are still called in this case. + List messages = new ArrayList<>(); + for (Renderer renderer : renderers) { + if (renderer.getTrackType() == TRACK_TYPE_VIDEO) { + messages.add( + player + .createMessage(renderer) + .setType(MSG_SET_VIDEO_OUTPUT) + .setPayload(videoOutput) + .send()); + } + } + boolean messageDeliveryTimedOut = false; + if (this.videoOutput != null && this.videoOutput != videoOutput) { + // We're replacing an output. Block to ensure that this output will not be accessed by the + // renderers after this method returns. + try { + for (PlayerMessage message : messages) { + message.blockUntilDelivered(detachSurfaceTimeoutMs); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (TimeoutException e) { + messageDeliveryTimedOut = true; + } + if (this.videoOutput == ownedSurface) { + // We're replacing a surface that we are responsible for releasing. + ownedSurface.release(); + ownedSurface = null; + } + } + this.videoOutput = videoOutput; + if (messageDeliveryTimedOut) { + player.stop( + /* reset= */ false, + ExoPlaybackException.createForUnexpected( + new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE), + PlaybackException.ERROR_CODE_TIMEOUT)); + } + } + + /** + * Sets the holder of the surface that will be displayed to the user, but which should + * not be the output for video renderers. This case occurs when video frames need to be + * rendered to an intermediate surface (which is not the one held by the provided holder). + * + * @param nonVideoOutputSurfaceHolder The holder of the surface that will eventually be displayed + * to the user. + */ + private void setNonVideoOutputSurfaceHolderInternal(SurfaceHolder nonVideoOutputSurfaceHolder) { + // Although we won't use the view's surface directly as the video output, still use the holder + // to query the surface size, to be informed in changes to the size via componentListener, and + // for equality checking in clearVideoSurfaceHolder. + surfaceHolderSurfaceIsVideoOutput = false; + surfaceHolder = nonVideoOutputSurfaceHolder; + surfaceHolder.addCallback(componentListener); + Surface surface = surfaceHolder.getSurface(); + if (surface != null && surface.isValid()) { + Rect surfaceSize = surfaceHolder.getSurfaceFrame(); + maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); + } else { + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + } + + private void maybeNotifySurfaceSizeChanged(int width, int height) { + if (width != surfaceWidth || height != surfaceHeight) { + surfaceWidth = width; + surfaceHeight = height; + analyticsCollector.onSurfaceSizeChanged(width, height); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onSurfaceSizeChanged(width, height); + } + } + } + + private void sendVolumeToRenderers() { + float scaledVolume = volume * audioFocusManager.getVolumeMultiplier(); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_VOLUME, scaledVolume); + } + + @SuppressWarnings("SuspiciousMethodCalls") + private void notifySkipSilenceEnabledChanged() { + analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); + } + } + + private void updatePlayWhenReady( + boolean playWhenReady, + @AudioFocusManager.PlayerCommand int playerCommand, + @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason) { + playWhenReady = playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY; + @PlaybackSuppressionReason + int playbackSuppressionReason = + playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY + ? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS + : Player.PLAYBACK_SUPPRESSION_REASON_NONE; + player.setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason); + } + + private void updateWakeAndWifiLock() { + @State int playbackState = getPlaybackState(); + switch (playbackState) { + case Player.STATE_READY: + case Player.STATE_BUFFERING: + boolean isSleeping = experimentalIsSleepingForOffload(); + wakeLockManager.setStayAwake(getPlayWhenReady() && !isSleeping); + // The wifi lock is not released while sleeping to avoid interrupting downloads. + wifiLockManager.setStayAwake(getPlayWhenReady()); + break; + case Player.STATE_ENDED: + case Player.STATE_IDLE: + wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); + break; + default: + throw new IllegalStateException(); + } + } + + private void verifyApplicationThread() { + // The constructor may be executed on a background thread. Wait with accessing the player from + // the app thread until the constructor finished executing. + constructorFinished.blockUninterruptible(); + if (Thread.currentThread() != getApplicationLooper().getThread()) { + String message = + Util.formatInvariant( + "Player is accessed on the wrong thread.\n" + + "Current thread: '%s'\n" + + "Expected thread: '%s'\n" + + "See https://exoplayer.dev/issues/player-accessed-on-wrong-thread", + Thread.currentThread().getName(), getApplicationLooper().getThread().getName()); + if (throwsWhenUsingWrongThread) { + throw new IllegalStateException(message); + } + Log.w(TAG, message, hasNotifiedFullWrongThreadWarning ? null : new IllegalStateException()); + hasNotifiedFullWrongThreadWarning = true; + } + } + + private void sendRendererMessage( + @C.TrackType int trackType, int messageType, @Nullable Object payload) { + for (Renderer renderer : renderers) { + if (renderer.getTrackType() == trackType) { + player.createMessage(renderer).setType(messageType).setPayload(payload).send(); + } + } + } + + /** + * Initializes {@link #keepSessionIdAudioTrack} to keep an audio session ID alive. If the audio + * session ID is {@link C#AUDIO_SESSION_ID_UNSET} then a new audio session ID is generated. + * + *

    Use of this method is only required on API level 21 and earlier. + * + * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} to generate a + * new one. + * @return The audio session ID. + */ + private int initializeKeepSessionIdAudioTrack(int audioSessionId) { + if (keepSessionIdAudioTrack != null + && keepSessionIdAudioTrack.getAudioSessionId() != audioSessionId) { + keepSessionIdAudioTrack.release(); + keepSessionIdAudioTrack = null; + } + if (keepSessionIdAudioTrack == null) { + int sampleRate = 4000; // Minimum sample rate supported by the platform. + int channelConfig = AudioFormat.CHANNEL_OUT_MONO; + @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT; + int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback. + keepSessionIdAudioTrack = + new AudioTrack( + C.STREAM_TYPE_DEFAULT, + sampleRate, + channelConfig, + encoding, + bufferSize, + AudioTrack.MODE_STATIC, + audioSessionId); + } + return keepSessionIdAudioTrack.getAudioSessionId(); + } + + private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { + return new DeviceInfo( + DeviceInfo.PLAYBACK_TYPE_LOCAL, + streamVolumeManager.getMinVolume(), + streamVolumeManager.getMaxVolume()); + } + + private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) { + return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY + ? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS + : PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; + } + + private final class ComponentListener + implements VideoRendererEventListener, + AudioRendererEventListener, + TextOutput, + MetadataOutput, + SurfaceHolder.Callback, + TextureView.SurfaceTextureListener, + SphericalGLSurfaceView.VideoSurfaceListener, + AudioFocusManager.PlayerControl, + AudioBecomingNoisyManager.EventListener, + StreamVolumeManager.Listener, + Player.EventListener, + AudioOffloadListener { + + // VideoRendererEventListener implementation + + @Override + public void onVideoEnabled(DecoderCounters counters) { + videoDecoderCounters = counters; + analyticsCollector.onVideoEnabled(counters); + } + + @Override + public void onVideoDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + analyticsCollector.onVideoDecoderInitialized( + decoderName, initializedTimestampMs, initializationDurationMs); + } + + @Override + public void onVideoInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + videoFormat = format; + analyticsCollector.onVideoInputFormatChanged(format, decoderReuseEvaluation); + } + + @Override + public void onDroppedFrames(int count, long elapsed) { + analyticsCollector.onDroppedFrames(count, elapsed); + } + + @Override + public void onVideoSizeChanged(VideoSize videoSize) { + SimpleExoPlayer.this.videoSize = videoSize; + analyticsCollector.onVideoSizeChanged(videoSize); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onVideoSizeChanged(videoSize); + } + } + + @Override + public void onRenderedFirstFrame(Object output, long renderTimeMs) { + analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); + if (videoOutput == output) { + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onRenderedFirstFrame(); + } + } + } + + @Override + public void onVideoDecoderReleased(String decoderName) { + analyticsCollector.onVideoDecoderReleased(decoderName); + } + + @Override + public void onVideoDisabled(DecoderCounters counters) { + analyticsCollector.onVideoDisabled(counters); + videoFormat = null; + videoDecoderCounters = null; + } + + @Override + public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { + analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount); + } + + @Override + public void onVideoCodecError(Exception videoCodecError) { + analyticsCollector.onVideoCodecError(videoCodecError); + } + + // AudioRendererEventListener implementation + + @Override + public void onAudioEnabled(DecoderCounters counters) { + audioDecoderCounters = counters; + analyticsCollector.onAudioEnabled(counters); + } + + @Override + public void onAudioDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + analyticsCollector.onAudioDecoderInitialized( + decoderName, initializedTimestampMs, initializationDurationMs); + } + + @Override + public void onAudioInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + audioFormat = format; + analyticsCollector.onAudioInputFormatChanged(format, decoderReuseEvaluation); + } + + @Override + public void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { + analyticsCollector.onAudioPositionAdvancing(playoutStartSystemTimeMs); + } + + @Override + public void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { + analyticsCollector.onAudioUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + } + + @Override + public void onAudioDecoderReleased(String decoderName) { + analyticsCollector.onAudioDecoderReleased(decoderName); + } + + @Override + public void onAudioDisabled(DecoderCounters counters) { + analyticsCollector.onAudioDisabled(counters); + audioFormat = null; + audioDecoderCounters = null; + } + + @Override + public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { + if (SimpleExoPlayer.this.skipSilenceEnabled == skipSilenceEnabled) { + return; + } + SimpleExoPlayer.this.skipSilenceEnabled = skipSilenceEnabled; + notifySkipSilenceEnabledChanged(); + } + + @Override + public void onAudioSinkError(Exception audioSinkError) { + analyticsCollector.onAudioSinkError(audioSinkError); + } + + @Override + public void onAudioCodecError(Exception audioCodecError) { + analyticsCollector.onAudioCodecError(audioCodecError); + } + + // TextOutput implementation + + @Override + public void onCues(List cues) { + currentCues = cues; + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listeners : listeners) { + listeners.onCues(cues); + } + } + + // MetadataOutput implementation + + @Override + public void onMetadata(Metadata metadata) { + analyticsCollector.onMetadata(metadata); + player.onMetadata(metadata); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onMetadata(metadata); + } + } + + // SurfaceHolder.Callback implementation + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(holder.getSurface()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(/* videoOutput= */ null); + } + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + + // TextureView.SurfaceTextureListener implementation + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + setSurfaceTextureInternal(surfaceTexture); + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + // Do nothing. + } + + // SphericalGLSurfaceView.VideoSurfaceListener + + @Override + public void onVideoSurfaceCreated(Surface surface) { + setVideoOutputInternal(surface); + } + + @Override + public void onVideoSurfaceDestroyed(Surface surface) { + setVideoOutputInternal(/* videoOutput= */ null); + } + + // AudioFocusManager.PlayerControl implementation + + @Override + public void setVolumeMultiplier(float volumeMultiplier) { + sendVolumeToRenderers(); + } + + @Override + public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) { + boolean playWhenReady = getPlayWhenReady(); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + } + + // AudioBecomingNoisyManager.EventListener implementation. + + @Override + public void onAudioBecomingNoisy() { + updatePlayWhenReady( + /* playWhenReady= */ false, + AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY, + Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY); + } + + // StreamVolumeManager.Listener implementation. + + @Override + public void onStreamTypeChanged(@C.StreamType int streamType) { + DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); + if (!deviceInfo.equals(SimpleExoPlayer.this.deviceInfo)) { + SimpleExoPlayer.this.deviceInfo = deviceInfo; + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onDeviceInfoChanged(deviceInfo); + } + } + } + + @Override + public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onDeviceVolumeChanged(streamVolume, streamMuted); + } + } + + // Player.EventListener implementation. + + @Override + public void onIsLoadingChanged(boolean isLoading) { + if (priorityTaskManager != null) { + if (isLoading && !isPriorityTaskManagerRegistered) { + priorityTaskManager.add(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = true; + } else if (!isLoading && isPriorityTaskManagerRegistered) { + priorityTaskManager.remove(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = false; + } + } + } + + @Override + public void onPlaybackStateChanged(@State int playbackState) { + updateWakeAndWifiLock(); + } + + @Override + public void onPlayWhenReadyChanged( + boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { + updateWakeAndWifiLock(); + } + + // Player.AudioOffloadListener implementation. + + @Override + public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { + updateWakeAndWifiLock(); + } + } + + /** Listeners that are called on the playback thread. */ + private static final class FrameMetadataListener + implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { + + @MessageType + public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = + Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; + + @MessageType + public static final int MSG_SET_CAMERA_MOTION_LISTENER = + Renderer.MSG_SET_CAMERA_MOTION_LISTENER; + + @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; + + @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; + @Nullable private CameraMotionListener cameraMotionListener; + @Nullable private VideoFrameMetadataListener internalVideoFrameMetadataListener; + @Nullable private CameraMotionListener internalCameraMotionListener; + + @Override + public void handleMessage(@MessageType int messageType, @Nullable Object message) { + switch (messageType) { + case MSG_SET_VIDEO_FRAME_METADATA_LISTENER: + videoFrameMetadataListener = (VideoFrameMetadataListener) message; + break; + case MSG_SET_CAMERA_MOTION_LISTENER: + cameraMotionListener = (CameraMotionListener) message; + break; + case MSG_SET_SPHERICAL_SURFACE_VIEW: + @Nullable SphericalGLSurfaceView surfaceView = (SphericalGLSurfaceView) message; + if (surfaceView == null) { + internalVideoFrameMetadataListener = null; + internalCameraMotionListener = null; + } else { + internalVideoFrameMetadataListener = surfaceView.getVideoFrameMetadataListener(); + internalCameraMotionListener = surfaceView.getCameraMotionListener(); + } + break; + case Renderer.MSG_SET_AUDIO_ATTRIBUTES: + case Renderer.MSG_SET_AUDIO_SESSION_ID: + case Renderer.MSG_SET_AUX_EFFECT_INFO: + case Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY: + case Renderer.MSG_SET_SCALING_MODE: + case Renderer.MSG_SET_SKIP_SILENCE_ENABLED: + case Renderer.MSG_SET_VIDEO_OUTPUT: + case Renderer.MSG_SET_VOLUME: + case Renderer.MSG_SET_WAKEUP_LISTENER: + default: + break; + } + } + + // VideoFrameMetadataListener + + @Override + public void onVideoFrameAboutToBeRendered( + long presentationTimeUs, + long releaseTimeNs, + Format format, + @Nullable MediaFormat mediaFormat) { + if (internalVideoFrameMetadataListener != null) { + internalVideoFrameMetadataListener.onVideoFrameAboutToBeRendered( + presentationTimeUs, releaseTimeNs, format, mediaFormat); + } + if (videoFrameMetadataListener != null) { + videoFrameMetadataListener.onVideoFrameAboutToBeRendered( + presentationTimeUs, releaseTimeNs, format, mediaFormat); + } + } + + // CameraMotionListener + + @Override + public void onCameraMotion(long timeUs, float[] rotation) { + if (internalCameraMotionListener != null) { + internalCameraMotionListener.onCameraMotion(timeUs, rotation); + } + if (cameraMotionListener != null) { + cameraMotionListener.onCameraMotion(timeUs, rotation); + } + } + + @Override + public void onCameraMotionReset() { + if (internalCameraMotionListener != null) { + internalCameraMotionListener.onCameraMotionReset(); + } + if (cameraMotionListener != null) { + cameraMotionListener.onCameraMotionReset(); + } + } } } From 31aa9d458d9b654639b9e7207386a595585cf0cb Mon Sep 17 00:00:00 2001 From: claincly Date: Mon, 7 Feb 2022 21:57:34 +0000 Subject: [PATCH 163/251] Fix profile-level setting. On some old devices, the encoding level needs to be set with the encoding profile, but not on newer devices. The profile/level override is applied by following https://developer.android.com/guide/topics/media/sharing-video PiperOrigin-RevId: 427008536 --- .../transformer/DefaultEncoderFactory.java | 49 ++++++++++++------- .../media3/transformer/EncoderUtil.java | 18 +++---- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 5735e79c76..9b7d2ba4e1 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -18,6 +18,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.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.transformer.CodecFactoryUtil.createCodec; @@ -151,30 +152,44 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { // Applying suggested profile/level settings from // https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles if (Util.SDK_INT >= 29) { - if (EncoderUtil.isProfileLevelSupported( - encoderInfo, - mimeType, - MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, - EncoderUtil.LEVEL_UNSET)) { + int supportedEncodingLevel = + EncoderUtil.findHighestSupportedEncodingLevel( + encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) { // Use the highest supported profile and use B-frames. mediaFormat.setInteger( MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel); mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 1); } } else if (Util.SDK_INT >= 26) { - if (EncoderUtil.isProfileLevelSupported( - encoderInfo, - mimeType, - MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, - EncoderUtil.LEVEL_UNSET)) { - // Use the highest-supported profile, but disable the generation of B-frames. This - // accommodates some limitations in the MediaMuxer in these system versions. + int supportedEncodingLevel = + EncoderUtil.findHighestSupportedEncodingLevel( + encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) { + // Use the highest-supported profile, but disable the generation of B-frames using + // MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these + // system versions. mediaFormat.setInteger( MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel); + // TODO(b/210593256): Set KEY_LATENCY to 2 to enable B-frame production after switching to + // in-app muxing. mediaFormat.setInteger(MediaFormat.KEY_LATENCY, 1); } + } else if (Util.SDK_INT >= 23) { + int supportedLevel = + EncoderUtil.findHighestSupportedEncodingLevel( + encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); + checkState(supportedLevel != EncoderUtil.LEVEL_UNSET); + // Use the baseline profile for safest results, as encoding in baseline is required per + // https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding + mediaFormat.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); + mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedLevel); } else { - // Use the baseline profile for safest results. + // Use the baseline profile for safest results, as encoding in baseline is required per + // https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding mediaFormat.setInteger( MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); } @@ -270,11 +285,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { @Nullable String codecs = null; if (profileLevel != null && requestedFormat.sampleMimeType.equals(mimeType) - && EncoderUtil.isProfileLevelSupported( - pickedEncoder, - mimeType, - /* profile= */ profileLevel.first, - /* level= */ profileLevel.second)) { + && profileLevel.second + <= EncoderUtil.findHighestSupportedEncodingLevel( + pickedEncoder, mimeType, /* profile= */ profileLevel.first)) { codecs = requestedFormat.codecs; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java index a95d29ffae..3a6c9bc63d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java @@ -113,28 +113,26 @@ public final class EncoderUtil { } /** - * Checks whether the {@link MediaCodecInfo encoder} supports the given profile and level. + * Finds the highest supported encoding level given a profile. * * @param encoderInfo The {@link MediaCodecInfo encoderInfo}. * @param mimeType The {@link MimeTypes MIME type}. * @param profile The encoding profile. - * @param level The encoding level, specify {@link #LEVEL_UNSET} if checking whether the encoder - * supports a specific profile. - * @return Whether the profile and level (if set) is supported by the encoder. + * @return The highest supported encoding level, as documented in {@link + * MediaCodecInfo.CodecProfileLevel}, or {@link #LEVEL_UNSET} if the profile is not supported. */ - public static boolean isProfileLevelSupported( - MediaCodecInfo encoderInfo, String mimeType, int profile, int level) { + public static int findHighestSupportedEncodingLevel( + MediaCodecInfo encoderInfo, String mimeType, int profile) { // TODO(b/214964116): Merge into MediaCodecUtil. MediaCodecInfo.CodecProfileLevel[] profileLevels = encoderInfo.getCapabilitiesForType(mimeType).profileLevels; for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) { - if (profileLevel.profile == profile - && (level == LEVEL_UNSET || profileLevel.level == level)) { - return true; + if (profileLevel.profile == profile) { + return profileLevel.level; } } - return false; + return LEVEL_UNSET; } /** From 1270eb012ddc1095ad3e062158535c8792f2fd4c Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 7 Feb 2022 23:10:33 +0000 Subject: [PATCH 164/251] Fix For Sample-To-Bytes Calculation In OpusDecoder Aligning the byte size calculation with the sizes defined in opus_jni.cc for the `outputFloat=true` case #minor-release PiperOrigin-RevId: 427028982 --- .../java/androidx/media3/decoder/opus/OpusDecoder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java index 232dbafdce..62fb9c04b1 100644 --- a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java +++ b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java @@ -213,7 +213,7 @@ public final class OpusDecoder outputData.position(0); outputData.limit(result); if (skipSamples > 0) { - int bytesPerSample = channelCount * 2; + int bytesPerSample = samplesToBytes(1, channelCount, outputFloat); int skipBytes = skipSamples * bytesPerSample; if (result <= skipBytes) { skipSamples -= result / bytesPerSample; @@ -280,6 +280,12 @@ public final class OpusDecoder return DEFAULT_SEEK_PRE_ROLL_SAMPLES; } + /** Returns number of bytes to represent {@code samples}. */ + private static int samplesToBytes(int samples, int channelCount, boolean outputFloat) { + int bytesPerChannel = outputFloat ? 4 : 2; + return samples * channelCount * bytesPerChannel; + } + private static int readSignedLittleEndian16(byte[] input, int offset) { int value = input[offset] & 0xFF; value |= (input[offset + 1] & 0xFF) << 8; From d807b87e3494fcdfe1cc014003f84b4551640473 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 09:44:33 +0000 Subject: [PATCH 165/251] Rollback of https://github.com/androidx/media/commit/795762545b0972631fc800814a82a1e9bf99f9eb *** Original commit *** Rollback of https://github.com/androidx/media/commit/d93b0093aeaa860011ad447710a15dc800df7c95 *** Original commit *** Move SimpleExoPlayer logic into ExoPlayerImpl This makes SimpleExoPlayer a simple forwarding wrapper which can be removed in the future. The changes are all purely mechanical with none of the potential further simplifications made yet... *** PiperOrigin-RevId: 427131338 --- .../media3/exoplayer/ExoPlayerImpl.java | 1657 +++++++++++++++-- .../media3/exoplayer/SimpleExoPlayer.java | 1378 ++------------ 2 files changed, 1667 insertions(+), 1368 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 823f3ffd1a..6cfdc10e35 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -15,20 +15,31 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.C.TRACK_TYPE_AUDIO; +import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION; +import static androidx.media3.common.C.TRACK_TYPE_VIDEO; +import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; +import static androidx.media3.common.Player.COMMAND_GET_TEXT; import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; import static androidx.media3.common.Player.COMMAND_GET_TRACK_INFOS; +import static androidx.media3.common.Player.COMMAND_GET_VOLUME; import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; import static androidx.media3.common.Player.COMMAND_PREPARE; import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA; import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; import static androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS; +import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; +import static androidx.media3.common.Player.COMMAND_SET_VOLUME; import static androidx.media3.common.Player.COMMAND_STOP; import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; @@ -42,6 +53,7 @@ import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIS import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_SEEK; import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; +import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS; import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; import static androidx.media3.common.Player.STATE_BUFFERING; import static androidx.media3.common.Player.STATE_ENDED; @@ -51,18 +63,41 @@ import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.castNonNull; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_ATTRIBUTES; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_SESSION_ID; +import static androidx.media3.exoplayer.Renderer.MSG_SET_AUX_EFFECT_INFO; +import static androidx.media3.exoplayer.Renderer.MSG_SET_CAMERA_MOTION_LISTENER; +import static androidx.media3.exoplayer.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY; +import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE; +import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME; import static java.lang.Math.max; import static java.lang.Math.min; import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.SurfaceTexture; +import android.media.AudioFormat; +import android.media.AudioTrack; +import android.media.MediaFormat; import android.media.metrics.LogSessionId; import android.os.Handler; import android.os.Looper; import android.util.Pair; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.IllegalSeekPositionException; import androidx.media3.common.MediaItem; @@ -83,34 +118,49 @@ import androidx.media3.common.Player.PositionInfo; import androidx.media3.common.Player.RepeatMode; import androidx.media3.common.Player.State; import androidx.media3.common.Player.TimelineChangeReason; +import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelectionArray; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; +import androidx.media3.common.VideoSize; +import androidx.media3.common.text.Cue; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Clock; +import androidx.media3.common.util.ConditionVariable; import androidx.media3.common.util.HandlerWrapper; import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.ExoPlayer.AudioOffloadListener; import androidx.media3.exoplayer.PlayerMessage.Target; +import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; +import androidx.media3.exoplayer.analytics.AnalyticsListener; import androidx.media3.exoplayer.analytics.PlayerId; +import androidx.media3.exoplayer.audio.AudioRendererEventListener; +import androidx.media3.exoplayer.metadata.MetadataOutput; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.ShuffleOrder; +import androidx.media3.exoplayer.text.TextOutput; import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.trackselection.TrackSelector; import androidx.media3.exoplayer.trackselection.TrackSelectorResult; import androidx.media3.exoplayer.upstream.BandwidthMeter; +import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; +import androidx.media3.exoplayer.video.VideoFrameMetadataListener; +import androidx.media3.exoplayer.video.VideoRendererEventListener; +import androidx.media3.exoplayer.video.spherical.CameraMotionListener; +import androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.TimeoutException; /** A helper class for the {@link SimpleExoPlayer} implementation of {@link ExoPlayer}. */ /* package */ final class ExoPlayerImpl { @@ -131,13 +181,18 @@ import java.util.concurrent.CopyOnWriteArraySet; /* package */ final TrackSelectorResult emptyTrackSelectorResult; /* package */ final Commands permanentAvailableCommands; + private final ConditionVariable constructorFinished; + private final Context applicationContext; private final Player wrappingPlayer; private final Renderer[] renderers; private final TrackSelector trackSelector; private final HandlerWrapper playbackInfoUpdateHandler; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; - private final ListenerSet listeners; + + @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners. + private final ListenerSet eventListeners; + private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -150,6 +205,15 @@ import java.util.concurrent.CopyOnWriteArraySet; private final long seekBackIncrementMs; private final long seekForwardIncrementMs; private final Clock clock; + private final ComponentListener componentListener; + private final FrameMetadataListener frameMetadataListener; + private final CopyOnWriteArraySet listeners; + private final AudioBecomingNoisyManager audioBecomingNoisyManager; + private final AudioFocusManager audioFocusManager; + private final StreamVolumeManager streamVolumeManager; + private final WakeLockManager wakeLockManager; + private final WifiLockManager wifiLockManager; + private final long detachSurfaceTimeoutMs; @RepeatMode private int repeatMode; private boolean shuffleModeEnabled; @@ -164,6 +228,35 @@ import java.util.concurrent.CopyOnWriteArraySet; private Commands availableCommands; private MediaMetadata mediaMetadata; private MediaMetadata playlistMetadata; + @Nullable private Format videoFormat; + @Nullable private Format audioFormat; + @Nullable private AudioTrack keepSessionIdAudioTrack; + @Nullable private Object videoOutput; + @Nullable private Surface ownedSurface; + @Nullable private SurfaceHolder surfaceHolder; + @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; + private boolean surfaceHolderSurfaceIsVideoOutput; + @Nullable private TextureView textureView; + @C.VideoScalingMode private int videoScalingMode; + @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; + private int surfaceWidth; + private int surfaceHeight; + @Nullable private DecoderCounters videoDecoderCounters; + @Nullable private DecoderCounters audioDecoderCounters; + private int audioSessionId; + private AudioAttributes audioAttributes; + private float volume; + private boolean skipSilenceEnabled; + private List currentCues; + @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; + @Nullable private CameraMotionListener cameraMotionListener; + private boolean throwsWhenUsingWrongThread; + private boolean hasNotifiedFullWrongThreadWarning; + @Nullable private PriorityTaskManager priorityTaskManager; + private boolean isPriorityTaskManagerRegistered; + private boolean playerReleased; + private DeviceInfo deviceInfo; + private VideoSize videoSize; // MediaMetadata built from static (TrackGroup Format) and dynamic (onMetadata(Metadata)) metadata // sources. @@ -177,145 +270,178 @@ import java.util.concurrent.CopyOnWriteArraySet; private int maskingPeriodIndex; private long maskingWindowPositionMs; - /** - * Constructs an instance. Must be called from a thread that has an associated {@link Looper}. - * - * @param renderers The {@link Renderer}s. - * @param trackSelector The {@link TrackSelector}. - * @param mediaSourceFactory The {@link MediaSource.Factory}. - * @param loadControl The {@link LoadControl}. - * @param bandwidthMeter The {@link BandwidthMeter}. - * @param analyticsCollector The {@link AnalyticsCollector}. - * @param useLazyPreparation Whether playlist items are prepared lazily. If false, all manifest - * loads and other initial preparation steps happen immediately. If true, these initial - * preparations are triggered only when the player starts buffering the media. - * @param seekParameters The {@link SeekParameters}. - * @param seekBackIncrementMs The seek back increment in milliseconds. - * @param seekForwardIncrementMs The seek forward increment in milliseconds. - * @param livePlaybackSpeedControl The {@link LivePlaybackSpeedControl}. - * @param releaseTimeoutMs The timeout for calls to {@link #release()} in milliseconds. - * @param pauseAtEndOfMediaItems Whether to pause playback at the end of each media item. - * @param clock The {@link Clock}. - * @param applicationLooper The {@link Looper} that must be used for all calls to the player and - * which is used to call listeners on. - * @param wrappingPlayer The {@link Player} using this class. - * @param additionalPermanentAvailableCommands The {@link Commands} that are permanently available - * in the wrapping player but that are not in this player. - */ @SuppressLint("HandlerLeak") - public ExoPlayerImpl( - Renderer[] renderers, - TrackSelector trackSelector, - MediaSource.Factory mediaSourceFactory, - LoadControl loadControl, - BandwidthMeter bandwidthMeter, - AnalyticsCollector analyticsCollector, - boolean useLazyPreparation, - SeekParameters seekParameters, - long seekBackIncrementMs, - long seekForwardIncrementMs, - LivePlaybackSpeedControl livePlaybackSpeedControl, - long releaseTimeoutMs, - boolean pauseAtEndOfMediaItems, - Clock clock, - Looper applicationLooper, - Player wrappingPlayer, - Commands additionalPermanentAvailableCommands) { - Log.i( - TAG, - "Init " - + Integer.toHexString(System.identityHashCode(this)) - + " [" - + MediaLibraryInfo.VERSION_SLASHY - + "] [" - + Util.DEVICE_DEBUG_INFO - + "]"); - checkState(renderers.length > 0); - this.renderers = checkNotNull(renderers); - this.trackSelector = checkNotNull(trackSelector); - this.mediaSourceFactory = mediaSourceFactory; - this.bandwidthMeter = bandwidthMeter; - this.analyticsCollector = analyticsCollector; - this.useLazyPreparation = useLazyPreparation; - this.seekParameters = seekParameters; - this.seekBackIncrementMs = seekBackIncrementMs; - this.seekForwardIncrementMs = seekForwardIncrementMs; - this.pauseAtEndOfMediaItems = pauseAtEndOfMediaItems; - this.applicationLooper = applicationLooper; - this.clock = clock; - this.wrappingPlayer = wrappingPlayer; - repeatMode = Player.REPEAT_MODE_OFF; - listeners = - new ListenerSet<>( - applicationLooper, - clock, - (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); - audioOffloadListeners = new CopyOnWriteArraySet<>(); - mediaSourceHolderSnapshots = new ArrayList<>(); - shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); - emptyTrackSelectorResult = - new TrackSelectorResult( - new RendererConfiguration[renderers.length], - new ExoTrackSelection[renderers.length], - TracksInfo.EMPTY, - /* info= */ null); - period = new Timeline.Period(); - window = new Timeline.Window(); - permanentAvailableCommands = - new Commands.Builder() - .addAll( - COMMAND_PLAY_PAUSE, - COMMAND_PREPARE, - COMMAND_STOP, - COMMAND_SET_SPEED_AND_PITCH, - COMMAND_SET_SHUFFLE_MODE, - COMMAND_SET_REPEAT_MODE, - COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_GET_TIMELINE, - COMMAND_GET_MEDIA_ITEMS_METADATA, - COMMAND_SET_MEDIA_ITEMS_METADATA, - COMMAND_CHANGE_MEDIA_ITEMS, - COMMAND_GET_TRACK_INFOS) - .addIf(COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) - .addAll(additionalPermanentAvailableCommands) - .build(); - availableCommands = - new Commands.Builder() - .addAll(permanentAvailableCommands) - .add(COMMAND_SEEK_TO_DEFAULT_POSITION) - .add(COMMAND_SEEK_TO_MEDIA_ITEM) - .build(); - mediaMetadata = MediaMetadata.EMPTY; - playlistMetadata = MediaMetadata.EMPTY; - staticAndDynamicMediaMetadata = MediaMetadata.EMPTY; - maskingWindowIndex = C.INDEX_UNSET; - playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); - playbackInfoUpdateListener = - playbackInfoUpdate -> - playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); - playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); - analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); - addListener(analyticsCollector); - bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); - internalPlayer = - new ExoPlayerImplInternal( - renderers, - trackSelector, - emptyTrackSelectorResult, - loadControl, - bandwidthMeter, - repeatMode, - shuffleModeEnabled, - analyticsCollector, - seekParameters, - livePlaybackSpeedControl, - releaseTimeoutMs, - pauseAtEndOfMediaItems, - applicationLooper, - clock, - playbackInfoUpdateListener, - playerId); + public ExoPlayerImpl(ExoPlayer.Builder builder, Player wrappingPlayer) { + constructorFinished = new ConditionVariable(); + try { + Log.i( + TAG, + "Init " + + Integer.toHexString(System.identityHashCode(this)) + + " [" + + MediaLibraryInfo.VERSION_SLASHY + + "] [" + + Util.DEVICE_DEBUG_INFO + + "]"); + applicationContext = builder.context.getApplicationContext(); + analyticsCollector = builder.analyticsCollectorSupplier.get(); + priorityTaskManager = builder.priorityTaskManager; + audioAttributes = builder.audioAttributes; + videoScalingMode = builder.videoScalingMode; + videoChangeFrameRateStrategy = builder.videoChangeFrameRateStrategy; + skipSilenceEnabled = builder.skipSilenceEnabled; + detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; + componentListener = new ComponentListener(); + frameMetadataListener = new FrameMetadataListener(); + listeners = new CopyOnWriteArraySet<>(); + Handler eventHandler = new Handler(builder.looper); + renderers = + builder + .renderersFactorySupplier + .get() + .createRenderers( + eventHandler, + componentListener, + componentListener, + componentListener, + componentListener); + checkState(renderers.length > 0); + this.trackSelector = builder.trackSelectorSupplier.get(); + this.mediaSourceFactory = builder.mediaSourceFactorySupplier.get(); + this.bandwidthMeter = builder.bandwidthMeterSupplier.get(); + this.useLazyPreparation = builder.useLazyPreparation; + this.seekParameters = builder.seekParameters; + this.seekBackIncrementMs = builder.seekBackIncrementMs; + this.seekForwardIncrementMs = builder.seekForwardIncrementMs; + this.pauseAtEndOfMediaItems = builder.pauseAtEndOfMediaItems; + this.applicationLooper = builder.looper; + this.clock = builder.clock; + this.wrappingPlayer = wrappingPlayer; + eventListeners = + new ListenerSet<>( + applicationLooper, + clock, + (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); + audioOffloadListeners = new CopyOnWriteArraySet<>(); + mediaSourceHolderSnapshots = new ArrayList<>(); + shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); + emptyTrackSelectorResult = + new TrackSelectorResult( + new RendererConfiguration[renderers.length], + new ExoTrackSelection[renderers.length], + TracksInfo.EMPTY, + /* info= */ null); + period = new Timeline.Period(); + window = new Timeline.Window(); + permanentAvailableCommands = + new Commands.Builder() + .addAll( + COMMAND_PLAY_PAUSE, + COMMAND_PREPARE, + COMMAND_STOP, + COMMAND_SET_SPEED_AND_PITCH, + COMMAND_SET_SHUFFLE_MODE, + COMMAND_SET_REPEAT_MODE, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_TIMELINE, + COMMAND_GET_MEDIA_ITEMS_METADATA, + COMMAND_SET_MEDIA_ITEMS_METADATA, + COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_TRACK_INFOS, + COMMAND_GET_AUDIO_ATTRIBUTES, + COMMAND_GET_VOLUME, + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_SET_VIDEO_SURFACE, + COMMAND_GET_TEXT) + .addIf( + COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) + .build(); + availableCommands = + new Commands.Builder() + .addAll(permanentAvailableCommands) + .add(COMMAND_SEEK_TO_DEFAULT_POSITION) + .add(COMMAND_SEEK_TO_MEDIA_ITEM) + .build(); + playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); + playbackInfoUpdateListener = + playbackInfoUpdate -> + playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); + playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); + analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); + PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); + internalPlayer = + new ExoPlayerImplInternal( + renderers, + trackSelector, + emptyTrackSelectorResult, + builder.loadControlSupplier.get(), + bandwidthMeter, + repeatMode, + shuffleModeEnabled, + analyticsCollector, + seekParameters, + builder.livePlaybackSpeedControl, + builder.releaseTimeoutMs, + pauseAtEndOfMediaItems, + applicationLooper, + clock, + playbackInfoUpdateListener, + playerId); + + volume = 1; + repeatMode = Player.REPEAT_MODE_OFF; + mediaMetadata = MediaMetadata.EMPTY; + playlistMetadata = MediaMetadata.EMPTY; + staticAndDynamicMediaMetadata = MediaMetadata.EMPTY; + maskingWindowIndex = C.INDEX_UNSET; + if (Util.SDK_INT < 21) { + audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); + } else { + audioSessionId = Util.generateAudioSessionIdV21(applicationContext); + } + currentCues = ImmutableList.of(); + throwsWhenUsingWrongThread = true; + + addEventListener(analyticsCollector); + bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); + addEventListener(componentListener); + addAudioOffloadListener(componentListener); + if (builder.foregroundModeTimeoutMs > 0) { + experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); + } + + audioBecomingNoisyManager = + new AudioBecomingNoisyManager(builder.context, eventHandler, componentListener); + audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); + audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); + audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); + streamVolumeManager = + new StreamVolumeManager(builder.context, eventHandler, componentListener); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + wakeLockManager = new WakeLockManager(builder.context); + wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); + wifiLockManager = new WifiLockManager(builder.context); + wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); + deviceInfo = createDeviceInfo(streamVolumeManager); + videoSize = VideoSize.UNKNOWN; + + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_VIDEO_FRAME_METADATA_LISTENER, frameMetadataListener); + sendRendererMessage( + TRACK_TYPE_CAMERA_MOTION, MSG_SET_CAMERA_MOTION_LISTENER, frameMetadataListener); + } finally { + constructorFinished.open(); + } } /** @@ -333,63 +459,72 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) { + verifyApplicationThread(); internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); } public boolean experimentalIsSleepingForOffload() { + verifyApplicationThread(); return playbackInfo.sleepingForOffload; } public Looper getPlaybackLooper() { + // Don't verify application thread. We allow calls to this method from any thread. return internalPlayer.getPlaybackLooper(); } public Looper getApplicationLooper() { + // Don't verify application thread. We allow calls to this method from any thread. return applicationLooper; } public Clock getClock() { + // Don't verify application thread. We allow calls to this method from any thread. return clock; } - public void addListener(Listener listener) { - addEventListener(listener); - } - @SuppressWarnings("deprecation") // Register deprecated EventListener. public void addEventListener(Player.EventListener eventListener) { - listeners.add(eventListener); + // Don't verify application thread. We allow calls to this method from any thread. + eventListeners.add(eventListener); } @SuppressWarnings("deprecation") // Deregister deprecated EventListener. public void removeEventListener(Player.EventListener eventListener) { - listeners.remove(eventListener); + // Don't verify application thread. We allow calls to this method from any thread. + eventListeners.remove(eventListener); } public void addAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); } public void removeAudioOffloadListener(AudioOffloadListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.remove(listener); } public Commands getAvailableCommands() { + verifyApplicationThread(); return availableCommands; } @State public int getPlaybackState() { + verifyApplicationThread(); return playbackInfo.playbackState; } @PlaybackSuppressionReason public int getPlaybackSuppressionReason() { + verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } @Nullable public ExoPlaybackException getPlayerError() { + verifyApplicationThread(); return playbackInfo.playbackError; } @@ -400,6 +535,12 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void prepare() { + verifyApplicationThread(); + boolean playWhenReady = getPlayWhenReady(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, Player.STATE_BUFFERING); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); if (playbackInfo.playbackState != Player.STATE_IDLE) { return; } @@ -429,6 +570,7 @@ import java.util.concurrent.CopyOnWriteArraySet; */ @Deprecated public void prepare(MediaSource mediaSource) { + verifyApplicationThread(); setMediaSource(mediaSource); prepare(); } @@ -439,36 +581,44 @@ import java.util.concurrent.CopyOnWriteArraySet; */ @Deprecated public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + verifyApplicationThread(); setMediaSource(mediaSource, resetPosition); prepare(); } public void setMediaItems(List mediaItems, boolean resetPosition) { + verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), resetPosition); } public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { + verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), startIndex, startPositionMs); } public void setMediaSource(MediaSource mediaSource) { + verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource)); } public void setMediaSource(MediaSource mediaSource, long startPositionMs) { + verifyApplicationThread(); setMediaSources( Collections.singletonList(mediaSource), /* startWindowIndex= */ 0, startPositionMs); } public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { + verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource), resetPosition); } public void setMediaSources(List mediaSources) { + verifyApplicationThread(); setMediaSources(mediaSources, /* resetPosition= */ true); } public void setMediaSources(List mediaSources, boolean resetPosition) { + verifyApplicationThread(); setMediaSourcesInternal( mediaSources, /* startWindowIndex= */ C.INDEX_UNSET, @@ -478,28 +628,34 @@ import java.util.concurrent.CopyOnWriteArraySet; public void setMediaSources( List mediaSources, int startWindowIndex, long startPositionMs) { + verifyApplicationThread(); setMediaSourcesInternal( mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false); } public void addMediaItems(int index, List mediaItems) { + verifyApplicationThread(); index = min(index, mediaSourceHolderSnapshots.size()); addMediaSources(index, createMediaSources(mediaItems)); } public void addMediaSource(MediaSource mediaSource) { + verifyApplicationThread(); addMediaSources(Collections.singletonList(mediaSource)); } public void addMediaSource(int index, MediaSource mediaSource) { + verifyApplicationThread(); addMediaSources(index, Collections.singletonList(mediaSource)); } public void addMediaSources(List mediaSources) { + verifyApplicationThread(); addMediaSources(/* index= */ mediaSourceHolderSnapshots.size(), mediaSources); } public void addMediaSources(int index, List mediaSources) { + verifyApplicationThread(); Assertions.checkArgument(index >= 0); Timeline oldTimeline = getCurrentTimeline(); pendingOperationAcks++; @@ -523,6 +679,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void removeMediaItems(int fromIndex, int toIndex) { + verifyApplicationThread(); toIndex = min(toIndex, mediaSourceHolderSnapshots.size()); PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); boolean positionDiscontinuity = @@ -539,6 +696,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) { + verifyApplicationThread(); Assertions.checkArgument( fromIndex >= 0 && fromIndex <= toIndex @@ -567,6 +725,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void setShuffleOrder(ShuffleOrder shuffleOrder) { + verifyApplicationThread(); Timeline timeline = createMaskingTimeline(); PlaybackInfo newPlaybackInfo = maskTimelineAndPosition( @@ -589,6 +748,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { + verifyApplicationThread(); if (this.pauseAtEndOfMediaItems == pauseAtEndOfMediaItems) { return; } @@ -597,9 +757,18 @@ import java.util.concurrent.CopyOnWriteArraySet; } public boolean getPauseAtEndOfMediaItems() { + verifyApplicationThread(); return pauseAtEndOfMediaItems; } + public void setPlayWhenReady(boolean playWhenReady) { + verifyApplicationThread(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + } + public void setPlayWhenReady( boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason, @@ -624,46 +793,54 @@ import java.util.concurrent.CopyOnWriteArraySet; } public boolean getPlayWhenReady() { + verifyApplicationThread(); return playbackInfo.playWhenReady; } public void setRepeatMode(@RepeatMode int repeatMode) { + verifyApplicationThread(); if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; internalPlayer.setRepeatMode(repeatMode); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); } } @RepeatMode public int getRepeatMode() { + verifyApplicationThread(); return repeatMode; } public void setShuffleModeEnabled(boolean shuffleModeEnabled) { + verifyApplicationThread(); if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); } } public boolean getShuffleModeEnabled() { + verifyApplicationThread(); return shuffleModeEnabled; } public boolean isLoading() { + verifyApplicationThread(); return playbackInfo.isLoading; } public void seekTo(int mediaItemIndex, long positionMs) { + verifyApplicationThread(); + analyticsCollector.notifySeekStarted(); Timeline timeline = playbackInfo.timeline; if (mediaItemIndex < 0 || (!timeline.isEmpty() && mediaItemIndex >= timeline.getWindowCount())) { @@ -704,18 +881,22 @@ import java.util.concurrent.CopyOnWriteArraySet; } public long getSeekBackIncrement() { + verifyApplicationThread(); return seekBackIncrementMs; } public long getSeekForwardIncrement() { + verifyApplicationThread(); return seekForwardIncrementMs; } public long getMaxSeekToPreviousPosition() { + verifyApplicationThread(); return C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS; } public void setPlaybackParameters(PlaybackParameters playbackParameters) { + verifyApplicationThread(); if (playbackParameters == null) { playbackParameters = PlaybackParameters.DEFAULT; } @@ -737,10 +918,12 @@ import java.util.concurrent.CopyOnWriteArraySet; } public PlaybackParameters getPlaybackParameters() { + verifyApplicationThread(); return playbackInfo.playbackParameters; } public void setSeekParameters(@Nullable SeekParameters seekParameters) { + verifyApplicationThread(); if (seekParameters == null) { seekParameters = SeekParameters.DEFAULT; } @@ -751,10 +934,12 @@ import java.util.concurrent.CopyOnWriteArraySet; } public SeekParameters getSeekParameters() { + verifyApplicationThread(); return seekParameters; } public void setForegroundMode(boolean foregroundMode) { + verifyApplicationThread(); if (this.foregroundMode != foregroundMode) { this.foregroundMode = foregroundMode; if (!internalPlayer.setForegroundMode(foregroundMode)) { @@ -768,8 +953,15 @@ import java.util.concurrent.CopyOnWriteArraySet; } } + public void stop() { + stop(/* reset= */ false); + } + public void stop(boolean reset) { + verifyApplicationThread(); + audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); stop(reset, /* error= */ null); + currentCues = ImmutableList.of(); } /** @@ -822,9 +1014,19 @@ import java.util.concurrent.CopyOnWriteArraySet; + "] [" + MediaLibraryInfo.registeredModules() + "]"); + verifyApplicationThread(); + if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) { + keepSessionIdAudioTrack.release(); + keepSessionIdAudioTrack = null; + } + audioBecomingNoisyManager.setEnabled(false); + streamVolumeManager.release(); + wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); + audioFocusManager.release(); if (!internalPlayer.release()) { // One of the renderers timed out releasing its resources. - listeners.sendEvent( + eventListeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( @@ -832,26 +1034,34 @@ import java.util.concurrent.CopyOnWriteArraySet; new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), PlaybackException.ERROR_CODE_TIMEOUT))); } - listeners.release(); + eventListeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(playbackInfo.periodId); playbackInfo.bufferedPositionUs = playbackInfo.positionUs; playbackInfo.totalBufferedDurationUs = 0; + analyticsCollector.release(); + removeSurfaceCallbacks(); + if (ownedSurface != null) { + ownedSurface.release(); + ownedSurface = null; + } + if (isPriorityTaskManagerRegistered) { + checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = false; + } + currentCues = ImmutableList.of(); + playerReleased = true; } public PlayerMessage createMessage(Target target) { - return new PlayerMessage( - internalPlayer, - target, - playbackInfo.timeline, - getCurrentMediaItemIndex(), - clock, - internalPlayer.getPlaybackLooper()); + verifyApplicationThread(); + return createMessageInternal(target); } public int getCurrentPeriodIndex() { + verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { return maskingPeriodIndex; } else { @@ -860,11 +1070,13 @@ import java.util.concurrent.CopyOnWriteArraySet; } public int getCurrentMediaItemIndex() { + verifyApplicationThread(); int currentWindowIndex = getCurrentWindowIndexInternal(); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } public long getDuration() { + verifyApplicationThread(); if (isPlayingAd()) { MediaPeriodId periodId = playbackInfo.periodId; playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period); @@ -882,10 +1094,12 @@ import java.util.concurrent.CopyOnWriteArraySet; } public long getCurrentPosition() { + verifyApplicationThread(); return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } public long getBufferedPosition() { + verifyApplicationThread(); if (isPlayingAd()) { return playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId) ? Util.usToMs(playbackInfo.bufferedPositionUs) @@ -895,22 +1109,27 @@ import java.util.concurrent.CopyOnWriteArraySet; } public long getTotalBufferedDuration() { + verifyApplicationThread(); return Util.usToMs(playbackInfo.totalBufferedDurationUs); } public boolean isPlayingAd() { + verifyApplicationThread(); return playbackInfo.periodId.isAd(); } public int getCurrentAdGroupIndex() { + verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; } public int getCurrentAdIndexInAdGroup() { + verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; } public long getContentPosition() { + verifyApplicationThread(); if (isPlayingAd()) { playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); return playbackInfo.requestedContentPositionUs == C.TIME_UNSET @@ -925,6 +1144,7 @@ import java.util.concurrent.CopyOnWriteArraySet; } public long getContentBufferedPosition() { + verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { return maskingWindowPositionMs; } @@ -948,53 +1168,63 @@ import java.util.concurrent.CopyOnWriteArraySet; } public int getRendererCount() { + verifyApplicationThread(); return renderers.length; } public @C.TrackType int getRendererType(int index) { + verifyApplicationThread(); return renderers[index].getTrackType(); } public Renderer getRenderer(int index) { + verifyApplicationThread(); return renderers[index]; } public TrackSelector getTrackSelector() { + verifyApplicationThread(); return trackSelector; } public TrackGroupArray getCurrentTrackGroups() { + verifyApplicationThread(); return playbackInfo.trackGroups; } public TrackSelectionArray getCurrentTrackSelections() { + verifyApplicationThread(); return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections); } public TracksInfo getCurrentTracksInfo() { + verifyApplicationThread(); return playbackInfo.trackSelectorResult.tracksInfo; } public TrackSelectionParameters getTrackSelectionParameters() { + verifyApplicationThread(); return trackSelector.getParameters(); } public void setTrackSelectionParameters(TrackSelectionParameters parameters) { + verifyApplicationThread(); if (!trackSelector.isSetParametersSupported() || parameters.equals(trackSelector.getParameters())) { return; } trackSelector.setParameters(parameters); - listeners.queueEvent( + eventListeners.queueEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } public MediaMetadata getMediaMetadata() { + verifyApplicationThread(); return mediaMetadata; } - public void onMetadata(Metadata metadata) { + private void onMetadata(Metadata metadata) { staticAndDynamicMediaMetadata = staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build(); @@ -1004,29 +1234,452 @@ import java.util.concurrent.CopyOnWriteArraySet; return; } mediaMetadata = newMediaMetadata; - listeners.sendEvent( + eventListeners.sendEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } public MediaMetadata getPlaylistMetadata() { + verifyApplicationThread(); return playlistMetadata; } public void setPlaylistMetadata(MediaMetadata playlistMetadata) { + verifyApplicationThread(); checkNotNull(playlistMetadata); if (playlistMetadata.equals(this.playlistMetadata)) { return; } this.playlistMetadata = playlistMetadata; - listeners.sendEvent( + eventListeners.sendEvent( EVENT_PLAYLIST_METADATA_CHANGED, listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } public Timeline getCurrentTimeline() { + verifyApplicationThread(); return playbackInfo.timeline; } + public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { + verifyApplicationThread(); + this.videoScalingMode = videoScalingMode; + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); + } + + @C.VideoScalingMode + public int getVideoScalingMode() { + return videoScalingMode; + } + + public void setVideoChangeFrameRateStrategy( + @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { + verifyApplicationThread(); + if (this.videoChangeFrameRateStrategy == videoChangeFrameRateStrategy) { + return; + } + this.videoChangeFrameRateStrategy = videoChangeFrameRateStrategy; + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); + } + + @C.VideoChangeFrameRateStrategy + public int getVideoChangeFrameRateStrategy() { + return videoChangeFrameRateStrategy; + } + + public VideoSize getVideoSize() { + return videoSize; + } + + public void clearVideoSurface() { + verifyApplicationThread(); + removeSurfaceCallbacks(); + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + + public void clearVideoSurface(@Nullable Surface surface) { + verifyApplicationThread(); + if (surface != null && surface == videoOutput) { + clearVideoSurface(); + } + } + + public void setVideoSurface(@Nullable Surface surface) { + verifyApplicationThread(); + removeSurfaceCallbacks(); + setVideoOutputInternal(surface); + int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; + maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); + } + + public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + verifyApplicationThread(); + if (surfaceHolder == null) { + clearVideoSurface(); + } else { + removeSurfaceCallbacks(); + this.surfaceHolderSurfaceIsVideoOutput = true; + this.surfaceHolder = surfaceHolder; + surfaceHolder.addCallback(componentListener); + Surface surface = surfaceHolder.getSurface(); + if (surface != null && surface.isValid()) { + setVideoOutputInternal(surface); + Rect surfaceSize = surfaceHolder.getSurfaceFrame(); + maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); + } else { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + } + } + + public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { + verifyApplicationThread(); + if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { + clearVideoSurface(); + } + } + + public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { + verifyApplicationThread(); + if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { + removeSurfaceCallbacks(); + setVideoOutputInternal(surfaceView); + setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); + } else if (surfaceView instanceof SphericalGLSurfaceView) { + removeSurfaceCallbacks(); + sphericalGLSurfaceView = (SphericalGLSurfaceView) surfaceView; + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) + .setPayload(sphericalGLSurfaceView) + .send(); + sphericalGLSurfaceView.addVideoSurfaceListener(componentListener); + setVideoOutputInternal(sphericalGLSurfaceView.getVideoSurface()); + setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); + } else { + setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); + } + } + + public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { + verifyApplicationThread(); + clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); + } + + public void setVideoTextureView(@Nullable TextureView textureView) { + verifyApplicationThread(); + if (textureView == null) { + clearVideoSurface(); + } else { + removeSurfaceCallbacks(); + this.textureView = textureView; + if (textureView.getSurfaceTextureListener() != null) { + Log.w(TAG, "Replacing existing SurfaceTextureListener."); + } + textureView.setSurfaceTextureListener(componentListener); + @Nullable + SurfaceTexture surfaceTexture = + textureView.isAvailable() ? textureView.getSurfaceTexture() : null; + if (surfaceTexture == null) { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } else { + setSurfaceTextureInternal(surfaceTexture); + maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight()); + } + } + } + + public void clearVideoTextureView(@Nullable TextureView textureView) { + verifyApplicationThread(); + if (textureView != null && textureView == this.textureView) { + clearVideoSurface(); + } + } + + public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { + verifyApplicationThread(); + if (playerReleased) { + return; + } + if (!Util.areEqual(this.audioAttributes, audioAttributes)) { + this.audioAttributes = audioAttributes; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + analyticsCollector.onAudioAttributesChanged(audioAttributes); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onAudioAttributesChanged(audioAttributes); + } + } + + audioFocusManager.setAudioAttributes(handleAudioFocus ? audioAttributes : null); + boolean playWhenReady = getPlayWhenReady(); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + } + + public AudioAttributes getAudioAttributes() { + return audioAttributes; + } + + public void setAudioSessionId(int audioSessionId) { + verifyApplicationThread(); + if (this.audioSessionId == audioSessionId) { + return; + } + if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { + if (Util.SDK_INT < 21) { + audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); + } else { + audioSessionId = Util.generateAudioSessionIdV21(applicationContext); + } + } else if (Util.SDK_INT < 21) { + // We need to re-initialize keepSessionIdAudioTrack to make sure the session is kept alive for + // as long as the player is using it. + initializeKeepSessionIdAudioTrack(audioSessionId); + } + this.audioSessionId = audioSessionId; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); + analyticsCollector.onAudioSessionIdChanged(audioSessionId); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onAudioSessionIdChanged(audioSessionId); + } + } + + public int getAudioSessionId() { + return audioSessionId; + } + + public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { + verifyApplicationThread(); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUX_EFFECT_INFO, auxEffectInfo); + } + + public void clearAuxEffectInfo() { + setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); + } + + public void setVolume(float volume) { + verifyApplicationThread(); + volume = Util.constrainValue(volume, /* min= */ 0, /* max= */ 1); + if (this.volume == volume) { + return; + } + this.volume = volume; + sendVolumeToRenderers(); + analyticsCollector.onVolumeChanged(volume); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onVolumeChanged(volume); + } + } + + public float getVolume() { + return volume; + } + + public boolean getSkipSilenceEnabled() { + return skipSilenceEnabled; + } + + public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { + verifyApplicationThread(); + if (this.skipSilenceEnabled == skipSilenceEnabled) { + return; + } + this.skipSilenceEnabled = skipSilenceEnabled; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); + notifySkipSilenceEnabledChanged(); + } + + public AnalyticsCollector getAnalyticsCollector() { + return analyticsCollector; + } + + public void addAnalyticsListener(AnalyticsListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + checkNotNull(listener); + analyticsCollector.addListener(listener); + } + + public void removeAnalyticsListener(AnalyticsListener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + analyticsCollector.removeListener(listener); + } + + public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { + verifyApplicationThread(); + if (playerReleased) { + return; + } + audioBecomingNoisyManager.setEnabled(handleAudioBecomingNoisy); + } + + public void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) { + verifyApplicationThread(); + if (Util.areEqual(this.priorityTaskManager, priorityTaskManager)) { + return; + } + if (isPriorityTaskManagerRegistered) { + checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); + } + if (priorityTaskManager != null && isLoading()) { + priorityTaskManager.add(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = true; + } else { + isPriorityTaskManagerRegistered = false; + } + this.priorityTaskManager = priorityTaskManager; + } + + @Nullable + public Format getVideoFormat() { + return videoFormat; + } + + @Nullable + public Format getAudioFormat() { + return audioFormat; + } + + @Nullable + public DecoderCounters getVideoDecoderCounters() { + return videoDecoderCounters; + } + + @Nullable + public DecoderCounters getAudioDecoderCounters() { + return audioDecoderCounters; + } + + public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + verifyApplicationThread(); + videoFrameMetadataListener = listener; + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) + .setPayload(listener) + .send(); + } + + public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { + verifyApplicationThread(); + if (videoFrameMetadataListener != listener) { + return; + } + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) + .setPayload(null) + .send(); + } + + public void setCameraMotionListener(CameraMotionListener listener) { + verifyApplicationThread(); + cameraMotionListener = listener; + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) + .setPayload(listener) + .send(); + } + + public void clearCameraMotionListener(CameraMotionListener listener) { + verifyApplicationThread(); + if (cameraMotionListener != listener) { + return; + } + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) + .setPayload(null) + .send(); + } + + public List getCurrentCues() { + verifyApplicationThread(); + return currentCues; + } + + public void addListener(Listener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + checkNotNull(listener); + listeners.add(listener); + addEventListener(listener); + } + + public void removeListener(Listener listener) { + // Don't verify application thread. We allow calls to this method from any thread. + checkNotNull(listener); + listeners.remove(listener); + removeEventListener(listener); + } + + public void setHandleWakeLock(boolean handleWakeLock) { + setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); + } + + public void setWakeMode(@C.WakeMode int wakeMode) { + verifyApplicationThread(); + switch (wakeMode) { + case C.WAKE_MODE_NONE: + wakeLockManager.setEnabled(false); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_LOCAL: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_NETWORK: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(true); + break; + default: + break; + } + } + + public DeviceInfo getDeviceInfo() { + verifyApplicationThread(); + return deviceInfo; + } + + public int getDeviceVolume() { + verifyApplicationThread(); + return streamVolumeManager.getVolume(); + } + + public boolean isDeviceMuted() { + verifyApplicationThread(); + return streamVolumeManager.isMuted(); + } + + public void setDeviceVolume(int volume) { + verifyApplicationThread(); + streamVolumeManager.setVolume(volume); + } + + public void increaseDeviceVolume() { + verifyApplicationThread(); + streamVolumeManager.increaseVolume(); + } + + public void decreaseDeviceVolume() { + verifyApplicationThread(); + streamVolumeManager.decreaseVolume(); + } + + public void setDeviceMuted(boolean muted) { + verifyApplicationThread(); + streamVolumeManager.setMuted(muted); + } + + /* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) { + this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread; + } + private int getCurrentWindowIndexInternal() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; @@ -1162,7 +1815,7 @@ import java.util.concurrent.CopyOnWriteArraySet; mediaMetadata = newMediaMetadata; if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); } @@ -1171,7 +1824,7 @@ import java.util.concurrent.CopyOnWriteArraySet; getPreviousPositionInfo( positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_POSITION_DISCONTINUITY, listener -> { listener.onPositionDiscontinuity(positionDiscontinuityReason); @@ -1181,16 +1834,16 @@ import java.util.concurrent.CopyOnWriteArraySet; } if (mediaItemTransitioned) { @Nullable final MediaItem finalMediaItem = mediaItem; - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); if (newPlaybackInfo.playbackError != null) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); } @@ -1199,21 +1852,21 @@ import java.util.concurrent.CopyOnWriteArraySet; trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); } if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; - listeners.queueEvent( + eventListeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { listener.onLoadingChanged(newPlaybackInfo.isLoading); @@ -1222,19 +1875,19 @@ import java.util.concurrent.CopyOnWriteArraySet; } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - listeners.queueEvent( + eventListeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged( @@ -1242,27 +1895,27 @@ import java.util.concurrent.CopyOnWriteArraySet; } if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged( newPlaybackInfo.playbackSuppressionReason)); } if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); } if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); } if (seekProcessed) { - listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); } updateAvailableCommands(); - listeners.flushEvents(); + eventListeners.flushEvents(); if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { for (AudioOffloadListener listener : audioOffloadListeners) { @@ -1419,7 +2072,7 @@ import java.util.concurrent.CopyOnWriteArraySet; Commands previousAvailableCommands = availableCommands; availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { - listeners.queueEvent( + eventListeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, listener -> listener.onAvailableCommandsChanged(availableCommands)); } @@ -1710,6 +2363,17 @@ import java.util.concurrent.CopyOnWriteArraySet; return positionUs; } + private PlayerMessage createMessageInternal(Target target) { + int currentWindowIndex = getCurrentWindowIndexInternal(); + return new PlayerMessage( + internalPlayer, + target, + playbackInfo.timeline, + currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex, + clock, + internalPlayer.getPlaybackLooper()); + } + /** * Builds a {@link MediaMetadata} from the main sources. * @@ -1727,6 +2391,235 @@ import java.util.concurrent.CopyOnWriteArraySet; return staticAndDynamicMediaMetadata.buildUpon().populate(mediaItem.mediaMetadata).build(); } + private void removeSurfaceCallbacks() { + if (sphericalGLSurfaceView != null) { + createMessageInternal(frameMetadataListener) + .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) + .setPayload(null) + .send(); + sphericalGLSurfaceView.removeVideoSurfaceListener(componentListener); + sphericalGLSurfaceView = null; + } + if (textureView != null) { + if (textureView.getSurfaceTextureListener() != componentListener) { + Log.w(TAG, "SurfaceTextureListener already unset or replaced."); + } else { + textureView.setSurfaceTextureListener(null); + } + textureView = null; + } + if (surfaceHolder != null) { + surfaceHolder.removeCallback(componentListener); + surfaceHolder = null; + } + } + + private void setSurfaceTextureInternal(SurfaceTexture surfaceTexture) { + Surface surface = new Surface(surfaceTexture); + setVideoOutputInternal(surface); + ownedSurface = surface; + } + + private void setVideoOutputInternal(@Nullable Object videoOutput) { + // Note: We don't turn this method into a no-op if the output is being replaced with itself so + // as to ensure onRenderedFirstFrame callbacks are still called in this case. + List messages = new ArrayList<>(); + for (Renderer renderer : renderers) { + if (renderer.getTrackType() == TRACK_TYPE_VIDEO) { + messages.add( + createMessageInternal(renderer) + .setType(MSG_SET_VIDEO_OUTPUT) + .setPayload(videoOutput) + .send()); + } + } + boolean messageDeliveryTimedOut = false; + if (this.videoOutput != null && this.videoOutput != videoOutput) { + // We're replacing an output. Block to ensure that this output will not be accessed by the + // renderers after this method returns. + try { + for (PlayerMessage message : messages) { + message.blockUntilDelivered(detachSurfaceTimeoutMs); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (TimeoutException e) { + messageDeliveryTimedOut = true; + } + if (this.videoOutput == ownedSurface) { + // We're replacing a surface that we are responsible for releasing. + ownedSurface.release(); + ownedSurface = null; + } + } + this.videoOutput = videoOutput; + if (messageDeliveryTimedOut) { + stop( + /* reset= */ false, + ExoPlaybackException.createForUnexpected( + new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE), + PlaybackException.ERROR_CODE_TIMEOUT)); + } + } + + /** + * Sets the holder of the surface that will be displayed to the user, but which should + * not be the output for video renderers. This case occurs when video frames need to be + * rendered to an intermediate surface (which is not the one held by the provided holder). + * + * @param nonVideoOutputSurfaceHolder The holder of the surface that will eventually be displayed + * to the user. + */ + private void setNonVideoOutputSurfaceHolderInternal(SurfaceHolder nonVideoOutputSurfaceHolder) { + // Although we won't use the view's surface directly as the video output, still use the holder + // to query the surface size, to be informed in changes to the size via componentListener, and + // for equality checking in clearVideoSurfaceHolder. + surfaceHolderSurfaceIsVideoOutput = false; + surfaceHolder = nonVideoOutputSurfaceHolder; + surfaceHolder.addCallback(componentListener); + Surface surface = surfaceHolder.getSurface(); + if (surface != null && surface.isValid()) { + Rect surfaceSize = surfaceHolder.getSurfaceFrame(); + maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); + } else { + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + } + + private void maybeNotifySurfaceSizeChanged(int width, int height) { + if (width != surfaceWidth || height != surfaceHeight) { + surfaceWidth = width; + surfaceHeight = height; + analyticsCollector.onSurfaceSizeChanged(width, height); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onSurfaceSizeChanged(width, height); + } + } + } + + private void sendVolumeToRenderers() { + float scaledVolume = volume * audioFocusManager.getVolumeMultiplier(); + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_VOLUME, scaledVolume); + } + + private void notifySkipSilenceEnabledChanged() { + analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); + } + } + + private void updatePlayWhenReady( + boolean playWhenReady, + @AudioFocusManager.PlayerCommand int playerCommand, + @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason) { + playWhenReady = playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY; + @PlaybackSuppressionReason + int playbackSuppressionReason = + playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY + ? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS + : Player.PLAYBACK_SUPPRESSION_REASON_NONE; + setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason); + } + + private void updateWakeAndWifiLock() { + @State int playbackState = getPlaybackState(); + switch (playbackState) { + case Player.STATE_READY: + case Player.STATE_BUFFERING: + boolean isSleeping = experimentalIsSleepingForOffload(); + wakeLockManager.setStayAwake(getPlayWhenReady() && !isSleeping); + // The wifi lock is not released while sleeping to avoid interrupting downloads. + wifiLockManager.setStayAwake(getPlayWhenReady()); + break; + case Player.STATE_ENDED: + case Player.STATE_IDLE: + wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); + break; + default: + throw new IllegalStateException(); + } + } + + private void verifyApplicationThread() { + // The constructor may be executed on a background thread. Wait with accessing the player from + // the app thread until the constructor finished executing. + constructorFinished.blockUninterruptible(); + if (Thread.currentThread() != getApplicationLooper().getThread()) { + String message = + Util.formatInvariant( + "Player is accessed on the wrong thread.\n" + + "Current thread: '%s'\n" + + "Expected thread: '%s'\n" + + "See https://exoplayer.dev/issues/player-accessed-on-wrong-thread", + Thread.currentThread().getName(), getApplicationLooper().getThread().getName()); + if (throwsWhenUsingWrongThread) { + throw new IllegalStateException(message); + } + Log.w(TAG, message, hasNotifiedFullWrongThreadWarning ? null : new IllegalStateException()); + hasNotifiedFullWrongThreadWarning = true; + } + } + + private void sendRendererMessage( + @C.TrackType int trackType, int messageType, @Nullable Object payload) { + for (Renderer renderer : renderers) { + if (renderer.getTrackType() == trackType) { + createMessageInternal(renderer).setType(messageType).setPayload(payload).send(); + } + } + } + + /** + * Initializes {@link #keepSessionIdAudioTrack} to keep an audio session ID alive. If the audio + * session ID is {@link C#AUDIO_SESSION_ID_UNSET} then a new audio session ID is generated. + * + *

    Use of this method is only required on API level 21 and earlier. + * + * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} to generate a + * new one. + * @return The audio session ID. + */ + private int initializeKeepSessionIdAudioTrack(int audioSessionId) { + if (keepSessionIdAudioTrack != null + && keepSessionIdAudioTrack.getAudioSessionId() != audioSessionId) { + keepSessionIdAudioTrack.release(); + keepSessionIdAudioTrack = null; + } + if (keepSessionIdAudioTrack == null) { + int sampleRate = 4000; // Minimum sample rate supported by the platform. + int channelConfig = AudioFormat.CHANNEL_OUT_MONO; + @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT; + int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback. + keepSessionIdAudioTrack = + new AudioTrack( + C.STREAM_TYPE_DEFAULT, + sampleRate, + channelConfig, + encoding, + bufferSize, + AudioTrack.MODE_STATIC, + audioSessionId); + } + return keepSessionIdAudioTrack.getAudioSessionId(); + } + + private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { + return new DeviceInfo( + DeviceInfo.PLAYBACK_TYPE_LOCAL, + streamVolumeManager.getMinVolume(), + streamVolumeManager.getMaxVolume()); + } + + private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) { + return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY + ? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS + : PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; + } + private static boolean isPlaying(PlaybackInfo playbackInfo) { return playbackInfo.playbackState == Player.STATE_READY && playbackInfo.playWhenReady @@ -1755,6 +2648,410 @@ import java.util.concurrent.CopyOnWriteArraySet; } } + // TODO(b/204189802): Remove self-listening to deprecated EventListener. + @SuppressWarnings("deprecation") + private final class ComponentListener + implements VideoRendererEventListener, + AudioRendererEventListener, + TextOutput, + MetadataOutput, + SurfaceHolder.Callback, + TextureView.SurfaceTextureListener, + SphericalGLSurfaceView.VideoSurfaceListener, + AudioFocusManager.PlayerControl, + AudioBecomingNoisyManager.EventListener, + StreamVolumeManager.Listener, + Player.EventListener, + AudioOffloadListener { + + // VideoRendererEventListener implementation + + @Override + public void onVideoEnabled(DecoderCounters counters) { + videoDecoderCounters = counters; + analyticsCollector.onVideoEnabled(counters); + } + + @Override + public void onVideoDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + analyticsCollector.onVideoDecoderInitialized( + decoderName, initializedTimestampMs, initializationDurationMs); + } + + @Override + public void onVideoInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + videoFormat = format; + analyticsCollector.onVideoInputFormatChanged(format, decoderReuseEvaluation); + } + + @Override + public void onDroppedFrames(int count, long elapsed) { + analyticsCollector.onDroppedFrames(count, elapsed); + } + + @Override + public void onVideoSizeChanged(VideoSize videoSize) { + ExoPlayerImpl.this.videoSize = videoSize; + analyticsCollector.onVideoSizeChanged(videoSize); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onVideoSizeChanged(videoSize); + } + } + + @Override + public void onRenderedFirstFrame(Object output, long renderTimeMs) { + analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); + if (videoOutput == output) { + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onRenderedFirstFrame(); + } + } + } + + @Override + public void onVideoDecoderReleased(String decoderName) { + analyticsCollector.onVideoDecoderReleased(decoderName); + } + + @Override + public void onVideoDisabled(DecoderCounters counters) { + analyticsCollector.onVideoDisabled(counters); + videoFormat = null; + videoDecoderCounters = null; + } + + @Override + public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { + analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount); + } + + @Override + public void onVideoCodecError(Exception videoCodecError) { + analyticsCollector.onVideoCodecError(videoCodecError); + } + + // AudioRendererEventListener implementation + + @Override + public void onAudioEnabled(DecoderCounters counters) { + audioDecoderCounters = counters; + analyticsCollector.onAudioEnabled(counters); + } + + @Override + public void onAudioDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + analyticsCollector.onAudioDecoderInitialized( + decoderName, initializedTimestampMs, initializationDurationMs); + } + + @Override + public void onAudioInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + audioFormat = format; + analyticsCollector.onAudioInputFormatChanged(format, decoderReuseEvaluation); + } + + @Override + public void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { + analyticsCollector.onAudioPositionAdvancing(playoutStartSystemTimeMs); + } + + @Override + public void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { + analyticsCollector.onAudioUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + } + + @Override + public void onAudioDecoderReleased(String decoderName) { + analyticsCollector.onAudioDecoderReleased(decoderName); + } + + @Override + public void onAudioDisabled(DecoderCounters counters) { + analyticsCollector.onAudioDisabled(counters); + audioFormat = null; + audioDecoderCounters = null; + } + + @Override + public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { + if (ExoPlayerImpl.this.skipSilenceEnabled == skipSilenceEnabled) { + return; + } + ExoPlayerImpl.this.skipSilenceEnabled = skipSilenceEnabled; + notifySkipSilenceEnabledChanged(); + } + + @Override + public void onAudioSinkError(Exception audioSinkError) { + analyticsCollector.onAudioSinkError(audioSinkError); + } + + @Override + public void onAudioCodecError(Exception audioCodecError) { + analyticsCollector.onAudioCodecError(audioCodecError); + } + + // TextOutput implementation + + @Override + public void onCues(List cues) { + currentCues = cues; + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listeners : listeners) { + listeners.onCues(cues); + } + } + + // MetadataOutput implementation + + @Override + public void onMetadata(Metadata metadata) { + analyticsCollector.onMetadata(metadata); + ExoPlayerImpl.this.onMetadata(metadata); + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onMetadata(metadata); + } + } + + // SurfaceHolder.Callback implementation + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(holder.getSurface()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + if (surfaceHolderSurfaceIsVideoOutput) { + setVideoOutputInternal(/* videoOutput= */ null); + } + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + } + + // TextureView.SurfaceTextureListener implementation + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + setSurfaceTextureInternal(surfaceTexture); + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { + maybeNotifySurfaceSizeChanged(width, height); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + setVideoOutputInternal(/* videoOutput= */ null); + maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + // Do nothing. + } + + // SphericalGLSurfaceView.VideoSurfaceListener + + @Override + public void onVideoSurfaceCreated(Surface surface) { + setVideoOutputInternal(surface); + } + + @Override + public void onVideoSurfaceDestroyed(Surface surface) { + setVideoOutputInternal(/* videoOutput= */ null); + } + + // AudioFocusManager.PlayerControl implementation + + @Override + public void setVolumeMultiplier(float volumeMultiplier) { + sendVolumeToRenderers(); + } + + @Override + public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) { + boolean playWhenReady = getPlayWhenReady(); + updatePlayWhenReady( + playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + } + + // AudioBecomingNoisyManager.EventListener implementation. + + @Override + public void onAudioBecomingNoisy() { + updatePlayWhenReady( + /* playWhenReady= */ false, + AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY, + Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY); + } + + // StreamVolumeManager.Listener implementation. + + @Override + public void onStreamTypeChanged(@C.StreamType int streamType) { + DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); + if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { + ExoPlayerImpl.this.deviceInfo = deviceInfo; + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onDeviceInfoChanged(deviceInfo); + } + } + } + + @Override + public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { + // TODO(internal b/187152483): Events should be dispatched via ListenerSet + for (Listener listener : listeners) { + listener.onDeviceVolumeChanged(streamVolume, streamMuted); + } + } + + // Player.EventListener implementation. + + @Override + public void onIsLoadingChanged(boolean isLoading) { + if (priorityTaskManager != null) { + if (isLoading && !isPriorityTaskManagerRegistered) { + priorityTaskManager.add(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = true; + } else if (!isLoading && isPriorityTaskManagerRegistered) { + priorityTaskManager.remove(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = false; + } + } + } + + @Override + public void onPlaybackStateChanged(@State int playbackState) { + updateWakeAndWifiLock(); + } + + @Override + public void onPlayWhenReadyChanged( + boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { + updateWakeAndWifiLock(); + } + + // Player.AudioOffloadListener implementation. + + @Override + public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { + updateWakeAndWifiLock(); + } + } + + /** Listeners that are called on the playback thread. */ + private static final class FrameMetadataListener + implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { + + @MessageType + public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = + Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; + + @MessageType + public static final int MSG_SET_CAMERA_MOTION_LISTENER = + Renderer.MSG_SET_CAMERA_MOTION_LISTENER; + + @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; + + @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; + @Nullable private CameraMotionListener cameraMotionListener; + @Nullable private VideoFrameMetadataListener internalVideoFrameMetadataListener; + @Nullable private CameraMotionListener internalCameraMotionListener; + + @Override + public void handleMessage(@MessageType int messageType, @Nullable Object message) { + switch (messageType) { + case MSG_SET_VIDEO_FRAME_METADATA_LISTENER: + videoFrameMetadataListener = (VideoFrameMetadataListener) message; + break; + case MSG_SET_CAMERA_MOTION_LISTENER: + cameraMotionListener = (CameraMotionListener) message; + break; + case MSG_SET_SPHERICAL_SURFACE_VIEW: + @Nullable SphericalGLSurfaceView surfaceView = (SphericalGLSurfaceView) message; + if (surfaceView == null) { + internalVideoFrameMetadataListener = null; + internalCameraMotionListener = null; + } else { + internalVideoFrameMetadataListener = surfaceView.getVideoFrameMetadataListener(); + internalCameraMotionListener = surfaceView.getCameraMotionListener(); + } + break; + case Renderer.MSG_SET_AUDIO_ATTRIBUTES: + case Renderer.MSG_SET_AUDIO_SESSION_ID: + case Renderer.MSG_SET_AUX_EFFECT_INFO: + case Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY: + case Renderer.MSG_SET_SCALING_MODE: + case Renderer.MSG_SET_SKIP_SILENCE_ENABLED: + case Renderer.MSG_SET_VIDEO_OUTPUT: + case Renderer.MSG_SET_VOLUME: + case Renderer.MSG_SET_WAKEUP_LISTENER: + default: + break; + } + } + + // VideoFrameMetadataListener + + @Override + public void onVideoFrameAboutToBeRendered( + long presentationTimeUs, + long releaseTimeNs, + Format format, + @Nullable MediaFormat mediaFormat) { + if (internalVideoFrameMetadataListener != null) { + internalVideoFrameMetadataListener.onVideoFrameAboutToBeRendered( + presentationTimeUs, releaseTimeNs, format, mediaFormat); + } + if (videoFrameMetadataListener != null) { + videoFrameMetadataListener.onVideoFrameAboutToBeRendered( + presentationTimeUs, releaseTimeNs, format, mediaFormat); + } + } + + // CameraMotionListener + + @Override + public void onCameraMotion(long timeUs, float[] rotation) { + if (internalCameraMotionListener != null) { + internalCameraMotionListener.onCameraMotion(timeUs, rotation); + } + if (cameraMotionListener != null) { + cameraMotionListener.onCameraMotion(timeUs, rotation); + } + } + + @Override + public void onCameraMotionReset() { + if (internalCameraMotionListener != null) { + internalCameraMotionListener.onCameraMotionReset(); + } + if (cameraMotionListener != null) { + cameraMotionListener.onCameraMotionReset(); + } + } + } + @RequiresApi(31) private static final class Api31 { private Api31() {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index bc9f029e28..a057989091 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -15,28 +15,7 @@ */ package androidx.media3.exoplayer; -import static androidx.media3.common.C.TRACK_TYPE_AUDIO; -import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION; -import static androidx.media3.common.C.TRACK_TYPE_VIDEO; -import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_ATTRIBUTES; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUDIO_SESSION_ID; -import static androidx.media3.exoplayer.Renderer.MSG_SET_AUX_EFFECT_INFO; -import static androidx.media3.exoplayer.Renderer.MSG_SET_CAMERA_MOTION_LISTENER; -import static androidx.media3.exoplayer.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY; -import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE; -import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT; -import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME; - import android.content.Context; -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.media.AudioFormat; -import android.media.AudioTrack; -import android.media.MediaFormat; -import android.os.Handler; import android.os.Looper; import android.view.Surface; import android.view.SurfaceHolder; @@ -53,8 +32,6 @@ import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; -import androidx.media3.common.Metadata; -import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; @@ -67,31 +44,18 @@ import androidx.media3.common.VideoSize; import androidx.media3.common.text.Cue; import androidx.media3.common.util.Clock; import androidx.media3.common.util.ConditionVariable; -import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; -import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; -import androidx.media3.exoplayer.audio.AudioRendererEventListener; -import androidx.media3.exoplayer.metadata.MetadataOutput; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.ShuffleOrder; -import androidx.media3.exoplayer.text.TextOutput; import androidx.media3.exoplayer.trackselection.TrackSelector; import androidx.media3.exoplayer.upstream.BandwidthMeter; -import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; import androidx.media3.exoplayer.video.VideoFrameMetadataListener; -import androidx.media3.exoplayer.video.VideoRendererEventListener; import androidx.media3.exoplayer.video.spherical.CameraMotionListener; -import androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView; import androidx.media3.extractor.ExtractorsFactory; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeoutException; /** @deprecated Use {@link ExoPlayer} instead. */ @UnstableApi @@ -356,52 +320,8 @@ public class SimpleExoPlayer extends BasePlayer } } - private static final String TAG = "SimpleExoPlayer"; - - private final Renderer[] renderers; - private final ConditionVariable constructorFinished; - private final Context applicationContext; private final ExoPlayerImpl player; - private final ComponentListener componentListener; - private final FrameMetadataListener frameMetadataListener; - private final CopyOnWriteArraySet listeners; - private final AnalyticsCollector analyticsCollector; - private final AudioBecomingNoisyManager audioBecomingNoisyManager; - private final AudioFocusManager audioFocusManager; - private final StreamVolumeManager streamVolumeManager; - private final WakeLockManager wakeLockManager; - private final WifiLockManager wifiLockManager; - private final long detachSurfaceTimeoutMs; - - @Nullable private Format videoFormat; - @Nullable private Format audioFormat; - @Nullable private AudioTrack keepSessionIdAudioTrack; - @Nullable private Object videoOutput; - @Nullable private Surface ownedSurface; - @Nullable private SurfaceHolder surfaceHolder; - @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; - private boolean surfaceHolderSurfaceIsVideoOutput; - @Nullable private TextureView textureView; - @C.VideoScalingMode private int videoScalingMode; - @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; - private int surfaceWidth; - private int surfaceHeight; - @Nullable private DecoderCounters videoDecoderCounters; - @Nullable private DecoderCounters audioDecoderCounters; - private int audioSessionId; - private AudioAttributes audioAttributes; - private float volume; - private boolean skipSilenceEnabled; - private List currentCues; - @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; - @Nullable private CameraMotionListener cameraMotionListener; - private boolean throwsWhenUsingWrongThread; - private boolean hasNotifiedFullWrongThreadWarning; - @Nullable private PriorityTaskManager priorityTaskManager; - private boolean isPriorityTaskManagerRegistered; - private boolean playerReleased; - private DeviceInfo deviceInfo; - private VideoSize videoSize; + private final ConditionVariable constructorFinished; /** @deprecated Use the {@link ExoPlayer.Builder}. */ @Deprecated @@ -439,103 +359,7 @@ public class SimpleExoPlayer extends BasePlayer /* package */ SimpleExoPlayer(ExoPlayer.Builder builder) { constructorFinished = new ConditionVariable(); try { - applicationContext = builder.context.getApplicationContext(); - analyticsCollector = builder.analyticsCollectorSupplier.get(); - priorityTaskManager = builder.priorityTaskManager; - audioAttributes = builder.audioAttributes; - videoScalingMode = builder.videoScalingMode; - videoChangeFrameRateStrategy = builder.videoChangeFrameRateStrategy; - skipSilenceEnabled = builder.skipSilenceEnabled; - detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; - componentListener = new ComponentListener(); - frameMetadataListener = new FrameMetadataListener(); - listeners = new CopyOnWriteArraySet<>(); - Handler eventHandler = new Handler(builder.looper); - renderers = - builder - .renderersFactorySupplier - .get() - .createRenderers( - eventHandler, - componentListener, - componentListener, - componentListener, - componentListener); - - // Set initial values. - volume = 1; - if (Util.SDK_INT < 21) { - audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); - } else { - audioSessionId = Util.generateAudioSessionIdV21(applicationContext); - } - currentCues = Collections.emptyList(); - throwsWhenUsingWrongThread = true; - - // Build the player and associated objects. - Commands additionalPermanentAvailableCommands = - new Commands.Builder() - .addAll( - COMMAND_GET_AUDIO_ATTRIBUTES, - COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, - COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_SET_VIDEO_SURFACE, - COMMAND_GET_TEXT) - .build(); - player = - new ExoPlayerImpl( - renderers, - builder.trackSelectorSupplier.get(), - builder.mediaSourceFactorySupplier.get(), - builder.loadControlSupplier.get(), - builder.bandwidthMeterSupplier.get(), - analyticsCollector, - builder.useLazyPreparation, - builder.seekParameters, - builder.seekBackIncrementMs, - builder.seekForwardIncrementMs, - builder.livePlaybackSpeedControl, - builder.releaseTimeoutMs, - builder.pauseAtEndOfMediaItems, - builder.clock, - builder.looper, - /* wrappingPlayer= */ this, - additionalPermanentAvailableCommands); - player.addEventListener(componentListener); - player.addAudioOffloadListener(componentListener); - if (builder.foregroundModeTimeoutMs > 0) { - player.experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); - } - - audioBecomingNoisyManager = - new AudioBecomingNoisyManager(builder.context, eventHandler, componentListener); - audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); - audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); - audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); - streamVolumeManager = - new StreamVolumeManager(builder.context, eventHandler, componentListener); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - wakeLockManager = new WakeLockManager(builder.context); - wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); - wifiLockManager = new WifiLockManager(builder.context); - wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); - deviceInfo = createDeviceInfo(streamVolumeManager); - videoSize = VideoSize.UNKNOWN; - - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_VIDEO_FRAME_METADATA_LISTENER, frameMetadataListener); - sendRendererMessage( - TRACK_TYPE_CAMERA_MOTION, MSG_SET_CAMERA_MOTION_LISTENER, frameMetadataListener); + player = new ExoPlayerImpl(builder, /* wrappingPlayer= */ this); } finally { constructorFinished.open(); } @@ -543,13 +367,13 @@ public class SimpleExoPlayer extends BasePlayer @Override public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); } @Override public boolean experimentalIsSleepingForOffload() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.experimentalIsSleepingForOffload(); } @@ -579,493 +403,336 @@ public class SimpleExoPlayer extends BasePlayer @Override public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { - verifyApplicationThread(); - this.videoScalingMode = videoScalingMode; - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); + blockUntilConstructorFinished(); + player.setVideoScalingMode(videoScalingMode); } @Override @C.VideoScalingMode public int getVideoScalingMode() { - return videoScalingMode; + blockUntilConstructorFinished(); + return player.getVideoScalingMode(); } @Override public void setVideoChangeFrameRateStrategy( @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { - verifyApplicationThread(); - if (this.videoChangeFrameRateStrategy == videoChangeFrameRateStrategy) { - return; - } - this.videoChangeFrameRateStrategy = videoChangeFrameRateStrategy; - sendRendererMessage( - TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); + blockUntilConstructorFinished(); + player.setVideoChangeFrameRateStrategy(videoChangeFrameRateStrategy); } @Override @C.VideoChangeFrameRateStrategy public int getVideoChangeFrameRateStrategy() { - return videoChangeFrameRateStrategy; + blockUntilConstructorFinished(); + return player.getVideoChangeFrameRateStrategy(); } @Override public VideoSize getVideoSize() { - return videoSize; + blockUntilConstructorFinished(); + return player.getVideoSize(); } @Override public void clearVideoSurface() { - verifyApplicationThread(); - removeSurfaceCallbacks(); - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); + blockUntilConstructorFinished(); + player.clearVideoSurface(); } @Override public void clearVideoSurface(@Nullable Surface surface) { - verifyApplicationThread(); - if (surface != null && surface == videoOutput) { - clearVideoSurface(); - } + blockUntilConstructorFinished(); + player.clearVideoSurface(surface); } @Override public void setVideoSurface(@Nullable Surface surface) { - verifyApplicationThread(); - removeSurfaceCallbacks(); - setVideoOutputInternal(surface); - int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET; - maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); + blockUntilConstructorFinished(); + player.setVideoSurface(surface); } @Override public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - verifyApplicationThread(); - if (surfaceHolder == null) { - clearVideoSurface(); - } else { - removeSurfaceCallbacks(); - this.surfaceHolderSurfaceIsVideoOutput = true; - this.surfaceHolder = surfaceHolder; - surfaceHolder.addCallback(componentListener); - Surface surface = surfaceHolder.getSurface(); - if (surface != null && surface.isValid()) { - setVideoOutputInternal(surface); - Rect surfaceSize = surfaceHolder.getSurfaceFrame(); - maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); - } else { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - } + blockUntilConstructorFinished(); + player.setVideoSurfaceHolder(surfaceHolder); } @Override public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { - verifyApplicationThread(); - if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { - clearVideoSurface(); - } + blockUntilConstructorFinished(); + player.clearVideoSurfaceHolder(surfaceHolder); } @Override public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { - verifyApplicationThread(); - if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { - removeSurfaceCallbacks(); - setVideoOutputInternal(surfaceView); - setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); - } else if (surfaceView instanceof SphericalGLSurfaceView) { - removeSurfaceCallbacks(); - sphericalGLSurfaceView = (SphericalGLSurfaceView) surfaceView; - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) - .setPayload(sphericalGLSurfaceView) - .send(); - sphericalGLSurfaceView.addVideoSurfaceListener(componentListener); - setVideoOutputInternal(sphericalGLSurfaceView.getVideoSurface()); - setNonVideoOutputSurfaceHolderInternal(surfaceView.getHolder()); - } else { - setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); - } + blockUntilConstructorFinished(); + player.setVideoSurfaceView(surfaceView); } @Override public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { - verifyApplicationThread(); - clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); + blockUntilConstructorFinished(); + player.clearVideoSurfaceView(surfaceView); } @Override public void setVideoTextureView(@Nullable TextureView textureView) { - verifyApplicationThread(); - if (textureView == null) { - clearVideoSurface(); - } else { - removeSurfaceCallbacks(); - this.textureView = textureView; - if (textureView.getSurfaceTextureListener() != null) { - Log.w(TAG, "Replacing existing SurfaceTextureListener."); - } - textureView.setSurfaceTextureListener(componentListener); - @Nullable - SurfaceTexture surfaceTexture = - textureView.isAvailable() ? textureView.getSurfaceTexture() : null; - if (surfaceTexture == null) { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } else { - setSurfaceTextureInternal(surfaceTexture); - maybeNotifySurfaceSizeChanged(textureView.getWidth(), textureView.getHeight()); - } - } + blockUntilConstructorFinished(); + player.setVideoTextureView(textureView); } @Override public void clearVideoTextureView(@Nullable TextureView textureView) { - verifyApplicationThread(); - if (textureView != null && textureView == this.textureView) { - clearVideoSurface(); - } + blockUntilConstructorFinished(); + player.clearVideoTextureView(textureView); } @Override public void addAudioOffloadListener(AudioOffloadListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. + blockUntilConstructorFinished(); player.addAudioOffloadListener(listener); } @Override public void removeAudioOffloadListener(AudioOffloadListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. + blockUntilConstructorFinished(); player.removeAudioOffloadListener(listener); } @Override public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { - verifyApplicationThread(); - if (playerReleased) { - return; - } - if (!Util.areEqual(this.audioAttributes, audioAttributes)) { - this.audioAttributes = audioAttributes; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - analyticsCollector.onAudioAttributesChanged(audioAttributes); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onAudioAttributesChanged(audioAttributes); - } - } - - audioFocusManager.setAudioAttributes(handleAudioFocus ? audioAttributes : null); - boolean playWhenReady = getPlayWhenReady(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + blockUntilConstructorFinished(); + player.setAudioAttributes(audioAttributes, handleAudioFocus); } @Override public AudioAttributes getAudioAttributes() { - return audioAttributes; + blockUntilConstructorFinished(); + return player.getAudioAttributes(); } @Override public void setAudioSessionId(int audioSessionId) { - verifyApplicationThread(); - if (this.audioSessionId == audioSessionId) { - return; - } - if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { - if (Util.SDK_INT < 21) { - audioSessionId = initializeKeepSessionIdAudioTrack(C.AUDIO_SESSION_ID_UNSET); - } else { - audioSessionId = Util.generateAudioSessionIdV21(applicationContext); - } - } else if (Util.SDK_INT < 21) { - // We need to re-initialize keepSessionIdAudioTrack to make sure the session is kept alive for - // as long as the player is using it. - initializeKeepSessionIdAudioTrack(audioSessionId); - } - this.audioSessionId = audioSessionId; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - analyticsCollector.onAudioSessionIdChanged(audioSessionId); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onAudioSessionIdChanged(audioSessionId); - } + blockUntilConstructorFinished(); + player.setAudioSessionId(audioSessionId); } @Override public int getAudioSessionId() { - return audioSessionId; + blockUntilConstructorFinished(); + return player.getAudioSessionId(); } @Override public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { - verifyApplicationThread(); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUX_EFFECT_INFO, auxEffectInfo); + blockUntilConstructorFinished(); + player.setAuxEffectInfo(auxEffectInfo); } @Override public void clearAuxEffectInfo() { - setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); + blockUntilConstructorFinished(); + player.clearAuxEffectInfo(); } @Override public void setVolume(float volume) { - verifyApplicationThread(); - volume = Util.constrainValue(volume, /* min= */ 0, /* max= */ 1); - if (this.volume == volume) { - return; - } - this.volume = volume; - sendVolumeToRenderers(); - analyticsCollector.onVolumeChanged(volume); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onVolumeChanged(volume); - } + blockUntilConstructorFinished(); + player.setVolume(volume); } @Override public float getVolume() { - return volume; + blockUntilConstructorFinished(); + return player.getVolume(); } @Override public boolean getSkipSilenceEnabled() { - return skipSilenceEnabled; + blockUntilConstructorFinished(); + return player.getSkipSilenceEnabled(); } @Override public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { - verifyApplicationThread(); - if (this.skipSilenceEnabled == skipSilenceEnabled) { - return; - } - this.skipSilenceEnabled = skipSilenceEnabled; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); - notifySkipSilenceEnabledChanged(); + blockUntilConstructorFinished(); + player.setSkipSilenceEnabled(skipSilenceEnabled); } @Override public AnalyticsCollector getAnalyticsCollector() { - return analyticsCollector; + blockUntilConstructorFinished(); + return player.getAnalyticsCollector(); } @Override public void addAnalyticsListener(AnalyticsListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); - analyticsCollector.addListener(listener); + blockUntilConstructorFinished(); + player.addAnalyticsListener(listener); } @Override public void removeAnalyticsListener(AnalyticsListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - analyticsCollector.removeListener(listener); + blockUntilConstructorFinished(); + player.removeAnalyticsListener(listener); } @Override public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { - verifyApplicationThread(); - if (playerReleased) { - return; - } - audioBecomingNoisyManager.setEnabled(handleAudioBecomingNoisy); + blockUntilConstructorFinished(); + player.setHandleAudioBecomingNoisy(handleAudioBecomingNoisy); } @Override public void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) { - verifyApplicationThread(); - if (Util.areEqual(this.priorityTaskManager, priorityTaskManager)) { - return; - } - if (isPriorityTaskManagerRegistered) { - checkNotNull(this.priorityTaskManager).remove(C.PRIORITY_PLAYBACK); - } - if (priorityTaskManager != null && isLoading()) { - priorityTaskManager.add(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = true; - } else { - isPriorityTaskManagerRegistered = false; - } - this.priorityTaskManager = priorityTaskManager; + blockUntilConstructorFinished(); + player.setPriorityTaskManager(priorityTaskManager); } @Override @Nullable public Format getVideoFormat() { - return videoFormat; + blockUntilConstructorFinished(); + return player.getVideoFormat(); } @Override @Nullable public Format getAudioFormat() { - return audioFormat; + blockUntilConstructorFinished(); + return player.getAudioFormat(); } @Override @Nullable public DecoderCounters getVideoDecoderCounters() { - return videoDecoderCounters; + blockUntilConstructorFinished(); + return player.getVideoDecoderCounters(); } @Override @Nullable public DecoderCounters getAudioDecoderCounters() { - return audioDecoderCounters; + blockUntilConstructorFinished(); + return player.getAudioDecoderCounters(); } @Override public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - verifyApplicationThread(); - videoFrameMetadataListener = listener; - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) - .setPayload(listener) - .send(); + blockUntilConstructorFinished(); + player.setVideoFrameMetadataListener(listener); } @Override public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { - verifyApplicationThread(); - if (videoFrameMetadataListener != listener) { - return; - } - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_VIDEO_FRAME_METADATA_LISTENER) - .setPayload(null) - .send(); + blockUntilConstructorFinished(); + player.clearVideoFrameMetadataListener(listener); } @Override public void setCameraMotionListener(CameraMotionListener listener) { - verifyApplicationThread(); - cameraMotionListener = listener; - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) - .setPayload(listener) - .send(); + blockUntilConstructorFinished(); + player.setCameraMotionListener(listener); } @Override public void clearCameraMotionListener(CameraMotionListener listener) { - verifyApplicationThread(); - if (cameraMotionListener != listener) { - return; - } - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_CAMERA_MOTION_LISTENER) - .setPayload(null) - .send(); + blockUntilConstructorFinished(); + player.clearCameraMotionListener(listener); } @Override public List getCurrentCues() { - verifyApplicationThread(); - return currentCues; + blockUntilConstructorFinished(); + return player.getCurrentCues(); } // ExoPlayer implementation @Override public Looper getPlaybackLooper() { + blockUntilConstructorFinished(); return player.getPlaybackLooper(); } @Override public Looper getApplicationLooper() { + blockUntilConstructorFinished(); return player.getApplicationLooper(); } @Override public Clock getClock() { + blockUntilConstructorFinished(); return player.getClock(); } @Override public void addListener(Listener listener) { - checkNotNull(listener); - listeners.add(listener); - EventListener eventListener = listener; - addListener(eventListener); + blockUntilConstructorFinished(); + player.addListener(listener); } @Deprecated @Override public void addListener(Player.EventListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. - checkNotNull(listener); + blockUntilConstructorFinished(); player.addEventListener(listener); } @Override public void removeListener(Listener listener) { - checkNotNull(listener); - listeners.remove(listener); - EventListener eventListener = listener; - removeListener(eventListener); + blockUntilConstructorFinished(); + player.removeListener(listener); } @Deprecated @Override public void removeListener(Player.EventListener listener) { - // Don't verify application thread. We allow calls to this method from any thread. + blockUntilConstructorFinished(); player.removeEventListener(listener); } @Override @State public int getPlaybackState() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPlaybackState(); } @Override @PlaybackSuppressionReason public int getPlaybackSuppressionReason() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPlaybackSuppressionReason(); } @Override @Nullable public ExoPlaybackException getPlayerError() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPlayerError(); } /** @deprecated Use {@link #prepare()} instead. */ @Deprecated @Override + @SuppressWarnings("deprecation") // Calling deprecated method. public void retry() { - verifyApplicationThread(); - prepare(); + blockUntilConstructorFinished(); + player.retry(); } @Override public Commands getAvailableCommands() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getAvailableCommands(); } @Override public void prepare() { - verifyApplicationThread(); - boolean playWhenReady = getPlayWhenReady(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, Player.STATE_BUFFERING); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + blockUntilConstructorFinished(); player.prepare(); } @@ -1074,9 +741,10 @@ public class SimpleExoPlayer extends BasePlayer */ @Deprecated @Override - @SuppressWarnings("deprecation") + @SuppressWarnings("deprecation") // Forwarding deprecated method. public void prepare(MediaSource mediaSource) { - prepare(mediaSource, /* resetPosition= */ true, /* resetState= */ true); + blockUntilConstructorFinished(); + player.prepare(mediaSource); } /** @@ -1085,1115 +753,449 @@ public class SimpleExoPlayer extends BasePlayer */ @Deprecated @Override + @SuppressWarnings("deprecation") // Forwarding deprecated method. public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { - verifyApplicationThread(); - setMediaSources(Collections.singletonList(mediaSource), resetPosition); - prepare(); + blockUntilConstructorFinished(); + player.prepare(mediaSource, resetPosition, resetState); } @Override public void setMediaItems(List mediaItems, boolean resetPosition) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaItems(mediaItems, resetPosition); } @Override public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaItems(mediaItems, startIndex, startPositionMs); } @Override public void setMediaSources(List mediaSources) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSources(mediaSources); } @Override public void setMediaSources(List mediaSources, boolean resetPosition) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSources(mediaSources, resetPosition); } @Override public void setMediaSources( List mediaSources, int startMediaItemIndex, long startPositionMs) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSources(mediaSources, startMediaItemIndex, startPositionMs); } @Override public void setMediaSource(MediaSource mediaSource) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSource(mediaSource); } @Override public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSource(mediaSource, resetPosition); } @Override public void setMediaSource(MediaSource mediaSource, long startPositionMs) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setMediaSource(mediaSource, startPositionMs); } @Override public void addMediaItems(int index, List mediaItems) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.addMediaItems(index, mediaItems); } @Override public void addMediaSource(MediaSource mediaSource) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.addMediaSource(mediaSource); } @Override public void addMediaSource(int index, MediaSource mediaSource) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.addMediaSource(index, mediaSource); } @Override public void addMediaSources(List mediaSources) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.addMediaSources(mediaSources); } @Override public void addMediaSources(int index, List mediaSources) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.addMediaSources(index, mediaSources); } @Override public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.moveMediaItems(fromIndex, toIndex, newIndex); } @Override public void removeMediaItems(int fromIndex, int toIndex) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.removeMediaItems(fromIndex, toIndex); } @Override public void setShuffleOrder(ShuffleOrder shuffleOrder) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setShuffleOrder(shuffleOrder); } @Override public void setPlayWhenReady(boolean playWhenReady) { - verifyApplicationThread(); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + blockUntilConstructorFinished(); + player.setPlayWhenReady(playWhenReady); } @Override public boolean getPlayWhenReady() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPlayWhenReady(); } @Override public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setPauseAtEndOfMediaItems(pauseAtEndOfMediaItems); } @Override public boolean getPauseAtEndOfMediaItems() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPauseAtEndOfMediaItems(); } @Override public @RepeatMode int getRepeatMode() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getRepeatMode(); } @Override public void setRepeatMode(@RepeatMode int repeatMode) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setRepeatMode(repeatMode); } @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setShuffleModeEnabled(shuffleModeEnabled); } @Override public boolean getShuffleModeEnabled() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getShuffleModeEnabled(); } @Override public boolean isLoading() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.isLoading(); } @Override public void seekTo(int mediaItemIndex, long positionMs) { - verifyApplicationThread(); - analyticsCollector.notifySeekStarted(); + blockUntilConstructorFinished(); player.seekTo(mediaItemIndex, positionMs); } @Override public long getSeekBackIncrement() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getSeekBackIncrement(); } @Override public long getSeekForwardIncrement() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getSeekForwardIncrement(); } @Override public long getMaxSeekToPreviousPosition() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getMaxSeekToPreviousPosition(); } @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setPlaybackParameters(playbackParameters); } @Override public PlaybackParameters getPlaybackParameters() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getPlaybackParameters(); } @Override public void setSeekParameters(@Nullable SeekParameters seekParameters) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setSeekParameters(seekParameters); } @Override public SeekParameters getSeekParameters() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getSeekParameters(); } @Override public void setForegroundMode(boolean foregroundMode) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setForegroundMode(foregroundMode); } @Override public void stop() { - stop(/* reset= */ false); + blockUntilConstructorFinished(); + player.stop(); } @Deprecated @Override public void stop(boolean reset) { - verifyApplicationThread(); - audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); + blockUntilConstructorFinished(); player.stop(reset); - currentCues = Collections.emptyList(); } @Override public void release() { - verifyApplicationThread(); - if (Util.SDK_INT < 21 && keepSessionIdAudioTrack != null) { - keepSessionIdAudioTrack.release(); - keepSessionIdAudioTrack = null; - } - audioBecomingNoisyManager.setEnabled(false); - streamVolumeManager.release(); - wakeLockManager.setStayAwake(false); - wifiLockManager.setStayAwake(false); - audioFocusManager.release(); + blockUntilConstructorFinished(); player.release(); - analyticsCollector.release(); - removeSurfaceCallbacks(); - if (ownedSurface != null) { - ownedSurface.release(); - ownedSurface = null; - } - if (isPriorityTaskManagerRegistered) { - checkNotNull(priorityTaskManager).remove(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = false; - } - currentCues = Collections.emptyList(); - playerReleased = true; } @Override public PlayerMessage createMessage(PlayerMessage.Target target) { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.createMessage(target); } @Override public int getRendererCount() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getRendererCount(); } @Override public @C.TrackType int getRendererType(int index) { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getRendererType(index); } @Override public Renderer getRenderer(int index) { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getRenderer(index); } @Override public TrackSelector getTrackSelector() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getTrackSelector(); } @Override public TrackGroupArray getCurrentTrackGroups() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentTrackGroups(); } @Override public TrackSelectionArray getCurrentTrackSelections() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentTrackSelections(); } @Override public TracksInfo getCurrentTracksInfo() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentTracksInfo(); } @Override public TrackSelectionParameters getTrackSelectionParameters() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getTrackSelectionParameters(); } @Override public void setTrackSelectionParameters(TrackSelectionParameters parameters) { - verifyApplicationThread(); + blockUntilConstructorFinished(); player.setTrackSelectionParameters(parameters); } @Override public MediaMetadata getMediaMetadata() { + blockUntilConstructorFinished(); return player.getMediaMetadata(); } @Override public MediaMetadata getPlaylistMetadata() { + blockUntilConstructorFinished(); return player.getPlaylistMetadata(); } @Override public void setPlaylistMetadata(MediaMetadata mediaMetadata) { + blockUntilConstructorFinished(); player.setPlaylistMetadata(mediaMetadata); } @Override public Timeline getCurrentTimeline() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentTimeline(); } @Override public int getCurrentPeriodIndex() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentPeriodIndex(); } @Override public int getCurrentMediaItemIndex() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentMediaItemIndex(); } @Override public long getDuration() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getDuration(); } @Override public long getCurrentPosition() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentPosition(); } @Override public long getBufferedPosition() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getBufferedPosition(); } @Override public long getTotalBufferedDuration() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getTotalBufferedDuration(); } @Override public boolean isPlayingAd() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.isPlayingAd(); } @Override public int getCurrentAdGroupIndex() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentAdGroupIndex(); } @Override public int getCurrentAdIndexInAdGroup() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getCurrentAdIndexInAdGroup(); } @Override public long getContentPosition() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getContentPosition(); } @Override public long getContentBufferedPosition() { - verifyApplicationThread(); + blockUntilConstructorFinished(); return player.getContentBufferedPosition(); } @Deprecated @Override public void setHandleWakeLock(boolean handleWakeLock) { - setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); + blockUntilConstructorFinished(); + player.setHandleWakeLock(handleWakeLock); } @Override public void setWakeMode(@C.WakeMode int wakeMode) { - verifyApplicationThread(); - switch (wakeMode) { - case C.WAKE_MODE_NONE: - wakeLockManager.setEnabled(false); - wifiLockManager.setEnabled(false); - break; - case C.WAKE_MODE_LOCAL: - wakeLockManager.setEnabled(true); - wifiLockManager.setEnabled(false); - break; - case C.WAKE_MODE_NETWORK: - wakeLockManager.setEnabled(true); - wifiLockManager.setEnabled(true); - break; - default: - break; - } + blockUntilConstructorFinished(); + player.setWakeMode(wakeMode); } @Override public DeviceInfo getDeviceInfo() { - verifyApplicationThread(); - return deviceInfo; + blockUntilConstructorFinished(); + return player.getDeviceInfo(); } @Override public int getDeviceVolume() { - verifyApplicationThread(); - return streamVolumeManager.getVolume(); + blockUntilConstructorFinished(); + return player.getDeviceVolume(); } @Override public boolean isDeviceMuted() { - verifyApplicationThread(); - return streamVolumeManager.isMuted(); + blockUntilConstructorFinished(); + return player.isDeviceMuted(); } @Override public void setDeviceVolume(int volume) { - verifyApplicationThread(); - streamVolumeManager.setVolume(volume); + blockUntilConstructorFinished(); + player.setDeviceVolume(volume); } @Override public void increaseDeviceVolume() { - verifyApplicationThread(); - streamVolumeManager.increaseVolume(); + blockUntilConstructorFinished(); + player.increaseDeviceVolume(); } @Override public void decreaseDeviceVolume() { - verifyApplicationThread(); - streamVolumeManager.decreaseVolume(); + blockUntilConstructorFinished(); + player.decreaseDeviceVolume(); } @Override public void setDeviceMuted(boolean muted) { - verifyApplicationThread(); - streamVolumeManager.setMuted(muted); + blockUntilConstructorFinished(); + player.setDeviceMuted(muted); } /* package */ void setThrowsWhenUsingWrongThread(boolean throwsWhenUsingWrongThread) { - this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread; + blockUntilConstructorFinished(); + player.setThrowsWhenUsingWrongThread(throwsWhenUsingWrongThread); } - // Internal methods. - - private void removeSurfaceCallbacks() { - if (sphericalGLSurfaceView != null) { - player - .createMessage(frameMetadataListener) - .setType(FrameMetadataListener.MSG_SET_SPHERICAL_SURFACE_VIEW) - .setPayload(null) - .send(); - sphericalGLSurfaceView.removeVideoSurfaceListener(componentListener); - sphericalGLSurfaceView = null; - } - if (textureView != null) { - if (textureView.getSurfaceTextureListener() != componentListener) { - Log.w(TAG, "SurfaceTextureListener already unset or replaced."); - } else { - textureView.setSurfaceTextureListener(null); - } - textureView = null; - } - if (surfaceHolder != null) { - surfaceHolder.removeCallback(componentListener); - surfaceHolder = null; - } - } - - private void setSurfaceTextureInternal(SurfaceTexture surfaceTexture) { - Surface surface = new Surface(surfaceTexture); - setVideoOutputInternal(surface); - ownedSurface = surface; - } - - private void setVideoOutputInternal(@Nullable Object videoOutput) { - // Note: We don't turn this method into a no-op if the output is being replaced with itself so - // as to ensure onRenderedFirstFrame callbacks are still called in this case. - List messages = new ArrayList<>(); - for (Renderer renderer : renderers) { - if (renderer.getTrackType() == TRACK_TYPE_VIDEO) { - messages.add( - player - .createMessage(renderer) - .setType(MSG_SET_VIDEO_OUTPUT) - .setPayload(videoOutput) - .send()); - } - } - boolean messageDeliveryTimedOut = false; - if (this.videoOutput != null && this.videoOutput != videoOutput) { - // We're replacing an output. Block to ensure that this output will not be accessed by the - // renderers after this method returns. - try { - for (PlayerMessage message : messages) { - message.blockUntilDelivered(detachSurfaceTimeoutMs); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (TimeoutException e) { - messageDeliveryTimedOut = true; - } - if (this.videoOutput == ownedSurface) { - // We're replacing a surface that we are responsible for releasing. - ownedSurface.release(); - ownedSurface = null; - } - } - this.videoOutput = videoOutput; - if (messageDeliveryTimedOut) { - player.stop( - /* reset= */ false, - ExoPlaybackException.createForUnexpected( - new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE), - PlaybackException.ERROR_CODE_TIMEOUT)); - } - } - - /** - * Sets the holder of the surface that will be displayed to the user, but which should - * not be the output for video renderers. This case occurs when video frames need to be - * rendered to an intermediate surface (which is not the one held by the provided holder). - * - * @param nonVideoOutputSurfaceHolder The holder of the surface that will eventually be displayed - * to the user. - */ - private void setNonVideoOutputSurfaceHolderInternal(SurfaceHolder nonVideoOutputSurfaceHolder) { - // Although we won't use the view's surface directly as the video output, still use the holder - // to query the surface size, to be informed in changes to the size via componentListener, and - // for equality checking in clearVideoSurfaceHolder. - surfaceHolderSurfaceIsVideoOutput = false; - surfaceHolder = nonVideoOutputSurfaceHolder; - surfaceHolder.addCallback(componentListener); - Surface surface = surfaceHolder.getSurface(); - if (surface != null && surface.isValid()) { - Rect surfaceSize = surfaceHolder.getSurfaceFrame(); - maybeNotifySurfaceSizeChanged(surfaceSize.width(), surfaceSize.height()); - } else { - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - } - - private void maybeNotifySurfaceSizeChanged(int width, int height) { - if (width != surfaceWidth || height != surfaceHeight) { - surfaceWidth = width; - surfaceHeight = height; - analyticsCollector.onSurfaceSizeChanged(width, height); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onSurfaceSizeChanged(width, height); - } - } - } - - private void sendVolumeToRenderers() { - float scaledVolume = volume * audioFocusManager.getVolumeMultiplier(); - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_VOLUME, scaledVolume); - } - - @SuppressWarnings("SuspiciousMethodCalls") - private void notifySkipSilenceEnabledChanged() { - analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); - } - } - - private void updatePlayWhenReady( - boolean playWhenReady, - @AudioFocusManager.PlayerCommand int playerCommand, - @Player.PlayWhenReadyChangeReason int playWhenReadyChangeReason) { - playWhenReady = playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY; - @PlaybackSuppressionReason - int playbackSuppressionReason = - playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY - ? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS - : Player.PLAYBACK_SUPPRESSION_REASON_NONE; - player.setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason); - } - - private void updateWakeAndWifiLock() { - @State int playbackState = getPlaybackState(); - switch (playbackState) { - case Player.STATE_READY: - case Player.STATE_BUFFERING: - boolean isSleeping = experimentalIsSleepingForOffload(); - wakeLockManager.setStayAwake(getPlayWhenReady() && !isSleeping); - // The wifi lock is not released while sleeping to avoid interrupting downloads. - wifiLockManager.setStayAwake(getPlayWhenReady()); - break; - case Player.STATE_ENDED: - case Player.STATE_IDLE: - wakeLockManager.setStayAwake(false); - wifiLockManager.setStayAwake(false); - break; - default: - throw new IllegalStateException(); - } - } - - private void verifyApplicationThread() { + private void blockUntilConstructorFinished() { // The constructor may be executed on a background thread. Wait with accessing the player from // the app thread until the constructor finished executing. constructorFinished.blockUninterruptible(); - if (Thread.currentThread() != getApplicationLooper().getThread()) { - String message = - Util.formatInvariant( - "Player is accessed on the wrong thread.\n" - + "Current thread: '%s'\n" - + "Expected thread: '%s'\n" - + "See https://exoplayer.dev/issues/player-accessed-on-wrong-thread", - Thread.currentThread().getName(), getApplicationLooper().getThread().getName()); - if (throwsWhenUsingWrongThread) { - throw new IllegalStateException(message); - } - Log.w(TAG, message, hasNotifiedFullWrongThreadWarning ? null : new IllegalStateException()); - hasNotifiedFullWrongThreadWarning = true; - } - } - - private void sendRendererMessage( - @C.TrackType int trackType, int messageType, @Nullable Object payload) { - for (Renderer renderer : renderers) { - if (renderer.getTrackType() == trackType) { - player.createMessage(renderer).setType(messageType).setPayload(payload).send(); - } - } - } - - /** - * Initializes {@link #keepSessionIdAudioTrack} to keep an audio session ID alive. If the audio - * session ID is {@link C#AUDIO_SESSION_ID_UNSET} then a new audio session ID is generated. - * - *

    Use of this method is only required on API level 21 and earlier. - * - * @param audioSessionId The audio session ID, or {@link C#AUDIO_SESSION_ID_UNSET} to generate a - * new one. - * @return The audio session ID. - */ - private int initializeKeepSessionIdAudioTrack(int audioSessionId) { - if (keepSessionIdAudioTrack != null - && keepSessionIdAudioTrack.getAudioSessionId() != audioSessionId) { - keepSessionIdAudioTrack.release(); - keepSessionIdAudioTrack = null; - } - if (keepSessionIdAudioTrack == null) { - int sampleRate = 4000; // Minimum sample rate supported by the platform. - int channelConfig = AudioFormat.CHANNEL_OUT_MONO; - @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT; - int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback. - keepSessionIdAudioTrack = - new AudioTrack( - C.STREAM_TYPE_DEFAULT, - sampleRate, - channelConfig, - encoding, - bufferSize, - AudioTrack.MODE_STATIC, - audioSessionId); - } - return keepSessionIdAudioTrack.getAudioSessionId(); - } - - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { - return new DeviceInfo( - DeviceInfo.PLAYBACK_TYPE_LOCAL, - streamVolumeManager.getMinVolume(), - streamVolumeManager.getMaxVolume()); - } - - private static int getPlayWhenReadyChangeReason(boolean playWhenReady, int playerCommand) { - return playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY - ? PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS - : PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; - } - - private final class ComponentListener - implements VideoRendererEventListener, - AudioRendererEventListener, - TextOutput, - MetadataOutput, - SurfaceHolder.Callback, - TextureView.SurfaceTextureListener, - SphericalGLSurfaceView.VideoSurfaceListener, - AudioFocusManager.PlayerControl, - AudioBecomingNoisyManager.EventListener, - StreamVolumeManager.Listener, - Player.EventListener, - AudioOffloadListener { - - // VideoRendererEventListener implementation - - @Override - public void onVideoEnabled(DecoderCounters counters) { - videoDecoderCounters = counters; - analyticsCollector.onVideoEnabled(counters); - } - - @Override - public void onVideoDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - analyticsCollector.onVideoDecoderInitialized( - decoderName, initializedTimestampMs, initializationDurationMs); - } - - @Override - public void onVideoInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - videoFormat = format; - analyticsCollector.onVideoInputFormatChanged(format, decoderReuseEvaluation); - } - - @Override - public void onDroppedFrames(int count, long elapsed) { - analyticsCollector.onDroppedFrames(count, elapsed); - } - - @Override - public void onVideoSizeChanged(VideoSize videoSize) { - SimpleExoPlayer.this.videoSize = videoSize; - analyticsCollector.onVideoSizeChanged(videoSize); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onVideoSizeChanged(videoSize); - } - } - - @Override - public void onRenderedFirstFrame(Object output, long renderTimeMs) { - analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); - if (videoOutput == output) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onRenderedFirstFrame(); - } - } - } - - @Override - public void onVideoDecoderReleased(String decoderName) { - analyticsCollector.onVideoDecoderReleased(decoderName); - } - - @Override - public void onVideoDisabled(DecoderCounters counters) { - analyticsCollector.onVideoDisabled(counters); - videoFormat = null; - videoDecoderCounters = null; - } - - @Override - public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { - analyticsCollector.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount); - } - - @Override - public void onVideoCodecError(Exception videoCodecError) { - analyticsCollector.onVideoCodecError(videoCodecError); - } - - // AudioRendererEventListener implementation - - @Override - public void onAudioEnabled(DecoderCounters counters) { - audioDecoderCounters = counters; - analyticsCollector.onAudioEnabled(counters); - } - - @Override - public void onAudioDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - analyticsCollector.onAudioDecoderInitialized( - decoderName, initializedTimestampMs, initializationDurationMs); - } - - @Override - public void onAudioInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - audioFormat = format; - analyticsCollector.onAudioInputFormatChanged(format, decoderReuseEvaluation); - } - - @Override - public void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { - analyticsCollector.onAudioPositionAdvancing(playoutStartSystemTimeMs); - } - - @Override - public void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - analyticsCollector.onAudioUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - } - - @Override - public void onAudioDecoderReleased(String decoderName) { - analyticsCollector.onAudioDecoderReleased(decoderName); - } - - @Override - public void onAudioDisabled(DecoderCounters counters) { - analyticsCollector.onAudioDisabled(counters); - audioFormat = null; - audioDecoderCounters = null; - } - - @Override - public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { - if (SimpleExoPlayer.this.skipSilenceEnabled == skipSilenceEnabled) { - return; - } - SimpleExoPlayer.this.skipSilenceEnabled = skipSilenceEnabled; - notifySkipSilenceEnabledChanged(); - } - - @Override - public void onAudioSinkError(Exception audioSinkError) { - analyticsCollector.onAudioSinkError(audioSinkError); - } - - @Override - public void onAudioCodecError(Exception audioCodecError) { - analyticsCollector.onAudioCodecError(audioCodecError); - } - - // TextOutput implementation - - @Override - public void onCues(List cues) { - currentCues = cues; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listeners) { - listeners.onCues(cues); - } - } - - // MetadataOutput implementation - - @Override - public void onMetadata(Metadata metadata) { - analyticsCollector.onMetadata(metadata); - player.onMetadata(metadata); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onMetadata(metadata); - } - } - - // SurfaceHolder.Callback implementation - - @Override - public void surfaceCreated(SurfaceHolder holder) { - if (surfaceHolderSurfaceIsVideoOutput) { - setVideoOutputInternal(holder.getSurface()); - } - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - if (surfaceHolderSurfaceIsVideoOutput) { - setVideoOutputInternal(/* videoOutput= */ null); - } - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - } - - // TextureView.SurfaceTextureListener implementation - - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { - setSurfaceTextureInternal(surfaceTexture); - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { - maybeNotifySurfaceSizeChanged(width, height); - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { - setVideoOutputInternal(/* videoOutput= */ null); - maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - return true; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { - // Do nothing. - } - - // SphericalGLSurfaceView.VideoSurfaceListener - - @Override - public void onVideoSurfaceCreated(Surface surface) { - setVideoOutputInternal(surface); - } - - @Override - public void onVideoSurfaceDestroyed(Surface surface) { - setVideoOutputInternal(/* videoOutput= */ null); - } - - // AudioFocusManager.PlayerControl implementation - - @Override - public void setVolumeMultiplier(float volumeMultiplier) { - sendVolumeToRenderers(); - } - - @Override - public void executePlayerCommand(@AudioFocusManager.PlayerCommand int playerCommand) { - boolean playWhenReady = getPlayWhenReady(); - updatePlayWhenReady( - playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); - } - - // AudioBecomingNoisyManager.EventListener implementation. - - @Override - public void onAudioBecomingNoisy() { - updatePlayWhenReady( - /* playWhenReady= */ false, - AudioFocusManager.PLAYER_COMMAND_DO_NOT_PLAY, - Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY); - } - - // StreamVolumeManager.Listener implementation. - - @Override - public void onStreamTypeChanged(@C.StreamType int streamType) { - DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); - if (!deviceInfo.equals(SimpleExoPlayer.this.deviceInfo)) { - SimpleExoPlayer.this.deviceInfo = deviceInfo; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onDeviceInfoChanged(deviceInfo); - } - } - } - - @Override - public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { - listener.onDeviceVolumeChanged(streamVolume, streamMuted); - } - } - - // Player.EventListener implementation. - - @Override - public void onIsLoadingChanged(boolean isLoading) { - if (priorityTaskManager != null) { - if (isLoading && !isPriorityTaskManagerRegistered) { - priorityTaskManager.add(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = true; - } else if (!isLoading && isPriorityTaskManagerRegistered) { - priorityTaskManager.remove(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = false; - } - } - } - - @Override - public void onPlaybackStateChanged(@State int playbackState) { - updateWakeAndWifiLock(); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - updateWakeAndWifiLock(); - } - - // Player.AudioOffloadListener implementation. - - @Override - public void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) { - updateWakeAndWifiLock(); - } - } - - /** Listeners that are called on the playback thread. */ - private static final class FrameMetadataListener - implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { - - @MessageType - public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = - Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; - - @MessageType - public static final int MSG_SET_CAMERA_MOTION_LISTENER = - Renderer.MSG_SET_CAMERA_MOTION_LISTENER; - - @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; - - @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; - @Nullable private CameraMotionListener cameraMotionListener; - @Nullable private VideoFrameMetadataListener internalVideoFrameMetadataListener; - @Nullable private CameraMotionListener internalCameraMotionListener; - - @Override - public void handleMessage(@MessageType int messageType, @Nullable Object message) { - switch (messageType) { - case MSG_SET_VIDEO_FRAME_METADATA_LISTENER: - videoFrameMetadataListener = (VideoFrameMetadataListener) message; - break; - case MSG_SET_CAMERA_MOTION_LISTENER: - cameraMotionListener = (CameraMotionListener) message; - break; - case MSG_SET_SPHERICAL_SURFACE_VIEW: - @Nullable SphericalGLSurfaceView surfaceView = (SphericalGLSurfaceView) message; - if (surfaceView == null) { - internalVideoFrameMetadataListener = null; - internalCameraMotionListener = null; - } else { - internalVideoFrameMetadataListener = surfaceView.getVideoFrameMetadataListener(); - internalCameraMotionListener = surfaceView.getCameraMotionListener(); - } - break; - case Renderer.MSG_SET_AUDIO_ATTRIBUTES: - case Renderer.MSG_SET_AUDIO_SESSION_ID: - case Renderer.MSG_SET_AUX_EFFECT_INFO: - case Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY: - case Renderer.MSG_SET_SCALING_MODE: - case Renderer.MSG_SET_SKIP_SILENCE_ENABLED: - case Renderer.MSG_SET_VIDEO_OUTPUT: - case Renderer.MSG_SET_VOLUME: - case Renderer.MSG_SET_WAKEUP_LISTENER: - default: - break; - } - } - - // VideoFrameMetadataListener - - @Override - public void onVideoFrameAboutToBeRendered( - long presentationTimeUs, - long releaseTimeNs, - Format format, - @Nullable MediaFormat mediaFormat) { - if (internalVideoFrameMetadataListener != null) { - internalVideoFrameMetadataListener.onVideoFrameAboutToBeRendered( - presentationTimeUs, releaseTimeNs, format, mediaFormat); - } - if (videoFrameMetadataListener != null) { - videoFrameMetadataListener.onVideoFrameAboutToBeRendered( - presentationTimeUs, releaseTimeNs, format, mediaFormat); - } - } - - // CameraMotionListener - - @Override - public void onCameraMotion(long timeUs, float[] rotation) { - if (internalCameraMotionListener != null) { - internalCameraMotionListener.onCameraMotion(timeUs, rotation); - } - if (cameraMotionListener != null) { - cameraMotionListener.onCameraMotion(timeUs, rotation); - } - } - - @Override - public void onCameraMotionReset() { - if (internalCameraMotionListener != null) { - internalCameraMotionListener.onCameraMotionReset(); - } - if (cameraMotionListener != null) { - cameraMotionListener.onCameraMotionReset(); - } - } } } From caf62842c4897dc84ba8dd83cc9cf7de7fa3279b Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 09:45:15 +0000 Subject: [PATCH 166/251] Rollback of https://github.com/androidx/media/commit/2a7743346c2b9d2f09f33499dc5461b2525a0de5 *** Original commit *** Rollback of https://github.com/androidx/media/commit/1521e50307bb74983ecef1fc2ddf5f996f27468b *** Original commit *** Wire up MediaMetricsListener and add configuration to disable it. The listener will automatically forward diagnostics info to the Android platform. ExoPlayer.Builder gets a new setter that allows to disable this feature if required. #... *** PiperOrigin-RevId: 427131438 --- .../androidx/media3/exoplayer/ExoPlayer.java | 25 +++++++++++++++++++ .../media3/exoplayer/ExoPlayerImpl.java | 21 +++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 7269e6c6c5..ab755cbc26 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -400,6 +400,7 @@ public interface ExoPlayer extends Player { /* package */ long releaseTimeoutMs; /* package */ long detachSurfaceTimeoutMs; /* package */ boolean pauseAtEndOfMediaItems; + /* package */ boolean usePlatformDiagnostics; /* package */ boolean buildCalled; /** @@ -440,6 +441,7 @@ public interface ExoPlayer extends Player { *

  • {@code releaseTimeoutMs}: {@link #DEFAULT_RELEASE_TIMEOUT_MS} *
  • {@code detachSurfaceTimeoutMs}: {@link #DEFAULT_DETACH_SURFACE_TIMEOUT_MS} *
  • {@code pauseAtEndOfMediaItems}: {@code false} + *
  • {@code usePlatformDiagnostics}: {@code true} *
  • {@link Clock}: {@link Clock#DEFAULT} * * @@ -592,6 +594,7 @@ public interface ExoPlayer extends Player { clock = Clock.DEFAULT; releaseTimeoutMs = DEFAULT_RELEASE_TIMEOUT_MS; detachSurfaceTimeoutMs = DEFAULT_DETACH_SURFACE_TIMEOUT_MS; + usePlatformDiagnostics = true; } /** @@ -971,6 +974,28 @@ public interface ExoPlayer extends Player { return this; } + /** + * Sets whether the player reports diagnostics data to the Android platform. + * + *

    If enabled, the player will use the {@link android.media.metrics.MediaMetricsManager} to + * create a {@link android.media.metrics.PlaybackSession} and forward playback events and + * performance data to this session. This helps to provide system performance and debugging + * information for media playback on the device. This data may also be collected by Google if sharing usage and diagnostics + * data is enabled by the user of the device. + * + * @param usePlatformDiagnostics Whether the player reports diagnostics data to the Android + * platform. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + @UnstableApi + public Builder setUsePlatformDiagnostics(boolean usePlatformDiagnostics) { + checkState(!buildCalled); + this.usePlatformDiagnostics = usePlatformDiagnostics; + return this; + } + /** * Sets the {@link Clock} that will be used by the player. Should only be set for testing * purposes. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 6cfdc10e35..e076dad419 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -139,6 +139,7 @@ import androidx.media3.exoplayer.PlayerMessage.Target; import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; +import androidx.media3.exoplayer.analytics.MediaMetricsListener; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.audio.AudioRendererEventListener; import androidx.media3.exoplayer.metadata.MetadataOutput; @@ -371,7 +372,11 @@ import java.util.concurrent.TimeoutException; playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); - PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() : Api31.createPlayerId(); + PlayerId playerId = + Util.SDK_INT < 31 + ? new PlayerId() + : Api31.registerMediaMetricsListener( + applicationContext, /* player= */ this, builder.usePlatformDiagnostics); internalPlayer = new ExoPlayerImplInternal( renderers, @@ -3057,9 +3062,17 @@ import java.util.concurrent.TimeoutException; private Api31() {} @DoNotInline - public static PlayerId createPlayerId() { - // TODO: Create a MediaMetricsListener and obtain LogSessionId from it. - return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); + public static PlayerId registerMediaMetricsListener( + Context context, ExoPlayerImpl player, boolean usePlatformDiagnostics) { + @Nullable MediaMetricsListener listener = MediaMetricsListener.create(context); + if (listener == null) { + Log.w(TAG, "MediaMetricsService unavailable."); + return new PlayerId(LogSessionId.LOG_SESSION_ID_NONE); + } + if (usePlatformDiagnostics) { + player.addAnalyticsListener(listener); + } + return new PlayerId(listener.getLogSessionId()); } } } From 287182952dc70f3ea3689378a1519dedc342d377 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 09:46:14 +0000 Subject: [PATCH 167/251] Fix the position of IntDefs to match TYPE_USE #minor-release PiperOrigin-RevId: 427131569 --- .../java/androidx/media3/cast/CastPlayer.java | 14 +++----- .../media3/common/AdPlaybackState.java | 2 +- .../androidx/media3/common/BasePlayer.java | 3 +- .../main/java/androidx/media3/common/C.java | 4 +-- .../androidx/media3/common/ColorInfo.java | 13 ++++--- .../androidx/media3/common/DrmInitData.java | 3 +- .../androidx/media3/common/FileTypes.java | 10 +++--- .../java/androidx/media3/common/Format.java | 12 +++---- .../androidx/media3/common/HeartRating.java | 2 +- .../androidx/media3/common/MediaMetadata.java | 4 +-- .../androidx/media3/common/MimeTypes.java | 6 ++-- .../media3/common/PercentageRating.java | 2 +- .../java/androidx/media3/common/Player.java | 3 +- .../androidx/media3/common/StarRating.java | 2 +- .../androidx/media3/common/ThumbRating.java | 2 +- .../androidx/media3/common/TrackGroup.java | 3 +- .../androidx/media3/common/TracksInfo.java | 8 ++--- .../java/androidx/media3/common/text/Cue.java | 17 ++++----- .../androidx/media3/common/text/RubySpan.java | 2 +- .../media3/common/text/TextEmphasisSpan.java | 6 ++-- .../common/util/NetworkTypeObserver.java | 12 +++---- .../androidx/media3/common/util/Util.java | 36 +++++++++---------- .../media3/common/util/XmlPullParserUtil.java | 7 ++-- .../datasource/DataSourceException.java | 2 +- .../androidx/media3/datasource/DataSpec.java | 8 ++--- .../media3/datasource/HttpDataSource.java | 6 ++-- .../datasource/cache/CacheDataSource.java | 2 +- .../java/androidx/media3/decoder/Buffer.java | 2 +- .../androidx/media3/decoder/CryptoInfo.java | 2 +- .../media3/decoder/DecoderInputBuffer.java | 2 +- .../decoder/VideoDecoderOutputBuffer.java | 2 +- .../media3/decoder/av1/Gav1Decoder.java | 2 +- .../decoder/av1/Libgav1VideoRenderer.java | 3 +- .../decoder/ffmpeg/FfmpegAudioDecoder.java | 5 ++- .../decoder/ffmpeg/FfmpegAudioRenderer.java | 6 ++-- .../decoder/ffmpeg/FfmpegVideoRenderer.java | 3 +- .../decoder/flac/LibflacAudioRenderer.java | 3 +- .../decoder/opus/LibopusAudioRenderer.java | 3 +- .../decoder/vp9/LibvpxVideoRenderer.java | 3 +- .../media3/decoder/vp9/VpxDecoder.java | 2 +- .../exoplayer/StreamVolumeManagerTest.java | 2 +- .../media3/exoplayer/AudioFocusManager.java | 9 +++-- .../media3/exoplayer/BaseRenderer.java | 6 ++-- .../exoplayer/DecoderReuseEvaluation.java | 4 +-- .../exoplayer/DefaultRenderersFactory.java | 2 +- .../exoplayer/ExoPlaybackException.java | 4 +-- .../media3/exoplayer/ExoPlayerImpl.java | 33 +++++++---------- .../exoplayer/ExoPlayerImplInternal.java | 9 ++--- .../media3/exoplayer/ExoTimeoutException.java | 2 +- .../media3/exoplayer/NoSampleRenderer.java | 6 ++-- .../media3/exoplayer/PlaybackInfo.java | 4 +-- .../exoplayer/RendererCapabilities.java | 25 +++++-------- .../media3/exoplayer/SimpleExoPlayer.java | 12 +++---- .../media3/exoplayer/StreamVolumeManager.java | 2 +- .../analytics/AnalyticsListener.java | 3 +- .../analytics/MediaMetricsListener.java | 4 +-- .../analytics/PlaybackStatsListener.java | 2 +- .../exoplayer/audio/AudioProcessor.java | 2 +- .../exoplayer/audio/DecoderAudioRenderer.java | 11 +++--- .../exoplayer/audio/DefaultAudioSink.java | 15 ++++---- .../exoplayer/audio/ForwardingAudioSink.java | 3 +- .../audio/MediaCodecAudioRenderer.java | 3 +- .../audio/SilenceSkippingAudioProcessor.java | 2 +- .../exoplayer/audio/SpatializerDelegate.java | 3 +- .../exoplayer/audio/TeeAudioProcessor.java | 2 +- .../audio/TrimmingAudioProcessor.java | 2 +- .../exoplayer/drm/DefaultDrmSession.java | 3 +- .../drm/DefaultDrmSessionManager.java | 5 ++- .../media3/exoplayer/drm/DrmSession.java | 2 +- .../exoplayer/drm/DrmSessionManager.java | 3 +- .../media3/exoplayer/drm/DrmUtil.java | 7 ++-- .../exoplayer/drm/DummyExoMediaDrm.java | 3 +- .../media3/exoplayer/drm/ExoMediaDrm.java | 5 ++- .../exoplayer/drm/FrameworkMediaDrm.java | 3 +- .../drm/UnsupportedDrmException.java | 2 +- .../media3/exoplayer/drm/WidevineUtil.java | 3 +- .../AsynchronousMediaCodecAdapter.java | 2 +- .../DefaultMediaCodecAdapterFactory.java | 2 +- .../mediacodec/MediaCodecRenderer.java | 19 +++++----- .../exoplayer/metadata/MetadataRenderer.java | 3 +- .../media3/exoplayer/offline/Download.java | 4 +-- .../exoplayer/offline/DownloadHelper.java | 3 +- .../exoplayer/offline/DownloadManager.java | 5 ++- .../exoplayer/scheduler/Requirements.java | 11 +++--- .../scheduler/RequirementsWatcher.java | 5 ++- .../exoplayer/source/ClippingMediaSource.java | 2 +- .../source/DefaultMediaSourceFactory.java | 3 +- .../exoplayer/source/MediaLoadData.java | 2 +- .../exoplayer/source/MediaSourceFactory.java | 3 +- .../exoplayer/source/MergingMediaSource.java | 2 +- .../source/ProgressiveMediaPeriod.java | 2 +- .../exoplayer/source/SilenceMediaSource.java | 2 +- .../ads/ServerSideAdInsertionMediaSource.java | 6 ++-- .../media3/exoplayer/source/chunk/Chunk.java | 4 +-- .../mediaparser/OutputConsumerAdapterV30.java | 3 +- .../exoplayer/text/ExoplayerCuesDecoder.java | 2 +- .../media3/exoplayer/text/TextRenderer.java | 5 ++- .../trackselection/DefaultTrackSelector.java | 28 ++++++--------- .../trackselection/MappingTrackSelector.java | 31 +++++++--------- .../upstream/DefaultBandwidthMeter.java | 4 +-- .../upstream/LoadErrorHandlingPolicy.java | 2 +- .../exoplayer/upstream/ParsingLoadable.java | 2 +- .../exoplayer/video/DecoderVideoRenderer.java | 4 +-- .../media3/exoplayer/video/DummySurface.java | 3 +- .../video/MediaCodecVideoRenderer.java | 5 ++- .../video/VideoFrameReleaseHelper.java | 2 +- .../video/spherical/CameraMotionRenderer.java | 3 +- .../video/spherical/ProjectionDecoder.java | 15 +++++--- .../video/spherical/SceneRenderer.java | 4 +-- .../media3/exoplayer/ExoPlayerTest.java | 2 +- .../audio/DecoderAudioRendererTest.java | 3 +- ...faultAudioTrackBufferSizeProviderTest.java | 6 ++-- .../DefaultTrackSelectorTest.java | 14 +++----- .../MappingTrackSelectorTest.java | 6 ++-- .../video/DecoderVideoRendererTest.java | 5 ++- .../video/MediaCodecVideoRendererTest.java | 4 +-- .../exoplayer/dash/DashMediaPeriod.java | 2 +- .../dash/manifest/DashManifestParser.java | 23 +++++------- .../hls/DefaultHlsExtractorFactory.java | 2 +- .../media3/exoplayer/hls/HlsChunkSource.java | 3 +- .../hls/playlist/HlsMediaPlaylist.java | 2 +- .../hls/playlist/HlsPlaylistParser.java | 7 ++-- .../media3/exoplayer/ima/AdTagLoader.java | 2 +- .../ServerSideAdInsertionStreamRequest.java | 4 +-- .../media3/exoplayer/ima/FakeExoPlayer.java | 8 ++--- .../rtsp/RtspAuthenticationInfo.java | 2 +- .../media3/exoplayer/rtsp/RtspClient.java | 5 ++- .../exoplayer/rtsp/RtspMediaPeriod.java | 3 +- .../exoplayer/rtsp/RtspMessageChannel.java | 2 +- .../exoplayer/rtsp/RtspMessageUtil.java | 3 +- .../media3/exoplayer/rtsp/RtspRequest.java | 2 +- .../exoplayer/rtsp/reader/RtpH264Reader.java | 5 ++- .../media3/extractor/BinarySearchSeeker.java | 2 +- .../extractor/DefaultExtractorsFactory.java | 18 +++++----- .../media3/extractor/TrackOutput.java | 2 +- .../extractor/TrueHdSampleRechunker.java | 2 +- .../media3/extractor/jpeg/JpegExtractor.java | 6 ++-- .../extractor/mkv/MatroskaExtractor.java | 18 +++++----- .../media3/extractor/mp3/Mp3Extractor.java | 2 +- .../media3/extractor/mp4/AtomParsers.java | 2 +- .../extractor/mp4/FragmentedMp4Extractor.java | 5 ++- .../media3/extractor/mp4/Mp4Extractor.java | 16 ++++----- .../media3/extractor/mp4/SefReader.java | 10 +++--- .../androidx/media3/extractor/mp4/Track.java | 2 +- .../extractor/mp4/TrackEncryptionBox.java | 3 +- .../extractor/text/SubtitleExtractor.java | 2 +- .../media3/extractor/text/ssa/SsaDecoder.java | 6 ++-- .../media3/extractor/text/ssa/SsaStyle.java | 10 +++--- .../extractor/text/ttml/TextEmphasis.java | 6 ++-- .../extractor/text/ttml/TtmlRegion.java | 8 ++--- .../media3/extractor/text/ttml/TtmlStyle.java | 28 +++++++-------- .../extractor/text/webvtt/WebvttCssStyle.java | 21 +++++------ .../text/webvtt/WebvttCueParser.java | 31 +++++++--------- .../media3/extractor/ts/Ac3Reader.java | 2 +- .../media3/extractor/ts/Ac4Reader.java | 2 +- .../ts/DefaultTsPayloadReaderFactory.java | 2 +- .../media3/extractor/ts/H263Reader.java | 2 +- .../media3/extractor/ts/TsExtractor.java | 6 ++-- .../media3/extractor/wav/WavExtractor.java | 7 ++-- .../extractor/mkv/DefaultEbmlReaderTest.java | 3 +- .../extractor/text/ttml/TtmlStyleTest.java | 2 +- .../androidx/media3/session/MockPlayer.java | 12 +++---- .../androidx/media3/test/utils/Action.java | 6 ++-- .../media3/test/utils/CapturingAudioSink.java | 2 +- .../media3/test/utils/DownloadBuilder.java | 2 +- .../media3/test/utils/DumpFileAsserts.java | 2 +- .../media3/test/utils/FakeExoMediaDrm.java | 3 +- .../media3/test/utils/FakeRenderer.java | 3 +- .../media3/test/utils/FakeSampleStream.java | 2 +- .../media3/test/utils/StubPlayer.java | 6 ++-- .../test/utils/WebServerDispatcher.java | 7 ++-- .../test/utils/truth/SpannedSubject.java | 8 ++--- .../TestDownloadManagerListener.java | 2 +- .../media3/transformer/Transformer.java | 5 ++- .../transformer/TransformerBaseRenderer.java | 3 +- 175 files changed, 439 insertions(+), 595 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 4d54065d0e..cc026ba69a 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -150,7 +150,7 @@ public final class CastPlayer extends BasePlayer { private TrackSelectionArray currentTrackSelection; private TracksInfo currentTracksInfo; private Commands availableCommands; - @Player.State private int playbackState; + private @Player.State int playbackState; private int currentWindowIndex; private long lastReportedPositionMs; private int pendingSeekCount; @@ -387,14 +387,12 @@ public final class CastPlayer extends BasePlayer { } @Override - @Player.State - public int getPlaybackState() { + public @Player.State int getPlaybackState() { return playbackState; } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { return Player.PLAYBACK_SUPPRESSION_REASON_NONE; } @@ -574,8 +572,7 @@ public final class CastPlayer extends BasePlayer { } @Override - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { return repeatMode.value; } @@ -1292,8 +1289,7 @@ public final class CastPlayer extends BasePlayer { * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link * Player.RepeatMode}. */ - @RepeatMode - private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { + private static @RepeatMode int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { MediaStatus mediaStatus = remoteMediaClient.getMediaStatus(); if (mediaStatus == null) { // No media session active, yet. diff --git a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java index 3b900b60e2..b713f32ef3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdPlaybackState.java @@ -67,7 +67,7 @@ public final class AdPlaybackState implements Bundleable { /** The URI of each ad in the ad group. */ public final @NullableType Uri[] uris; /** The state of each ad in the ad group. */ - @AdState public final int[] states; + public final @AdState int[] states; /** The durations of each ad in the ad group, in microseconds. */ public final long[] durationsUs; /** diff --git a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java index f79df0ff90..d782dcb0e6 100644 --- a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java @@ -384,8 +384,7 @@ public abstract class BasePlayer implements Player { : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); } - @RepeatMode - private int getRepeatModeForNavigation() { + private @RepeatMode int getRepeatModeForNavigation() { @RepeatMode int repeatMode = getRepeatMode(); return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; } diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 486e00a36b..a83ca8798f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -1250,8 +1250,8 @@ public final class C { replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)", imports = {"androidx.media3.common.util.Util"}) @Deprecated - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( + int mediaDrmErrorCode) { return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode); } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java index fb27d2d18c..829262bb88 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java @@ -38,8 +38,7 @@ public final class ColorInfo implements Bundleable { * made. */ @Pure - @C.ColorSpace - public static int isoColorPrimariesToColorSpace(int isoColorPrimaries) { + public static @C.ColorSpace int isoColorPrimariesToColorSpace(int isoColorPrimaries) { switch (isoColorPrimaries) { case 1: return C.COLOR_SPACE_BT709; @@ -61,8 +60,8 @@ public final class ColorInfo implements Bundleable { * mapping can be made. */ @Pure - @C.ColorTransfer - public static int isoTransferCharacteristicsToColorTransfer(int isoTransferCharacteristics) { + public static @C.ColorTransfer int isoTransferCharacteristicsToColorTransfer( + int isoTransferCharacteristics) { switch (isoTransferCharacteristics) { case 1: // BT.709. case 6: // SMPTE 170M. @@ -81,20 +80,20 @@ public final class ColorInfo implements Bundleable { * The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link * C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown. */ - @C.ColorSpace public final int colorSpace; + public final @C.ColorSpace int colorSpace; /** * The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link * C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown. */ - @C.ColorRange public final int colorRange; + public final @C.ColorRange int colorRange; /** * The color transfer characteristics of the video. Valid values are {@link C#COLOR_TRANSFER_HLG}, * {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link Format#NO_VALUE} if * unknown. */ - @C.ColorTransfer public final int colorTransfer; + public final @C.ColorTransfer int colorTransfer; /** HdrStaticInfo as defined in CTA-861.3, or null if none specified. */ @Nullable public final byte[] hdrStaticInfo; diff --git a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java index 639fb7ee91..2d9e19caab 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java +++ b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java @@ -52,7 +52,8 @@ public final class DrmInitData implements Comparator, Parcelable { * @param mediaData DRM session acquisition data obtained from the media. * @return A {@link DrmInitData} obtained from merging a media manifest and a media stream. */ - public static @Nullable DrmInitData createSessionCreationData( + @Nullable + public static DrmInitData createSessionCreationData( @Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) { ArrayList result = new ArrayList<>(); String schemeType = null; diff --git a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java index 50a988f980..cc5fd41dee 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FileTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/FileTypes.java @@ -114,8 +114,8 @@ public final class FileTypes { private FileTypes() {} /** Returns the {@link Type} corresponding to the response headers provided. */ - @FileTypes.Type - public static int inferFileTypeFromResponseHeaders(Map> responseHeaders) { + public static @FileTypes.Type int inferFileTypeFromResponseHeaders( + Map> responseHeaders) { @Nullable List contentTypes = responseHeaders.get(HEADER_CONTENT_TYPE); @Nullable String mimeType = contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.get(0); @@ -127,8 +127,7 @@ public final class FileTypes { * *

    Returns {@link #UNKNOWN} if the mime type is {@code null}. */ - @FileTypes.Type - public static int inferFileTypeFromMimeType(@Nullable String mimeType) { + public static @FileTypes.Type int inferFileTypeFromMimeType(@Nullable String mimeType) { if (mimeType == null) { return FileTypes.UNKNOWN; } @@ -178,8 +177,7 @@ public final class FileTypes { } /** Returns the {@link Type} corresponding to the {@link Uri} provided. */ - @FileTypes.Type - public static int inferFileTypeFromUri(Uri uri) { + public static @FileTypes.Type int inferFileTypeFromUri(Uri uri) { @Nullable String filename = uri.getLastPathSegment(); if (filename == null) { return FileTypes.UNKNOWN; diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index dd6460c9cf..773b9fab37 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -156,14 +156,14 @@ public final class Format implements Bundleable { private int rotationDegrees; private float pixelWidthHeightRatio; @Nullable private byte[] projectionData; - @C.StereoMode private int stereoMode; + private @C.StereoMode int stereoMode; @Nullable private ColorInfo colorInfo; // Audio specific. private int channelCount; private int sampleRate; - @C.PcmEncoding private int pcmEncoding; + private @C.PcmEncoding int pcmEncoding; private int encoderDelay; private int encoderPadding; @@ -173,7 +173,7 @@ public final class Format implements Bundleable { // Provided by the source. - @C.CryptoType private int cryptoType; + private @C.CryptoType int cryptoType; /** Creates a new instance with default values. */ public Builder() { @@ -727,7 +727,7 @@ public final class Format implements Bundleable { * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}. */ - @UnstableApi @C.StereoMode public final int stereoMode; + @UnstableApi public final @C.StereoMode int stereoMode; /** The color metadata associated with the video, or null if not applicable. */ @UnstableApi @Nullable public final ColorInfo colorInfo; @@ -738,7 +738,7 @@ public final class Format implements Bundleable { /** The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. */ public final int sampleRate; /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ - @UnstableApi @C.PcmEncoding public final int pcmEncoding; + @UnstableApi public final @C.PcmEncoding int pcmEncoding; /** * The number of frames to trim from the start of the decoded audio stream, or 0 if not * applicable. @@ -762,7 +762,7 @@ public final class Format implements Bundleable { * {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that * the samples are encrypted using an unsupported crypto type. */ - @UnstableApi @C.CryptoType public final int cryptoType; + @UnstableApi public final @C.CryptoType int cryptoType; // Lazily initialized hashcode. private int hashCode; diff --git a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java index 4a7117764c..08a6b405f3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/HeartRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/HeartRating.java @@ -79,7 +79,7 @@ public final class HeartRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_HEART; + private static final @RatingType int TYPE = RATING_TYPE_HEART; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index 7a847c24e2..4d257fe7d4 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -56,11 +56,11 @@ public final class MediaMetadata implements Bundleable { @Nullable private Rating userRating; @Nullable private Rating overallRating; @Nullable private byte[] artworkData; - @Nullable @PictureType private Integer artworkDataType; + @Nullable private @PictureType Integer artworkDataType; @Nullable private Uri artworkUri; @Nullable private Integer trackNumber; @Nullable private Integer totalTrackCount; - @Nullable @FolderType private Integer folderType; + @Nullable private @FolderType Integer folderType; @Nullable private Boolean isPlayable; @Nullable private Integer recordingYear; @Nullable private Integer recordingMonth; diff --git a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java index e1958db3e1..2c6973d1e1 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java +++ b/libraries/common/src/main/java/androidx/media3/common/MimeTypes.java @@ -552,8 +552,7 @@ public final class MimeTypes { * @return The corresponding {@link C.Encoding}, or {@link C#ENCODING_INVALID}. */ @UnstableApi - @C.Encoding - public static int getEncoding(String mimeType, @Nullable String codec) { + public static @C.Encoding int getEncoding(String mimeType, @Nullable String codec) { switch (mimeType) { case MimeTypes.AUDIO_MPEG: return C.ENCODING_MP3; @@ -728,8 +727,7 @@ public final class MimeTypes { } /** Returns the encoding for {@link #audioObjectTypeIndication}. */ - @C.Encoding - public int getEncoding() { + public @C.Encoding int getEncoding() { // See AUDIO_OBJECT_TYPE_AAC_* constants in AacUtil. switch (audioObjectTypeIndication) { case 2: diff --git a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java index f110efba16..afc20a1687 100644 --- a/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/PercentageRating.java @@ -77,7 +77,7 @@ public final class PercentageRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_PERCENTAGE; + private static final @RatingType int TYPE = RATING_TYPE_PERCENTAGE; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index aed681f729..1e7ed137e3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -686,8 +686,7 @@ public interface Player { @UnstableApi public static final class Builder { - @Command - private static final int[] SUPPORTED_COMMANDS = { + private static final @Command int[] SUPPORTED_COMMANDS = { COMMAND_PLAY_PAUSE, COMMAND_PREPARE, COMMAND_STOP, diff --git a/libraries/common/src/main/java/androidx/media3/common/StarRating.java b/libraries/common/src/main/java/androidx/media3/common/StarRating.java index c6547dde33..2c38f7cb75 100644 --- a/libraries/common/src/main/java/androidx/media3/common/StarRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/StarRating.java @@ -103,7 +103,7 @@ public final class StarRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_STAR; + private static final @RatingType int TYPE = RATING_TYPE_STAR; private static final int MAX_STARS_DEFAULT = 5; @Documented diff --git a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java index 3c68986b98..cd4ad73473 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java +++ b/libraries/common/src/main/java/androidx/media3/common/ThumbRating.java @@ -76,7 +76,7 @@ public final class ThumbRating extends Rating { // Bundleable implementation. - @RatingType private static final int TYPE = RATING_TYPE_THUMB; + private static final @RatingType int TYPE = RATING_TYPE_THUMB; @Documented @Retention(RetentionPolicy.SOURCE) diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java index f0c85adfb8..ad0bb81c23 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java @@ -207,8 +207,7 @@ public final class TrackGroup implements Bundleable { return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language; } - @C.RoleFlags - private static int normalizeRoleFlags(@C.RoleFlags int roleFlags) { + private static @C.RoleFlags int normalizeRoleFlags(@C.RoleFlags int roleFlags) { // Treat trick-play and non-trick-play formats as compatible. return roleFlags | C.ROLE_FLAG_TRICK_PLAY; } diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 066a92152e..437f7dda44 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -45,7 +45,7 @@ public final class TracksInfo implements Bundleable { */ public static final class TrackGroupInfo implements Bundleable { private final TrackGroup trackGroup; - @C.FormatSupport private final int[] trackSupport; + private final @C.FormatSupport int[] trackSupport; private final @C.TrackType int trackType; private final boolean[] trackSelected; @@ -83,8 +83,7 @@ public final class TracksInfo implements Bundleable { * @return The {@link C.FormatSupport} of the track. */ @UnstableApi - @C.FormatSupport - public int getTrackSupport(int trackIndex) { + public @C.FormatSupport int getTrackSupport(int trackIndex) { return trackSupport[trackIndex]; } @@ -229,8 +228,7 @@ public final class TracksInfo implements Bundleable { fromNullableBundle( TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP))); checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup - @C.FormatSupport - final int[] trackSupport = + final @C.FormatSupport int[] trackSupport = MoreObjects.firstNonNull( bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]); @C.TrackType diff --git a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java index d5f04a3540..6b9d9ded28 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/Cue.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/Cue.java @@ -569,7 +569,7 @@ public final class Cue implements Bundleable { @Nullable private Alignment textAlignment; @Nullable private Alignment multiRowAlignment; private float line; - @LineType private int lineType; + private @LineType int lineType; private @AnchorType int lineAnchor; private float position; private @AnchorType int positionAnchor; @@ -730,8 +730,7 @@ public final class Cue implements Bundleable { * @see Cue#lineType */ @Pure - @LineType - public int getLineType() { + public @LineType int getLineType() { return lineType; } @@ -751,8 +750,7 @@ public final class Cue implements Bundleable { * @see Cue#lineAnchor */ @Pure - @AnchorType - public int getLineAnchor() { + public @AnchorType int getLineAnchor() { return lineAnchor; } @@ -794,8 +792,7 @@ public final class Cue implements Bundleable { * @see Cue#positionAnchor */ @Pure - @AnchorType - public int getPositionAnchor() { + public @AnchorType int getPositionAnchor() { return positionAnchor; } @@ -817,8 +814,7 @@ public final class Cue implements Bundleable { * @see Cue#textSizeType */ @Pure - @TextSizeType - public int getTextSizeType() { + public @TextSizeType int getTextSizeType() { return textSizeType; } @@ -936,8 +932,7 @@ public final class Cue implements Bundleable { * @see Cue#verticalType */ @Pure - @VerticalType - public int getVerticalType() { + public @VerticalType int getVerticalType() { return verticalType; } diff --git a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java index 9c6ba1275e..482cb7a92d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/RubySpan.java @@ -39,7 +39,7 @@ public final class RubySpan implements LanguageFeatureSpan { public final String rubyText; /** The position of the ruby text relative to the base text. */ - @TextAnnotation.Position public final int position; + public final @TextAnnotation.Position int position; public RubySpan(String rubyText, @TextAnnotation.Position int position) { this.rubyText = rubyText; diff --git a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java index 68d6e236e5..11595c4f84 100644 --- a/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java +++ b/libraries/common/src/main/java/androidx/media3/common/text/TextEmphasisSpan.java @@ -83,13 +83,13 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan { public static final int MARK_FILL_OPEN = 2; /** The mark shape used for text emphasis. */ - @MarkShape public int markShape; + public @MarkShape int markShape; /** The mark fill for the text emphasis mark. */ - @MarkShape public int markFill; + public @MarkShape int markFill; /** The position of the text emphasis relative to the base text. */ - @TextAnnotation.Position public final int position; + public final @TextAnnotation.Position int position; public TextEmphasisSpan( @MarkShape int shape, @MarkFill int fill, @TextAnnotation.Position int position) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java index ceefb88e0f..9609cff496 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java @@ -85,8 +85,7 @@ public final class NetworkTypeObserver { private final Object networkTypeLock; @GuardedBy("networkTypeLock") - @C.NetworkType - private int networkType; + private @C.NetworkType int networkType; /** * Returns a network type observer instance. @@ -132,8 +131,7 @@ public final class NetworkTypeObserver { } /** Returns the current network type. */ - @C.NetworkType - public int getNetworkType() { + public @C.NetworkType int getNetworkType() { synchronized (networkTypeLock) { return networkType; } @@ -164,8 +162,7 @@ public final class NetworkTypeObserver { } } - @C.NetworkType - private static int getNetworkTypeFromConnectivityManager(Context context) { + private static @C.NetworkType int getNetworkTypeFromConnectivityManager(Context context) { NetworkInfo networkInfo; @Nullable ConnectivityManager connectivityManager = @@ -198,8 +195,7 @@ public final class NetworkTypeObserver { } } - @C.NetworkType - private static int getMobileNetworkType(NetworkInfo networkInfo) { + private static @C.NetworkType int getMobileNetworkType(NetworkInfo networkInfo) { switch (networkInfo.getSubtype()) { case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_GPRS: 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 5e1976f65c..d45d1d35dc 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 @@ -1560,8 +1560,7 @@ public final class Util { * C#ENCODING_PCM_16BIT}, {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. If * the bit depth is unsupported then {@link C#ENCODING_INVALID} is returned. */ - @C.PcmEncoding - public static int getPcmEncoding(int bitDepth) { + public static @C.PcmEncoding int getPcmEncoding(int bitDepth) { switch (bitDepth) { case 8: return C.ENCODING_PCM_8BIT; @@ -1671,8 +1670,7 @@ public final class Util { } /** Returns the {@link C.AudioUsage} corresponding to the specified {@link C.StreamType}. */ - @C.AudioUsage - public static int getAudioUsageForStreamType(@C.StreamType int streamType) { + public static @C.AudioUsage int getAudioUsageForStreamType(@C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: return C.USAGE_ALARM; @@ -1693,8 +1691,8 @@ public final class Util { } /** Returns the {@link C.AudioContentType} corresponding to the specified {@link C.StreamType}. */ - @C.AudioContentType - public static int getAudioContentTypeForStreamType(@C.StreamType int streamType) { + public static @C.AudioContentType int getAudioContentTypeForStreamType( + @C.StreamType int streamType) { switch (streamType) { case C.STREAM_TYPE_ALARM: case C.STREAM_TYPE_DTMF: @@ -1711,8 +1709,7 @@ public final class Util { } /** Returns the {@link C.StreamType} corresponding to the specified {@link C.AudioUsage}. */ - @C.StreamType - public static int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { + public static @C.StreamType int getStreamTypeForAudioUsage(@C.AudioUsage int usage) { switch (usage) { case C.USAGE_MEDIA: case C.USAGE_GAME: @@ -1762,7 +1759,8 @@ public final class Util { * "clearkey"}. * @return The derived {@link UUID}, or {@code null} if one could not be derived. */ - public static @Nullable UUID getDrmUuid(String drmScheme) { + @Nullable + public static UUID getDrmUuid(String drmScheme) { switch (Ascii.toLowerCase(drmScheme)) { case "widevine": return C.WIDEVINE_UUID; @@ -1784,8 +1782,8 @@ public final class Util { * MediaDrm.ErrorCodes} value. Returns {@link PlaybackException#ERROR_CODE_DRM_SYSTEM_ERROR} if * the provided error code isn't recognised. */ - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode( + int mediaDrmErrorCode) { switch (mediaDrmErrorCode) { case MediaDrm.ErrorCodes.ERROR_PROVISIONING_CONFIG: case MediaDrm.ErrorCodes.ERROR_PROVISIONING_PARSE: @@ -1821,8 +1819,7 @@ public final class Util { * @param overrideExtension If not null, used to infer the type. * @return The content type. */ - @ContentType - public static int inferContentType(Uri uri, @Nullable String overrideExtension) { + public static @ContentType int inferContentType(Uri uri, @Nullable String overrideExtension) { return TextUtils.isEmpty(overrideExtension) ? inferContentType(uri) : inferContentType("." + overrideExtension); @@ -1834,8 +1831,7 @@ public final class Util { * @param uri The {@link Uri}. * @return The content type. */ - @ContentType - public static int inferContentType(Uri uri) { + public static @ContentType int inferContentType(Uri uri) { @Nullable String scheme = uri.getScheme(); if (scheme != null && Ascii.equalsIgnoreCase("rtsp", scheme)) { return C.TYPE_RTSP; @@ -1851,8 +1847,7 @@ public final class Util { * @param fileName Name of the file. It can include the path of the file. * @return The content type. */ - @ContentType - public static int inferContentType(String fileName) { + public static @ContentType int inferContentType(String fileName) { fileName = Ascii.toLowerCase(fileName); if (fileName.endsWith(".mpd")) { return C.TYPE_DASH; @@ -1881,8 +1876,8 @@ public final class Util { * @param mimeType If MIME type, or {@code null}. * @return The content type. */ - @ContentType - public static int inferContentTypeForUriAndMimeType(Uri uri, @Nullable String mimeType) { + public static @ContentType int inferContentTypeForUriAndMimeType( + Uri uri, @Nullable String mimeType) { if (mimeType == null) { return Util.inferContentType(uri); } @@ -2032,7 +2027,8 @@ public final class Util { * @return The original value of the file name before it was escaped, or null if the escaped * fileName seems invalid. */ - public static @Nullable String unescapeFileName(String fileName) { + @Nullable + public static String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java index 46794813e8..3266ee427f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/XmlPullParserUtil.java @@ -93,7 +93,8 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - public static @Nullable String getAttributeValue(XmlPullParser xpp, String attributeName) { + @Nullable + public static String getAttributeValue(XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (xpp.getAttributeName(i).equals(attributeName)) { @@ -112,8 +113,8 @@ public final class XmlPullParserUtil { * @return The value of the attribute, or null if the current event is not a start tag or if no * such attribute was found. */ - public static @Nullable String getAttributeValueIgnorePrefix( - XmlPullParser xpp, String attributeName) { + @Nullable + public static String getAttributeValueIgnorePrefix(XmlPullParser xpp, String attributeName) { int attributeCount = xpp.getAttributeCount(); for (int i = 0; i < attributeCount; i++) { if (stripPrefix(xpp.getAttributeName(i)).equals(attributeName)) { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java index 5ea554ab78..fa14682255 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceException.java @@ -57,7 +57,7 @@ public class DataSourceException extends IOException { * The reason of this {@link DataSourceException}, should be one of the {@code ERROR_CODE_IO_*} in * {@link PlaybackException.ErrorCode}. */ - @PlaybackException.ErrorCode public final int reason; + public final @PlaybackException.ErrorCode int reason; /** * Constructs a DataSourceException. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java index 9927ec238d..646b5f274a 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSpec.java @@ -49,13 +49,13 @@ public final class DataSpec { @Nullable private Uri uri; private long uriPositionOffset; - @HttpMethod private int httpMethod; + private @HttpMethod int httpMethod; @Nullable private byte[] httpBody; private Map httpRequestHeaders; private long position; private long length; @Nullable private String key; - @Flags private int flags; + private @Flags int flags; @Nullable private Object customData; /** Creates a new instance with default values. */ @@ -330,7 +330,7 @@ public final class DataSpec { * The HTTP method to use when requesting the data. This value will be ignored by non-HTTP {@link * DataSource} implementations. */ - @HttpMethod public final int httpMethod; + public final @HttpMethod int httpMethod; /** * The HTTP request body, null otherwise. If the body is non-null, then {@code httpBody.length} @@ -382,7 +382,7 @@ public final class DataSpec { @Nullable public final String key; /** Request {@link Flags flags}. */ - @Flags public final int flags; + public final @Flags int flags; /** * Application specific data. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java index 4352008b78..967a00b49c 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java @@ -233,7 +233,7 @@ public interface HttpDataSource extends DataSource { /** The {@link DataSpec} associated with the current connection. */ public final DataSpec dataSpec; - @Type public final int type; + public final @Type int type; /** * @deprecated Use {@link #HttpDataSourceException(DataSpec, int, int) @@ -349,8 +349,8 @@ public interface HttpDataSource extends DataSource { this.type = type; } - @PlaybackException.ErrorCode - private static int assignErrorCode(@PlaybackException.ErrorCode int errorCode, @Type int type) { + private static @PlaybackException.ErrorCode int assignErrorCode( + @PlaybackException.ErrorCode int errorCode, @Type int type) { return errorCode == PlaybackException.ERROR_CODE_IO_UNSPECIFIED && type == TYPE_OPEN ? PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED : errorCode; diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java index d39cceb0f0..4eae94a1ee 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheDataSource.java @@ -72,7 +72,7 @@ public final class CacheDataSource implements DataSource { @Nullable private DataSource.Factory upstreamDataSourceFactory; @Nullable private PriorityTaskManager upstreamPriorityTaskManager; private int upstreamPriority; - @CacheDataSource.Flags private int flags; + private @CacheDataSource.Flags int flags; @Nullable private CacheDataSource.EventListener eventListener; public Factory() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java index 26101921d9..abbdba9c9b 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/Buffer.java @@ -22,7 +22,7 @@ import androidx.media3.common.util.UnstableApi; @UnstableApi public abstract class Buffer { - @C.BufferFlags private int flags; + private @C.BufferFlags int flags; /** Clears the buffer. */ public void clear() { diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java index 93443e28a5..4663b38788 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java @@ -48,7 +48,7 @@ public final class CryptoInfo { * * @see android.media.MediaCodec.CryptoInfo#mode */ - @C.CryptoMode public int mode; + public @C.CryptoMode int mode; /** * The number of leading unencrypted bytes in each sub-sample. If null, all bytes are treated as * encrypted and {@link #numBytesOfEncryptedData} must be specified. diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java index 05a0164493..265e2b0685 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/DecoderInputBuffer.java @@ -111,7 +111,7 @@ public class DecoderInputBuffer extends Buffer { */ @Nullable public ByteBuffer supplementalData; - @BufferReplacementMode private final int bufferReplacementMode; + private final @BufferReplacementMode int bufferReplacementMode; private final int paddingSize; /** Returns a new instance that's not able to hold any data. */ diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java index ad71e2d066..b0fb72294b 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/VideoDecoderOutputBuffer.java @@ -34,7 +34,7 @@ public class VideoDecoderOutputBuffer extends DecoderOutputBuffer { public int decoderPrivate; /** Output mode. */ - @C.VideoOutputMode public int mode; + public @C.VideoOutputMode int mode; /** RGB buffer for RGB mode. */ @Nullable public ByteBuffer data; diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java index 27b602caef..fb54007a84 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Decoder.java @@ -41,7 +41,7 @@ public final class Gav1Decoder private final long gav1DecoderContext; - @C.VideoOutputMode private volatile int outputMode; + private volatile @C.VideoOutputMode int outputMode; /** * Creates a Gav1Decoder. diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java index 7df78d3f1d..835c505b1f 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Libgav1VideoRenderer.java @@ -128,8 +128,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer { } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!MimeTypes.VIDEO_AV1.equalsIgnoreCase(format.sampleMimeType) || !Gav1Library.isAvailable()) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java index aa24f3176b..0e07287400 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder.java @@ -41,7 +41,7 @@ import java.util.List; private final String codecName; @Nullable private final byte[] extraData; - @C.PcmEncoding private final int encoding; + private final @C.PcmEncoding int encoding; private final int outputBufferSize; private long nativeContext; // May be reassigned on resetting the codec. @@ -158,8 +158,7 @@ import java.util.List; } /** Returns the encoding of output audio. */ - @C.PcmEncoding - public int getEncoding() { + public @C.PcmEncoding int getEncoding() { return encoding; } diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java index 047ab542dd..f9119e5e43 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegAudioRenderer.java @@ -90,8 +90,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { } @Override - @C.FormatSupport - protected int supportsFormatInternal(Format format) { + protected @C.FormatSupport int supportsFormatInternal(Format format) { boolean drmIsSupported = OpusLibrary.supportsCryptoType(format.cryptoType); if (!OpusLibrary.isAvailable() || !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) { diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java index 0cbc8eb5ab..3f8130c1ad 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/LibvpxVideoRenderer.java @@ -126,8 +126,7 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer { } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java index 6771056f7b..259ac2a544 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxDecoder.java @@ -49,7 +49,7 @@ public final class VpxDecoder @Nullable private ByteBuffer lastSupplementalData; - @C.VideoOutputMode private volatile int outputMode; + private volatile @C.VideoOutputMode int outputMode; /** * Creates a VP9 decoder. diff --git a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java index 404611de21..df1ea2f4a0 100644 --- a/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java +++ b/libraries/exoplayer/src/androidTest/java/androidx/media3/exoplayer/StreamVolumeManagerTest.java @@ -273,7 +273,7 @@ public class StreamVolumeManagerTest { private static class TestListener implements StreamVolumeManager.Listener { - @C.StreamType private int lastStreamType; + private @C.StreamType int lastStreamType; private int lastStreamVolume; private boolean lastStreamVolumeMuted; public final CountDownLatch onStreamVolumeChangedLatch; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index 68203349aa..e07c434534 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -139,7 +139,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private PlayerControl playerControl; @Nullable private AudioAttributes audioAttributes; - @AudioFocusState private int audioFocusState; + private @AudioFocusState int audioFocusState; private @AudioFocusGain int focusGainToRequest; private float volumeMultiplier = VOLUME_MULTIPLIER_DEFAULT; @@ -193,8 +193,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param playbackState The desired playback state. * @return A {@link PlayerCommand} to execute on the player. */ - @PlayerCommand - public int updateAudioFocus(boolean playWhenReady, @Player.State int playbackState) { + public @PlayerCommand int updateAudioFocus( + boolean playWhenReady, @Player.State int playbackState) { if (shouldAbandonAudioFocusIfHeld(playbackState)) { abandonAudioFocusIfHeld(); return playWhenReady ? PLAYER_COMMAND_PLAY_WHEN_READY : PLAYER_COMMAND_DO_NOT_PLAY; @@ -222,8 +222,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return playbackState == Player.STATE_IDLE || focusGainToRequest != AUDIOFOCUS_GAIN; } - @PlayerCommand - private int requestAudioFocus() { + private @PlayerCommand int requestAudioFocus() { if (audioFocusState == AUDIO_FOCUS_STATE_HAVE_FOCUS) { return PLAYER_COMMAND_PLAY_WHEN_READY; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java index f2667947d9..db410d3a8d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java @@ -200,8 +200,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // RendererCapabilities implementation. @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } @@ -424,8 +423,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { * the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and * flags are populated if this exception is thrown, but the read position is not advanced. */ - @ReadDataResult - protected final int readSource( + protected final @ReadDataResult int readSource( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { @ReadDataResult int result = Assertions.checkNotNull(stream).readData(formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java index 0257b4af1d..142555e985 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java @@ -125,13 +125,13 @@ public final class DecoderReuseEvaluation { public final Format newFormat; /** The {@link DecoderReuseResult result} of the evaluation. */ - @DecoderReuseResult public final int result; + public final @DecoderReuseResult int result; /** * {@link DecoderDiscardReasons Reasons} why the decoder cannot be reused. Always {@code 0} if * reuse is possible. May also be {code 0} if reuse is not possible for an unspecified reason. */ - @DecoderDiscardReasons public final int discardReasons; + public final @DecoderDiscardReasons int discardReasons; /** * @param decoderName The name of the decoder. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index f11ec4d5b8..451c0ee3a1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -95,7 +95,7 @@ public class DefaultRenderersFactory implements RenderersFactory { private final Context context; private final DefaultMediaCodecAdapterFactory codecAdapterFactory; - @ExtensionRendererMode private int extensionRendererMode; + private @ExtensionRendererMode int extensionRendererMode; private long allowedVideoJoiningTimeMs; private boolean enableDecoderFallback; private MediaCodecSelector mediaCodecSelector; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java index 2a2b03ab0f..52bf89b365 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlaybackException.java @@ -85,7 +85,7 @@ public final class ExoPlaybackException extends PlaybackException { @UnstableApi public static final int TYPE_REMOTE = 3; /** The {@link Type} of the playback failure. */ - @UnstableApi @Type public final int type; + @UnstableApi public final @Type int type; /** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */ @UnstableApi @Nullable public final String rendererName; @@ -104,7 +104,7 @@ public final class ExoPlaybackException extends PlaybackException { * renderer for {@link #rendererFormat}. If {@link #rendererFormat} is null, this is {@link * C#FORMAT_HANDLED}. */ - @UnstableApi @FormatSupport public final int rendererFormatSupport; + @UnstableApi public final @FormatSupport int rendererFormatSupport; /** The {@link MediaPeriodId} of the media associated with this error, or null if undetermined. */ @UnstableApi @Nullable public final MediaPeriodId mediaPeriodId; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index e076dad419..ab7c22607d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -216,12 +216,12 @@ import java.util.concurrent.TimeoutException; private final WifiLockManager wifiLockManager; private final long detachSurfaceTimeoutMs; - @RepeatMode private int repeatMode; + private @RepeatMode int repeatMode; private boolean shuffleModeEnabled; private int pendingOperationAcks; - @DiscontinuityReason private int pendingDiscontinuityReason; + private @DiscontinuityReason int pendingDiscontinuityReason; private boolean pendingDiscontinuity; - @PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason; + private @PlayWhenReadyChangeReason int pendingPlayWhenReadyChangeReason; private boolean foregroundMode; private SeekParameters seekParameters; private ShuffleOrder shuffleOrder; @@ -238,8 +238,8 @@ import java.util.concurrent.TimeoutException; @Nullable private SphericalGLSurfaceView sphericalGLSurfaceView; private boolean surfaceHolderSurfaceIsVideoOutput; @Nullable private TextureView textureView; - @C.VideoScalingMode private int videoScalingMode; - @C.VideoChangeFrameRateStrategy private int videoChangeFrameRateStrategy; + private @C.VideoScalingMode int videoScalingMode; + private @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy; private int surfaceWidth; private int surfaceHeight; @Nullable private DecoderCounters videoDecoderCounters; @@ -515,14 +515,12 @@ import java.util.concurrent.TimeoutException; return availableCommands; } - @State - public int getPlaybackState() { + public @State int getPlaybackState() { verifyApplicationThread(); return playbackInfo.playbackState; } - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } @@ -814,8 +812,7 @@ import java.util.concurrent.TimeoutException; } } - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { verifyApplicationThread(); return repeatMode; } @@ -1271,8 +1268,7 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); } - @C.VideoScalingMode - public int getVideoScalingMode() { + public @C.VideoScalingMode int getVideoScalingMode() { return videoScalingMode; } @@ -1287,8 +1283,7 @@ import java.util.concurrent.TimeoutException; TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); } - @C.VideoChangeFrameRateStrategy - public int getVideoChangeFrameRateStrategy() { + public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { return videoChangeFrameRateStrategy; } @@ -2968,15 +2963,13 @@ import java.util.concurrent.TimeoutException; private static final class FrameMetadataListener implements VideoFrameMetadataListener, CameraMotionListener, PlayerMessage.Target { - @MessageType - public static final int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = + public static final @MessageType int MSG_SET_VIDEO_FRAME_METADATA_LISTENER = Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; - @MessageType - public static final int MSG_SET_CAMERA_MOTION_LISTENER = + public static final @MessageType int MSG_SET_CAMERA_MOTION_LISTENER = Renderer.MSG_SET_CAMERA_MOTION_LISTENER; - @MessageType public static final int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; + public static final @MessageType int MSG_SET_SPHERICAL_SURFACE_VIEW = Renderer.MSG_CUSTOM_BASE; @Nullable private VideoFrameMetadataListener videoFrameMetadataListener; @Nullable private CameraMotionListener cameraMotionListener; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 6096159fbc..1bbed758c9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -93,9 +93,9 @@ import java.util.concurrent.atomic.AtomicBoolean; public PlaybackInfo playbackInfo; public int operationAcks; public boolean positionDiscontinuity; - @DiscontinuityReason public int discontinuityReason; + public @DiscontinuityReason int discontinuityReason; public boolean hasPlayWhenReadyChangeReason; - @PlayWhenReadyChangeReason public int playWhenReadyChangeReason; + public @PlayWhenReadyChangeReason int playWhenReadyChangeReason; public PlaybackInfoUpdate(PlaybackInfo playbackInfo) { this.playbackInfo = playbackInfo; @@ -216,7 +216,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private boolean pendingPauseAtEndOfPeriod; private boolean isRebuffering; private boolean shouldContinueLoading; - @Player.RepeatMode private int repeatMode; + private @Player.RepeatMode int repeatMode; private boolean shuffleModeEnabled; private boolean foregroundMode; private boolean requestForRendererSleep; @@ -2943,7 +2943,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * @return The uid in the new timeline of the first subsequent period, or null if no such period * was found. */ - /* package */ static @Nullable Object resolveSubsequentPeriod( + /* package */ @Nullable + static Object resolveSubsequentPeriod( Timeline.Window window, Timeline.Period period, @Player.RepeatMode int repeatMode, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java index cc1ca1404e..06dd0f9ff7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoTimeoutException.java @@ -62,7 +62,7 @@ public final class ExoTimeoutException extends RuntimeException { public static final int TIMEOUT_OPERATION_DETACH_SURFACE = 3; /** The operation on the ExoPlayer playback thread that timed out. */ - @TimeoutOperation public final int timeoutOperation; + public final @TimeoutOperation int timeoutOperation; /** * Creates the timeout exception. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java index 95999b5f75..c9cf750d62 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/NoSampleRenderer.java @@ -169,14 +169,12 @@ public abstract class NoSampleRenderer implements Renderer, RendererCapabilities // RendererCapabilities implementation. @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_NOT_SUPPORTED; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java index 68fb854178..6dab072666 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlaybackInfo.java @@ -55,7 +55,7 @@ import java.util.List; /** The start position after a reported position discontinuity, in microseconds. */ public final long discontinuityStartPositionUs; /** The current playback state. One of the {@link Player}.STATE_ constants. */ - @Player.State public final int playbackState; + public final @Player.State int playbackState; /** The current playback error, or null if this is not an error state. */ @Nullable public final ExoPlaybackException playbackError; /** Whether the player is currently loading. */ @@ -71,7 +71,7 @@ import java.util.List; /** Whether playback should proceed when {@link #playbackState} == {@link Player#STATE_READY}. */ public final boolean playWhenReady; /** Reason why playback is suppressed even though {@link #playWhenReady} is {@code true}. */ - @PlaybackSuppressionReason public final int playbackSuppressionReason; + public final @PlaybackSuppressionReason int playbackSuppressionReason; /** The playback parameters. */ public final PlaybackParameters playbackParameters; /** Whether offload scheduling is enabled for the main player loop. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index 755b2c77b7..a51f245cc3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -191,8 +191,7 @@ public interface RendererCapabilities { * @return The combined {@link Capabilities} of the given {@link C.FormatSupport}, {@link * #ADAPTIVE_NOT_SUPPORTED} and {@link #TUNNELING_NOT_SUPPORTED}. */ - @Capabilities - static int create(@C.FormatSupport int formatSupport) { + static @Capabilities int create(@C.FormatSupport int formatSupport) { return create(formatSupport, ADAPTIVE_NOT_SUPPORTED, TUNNELING_NOT_SUPPORTED); } @@ -208,8 +207,7 @@ public interface RendererCapabilities { * @param tunnelingSupport The {@link TunnelingSupport}. * @return The combined {@link Capabilities}. */ - @Capabilities - static int create( + static @Capabilities int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport) { @@ -235,8 +233,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @Capabilities - static int create( + static @Capabilities int create( @C.FormatSupport int formatSupport, @AdaptiveSupport int adaptiveSupport, @TunnelingSupport int tunnelingSupport, @@ -257,8 +254,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @C.FormatSupport - static int getFormatSupport(@Capabilities int supportFlags) { + static @C.FormatSupport int getFormatSupport(@Capabilities int supportFlags) { return supportFlags & FORMAT_SUPPORT_MASK; } @@ -270,8 +266,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @AdaptiveSupport - static int getAdaptiveSupport(@Capabilities int supportFlags) { + static @AdaptiveSupport int getAdaptiveSupport(@Capabilities int supportFlags) { return supportFlags & ADAPTIVE_SUPPORT_MASK; } @@ -283,8 +278,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @TunnelingSupport - static int getTunnelingSupport(@Capabilities int supportFlags) { + static @TunnelingSupport int getTunnelingSupport(@Capabilities int supportFlags) { return supportFlags & TUNNELING_SUPPORT_MASK; } @@ -296,8 +290,8 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @HardwareAccelerationSupport - static int getHardwareAccelerationSupport(@Capabilities int supportFlags) { + static @HardwareAccelerationSupport int getHardwareAccelerationSupport( + @Capabilities int supportFlags) { return supportFlags & HARDWARE_ACCELERATION_SUPPORT_MASK; } @@ -309,8 +303,7 @@ public interface RendererCapabilities { */ // Suppression needed for IntDef casting. @SuppressLint("WrongConstant") - @DecoderSupport - static int getDecoderSupport(@Capabilities int supportFlags) { + static @DecoderSupport int getDecoderSupport(@Capabilities int supportFlags) { return supportFlags & MODE_SUPPORT_MASK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index a057989091..6c40e2e8e2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -408,8 +408,7 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @C.VideoScalingMode - public int getVideoScalingMode() { + public @C.VideoScalingMode int getVideoScalingMode() { blockUntilConstructorFinished(); return player.getVideoScalingMode(); } @@ -422,8 +421,7 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @C.VideoChangeFrameRateStrategy - public int getVideoChangeFrameRateStrategy() { + public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { blockUntilConstructorFinished(); return player.getVideoChangeFrameRateStrategy(); } @@ -695,15 +693,13 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { blockUntilConstructorFinished(); return player.getPlaybackState(); } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { blockUntilConstructorFinished(); return player.getPlaybackSuppressionReason(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java index 43eb76a1a1..c5a8230154 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java @@ -55,7 +55,7 @@ import androidx.media3.common.util.Util; private final AudioManager audioManager; @Nullable private VolumeChangeReceiver receiver; - @C.StreamType private int streamType; + private @C.StreamType int streamType; private int volume; private boolean muted; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 59d8b333bc..54b631389e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -150,8 +150,7 @@ public interface AnalyticsListener { * @param index The index. Must be between 0 (inclusive) and {@link #size()} (exclusive). * @return The {@link EventFlags event} at the given index. */ - @EventFlags - public int get(int index) { + public @EventFlags int get(int index) { return flags.get(index); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java index 419e168b5c..50d07ce7ca 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java @@ -120,7 +120,7 @@ public final class MediaMetricsListener @Nullable private String activeSessionId; @Nullable private PlaybackMetrics.Builder metricsBuilder; - @Player.DiscontinuityReason private int discontinuityReason; + private @Player.DiscontinuityReason int discontinuityReason; private int currentPlaybackState; private int currentNetworkType; @Nullable private PlaybackException pendingPlayerError; @@ -887,7 +887,7 @@ public final class MediaMetricsListener private static final class PendingFormatUpdate { public final Format format; - @C.SelectionReason public final int selectionReason; + public final @C.SelectionReason int selectionReason; public final String sessionId; public PendingFormatUpdate( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java index cb5ff1aa94..76ff331955 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/PlaybackStatsListener.java @@ -84,7 +84,7 @@ public final class PlaybackStatsListener @Nullable private String discontinuityFromSession; private long discontinuityFromPositionMs; - @Player.DiscontinuityReason private int discontinuityReason; + private @Player.DiscontinuityReason int discontinuityReason; private int droppedFrames; @Nullable private Exception nonFatalException; private long bandwidthTimeMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java index de6c34e119..ed7b23813c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioProcessor.java @@ -45,7 +45,7 @@ public interface AudioProcessor { /** The number of interleaved channels. */ public final int channelCount; /** The type of linear PCM encoding. */ - @C.PcmEncoding public final int encoding; + public final @C.PcmEncoding int encoding; /** The number of bytes used to represent one audio frame. */ public final int bytesPerFrame; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 2f9ac4accf..87c3666605 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -138,7 +138,7 @@ public abstract class DecoderAudioRenderer< @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - @ReinitializationState private int decoderReinitializationState; + private @ReinitializationState int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean audioTrackNeedsConfigure; @@ -228,8 +228,7 @@ public abstract class DecoderAudioRenderer< } @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @@ -248,8 +247,7 @@ public abstract class DecoderAudioRenderer< * @param format The format, which has an audio {@link Format#sampleMimeType}. * @return The {@link C.FormatSupport} for this {@link Format}. */ - @C.FormatSupport - protected abstract int supportsFormatInternal(Format format); + protected abstract @C.FormatSupport int supportsFormatInternal(Format format); /** * Returns whether the renderer's {@link AudioSink} supports a given {@link Format}. @@ -266,8 +264,7 @@ public abstract class DecoderAudioRenderer< * * @see AudioSink#getFormatSupport(Format) (Format) */ - @SinkFormatSupport - protected final int getSinkFormatSupport(Format format) { + protected final @SinkFormatSupport int getSinkFormatSupport(Format format) { return audioSink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index d9859a99e6..7186ca79a5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -482,8 +482,8 @@ public final class DefaultAudioSink implements AudioSink { private final AudioTrackPositionTracker audioTrackPositionTracker; private final ArrayDeque mediaPositionParametersCheckpoints; private final boolean enableAudioTrackPlaybackParams; - @OffloadMode private final int offloadMode; - @MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29; + private final @OffloadMode int offloadMode; + private @MonotonicNonNull StreamEventCallbackV29 offloadStreamEventCallbackV29; private final PendingExceptionHolder initializationExceptionPendingExceptionHolder; private final PendingExceptionHolder writeExceptionPendingExceptionHolder; @@ -492,7 +492,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private PlayerId playerId; @Nullable private Listener listener; @Nullable private Configuration pendingConfiguration; - @MonotonicNonNull private Configuration configuration; + private @MonotonicNonNull Configuration configuration; @Nullable private AudioTrack audioTrack; private AudioAttributes audioAttributes; @@ -518,7 +518,7 @@ public final class DefaultAudioSink implements AudioSink { @Nullable private ByteBuffer inputBuffer; private int inputBufferAccessUnitCount; @Nullable private ByteBuffer outputBuffer; - @MonotonicNonNull private byte[] preV21OutputBuffer; + private @MonotonicNonNull byte[] preV21OutputBuffer; private int preV21OutputBufferOffset; private int drainingAudioProcessorIndex; private boolean handledEndOfStream; @@ -661,8 +661,7 @@ public final class DefaultAudioSink implements AudioSink { } @Override - @SinkFormatSupport - public int getFormatSupport(Format format) { + public @SinkFormatSupport int getFormatSupport(Format format) { if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) { if (!Util.isEncodingLinearPcm(format.pcmEncoding)) { Log.w(TAG, "Invalid PCM encoding: " + format.pcmEncoding); @@ -2136,11 +2135,11 @@ public final class DefaultAudioSink implements AudioSink { public final Format inputFormat; public final int inputPcmFrameSize; - @OutputMode public final int outputMode; + public final @OutputMode int outputMode; public final int outputPcmFrameSize; public final int outputSampleRate; public final int outputChannelConfig; - @C.Encoding public final int outputEncoding; + public final @C.Encoding int outputEncoding; public final int bufferSize; public final AudioProcessor[] availableAudioProcessors; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java index eda2b250de..06fdf5afd1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/ForwardingAudioSink.java @@ -50,8 +50,7 @@ public class ForwardingAudioSink implements AudioSink { } @Override - @SinkFormatSupport - public int getFormatSupport(Format format) { + public @SinkFormatSupport int getFormatSupport(Format format) { return sink.getFormatSupport(format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 172d0a3ca8..6c929efada 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -286,8 +286,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { if (!MimeTypes.isAudio(format.sampleMimeType)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java index 1390cc0aff..2a0ef051ab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SilenceSkippingAudioProcessor.java @@ -88,7 +88,7 @@ public final class SilenceSkippingAudioProcessor extends BaseAudioProcessor { */ private byte[] paddingBuffer; - @State private int state; + private @State int state; private int maybeSilenceBufferSize; private int paddingSize; private boolean hasOutputNoise; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java index d2778905b5..0c1556012c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java @@ -129,8 +129,7 @@ import java.util.concurrent.Executor; } /** Delegates to Spatializer.getImmersiveAudioLevel() */ - @ImmersiveAudioLevel - public int getImmersiveAudioLevel() { + public @ImmersiveAudioLevel int getImmersiveAudioLevel() { try { return (int) Util.castNonNull(getImmersiveAudioLevel.invoke(spatializer)); } catch (IllegalAccessException | InvocationTargetException e) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java index 8e9c8d63e0..aa9c5e87e9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TeeAudioProcessor.java @@ -128,7 +128,7 @@ public final class TeeAudioProcessor extends BaseAudioProcessor { private int sampleRateHz; private int channelCount; - @C.PcmEncoding private int encoding; + private @C.PcmEncoding int encoding; @Nullable private RandomAccessFile randomAccessFile; private int counter; private int bytesWritten; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java index a3d1173418..9e734f5230 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/TrimmingAudioProcessor.java @@ -25,7 +25,7 @@ import java.nio.ByteBuffer; /** Audio processor for trimming samples from the start/end of data. */ /* package */ final class TrimmingAudioProcessor extends BaseAudioProcessor { - @C.PcmEncoding private static final int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; + private static final @C.PcmEncoding int OUTPUT_ENCODING = C.ENCODING_PCM_16BIT; private int trimStartFrames; private int trimEndFrames; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java index 44dc7d3a05..c64eb57dab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java @@ -254,8 +254,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // DrmSession implementation. @Override - @DrmSession.State - public final int getState() { + public final @DrmSession.State int getState() { return state; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java index 1a3c8ebcf9..adcb450e28 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java @@ -312,7 +312,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { @Nullable private byte[] offlineLicenseKeySetId; private @MonotonicNonNull PlayerId playerId; - /* package */ volatile @Nullable MediaDrmHandler mediaDrmHandler; + /* package */ @Nullable volatile MediaDrmHandler mediaDrmHandler; /** * @param uuid The UUID of the drm scheme. @@ -590,8 +590,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { } @Override - @C.CryptoType - public int getCryptoType(Format format) { + public @C.CryptoType int getCryptoType(Format format) { @C.CryptoType int cryptoType = checkNotNull(exoMediaDrm).getCryptoType(); if (format.drmInitData == null) { int trackType = MimeTypes.getTrackType(format.sampleMimeType); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java index 73fcb430ee..968a3d207f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSession.java @@ -65,7 +65,7 @@ public interface DrmSession { class DrmSessionException extends IOException { /** The {@link PlaybackException.ErrorCode} that corresponds to the failure. */ - @PlaybackException.ErrorCode public final int errorCode; + public final @PlaybackException.ErrorCode int errorCode; public DrmSessionException(Throwable cause, @PlaybackException.ErrorCode int errorCode) { super(cause); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java index a1b1bae06e..0fab623522 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionManager.java @@ -66,8 +66,7 @@ public interface DrmSessionManager { } @Override - @C.CryptoType - public int getCryptoType(Format format) { + public @C.CryptoType int getCryptoType(Format format) { return format.drmInitData != null ? C.CRYPTO_TYPE_UNSUPPORTED : C.CRYPTO_TYPE_NONE; } }; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java index ec43643855..a47fb682bb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmUtil.java @@ -72,8 +72,7 @@ public final class DrmUtil { * @return The {@link PlaybackException.ErrorCode} that corresponds to the given DRM-related * exception. */ - @PlaybackException.ErrorCode - public static int getErrorCodeForMediaDrmException( + public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException( Exception exception, @ErrorSource int errorSource) { if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) { return Api21.mediaDrmStateExceptionToErrorCode(exception); @@ -128,8 +127,8 @@ public final class DrmUtil { } @DoNotInline - @PlaybackException.ErrorCode - public static int mediaDrmStateExceptionToErrorCode(Throwable throwable) { + public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode( + Throwable throwable) { @Nullable String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo(); int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java index 89afc0603f..58efd2f082 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java @@ -153,8 +153,7 @@ public final class DummyExoMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return C.CRYPTO_TYPE_UNSUPPORTED; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java index 5557e00392..ba537890c8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java @@ -267,7 +267,7 @@ public interface ExoMediaDrm { private final byte[] data; private final String licenseServerUrl; - @RequestType private final int requestType; + private final @RequestType int requestType; /** * Creates an instance with {@link #REQUEST_TYPE_UNKNOWN}. @@ -307,8 +307,7 @@ public interface ExoMediaDrm { * request does not specify a type. Note that when using a platform {@link MediaDrm} instance, * key requests only specify a type on API levels 23 and above. */ - @RequestType - public int getRequestType() { + public @RequestType int getRequestType() { return requestType; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java index 00bde4ce26..10fda5099a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java @@ -342,8 +342,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return C.CRYPTO_TYPE_FRAMEWORK; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java index f42dde626b..9b360d2776 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java @@ -49,7 +49,7 @@ public final class UnsupportedDrmException extends Exception { public static final int REASON_INSTANTIATION_ERROR = 2; /** Either {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ public UnsupportedDrmException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java index 74224d1c21..2815f8c796 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/WidevineUtil.java @@ -39,7 +39,8 @@ public final class WidevineUtil { * @return A {@link Pair} consisting of the remaining license and playback durations in seconds, * or null if called before the session has been opened or after it's been released. */ - public static @Nullable Pair getLicenseDurationRemainingSec(DrmSession drmSession) { + @Nullable + public static Pair getLicenseDurationRemainingSec(DrmSession drmSession) { Map keyStatus = drmSession.queryKeyStatus(); if (keyStatus == null) { return null; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index e4f465c46e..e013c4b990 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -141,7 +141,7 @@ import java.nio.ByteBuffer; private final boolean synchronizeCodecInteractionsWithQueueing; private final boolean enableImmediateCodecStartAfterFlush; private boolean codecReleased; - @State private int state; + private @State int state; private AsynchronousMediaCodecAdapter( MediaCodec codec, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index d77786a0e7..03e5aafd44 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -53,7 +53,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private static final String TAG = "DMCodecAdapterFactory"; - @Mode private int asynchronousMode; + private @Mode int asynchronousMode; private boolean enableSynchronizeCodecInteractionsWithQueueing; private boolean enableImmediateCodecStartAfterFlush; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index be578bf02b..6d328d506a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -327,7 +327,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Nullable private ArrayDeque availableCodecInfos; @Nullable private DecoderInitializationException preferredDecoderInitializationException; @Nullable private MediaCodecInfo codecInfo; - @AdaptationWorkaroundMode private int codecAdaptationWorkaroundMode; + private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsFlushWorkaround; private boolean codecNeedsSosFlushWorkaround; @@ -349,9 +349,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean bypassSampleBufferPending; private boolean bypassDrainAndReinitialize; private boolean codecReconfigured; - @ReconfigurationState private int codecReconfigurationState; - @DrainState private int codecDrainState; - @DrainAction private int codecDrainAction; + private @ReconfigurationState int codecReconfigurationState; + private @DrainState int codecDrainState; + private @DrainAction int codecDrainAction; private boolean codecReceivedBuffers; private boolean codecReceivedEos; private boolean codecHasOutputMediaFormat; @@ -437,14 +437,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } @Override - @AdaptiveSupport - public final int supportsMixedMimeTypeAdaptation() { + public final @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_NOT_SEAMLESS; } @Override - @Capabilities - public final int supportsFormat(Format format) throws ExoPlaybackException { + public final @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { try { return supportsFormat(mediaCodecSelector, format); } catch (DecoderQueryException e) { @@ -460,9 +458,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * @return The {@link Capabilities} for this {@link Format}. * @throws DecoderQueryException If there was an error querying decoders. */ - @Capabilities - protected abstract int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) - throws DecoderQueryException; + protected abstract @Capabilities int supportsFormat( + MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException; /** * Returns a list of decoders that can decode media in the specified format, in priority order. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java index 3c60048f18..4aca3b8164 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/metadata/MetadataRenderer.java @@ -94,8 +94,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java index 9594592d6d..6eae3298d3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/Download.java @@ -99,7 +99,7 @@ public final class Download { /** The download request. */ public final DownloadRequest request; /** The state of the download. */ - @State public final int state; + public final @State int state; /** The first time when download entry is created. */ public final long startTimeMs; /** The last update time. */ @@ -112,7 +112,7 @@ public final class Download { * If {@link #state} is {@link #STATE_FAILED} then this is the cause, otherwise {@link * #FAILURE_REASON_NONE}. */ - @FailureReason public final int failureReason; + public final @FailureReason int failureReason; /* package */ final DownloadProgress progress; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java index 0ba6bc8aa4..0d1ffa7937 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java @@ -1100,8 +1100,7 @@ public final class DownloadHelper { } @Override - @C.SelectionReason - public int getSelectionReason() { + public @C.SelectionReason int getSelectionReason() { return C.SELECTION_REASON_UNKNOWN; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java index e08e0d5bb2..2679e634f2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadManager.java @@ -351,8 +351,7 @@ public final class DownloadManager { * * @return The not met {@link Requirements.RequirementFlags}, or 0 if all requirements are met. */ - @Requirements.RequirementFlags - public int getNotMetRequirements() { + public @Requirements.RequirementFlags int getNotMetRequirements() { return notMetRequirements; } @@ -705,7 +704,7 @@ public final class DownloadManager { private final ArrayList downloads; private final HashMap activeTasks; - @Requirements.RequirementFlags private int notMetRequirements; + private @Requirements.RequirementFlags int notMetRequirements; private boolean downloadsPaused; private int maxParallelDownloads; private int minRetryCount; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index 34d435bb47..4bdd389fbc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -74,7 +74,7 @@ public final class Requirements implements Parcelable { */ public static final int DEVICE_STORAGE_NOT_LOW = 1 << 4; - @RequirementFlags private final int requirements; + private final @RequirementFlags int requirements; /** @param requirements A combination of requirement flags. */ public Requirements(@RequirementFlags int requirements) { @@ -86,8 +86,7 @@ public final class Requirements implements Parcelable { } /** Returns the requirements. */ - @RequirementFlags - public int getRequirements() { + public @RequirementFlags int getRequirements() { return requirements; } @@ -144,8 +143,7 @@ public final class Requirements implements Parcelable { * @param context Any context. * @return The requirements that are not met, or 0. */ - @RequirementFlags - public int getNotMetRequirements(Context context) { + public @RequirementFlags int getNotMetRequirements(Context context) { @RequirementFlags int notMetRequirements = getNotMetNetworkRequirements(context); if (isChargingRequired() && !isDeviceCharging(context)) { notMetRequirements |= DEVICE_CHARGING; @@ -159,8 +157,7 @@ public final class Requirements implements Parcelable { return notMetRequirements; } - @RequirementFlags - private int getNotMetNetworkRequirements(Context context) { + private @RequirementFlags int getNotMetNetworkRequirements(Context context) { if (!isNetworkRequired()) { return 0; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java index ca2380cd42..c7ae614e05 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java @@ -62,7 +62,7 @@ public final class RequirementsWatcher { @Nullable private DeviceStatusChangeReceiver receiver; - @Requirements.RequirementFlags private int notMetRequirements; + private @Requirements.RequirementFlags int notMetRequirements; @Nullable private NetworkCallback networkCallback; /** @@ -83,8 +83,7 @@ public final class RequirementsWatcher { * * @return Initial {@link Requirements.RequirementFlags RequirementFlags} that are not met, or 0. */ - @Requirements.RequirementFlags - public int start() { + public @Requirements.RequirementFlags int start() { notMetRequirements = requirements.getNotMetRequirements(context); IntentFilter filter = new IntentFilter(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java index 4263da5d62..6a734a752b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java @@ -63,7 +63,7 @@ public final class ClippingMediaSource extends CompositeMediaSource { public static final int REASON_START_EXCEEDS_END = 2; /** The reason clipping failed. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason The reason clipping failed. */ public IllegalClippingException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java index 4e2070b043..5cbc5628d4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java @@ -472,8 +472,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { mediaSourceFactories = new HashMap<>(); } - @C.ContentType - public int[] getSupportedTypes() { + public @C.ContentType int[] getSupportedTypes() { ensureAllSuppliersAreLoaded(); return Ints.toArray(supportedTypes); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java index f3a6a04ed5..c5df2e15ea 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaLoadData.java @@ -28,7 +28,7 @@ import androidx.media3.common.util.UnstableApi; public final class MediaLoadData { /** The {@link DataType data type}. */ - @DataType public final int dataType; + public final @DataType int dataType; /** * One of the {@link TrackType track types}, which is a media track type if the data corresponds * to media of a specific type, or {@link C#TRACK_TYPE_UNKNOWN} otherwise. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java index af34e47388..af47342210 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java @@ -47,8 +47,7 @@ public interface MediaSourceFactory extends MediaSource.Factory { } @Override - @C.ContentType - public int[] getSupportedTypes() { + public @C.ContentType int[] getSupportedTypes() { throw new UnsupportedOperationException(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java index c9c589fb74..8822b24268 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java @@ -61,7 +61,7 @@ public final class MergingMediaSource extends CompositeMediaSource { public static final int REASON_PERIOD_COUNT_MISMATCH = 0; /** The reason the merge failed. */ - @Reason public final int reason; + public final @Reason int reason; /** @param reason The reason the merge failed. */ public IllegalMergeException(@Reason int reason) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java index e5e74de76e..f20a57caeb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ProgressiveMediaPeriod.java @@ -131,7 +131,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SeekMap seekMap; private long durationUs; private boolean isLive; - @DataType private int dataType; + private @DataType int dataType; private boolean seenFirstTrackSelection; private boolean notifyDiscontinuity; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java index c783249821..d2857100fc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/SilenceMediaSource.java @@ -87,7 +87,7 @@ public final class SilenceMediaSource extends BaseMediaSource { public static final String MEDIA_ID = "SilenceMediaSource"; private static final int SAMPLE_RATE_HZ = 44100; - @C.PcmEncoding private static final int PCM_ENCODING = C.ENCODING_PCM_16BIT; + private static final @C.PcmEncoding int PCM_ENCODING = C.ENCODING_PCM_16BIT; private static final int CHANNEL_COUNT = 2; private static final Format FORMAT = new Format.Builder() diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index 248b7648ec..a0821d571e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -847,8 +847,7 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource return positionUs; } - @SampleStream.ReadDataResult - public int readData( + public @SampleStream.ReadDataResult int readData( MediaPeriodImpl mediaPeriod, int streamIndex, FormatHolder formatHolder, @@ -1204,8 +1203,7 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource } @Override - @ReadDataResult - public int readData( + public @ReadDataResult int readData( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return mediaPeriod.sharedPeriod.readData( mediaPeriod, streamIndex, formatHolder, buffer, readFlags); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java index 09c157a9a7..7a2d8060ec 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/Chunk.java @@ -42,7 +42,7 @@ public abstract class Chunk implements Loadable { /** The {@link DataSpec} that defines the data to be loaded. */ public final DataSpec dataSpec; /** The {@link DataType data type} of the chunk. For reporting only. */ - @DataType public final int type; + public final @DataType int type; /** The format of the track to which this chunk belongs. */ public final Format trackFormat; /** @@ -50,7 +50,7 @@ public abstract class Chunk implements Loadable { * C#SELECTION_REASON_UNKNOWN} if the chunk does not belong to a track, or if the selection reason * is unknown. */ - @C.SelectionReason public final int trackSelectionReason; + public final @C.SelectionReason int trackSelectionReason; /** * Optional data associated with the selection of the track to which this chunk belongs. Null if * the chunk does not belong to a track, or if there is no associated track selection data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java index 1e4dc6014f..1b6a07874b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/mediaparser/OutputConsumerAdapterV30.java @@ -540,8 +540,7 @@ public final class OutputConsumerAdapterV30 implements MediaParser.OutputConsume return new DrmInitData(schemeType, schemeDatas); } - @SelectionFlags - private static int getSelectionFlags(MediaFormat mediaFormat) { + private static @SelectionFlags int getSelectionFlags(MediaFormat mediaFormat) { int selectionFlags = 0; selectionFlags |= getFlag( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java index 4c668d3dc9..504b6f7843 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/ExoplayerCuesDecoder.java @@ -63,7 +63,7 @@ public final class ExoplayerCuesDecoder implements SubtitleDecoder { private final SubtitleInputBuffer inputBuffer; private final Deque availableOutputBuffers; - @InputBufferState private int inputBufferState; + private @InputBufferState int inputBufferState; private boolean released; public ExoplayerCuesDecoder() { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java index e6dac32716..af8809966a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/TextRenderer.java @@ -94,7 +94,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { private boolean inputStreamEnded; private boolean outputStreamEnded; private boolean waitingForKeyFrame; - @ReplacementState private int decoderReplacementState; + private @ReplacementState int decoderReplacementState; @Nullable private Format streamFormat; @Nullable private SubtitleDecoder decoder; @Nullable private SubtitleInputBuffer nextInputBuffer; @@ -141,8 +141,7 @@ public final class TextRenderer extends BaseRenderer implements Callback { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { if (decoderFactory.supportsFormat(format)) { return RendererCapabilities.create( format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index e8dc20c7e3..037304cfe2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -123,7 +123,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private boolean allowAudioMixedChannelCountAdaptiveness; private boolean allowAudioMixedDecoderSupportAdaptiveness; // Text - @C.SelectionFlags private int disabledTextTrackSelectionFlags; + private @C.SelectionFlags int disabledTextTrackSelectionFlags; // General private boolean exceedRendererCapabilitiesIfNecessary; private boolean tunnelingEnabled; @@ -894,7 +894,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * Bitmask of selection flags that are disabled for text track selections. See {@link * C.SelectionFlags}. The default value is {@code 0} (i.e. no flags). */ - @C.SelectionFlags public final int disabledTextTrackSelectionFlags; + public final @C.SelectionFlags int disabledTextTrackSelectionFlags; /** Returns an instance configured with default values. */ public static Parameters getDefaults(Context context) { @@ -2255,8 +2255,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } /** Returns to what extent the track is {@link SelectionEligibility eligible for selection}. */ - @SelectionEligibility - public abstract int getSelectionEligibility(); + public abstract @SelectionEligibility int getSelectionEligibility(); /** * Returns whether this track is compatible for an adaptive selection with the specified other @@ -2308,7 +2307,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final int preferredRoleFlagsScore; private final boolean hasMainOrNoRoleFlag; private final boolean allowMixedMimeTypes; - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean usesPrimaryDecoder; private final boolean usesHardwareAcceleration; private final int codecPreferenceScore; @@ -2375,8 +2374,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } @@ -2389,8 +2387,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { && this.usesHardwareAcceleration == otherTrack.usesHardwareAcceleration)); } - @SelectionEligibility - private int evaluateSelectionEligibility( + private @SelectionEligibility int evaluateSelectionEligibility( @Capabilities int rendererSupport, @AdaptiveSupport int requiredAdaptiveSupport) { if ((format.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) { // Ignore trick-play tracks for now. @@ -2502,7 +2499,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean isWithinConstraints; @Nullable private final String language; private final Parameters parameters; @@ -2594,8 +2591,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } @@ -2664,8 +2660,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { .result(); } - @SelectionEligibility - private int evaluateSelectionEligibility( + private @SelectionEligibility int evaluateSelectionEligibility( @Capabilities int rendererSupport, boolean hasMappedVideoTracks) { if (!isSupported(rendererSupport, parameters.exceedRendererCapabilitiesIfNecessary)) { return SELECTION_ELIGIBILITY_NO; @@ -2712,7 +2707,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { return listBuilder.build(); } - @SelectionEligibility private final int selectionEligibility; + private final @SelectionEligibility int selectionEligibility; private final boolean isWithinRendererCapabilities; private final boolean isDefault; private final boolean isForced; @@ -2777,8 +2772,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - @SelectionEligibility - public int getSelectionEligibility() { + public @SelectionEligibility int getSelectionEligibility() { return selectionEligibility; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index 980a5a7749..d1836b7094 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -107,8 +107,8 @@ public abstract class MappingTrackSelector extends TrackSelector { private final String[] rendererNames; private final @C.TrackType int[] rendererTrackTypes; private final TrackGroupArray[] rendererTrackGroups; - @AdaptiveSupport private final int[] rendererMixedMimeTypeAdaptiveSupports; - @Capabilities private final int[][][] rendererFormatSupports; + private final @AdaptiveSupport int[] rendererMixedMimeTypeAdaptiveSupports; + private final @Capabilities int[][][] rendererFormatSupports; private final TrackGroupArray unmappedTrackGroups; /** @@ -181,8 +181,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param rendererIndex The renderer index. * @return The {@link RendererSupport}. */ - @RendererSupport - public int getRendererSupport(int rendererIndex) { + public @RendererSupport int getRendererSupport(int rendererIndex) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; @Capabilities int[][] rendererFormatSupport = rendererFormatSupports[rendererIndex]; for (@Capabilities int[] trackGroupFormatSupport : rendererFormatSupport) { @@ -217,8 +216,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackType The {@link C.TrackType track type}. * @return The {@link RendererSupport}. */ - @RendererSupport - public int getTypeSupport(@C.TrackType int trackType) { + public @RendererSupport int getTypeSupport(@C.TrackType int trackType) { @RendererSupport int bestRendererSupport = RENDERER_SUPPORT_NO_TRACKS; for (int i = 0; i < rendererCount; i++) { if (rendererTrackTypes[i] == trackType) { @@ -236,8 +234,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link Capabilities}. */ - @Capabilities - public int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { + public @Capabilities int getCapabilities(int rendererIndex, int groupIndex, int trackIndex) { return rendererFormatSupports[rendererIndex][groupIndex][trackIndex]; } @@ -249,8 +246,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param trackIndex The index of the track within the track group. * @return The {@link FormatSupport}. */ - @FormatSupport - public int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { + public @FormatSupport int getTrackSupport(int rendererIndex, int groupIndex, int trackIndex) { return RendererCapabilities.getFormatSupport( getCapabilities(rendererIndex, groupIndex, trackIndex)); } @@ -272,8 +268,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer are included when determining support. * @return The {@link AdaptiveSupport}. */ - @AdaptiveSupport - public int getAdaptiveSupport( + public @AdaptiveSupport int getAdaptiveSupport( int rendererIndex, int groupIndex, boolean includeCapabilitiesExceededTracks) { int trackCount = rendererTrackGroups[rendererIndex].get(groupIndex).length; // Iterate over the tracks in the group, recording the indices of those to consider. @@ -299,8 +294,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @param groupIndex The index of the track group. * @return The {@link AdaptiveSupport}. */ - @AdaptiveSupport - public int getAdaptiveSupport(int rendererIndex, int groupIndex, int[] trackIndices) { + public @AdaptiveSupport int getAdaptiveSupport( + int rendererIndex, int groupIndex, int[] trackIndices) { int handledTrackCount = 0; @AdaptiveSupport int adaptiveSupport = RendererCapabilities.ADAPTIVE_SEAMLESS; boolean multipleMimeTypes = false; @@ -540,9 +535,8 @@ public abstract class MappingTrackSelector extends TrackSelector { * @return An array containing {@link Capabilities} for each track in the group. * @throws ExoPlaybackException If an error occurs determining the format support. */ - @Capabilities - private static int[] getFormatSupport(RendererCapabilities rendererCapabilities, TrackGroup group) - throws ExoPlaybackException { + private static @Capabilities int[] getFormatSupport( + RendererCapabilities rendererCapabilities, TrackGroup group) throws ExoPlaybackException { @Capabilities int[] formatSupport = new int[group.length]; for (int i = 0; i < group.length; i++) { formatSupport[i] = rendererCapabilities.supportsFormat(group.getFormat(i)); @@ -559,8 +553,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * renderer. * @throws ExoPlaybackException If an error occurs determining the adaptation support. */ - @AdaptiveSupport - private static int[] getMixedMimeTypeAdaptationSupports( + private static @AdaptiveSupport int[] getMixedMimeTypeAdaptationSupports( RendererCapabilities[] rendererCapabilities) throws ExoPlaybackException { @AdaptiveSupport int[] mixedMimeTypeAdaptationSupport = new int[rendererCapabilities.length]; for (int i = 0; i < mixedMimeTypeAdaptationSupport.length; i++) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java index 02c89e42a3..654a34d975 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java @@ -281,14 +281,14 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList private long sampleStartTimeMs; private long sampleBytesTransferred; - @C.NetworkType private int networkType; + private @C.NetworkType int networkType; private long totalElapsedTimeMs; private long totalBytesTransferred; private long bitrateEstimate; private long lastReportedBitrateEstimate; private boolean networkTypeOverrideSet; - @C.NetworkType private int networkTypeOverride; + private @C.NetworkType int networkTypeOverride; /** @deprecated Use {@link Builder} instead. */ @Deprecated diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java index fd40859f2d..40e5c13c2c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/LoadErrorHandlingPolicy.java @@ -130,7 +130,7 @@ public interface LoadErrorHandlingPolicy { /** A selected fallback option. */ final class FallbackSelection { /** The type of fallback. */ - @FallbackType public final int type; + public final @FallbackType int type; /** The duration for which the failing resource should be excluded, in milliseconds. */ public final long exclusionDurationMs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java index 5e978e19c4..859f920713 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/ParsingLoadable.java @@ -104,7 +104,7 @@ public final class ParsingLoadable implements Loadable { private final StatsDataSource dataSource; private final Parser parser; - private volatile @Nullable T result; + @Nullable private volatile T result; /** * @param dataSource A {@link DataSource} to use when loading the data. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java index 8d2144084f..2f247237d2 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DecoderVideoRenderer.java @@ -121,7 +121,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { private DecoderInputBuffer inputBuffer; private VideoDecoderOutputBuffer outputBuffer; - @VideoOutputMode private int outputMode; + private @VideoOutputMode int outputMode; @Nullable private Object output; @Nullable private Surface outputSurface; @Nullable private VideoDecoderOutputBufferRenderer outputBufferRenderer; @@ -130,7 +130,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer { @Nullable private DrmSession decoderDrmSession; @Nullable private DrmSession sourceDrmSession; - @ReinitializationState private int decoderReinitializationState; + private @ReinitializationState int decoderReinitializationState; private boolean decoderReceivedBuffers; private boolean renderedFirstFrameAfterReset; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java index f225812bbb..a7819ddd25 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/DummySurface.java @@ -105,8 +105,7 @@ public final class DummySurface extends Surface { } } - @SecureMode - private static int getSecureMode(Context context) { + private static @SecureMode int getSecureMode(Context context) { if (GlUtil.isProtectedContentExtensionSupported(context)) { if (GlUtil.isSurfacelessContextExtensionSupported()) { return SECURE_MODE_SURFACELESS_CONTEXT; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index d3aa4562ec..d1114f4ca3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -131,7 +131,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @Nullable private Surface surface; @Nullable private DummySurface dummySurface; private boolean haveReportedFirstFrameRenderedForCurrentSurface; - @C.VideoScalingMode private int scalingMode; + private @C.VideoScalingMode int scalingMode; private boolean renderedFirstFrameAfterReset; private boolean mayRenderFirstFrameAfterEnableIfNotStarted; private boolean renderedFirstFrameAfterEnable; @@ -342,8 +342,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { } @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java index d5646ca11c..2735852372 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java @@ -111,7 +111,7 @@ public final class VideoFrameReleaseHelper { private float surfacePlaybackFrameRate; private float playbackSpeed; - @C.VideoChangeFrameRateStrategy private int changeFrameRateStrategy; + private @C.VideoChangeFrameRateStrategy int changeFrameRateStrategy; private long vsyncDurationNs; private long vsyncOffsetNs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java index b1a222f49a..b372d03481 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/CameraMotionRenderer.java @@ -58,8 +58,7 @@ public final class CameraMotionRenderer extends BaseRenderer { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return MimeTypes.APPLICATION_CAMERA_MOTION.equals(format.sampleMimeType) ? RendererCapabilities.create(C.FORMAT_HANDLED) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java index 81444dd5b0..4b0acba57b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionDecoder.java @@ -58,7 +58,8 @@ import java.util.zip.Inflater; * @param stereoMode A {@link C.StereoMode} value. * @return The projection or null if the data can't be decoded. */ - public static @Nullable Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { + @Nullable + public static Projection decode(byte[] projectionData, @C.StereoMode int stereoMode) { ParsableByteArray input = new ParsableByteArray(projectionData); // MP4 containers include the proj box but webm containers do not. // Both containers use mshp. @@ -91,7 +92,8 @@ import java.util.zip.Inflater; return type == TYPE_PROJ; } - private static @Nullable ArrayList parseProj(ParsableByteArray input) { + @Nullable + private static ArrayList parseProj(ParsableByteArray input) { input.skipBytes(8); // size and type. int position = input.getPosition(); int limit = input.limit(); @@ -112,7 +114,8 @@ import java.util.zip.Inflater; return null; } - private static @Nullable ArrayList parseMshp(ParsableByteArray input) { + @Nullable + private static ArrayList parseMshp(ParsableByteArray input) { int version = input.readUnsignedByte(); if (version != 0) { return null; @@ -137,7 +140,8 @@ import java.util.zip.Inflater; } /** Parses MSHP data after the encoding_four_cc field. */ - private static @Nullable ArrayList parseRawMshpData(ParsableByteArray input) { + @Nullable + private static ArrayList parseRawMshpData(ParsableByteArray input) { ArrayList meshes = new ArrayList<>(); int position = input.getPosition(); int limit = input.limit(); @@ -160,7 +164,8 @@ import java.util.zip.Inflater; return meshes; } - private static @Nullable Mesh parseMesh(ParsableByteArray input) { + @Nullable + private static Mesh parseMesh(ParsableByteArray input) { // Read the coordinates. int coordinateCount = input.readInt(); if (coordinateCount > MAX_COORDINATE_COUNT) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java index b31cfa98ab..b2828a465e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/SceneRenderer.java @@ -50,8 +50,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private @MonotonicNonNull SurfaceTexture surfaceTexture; // Used by other threads only - @C.StereoMode private volatile int defaultStereoMode; - @C.StereoMode private int lastStereoMode; + private volatile @C.StereoMode int defaultStereoMode; + private @C.StereoMode int lastStereoMode; @Nullable private byte[] lastProjectionData; // Methods called on any thread. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index cc444ebb0f..0961f4492d 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -11488,7 +11488,7 @@ public final class ExoPlayerTest { private static final class PlayerStateGrabber extends PlayerRunnable { public boolean playWhenReady; - @Player.State public int playbackState; + public @Player.State int playbackState; @Nullable public Timeline timeline; @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java index dbf9c8dfc6..f0009b7ceb 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DecoderAudioRendererTest.java @@ -71,8 +71,7 @@ public class DecoderAudioRendererTest { } @Override - @C.FormatSupport - protected int supportsFormatInternal(Format format) { + protected @C.FormatSupport int supportsFormatInternal(Format format) { return FORMAT_HANDLED; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java index 8916391b8b..59b41fdc0e 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java @@ -44,8 +44,7 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class PcmTest { @Parameterized.Parameter(0) - @C.PcmEncoding - public int encoding; + public @C.PcmEncoding int encoding; @Parameterized.Parameter(1) public int channelCount; @@ -216,8 +215,7 @@ public class DefaultAudioTrackBufferSizeProviderTest { public static class EncodedTest { @Parameterized.Parameter(0) - @C.Encoding - public int encoding; + public @C.Encoding int encoding; @Parameterized.Parameters(name = "{index}: encoding={0}") public static ImmutableList data() { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java index 5c85327510..a5514c7068 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java @@ -2416,7 +2416,7 @@ public final class DefaultTrackSelectorTest { private static final class FakeRendererCapabilities implements RendererCapabilities { private final int trackType; - @Capabilities private final int supportValue; + private final @Capabilities int supportValue; /** * Returns {@link FakeRendererCapabilities} that advertises adaptive support for all tracks of @@ -2456,16 +2456,14 @@ public final class DefaultTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? supportValue : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } @@ -2504,16 +2502,14 @@ public final class DefaultTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return format.id != null && formatToCapability.containsKey(format.id) ? formatToCapability.get(format.id) : RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java index 4885587d6b..32023b11e2 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java @@ -245,8 +245,7 @@ public final class MappingTrackSelectorTest { } @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { return MimeTypes.getTrackType(format.sampleMimeType) == trackType ? RendererCapabilities.create( C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) @@ -254,8 +253,7 @@ public final class MappingTrackSelectorTest { } @Override - @AdaptiveSupport - public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { + public @AdaptiveSupport int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException { return ADAPTIVE_SEAMLESS; } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java index c8d195f9b8..95a9a67b61 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/DecoderVideoRendererTest.java @@ -84,7 +84,7 @@ public final class DecoderVideoRendererTest { /* maxDroppedFramesToNotify= */ -1) { private final Phaser inputBuffersInCodecPhaser = new Phaser(); - @C.VideoOutputMode private int outputMode; + private @C.VideoOutputMode int outputMode; @Override public String getName() { @@ -92,8 +92,7 @@ public final class DecoderVideoRendererTest { } @Override - @Capabilities - public int supportsFormat(Format format) { + public @Capabilities int supportsFormat(Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index b1df16d07a..d12739b2b4 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -111,8 +111,8 @@ public class MediaCodecVideoRendererTest { /* eventListener= */ eventListener, /* maxDroppedFramesToNotify= */ 1) { @Override - @Capabilities - protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) { + protected @Capabilities int supportsFormat( + MediaCodecSelector mediaCodecSelector, Format format) { return RendererCapabilities.create(C.FORMAT_HANDLED); } diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index be20b5e054..23b9262cf0 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -937,7 +937,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; public final int[] adaptationSetIndices; public final @C.TrackType int trackType; - @TrackGroupCategory public final int trackGroupCategory; + public final @TrackGroupCategory int trackGroupCategory; public final int eventStreamGroupIndex; public final int primaryTrackGroupIndex; diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java index 9ca9fc2102..6ee1ab5d6c 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java @@ -1481,8 +1481,8 @@ public class DashManifestParser extends DefaultHandler // Selection flag parsing. - @C.SelectionFlags - protected int parseSelectionFlagsFromRoleDescriptors(List roleDescriptors) { + protected @C.SelectionFlags int parseSelectionFlagsFromRoleDescriptors( + List roleDescriptors) { @C.SelectionFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1493,8 +1493,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.SelectionFlags - protected int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { + protected @C.SelectionFlags int parseSelectionFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1510,8 +1509,7 @@ public class DashManifestParser extends DefaultHandler // Role and Accessibility parsing. - @C.RoleFlags - protected int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { + protected @C.RoleFlags int parseRoleFlagsFromRoleDescriptors(List roleDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < roleDescriptors.size(); i++) { Descriptor descriptor = roleDescriptors.get(i); @@ -1522,8 +1520,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromAccessibilityDescriptors( + protected @C.RoleFlags int parseRoleFlagsFromAccessibilityDescriptors( List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { @@ -1538,8 +1535,8 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromProperties(List accessibilityDescriptors) { + protected @C.RoleFlags int parseRoleFlagsFromProperties( + List accessibilityDescriptors) { @C.RoleFlags int result = 0; for (int i = 0; i < accessibilityDescriptors.size(); i++) { Descriptor descriptor = accessibilityDescriptors.get(i); @@ -1551,8 +1548,7 @@ public class DashManifestParser extends DefaultHandler return result; } - @C.RoleFlags - protected int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { + protected @C.RoleFlags int parseRoleFlagsFromDashRoleScheme(@Nullable String value) { if (value == null) { return 0; } @@ -1587,8 +1583,7 @@ public class DashManifestParser extends DefaultHandler } } - @C.RoleFlags - protected int parseTvaAudioPurposeCsValue(@Nullable String value) { + protected @C.RoleFlags int parseTvaAudioPurposeCsValue(@Nullable String value) { if (value == null) { return 0; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java index 684bf120ed..aec437e327 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java @@ -62,7 +62,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { FileTypes.MP3, }; - @DefaultTsPayloadReaderFactory.Flags private final int payloadReaderFactoryFlags; + private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags; private final boolean exposeCea608WhenMissingDeclarations; /** diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java index e054b113ca..b843ae48d6 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsChunkSource.java @@ -303,8 +303,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * has been {@link #CHUNK_PUBLICATION_STATE_REMOVED removed} or is definitely {@link * #CHUNK_PUBLICATION_STATE_PUBLISHED published}. */ - @ChunkPublicationState - public int getChunkPublicationState(HlsMediaChunk mediaChunk) { + public @ChunkPublicationState int getChunkPublicationState(HlsMediaChunk mediaChunk) { if (mediaChunk.partIndex == C.INDEX_UNSET) { // Chunks based on full segments can't be removed and are always published. return CHUNK_PUBLICATION_STATE_PUBLISHED; diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java index c6a43c9eec..fb2444d4bb 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMediaPlaylist.java @@ -398,7 +398,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { public static final int PLAYLIST_TYPE_EVENT = 2; /** The type of the playlist. See {@link PlaylistType}. */ - @PlaylistType public final int playlistType; + public final @PlaylistType int playlistType; /** * The start offset in microseconds from the beginning of the playlist, as defined by * #EXT-X-START, or {@link C#TIME_UNSET} if undefined. The value is guaranteed to be between 0 and diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index c1e48a068b..63850c72a0 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -1087,8 +1087,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser variableDefinitions) { + private static @C.RoleFlags int parseRoleFlags( + String line, Map variableDefinitions) { String concatenatedCharacteristics = parseOptionalStringAttr(line, REGEX_CHARACTERISTICS, variableDefinitions); if (TextUtils.isEmpty(concatenatedCharacteristics)) { diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java index b55ec3b9ac..d4176e67fc 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java @@ -162,7 +162,7 @@ import java.util.Map; /** Whether IMA has sent an ad event to pause content since the last resume content event. */ private boolean imaPausedContent; /** The current ad playback state. */ - @ImaAdState private int imaAdState; + private @ImaAdState int imaAdState; /** The current ad media info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ @Nullable private AdMediaInfo imaAdMediaInfo; /** The current ad info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */ diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java index f9026648d2..d8a2345210 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java @@ -51,7 +51,7 @@ import java.util.Map; @Nullable private String authToken; @Nullable private String streamActivityMonitorId; private ImmutableMap adTagParameters; - @ContentType public int format = C.TYPE_HLS; + public @ContentType int format = C.TYPE_HLS; private int loadVideoTimeoutMs; /** Creates a new instance. */ @@ -270,7 +270,7 @@ import java.util.Map; @Nullable public final String contentUrl; @Nullable public final String authToken; @Nullable public final String streamActivityMonitorId; - @ContentType public int format = C.TYPE_HLS; + public @ContentType int format = C.TYPE_HLS; public final int loadVideoTimeoutMs; private ServerSideAdInsertionStreamRequest( diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java index 4d1fd431e7..6a9602c976 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/FakeExoPlayer.java @@ -42,7 +42,7 @@ import androidx.media3.test.utils.StubExoPlayer; private final MediaItem mediaItem = MediaItem.fromUri("http://google.com/0"); private Timeline timeline; - @Player.State private int state; + private @Player.State int state; private boolean playWhenReady; private int periodIndex; private long positionMs; @@ -236,8 +236,7 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - @Player.State - public int getPlaybackState() { + public @Player.State int getPlaybackState() { return state; } @@ -247,8 +246,7 @@ import androidx.media3.test.utils.StubExoPlayer; } @Override - @RepeatMode - public int getRepeatMode() { + public @RepeatMode int getRepeatMode() { return REPEAT_MODE_OFF; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java index ecb9994f3f..0ecdbc9fe4 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspAuthenticationInfo.java @@ -56,7 +56,7 @@ import java.security.NoSuchAlgorithmException; private static final String ALGORITHM = "MD5"; /** The authentication mechanism. */ - @AuthenticationMechanism public final int authenticationMechanism; + public final @AuthenticationMechanism int authenticationMechanism; /** The authentication realm. */ public final String realm; /** The nonce used in digest authentication; empty if using {@link #BASIC} authentication. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java index e8b3fe7867..84bcc4cd8d 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java @@ -141,7 +141,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private String sessionId; @Nullable private KeepAliveMonitor keepAliveMonitor; @Nullable private RtspAuthenticationInfo rtspAuthenticationInfo; - @RtspState private int rtspState; + private @RtspState int rtspState; private boolean hasUpdatedTimelineAndTracks; private boolean receivedAuthorizationRequest; private boolean hasPendingPauseRequest; @@ -204,8 +204,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } /** Returns the current {@link RtspState RTSP state}. */ - @RtspState - public int getState() { + public @RtspState int getState() { return rtspState; } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java index b0ba5c4c5e..28084cdef6 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaPeriod.java @@ -775,8 +775,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return sampleQueue.isReady(/* loadingFinished= */ canceled); } - @ReadDataResult - public int read( + public @ReadDataResult int read( FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) { return sampleQueue.read(formatHolder, buffer, readFlags, /* loadingFinished= */ canceled); } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java index dda52e8f98..a2d5970431 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageChannel.java @@ -353,7 +353,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final List messageLines; - @ReadingState private int state; + private @ReadingState int state; private long messageBodyLength; /** Creates a new instance. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java index e05b4c6469..db1e4116ea 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMessageUtil.java @@ -256,8 +256,7 @@ import java.util.regex.Pattern; } } - @RtspRequest.Method - private static int parseMethodString(String method) { + private static @RtspRequest.Method int parseMethodString(String method) { switch (method) { case "ANNOUNCE": return METHOD_ANNOUNCE; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java index 6d9e5f35e8..23cff58977 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspRequest.java @@ -88,7 +88,7 @@ import java.lang.annotation.Target; /** The {@link Uri} to which this request is sent. */ public final Uri uri; /** The request method, as defined in {@link Method}. */ - @Method public final int method; + public final @Method int method; /** The headers of this request. */ public final RtspHeaders headers; /** The body of this RTSP message, or empty string if absent. */ diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java index 6895010b75..659a28f70f 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java @@ -58,7 +58,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final RtpPayloadFormat payloadFormat; private @MonotonicNonNull TrackOutput trackOutput; - @C.BufferFlags private int bufferFlags; + private @C.BufferFlags int bufferFlags; private long firstReceivedTimestamp; private int previousSequenceNumber; @@ -296,8 +296,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; /* divisor= */ MEDIA_CLOCK_FREQUENCY); } - @C.BufferFlags - private static int getBufferFlagsFromNalType(int nalType) { + private static @C.BufferFlags int getBufferFlagsFromNalType(int nalType) { return nalType == NAL_UNIT_TYPE_IDR ? C.BUFFER_FLAG_KEY_FRAME : 0; } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java index 1ff09dfdff..a8bcb676a6 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java @@ -423,7 +423,7 @@ public abstract class BinarySearchSeeker { new TimestampSearchResult(TYPE_NO_TIMESTAMP, C.TIME_UNSET, C.POSITION_UNSET); /** The type of the result. */ - @Type private final int type; + private final @Type int type; /** * When {@link #type} is {@link #TYPE_POSITION_OVERESTIMATED}, the {@link diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java index f5ee3432f1..5470d906a8 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DefaultExtractorsFactory.java @@ -107,15 +107,15 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { private boolean constantBitrateSeekingEnabled; private boolean constantBitrateSeekingAlwaysEnabled; - @AdtsExtractor.Flags private int adtsFlags; - @AmrExtractor.Flags private int amrFlags; - @FlacExtractor.Flags private int flacFlags; - @MatroskaExtractor.Flags private int matroskaFlags; - @Mp4Extractor.Flags private int mp4Flags; - @FragmentedMp4Extractor.Flags private int fragmentedMp4Flags; - @Mp3Extractor.Flags private int mp3Flags; - @TsExtractor.Mode private int tsMode; - @DefaultTsPayloadReaderFactory.Flags private int tsFlags; + private @AdtsExtractor.Flags int adtsFlags; + private @AmrExtractor.Flags int amrFlags; + private @FlacExtractor.Flags int flacFlags; + private @MatroskaExtractor.Flags int matroskaFlags; + private @Mp4Extractor.Flags int mp4Flags; + private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags; + private @Mp3Extractor.Flags int mp3Flags; + private @TsExtractor.Mode int tsMode; + private @DefaultTsPayloadReaderFactory.Flags int tsFlags; private int tsTimestampSearchBytes; public DefaultExtractorsFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java index 710e11a7ad..e479c27aac 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrackOutput.java @@ -40,7 +40,7 @@ public interface TrackOutput { final class CryptoData { /** The encryption mode used for the sample. */ - @C.CryptoMode public final int cryptoMode; + public final @C.CryptoMode int cryptoMode; /** The encryption key associated with the sample. Its contents must not be modified. */ public final byte[] encryptionKey; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java index 93f675fb5f..53f5db50ad 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/TrueHdSampleRechunker.java @@ -33,7 +33,7 @@ public final class TrueHdSampleRechunker { private boolean foundSyncframe; private int chunkSampleCount; private long chunkTimeUs; - @C.BufferFlags private int chunkFlags; + private @C.BufferFlags int chunkFlags; private int chunkSize; private int chunkOffset; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java index 700d5538f7..3d2cca8171 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/JpegExtractor.java @@ -84,7 +84,7 @@ public final class JpegExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; - @State private int state; + private @State int state; private int marker; private int segmentLength; private long mp4StartPosition; @@ -128,8 +128,8 @@ public final class JpegExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { switch (state) { case STATE_READING_MARKER: readMarker(input); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 6442ad7993..5f4d246a9f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -431,7 +431,7 @@ public class MatroskaExtractor implements Extractor { private int[] blockSampleSizes; private int blockTrackNumber; private int blockTrackNumberLength; - @C.BufferFlags private int blockFlags; + private @C.BufferFlags int blockFlags; private int blockAdditionalId; private boolean blockHasReferenceBlock; @@ -531,8 +531,7 @@ public class MatroskaExtractor implements Extractor { * @see EbmlProcessor#getElementType(int) */ @CallSuper - @EbmlProcessor.ElementType - protected int getElementType(int id) { + protected @EbmlProcessor.ElementType int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: @@ -1908,8 +1907,7 @@ public class MatroskaExtractor implements Extractor { private final class InnerEbmlProcessor implements EbmlProcessor { @Override - @ElementType - public int getElementType(int id) { + public @ElementType int getElementType(int id) { return MatroskaExtractor.this.getElementType(id); } @@ -1981,16 +1979,16 @@ public class MatroskaExtractor implements Extractor { public int displayWidth = Format.NO_VALUE; public int displayHeight = Format.NO_VALUE; public int displayUnit = DISPLAY_UNIT_PIXELS; - @C.Projection public int projectionType = Format.NO_VALUE; + public @C.Projection int projectionType = Format.NO_VALUE; public float projectionPoseYaw = 0f; public float projectionPosePitch = 0f; public float projectionPoseRoll = 0f; public byte @MonotonicNonNull [] projectionData = null; - @C.StereoMode public int stereoMode = Format.NO_VALUE; + public @C.StereoMode int stereoMode = Format.NO_VALUE; public boolean hasColorInfo = false; - @C.ColorSpace public int colorSpace = Format.NO_VALUE; - @C.ColorTransfer public int colorTransfer = Format.NO_VALUE; - @C.ColorRange public int colorRange = Format.NO_VALUE; + public @C.ColorSpace int colorSpace = Format.NO_VALUE; + public @C.ColorTransfer int colorTransfer = Format.NO_VALUE; + public @C.ColorRange int colorRange = Format.NO_VALUE; public int maxContentLuminance = DEFAULT_MAX_CLL; public int maxFrameAverageLuminance = DEFAULT_MAX_FALL; public float primaryRChromaticityX = Format.NO_VALUE; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index 936c7be490..53ae30f9ee 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -142,7 +142,7 @@ public final class Mp3Extractor implements Extractor { private static final int SEEK_HEADER_VBRI = 0x56425249; private static final int SEEK_HEADER_UNSET = 0; - @Flags private final int flags; + private final @Flags int flags; private final long forcedFirstSampleTimestampUs; private final ParsableByteArray scratch; private final MpegAudioUtil.Header synchronizedHeader; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java index 985255a1b9..bd34ba5970 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java @@ -1910,7 +1910,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable public Format format; public int nalUnitLengthFieldLength; - @Track.Transformation public int requiredSampleTransformation; + public @Track.Transformation int requiredSampleTransformation; public StsdData(int numberOfEntries) { trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries]; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index dc83015d88..36284771d1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -132,7 +132,7 @@ public class FragmentedMp4Extractor implements Extractor { private static final int STATE_READING_SAMPLE_CONTINUE = 4; // Workarounds. - @Flags private final int flags; + private final @Flags int flags; @Nullable private final Track sideloadedTrack; // Sideloaded data. @@ -1689,8 +1689,7 @@ public class FragmentedMp4Extractor implements Extractor { } /** Returns the {@link C.BufferFlags} corresponding to the current sample. */ - @C.BufferFlags - public int getCurrentSampleFlags() { + public @C.BufferFlags int getCurrentSampleFlags() { int flags = !currentlyInFragment ? moovSampleTable.flags[currentSampleIndex] diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index b2b8bc74a3..e839cf1e9e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -141,7 +141,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { */ private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10 * 1024 * 1024; - @Flags private final int flags; + private final @Flags int flags; // Temporary arrays. private final ParsableByteArray nalStartCode; @@ -153,7 +153,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private final SefReader sefReader; private final List slowMotionMetadataEntries; - @State private int parserState; + private @State int parserState; private int atomType; private long atomSize; private int atomHeaderBytesRead; @@ -171,7 +171,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { private long @MonotonicNonNull [][] accumulatedSampleSizes; private int firstVideoTrackIndex; private long durationUs; - @FileType private int fileType; + private @FileType int fileType; @Nullable private MotionPhotoMetadata motionPhotoMetadata; /** Creates a new extractor for unfragmented MP4 streams. */ @@ -438,8 +438,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { return seekRequired && parserState != STATE_READING_SAMPLE; } - @ReadResult - private int readSefData(ExtractorInput input, PositionHolder seekPosition) throws IOException { + private @ReadResult int readSefData(ExtractorInput input, PositionHolder seekPosition) + throws IOException { @ReadResult int result = sefReader.read(input, seekPosition, slowMotionMetadataEntries); if (result == RESULT_SEEK && seekPosition.position == 0) { enterReadingAtomHeaderState(); @@ -861,8 +861,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { * @param atomData The ftyp atom data. * @return The {@link FileType}. */ - @FileType - private static int processFtypAtom(ParsableByteArray atomData) { + private static @FileType int processFtypAtom(ParsableByteArray atomData) { atomData.setPosition(Atom.HEADER_SIZE); int majorBrand = atomData.readInt(); @FileType int fileType = brandToFileType(majorBrand); @@ -879,8 +878,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { return FILE_TYPE_MP4; } - @FileType - private static int brandToFileType(int brand) { + private static @FileType int brandToFileType(int brand) { switch (brand) { case BRAND_QUICKTIME: return FILE_TYPE_QUICKTIME; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java index 84170809eb..b5161fbf1e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/SefReader.java @@ -97,7 +97,7 @@ import java.util.List; private static final Splitter ASTERISK_SPLITTER = Splitter.on('*'); private final List dataReferences; - @State private int readerState; + private @State int readerState; private int tailLength; public SefReader() { @@ -110,8 +110,7 @@ import java.util.List; readerState = STATE_SHOULD_CHECK_FOR_SEF; } - @Extractor.ReadResult - public int read( + public @Extractor.ReadResult int read( ExtractorInput input, PositionHolder seekPosition, List slowMotionMetadataEntries) @@ -250,8 +249,7 @@ import java.util.List; return new SlowMotionData(segments); } - @DataType - private static int nameToDataType(String name) throws ParserException { + private static @DataType int nameToDataType(String name) throws ParserException { switch (name) { case "SlowMotion_Data": return TYPE_SLOW_MOTION_DATA; @@ -269,7 +267,7 @@ import java.util.List; } private static final class DataReference { - @DataType public final int dataType; + public final @DataType int dataType; public final long startOffset; public final int size; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java index 2259bbae86..adf6c8db59 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Track.java @@ -69,7 +69,7 @@ public final class Track { * One of {@code TRANSFORMATION_*}. Defines the transformation to apply before outputting each * sample. */ - @Transformation public final int sampleTransformation; + public final @Transformation int sampleTransformation; /** Durations of edit list segments in the movie timescale. Null if there is no edit list. */ @Nullable public final long[] editListDurations; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java index 8791a4fe5c..6ef108cd30 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/TrackEncryptionBox.java @@ -79,8 +79,7 @@ public final class TrackEncryptionBox { schemeToCryptoMode(schemeType), keyId, defaultEncryptedBlocks, defaultClearBlocks); } - @C.CryptoMode - private static int schemeToCryptoMode(@Nullable String schemeType) { + private static @C.CryptoMode int schemeToCryptoMode(@Nullable String schemeType) { if (schemeType == null) { // If unknown, assume cenc. return C.CRYPTO_MODE_AES_CTR; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java index 46720375d1..df43d16ae5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java @@ -87,7 +87,7 @@ public class SubtitleExtractor implements Extractor { private @MonotonicNonNull ExtractorOutput extractorOutput; private @MonotonicNonNull TrackOutput trackOutput; private int bytesRead; - @State private int state; + private @State int state; private long seekTimeUs; /** diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java index 5c24fd14f0..3871ac6676 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaDecoder.java @@ -409,8 +409,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - @Cue.AnchorType - private static int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { + private static @Cue.AnchorType int toLineAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER: @@ -432,8 +431,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { } } - @Cue.AnchorType - private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { + private static @Cue.AnchorType int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) { switch (alignment) { case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT: case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java index 7bfd5785a5..08461769f7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaStyle.java @@ -93,7 +93,7 @@ import java.util.regex.Pattern; public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; public final String name; - @SsaAlignment public final int alignment; + public final @SsaAlignment int alignment; @Nullable @ColorInt public final Integer primaryColor; public final float fontSize; public final boolean bold; @@ -158,8 +158,7 @@ import java.util.regex.Pattern; } } - @SsaAlignment - private static int parseAlignment(String alignmentStr) { + private static @SsaAlignment int parseAlignment(String alignmentStr) { try { @SsaAlignment int alignment = Integer.parseInt(alignmentStr.trim()); if (isValidAlignment(alignment)) { @@ -373,7 +372,7 @@ import java.util.regex.Pattern; /** Matches "\anx" and returns x in group 1 */ private static final Pattern ALIGNMENT_OVERRIDE_PATTERN = Pattern.compile("\\\\an(\\d+)"); - @SsaAlignment public final int alignment; + public final @SsaAlignment int alignment; @Nullable public final PointF position; private Overrides(@SsaAlignment int alignment, @Nullable PointF position) { @@ -451,8 +450,7 @@ import java.util.regex.Pattern; Float.parseFloat(Assertions.checkNotNull(y).trim())); } - @SsaAlignment - private static int parseAlignmentOverride(String braceContents) { + private static @SsaAlignment int parseAlignmentOverride(String braceContents) { Matcher matcher = ALIGNMENT_OVERRIDE_PATTERN.matcher(braceContents); return matcher.find() ? parseAlignment(Assertions.checkNotNull(matcher.group(1))) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java index a9e11e3642..42d34effc5 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TextEmphasis.java @@ -97,13 +97,13 @@ import java.util.regex.Pattern; TtmlNode.ANNOTATION_POSITION_OUTSIDE); /** The text emphasis mark shape. */ - @MarkShape public final int markShape; + public final @MarkShape int markShape; /** The fill style of the text emphasis mark. */ - @TextEmphasisSpan.MarkFill public final int markFill; + public final @TextEmphasisSpan.MarkFill int markFill; /** The position of the text emphasis relative to the base text. */ - @Position public final int position; + public final @Position int position; private TextEmphasis( @MarkShape int markShape, diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java index fe75015684..1bf821c72e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlRegion.java @@ -23,13 +23,13 @@ import androidx.media3.common.text.Cue; public final String id; public final float position; public final float line; - @Cue.LineType public final int lineType; - @Cue.AnchorType public final int lineAnchor; + public final @Cue.LineType int lineType; + public final @Cue.AnchorType int lineAnchor; public final float width; public final float height; - @Cue.TextSizeType public final int textSizeType; + public final @Cue.TextSizeType int textSizeType; public final float textSize; - @Cue.VerticalType public final int verticalType; + public final @Cue.VerticalType int verticalType; public TtmlRegion(String id) { this( diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java index a2b71b0463..b706154a0f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlStyle.java @@ -81,18 +81,18 @@ import java.lang.annotation.Target; private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - @OptionalBoolean private int linethrough; - @OptionalBoolean private int underline; - @OptionalBoolean private int bold; - @OptionalBoolean private int italic; - @FontSizeUnit private int fontSizeUnit; + private @OptionalBoolean int linethrough; + private @OptionalBoolean int underline; + private @OptionalBoolean int bold; + private @OptionalBoolean int italic; + private @FontSizeUnit int fontSizeUnit; private float fontSize; @Nullable private String id; - @RubyType private int rubyType; - @TextAnnotation.Position private int rubyPosition; + private @RubyType int rubyType; + private @TextAnnotation.Position int rubyPosition; @Nullable private Layout.Alignment textAlign; @Nullable private Layout.Alignment multiRowAlign; - @OptionalBoolean private int textCombine; + private @OptionalBoolean int textCombine; @Nullable private TextEmphasis textEmphasis; private float shearPercentage; @@ -114,8 +114,7 @@ import java.lang.annotation.Target; * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - @StyleFlags - public int getStyle() { + public @StyleFlags int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -292,8 +291,7 @@ import java.lang.annotation.Target; return this; } - @RubyType - public int getRubyType() { + public @RubyType int getRubyType() { return rubyType; } @@ -302,8 +300,7 @@ import java.lang.annotation.Target; return this; } - @TextAnnotation.Position - public int getRubyPosition() { + public @TextAnnotation.Position int getRubyPosition() { return rubyPosition; } @@ -357,8 +354,7 @@ import java.lang.annotation.Target; return this; } - @FontSizeUnit - public int getFontSizeUnit() { + public @FontSizeUnit int getFontSizeUnit() { return fontSizeUnit; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 29753e1d4f..77713c1672 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -97,13 +97,13 @@ public final class WebvttCssStyle { private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; - @OptionalBoolean private int linethrough; - @OptionalBoolean private int underline; - @OptionalBoolean private int bold; - @OptionalBoolean private int italic; - @FontSizeUnit private int fontSizeUnit; + private @OptionalBoolean int linethrough; + private @OptionalBoolean int underline; + private @OptionalBoolean int bold; + private @OptionalBoolean int italic; + private @FontSizeUnit int fontSizeUnit; private float fontSize; - @TextAnnotation.Position private int rubyPosition; + private @TextAnnotation.Position int rubyPosition; private boolean combineUpright; public WebvttCssStyle() { @@ -186,8 +186,7 @@ public final class WebvttCssStyle { * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ - @StyleFlags - public int getStyle() { + public @StyleFlags int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } @@ -276,8 +275,7 @@ public final class WebvttCssStyle { return this; } - @FontSizeUnit - public int getFontSizeUnit() { + public @FontSizeUnit int getFontSizeUnit() { return fontSizeUnit; } @@ -290,8 +288,7 @@ public final class WebvttCssStyle { return this; } - @TextAnnotation.Position - public int getRubyPosition() { + public @TextAnnotation.Position int getRubyPosition() { return rubyPosition; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java index a80d381502..456dd61680 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCueParser.java @@ -406,8 +406,7 @@ public final class WebvttCueParser { } } - @Cue.AnchorType - private static int parseLineAnchor(String s) { + private static @Cue.AnchorType int parseLineAnchor(String s) { switch (s) { case "start": return Cue.ANCHOR_TYPE_START; @@ -431,8 +430,7 @@ public final class WebvttCueParser { builder.position = WebvttParserUtil.parsePercentage(s); } - @Cue.AnchorType - private static int parsePositionAnchor(String s) { + private static @Cue.AnchorType int parsePositionAnchor(String s) { switch (s) { case "line-left": case "start": @@ -449,8 +447,7 @@ public final class WebvttCueParser { } } - @Cue.VerticalType - private static int parseVerticalAttribute(String s) { + private static @Cue.VerticalType int parseVerticalAttribute(String s) { switch (s) { case "rl": return Cue.VERTICAL_TYPE_RL; @@ -462,8 +459,7 @@ public final class WebvttCueParser { } } - @TextAlignment - private static int parseTextAlignment(String s) { + private static @TextAlignment int parseTextAlignment(String s) { switch (s) { case "start": return TEXT_ALIGNMENT_START; @@ -611,8 +607,7 @@ public final class WebvttCueParser { } } - @TextAnnotation.Position - private static int getRubyPosition( + private static @TextAnnotation.Position int getRubyPosition( List styles, @Nullable String cueId, StartTag startTag) { List styleMatches = getApplicableStyles(styles, cueId, startTag); for (int i = 0; i < styleMatches.size(); i++) { @@ -624,8 +619,7 @@ public final class WebvttCueParser { return TextAnnotation.POSITION_UNKNOWN; } - @TextAnnotation.Position - private static int firstKnownRubyPosition( + private static @TextAnnotation.Position int firstKnownRubyPosition( @TextAnnotation.Position int position1, @TextAnnotation.Position int position2, @TextAnnotation.Position int position3) { @@ -770,16 +764,16 @@ public final class WebvttCueParser { public long startTimeUs; public long endTimeUs; public @MonotonicNonNull CharSequence text; - @TextAlignment public int textAlignment; + public @TextAlignment int textAlignment; public float line; // Equivalent to WebVTT's snap-to-lines flag: // https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag - @Cue.LineType public int lineType; - @Cue.AnchorType public int lineAnchor; + public @Cue.LineType int lineType; + public @Cue.AnchorType int lineAnchor; public float position; - @Cue.AnchorType public int positionAnchor; + public @Cue.AnchorType int positionAnchor; public float size; - @Cue.VerticalType public int verticalType; + public @Cue.VerticalType int verticalType; public WebvttCueInfoBuilder() { startTimeUs = 0; @@ -861,8 +855,7 @@ public final class WebvttCueParser { } // https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment - @Cue.AnchorType - private static int derivePositionAnchor(@TextAlignment int textAlignment) { + private static @Cue.AnchorType int derivePositionAnchor(@TextAlignment int textAlignment) { switch (textAlignment) { case TEXT_ALIGNMENT_LEFT: case TEXT_ALIGNMENT_START: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java index 527f32d965..1d80fbca08 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac3Reader.java @@ -62,7 +62,7 @@ public final class Ac3Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - @State private int state; + private @State int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java index 238b861ad7..95440cec23 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/Ac4Reader.java @@ -60,7 +60,7 @@ public final class Ac4Reader implements ElementaryStreamReader { private @MonotonicNonNull String formatId; private @MonotonicNonNull TrackOutput output; - @State private int state; + private @State int state; private int bytesRead; // Used to find the header. diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java index 105059098f..11fa10f074 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java @@ -104,7 +104,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact private static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86; - @Flags private final int flags; + private final @Flags int flags; private final List closedCaptionFormats; public DefaultTsPayloadReaderFactory() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index efa84e123f..11c81fef99 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -335,7 +335,7 @@ public final class H263Reader implements ElementaryStreamReader { private static final int STATE_WAIT_FOR_VOP_START = 4; private boolean isFilling; - @State private int state; + private @State int state; public int length; public int volStartPosition; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java index aa2a36b02b..ea033b979b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java @@ -118,7 +118,7 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_SIZE = TS_PACKET_SIZE * 50; private static final int SNIFF_TS_PACKET_COUNT = 5; - @Mode private final int mode; + private final @Mode int mode; private final int timestampSearchBytes; private final List timestampAdjusters; private final ParsableByteArray tsPacketBuffer; @@ -298,8 +298,8 @@ public final class TsExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { long inputLength = input.getLength(); if (tracksEnded) { boolean canReadDuration = inputLength != C.LENGTH_UNSET && mode != MODE_HLS; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java index 76461fa7cf..ca35a92b43 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/wav/WavExtractor.java @@ -122,8 +122,8 @@ public final class WavExtractor implements Extractor { } @Override - @ReadResult - public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { + public @ReadResult int read(ExtractorInput input, PositionHolder seekPosition) + throws IOException { assertInitialized(); switch (state) { case STATE_READING_FILE_TYPE: @@ -227,8 +227,7 @@ public final class WavExtractor implements Extractor { state = STATE_READING_SAMPLE_DATA; } - @ReadResult - private int readSampleData(ExtractorInput input) throws IOException { + private @ReadResult int readSampleData(ExtractorInput input) throws IOException { Assertions.checkState(dataEndPosition != C.POSITION_UNSET); long bytesLeft = dataEndPosition - input.getPosition(); return Assertions.checkNotNull(outputWriter).sampleData(input, bytesLeft) diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java index 6597835a12..7fe5bb9284 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/DefaultEbmlReaderTest.java @@ -178,8 +178,7 @@ public class DefaultEbmlReaderTest { private final List events = new ArrayList<>(); @Override - @EbmlProcessor.ElementType - public int getElementType(int id) { + public @EbmlProcessor.ElementType int getElementType(int id) { switch (id) { case ID_EBML: case ID_SEGMENT: diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java index 11c65d7d1c..3837b1260a 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlStyleTest.java @@ -42,7 +42,7 @@ public final class TtmlStyleTest { private static final String FONT_FAMILY = "serif"; @ColorInt private static final int FONT_COLOR = Color.WHITE; private static final float FONT_SIZE = 12.5f; - @TtmlStyle.FontSizeUnit private static final int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; + private static final @TtmlStyle.FontSizeUnit int FONT_SIZE_UNIT = TtmlStyle.FONT_SIZE_UNIT_EM; @ColorInt private static final int BACKGROUND_COLOR = Color.BLACK; private static final int RUBY_TYPE = TtmlStyle.RUBY_TYPE_TEXT; private static final int RUBY_POSITION = TextAnnotation.POSITION_AFTER; diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java index 15d3566214..416365e414 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java @@ -82,7 +82,7 @@ public class MockPlayer implements Player { public int newIndex; public int currentPeriodIndex; public int currentMediaItemIndex; - @RepeatMode public int repeatMode; + public @RepeatMode int repeatMode; public boolean shuffleModeEnabled; public VideoSize videoSize; @Nullable public Surface surface; @@ -95,8 +95,8 @@ public class MockPlayer implements Player { public int deviceVolume; public boolean deviceMuted; public boolean playWhenReady; - @PlaybackSuppressionReason public int playbackSuppressionReason; - @State public int playbackState; + public @PlaybackSuppressionReason int playbackSuppressionReason; + public @State int playbackState; public boolean isPlaying; public boolean isLoading; public MediaMetadata mediaMetadata; @@ -591,14 +591,12 @@ public class MockPlayer implements Player { } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { return playbackSuppressionReason; } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { return playbackState; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java index edf7665e65..6aaf678a37 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java @@ -495,7 +495,7 @@ public abstract class Action { /** Calls {@link Player#setRepeatMode(int)}. */ public static final class SetRepeatMode extends Action { - @Player.RepeatMode private final int repeatMode; + private final @Player.RepeatMode int repeatMode; /** * @param tag A tag to use for logging. @@ -742,7 +742,7 @@ public abstract class Action { @Nullable private final Timeline expectedTimeline; private final boolean ignoreExpectedReason; - @Player.TimelineChangeReason private final int expectedReason; + private final @Player.TimelineChangeReason int expectedReason; /** * Creates action waiting for a timeline change for a given reason. @@ -909,7 +909,7 @@ public abstract class Action { */ public static final class WaitForPlaybackState extends Action { - @Player.State private final int targetPlaybackState; + private final @Player.State int targetPlaybackState; /** * @param tag A tag to use for logging. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java index 4da0ab6ab2..c10e0fa685 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingAudioSink.java @@ -93,7 +93,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum private static final class DumpableConfiguration implements Dumper.Dumpable { - @C.PcmEncoding private final int inputPcmEncoding; + private final @C.PcmEncoding int inputPcmEncoding; private final int inputChannelCount; private final int inputSampleRate; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java index 743c6b1716..fa9c146a8e 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java @@ -47,7 +47,7 @@ public final class DownloadBuilder { @Nullable private String cacheKey; private byte[] customMetadata; - @Download.State private int state; + private @Download.State int state; private long startTimeMs; private long updateTimeMs; private long contentLength; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java index 7cab3c2d8a..7059c004ae 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DumpFileAsserts.java @@ -72,7 +72,7 @@ public class DumpFileAsserts { /** Write output to folder {@code /storage/emulated/0/Android/data} of device. */ private static final int WRITE_TO_DEVICE = 1 << 1; - @DumpFilesAction private static final int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; + private static final @DumpFilesAction int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING; private DumpFileAsserts() {} diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java index 314726f85e..fca4296d6d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java @@ -398,8 +398,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { } @Override - @C.CryptoType - public int getCryptoType() { + public @C.CryptoType int getCryptoType() { return FakeCryptoConfig.TYPE; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java index 9c95fbaa25..e4c99ffa64 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeRenderer.java @@ -162,8 +162,7 @@ public class FakeRenderer extends BaseRenderer { } @Override - @Capabilities - public int supportsFormat(Format format) throws ExoPlaybackException { + public @Capabilities int supportsFormat(Format format) throws ExoPlaybackException { int trackType = MimeTypes.getTrackType(format.sampleMimeType); return trackType != C.TRACK_TYPE_UNKNOWN && trackType == getTrackType() ? RendererCapabilities.create(C.FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java index d072791b45..d779b21109 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeSampleStream.java @@ -295,7 +295,7 @@ public class FakeSampleStream implements SampleStream { private static class SampleInfo { public final byte[] data; - @C.BufferFlags public final int flags; + public final @C.BufferFlags int flags; public final long timeUs; public SampleInfo(byte[] data, @C.BufferFlags int flags, long timeUs) { diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java index ce2411f93a..b3373e1f68 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java @@ -62,14 +62,12 @@ public class StubPlayer extends BasePlayer { } @Override - @State - public int getPlaybackState() { + public @State int getPlaybackState() { throw new UnsupportedOperationException(); } @Override - @PlaybackSuppressionReason - public int getPlaybackSuppressionReason() { + public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { throw new UnsupportedOperationException(); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java index d252878fee..63b20dfc7c 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java @@ -99,7 +99,7 @@ public class WebServerDispatcher extends Dispatcher { private byte @MonotonicNonNull [] data; private boolean supportsRangeRequests; private boolean resolvesToUnknownLength; - @GzipSupport private int gzipSupport; + private @GzipSupport int gzipSupport; /** Constructs an instance. */ public Builder() { @@ -187,7 +187,7 @@ public class WebServerDispatcher extends Dispatcher { private final byte[] data; private final boolean supportsRangeRequests; private final boolean resolvesToUnknownLength; - @GzipSupport private final int gzipSupport; + private final @GzipSupport int gzipSupport; private Resource( String path, @@ -223,8 +223,7 @@ public class WebServerDispatcher extends Dispatcher { } /** Returns the level of gzip support the server should provide for this resource. */ - @GzipSupport - public int getGzipSupport() { + public @GzipSupport int getGzipSupport() { return gzipSupport; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java index d5f16d72f2..9a30b04202 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/truth/SpannedSubject.java @@ -1115,7 +1115,7 @@ public final class SpannedSubject extends Subject { private static final class TextAndPosition { private final String text; - @TextAnnotation.Position private final int position; + private final @TextAnnotation.Position int position; private TextAndPosition(String text, int position) { this.text = text; @@ -1212,9 +1212,9 @@ public final class SpannedSubject extends Subject { private static final class MarkAndPosition { - @TextEmphasisSpan.MarkShape private final int markShape; - @TextEmphasisSpan.MarkFill private final int markFill; - @TextAnnotation.Position private final int position; + private final @TextEmphasisSpan.MarkShape int markShape; + private final @TextEmphasisSpan.MarkFill int markFill; + private final @TextAnnotation.Position int position; private MarkAndPosition( @TextEmphasisSpan.MarkShape int markShape, diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java index 1a79143a32..ff80c45666 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestDownloadManagerListener.java @@ -44,7 +44,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen private final ConditionVariable initializedCondition; private final ConditionVariable idleCondition; - @Download.FailureReason private int failureReason; + private @Download.FailureReason int failureReason; public TestDownloadManagerListener(DownloadManager downloadManager) { this.downloadManager = downloadManager; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 45037656a9..3b88fff41d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -552,7 +552,7 @@ public final class Transformer { @Nullable private MuxerWrapper muxerWrapper; @Nullable private ExoPlayer player; - @ProgressState private int progressState; + private @ProgressState int progressState; private Transformer( Context context, @@ -754,8 +754,7 @@ public final class Transformer { * @return The {@link ProgressState}. * @throws IllegalStateException If this method is called from the wrong thread. */ - @ProgressState - public int getProgress(ProgressHolder progressHolder) { + public @ProgressState int getProgress(ProgressHolder progressHolder) { verifyApplicationThread(); if (progressState == PROGRESS_STATE_AVAILABLE) { Player player = checkNotNull(this.player); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java index 94b398dcc1..d6e6c8b645 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerBaseRenderer.java @@ -67,8 +67,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @return The {@link Capabilities} for this format. */ @Override - @Capabilities - public final int supportsFormat(Format format) { + public final @Capabilities int supportsFormat(Format format) { return RendererCapabilities.create( MimeTypes.getTrackType(format.sampleMimeType) == getTrackType() ? C.FORMAT_HANDLED From 5cc3ce9a5018662e2bbf1f7b5d97e8e08b817cd5 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 09:59:07 +0000 Subject: [PATCH 168/251] Remove deprecated EventListener PiperOrigin-RevId: 427133817 --- .../java/androidx/media3/cast/CastPlayer.java | 36 +- .../media3/common/ForwardingPlayer.java | 322 +++++---- .../java/androidx/media3/common/Player.java | 654 ++++++++---------- .../media3/common/ForwardingPlayerTest.java | 15 - .../androidx/media3/exoplayer/ExoPlayer.java | 26 - .../media3/exoplayer/ExoPlayerImpl.java | 116 ++-- .../media3/exoplayer/SimpleExoPlayer.java | 15 - .../media3/test/utils/StubExoPlayer.java | 11 - 8 files changed, 498 insertions(+), 697 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index cc026ba69a..347d27904e 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -137,7 +137,7 @@ public final class CastPlayer extends BasePlayer { private final SeekResultCallback seekResultCallback; // Listeners and notification. - private final ListenerSet listeners; + private final ListenerSet listeners; @Nullable private SessionAvailabilityListener sessionAvailabilityListener; // Internal state. @@ -280,41 +280,11 @@ public final class CastPlayer extends BasePlayer { @Override public void addListener(Listener listener) { - EventListener eventListener = listener; - addListener(eventListener); - } - - /** - * Registers a listener to receive events from the player. - * - *

    The listener's methods will be called on the thread associated with {@link - * #getApplicationLooper()}. - * - * @param listener The listener to register. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @Deprecated - @SuppressWarnings("deprecation") - public void addListener(EventListener listener) { listeners.add(listener); } @Override public void removeListener(Listener listener) { - EventListener eventListener = listener; - removeListener(eventListener); - } - - /** - * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will - * no longer receive events from the player. - * - * @param listener The listener to unregister. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @Deprecated - @SuppressWarnings("deprecation") - public void removeListener(EventListener listener) { listeners.remove(listener); } @@ -473,7 +443,7 @@ public final class CastPlayer extends BasePlayer { } updateAvailableCommandsAndNotifyIfChanged(); } else if (pendingSeekCount == 0) { - listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } listeners.flushEvents(); } @@ -1477,7 +1447,7 @@ public final class CastPlayer extends BasePlayer { currentWindowIndex = pendingSeekWindowIndex; pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekPositionMs = C.TIME_UNSET; - listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index c0ea8befcc..5e00fa0663 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -819,191 +819,159 @@ public class ForwardingPlayer implements Player { return player; } - @SuppressWarnings("deprecation") // Use of deprecated type for backwards compatibility. - private static class ForwardingEventListener implements EventListener { + private static final class ForwardingListener implements Listener { private final ForwardingPlayer forwardingPlayer; - private final EventListener eventListener; + private final Listener listener; - private ForwardingEventListener( - ForwardingPlayer forwardingPlayer, EventListener eventListener) { + public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { this.forwardingPlayer = forwardingPlayer; - this.eventListener = eventListener; - } - - @Override - public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { - eventListener.onTimelineChanged(timeline, reason); - } - - @Override - public void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - eventListener.onMediaItemTransition(mediaItem, reason); - } - - @Override - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - eventListener.onTracksChanged(trackGroups, trackSelections); - } - - @Override - public void onTracksInfoChanged(TracksInfo tracksInfo) { - eventListener.onTracksInfoChanged(tracksInfo); - } - - @Override - public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { - eventListener.onMediaMetadataChanged(mediaMetadata); - } - - @Override - public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { - eventListener.onPlaylistMetadataChanged(mediaMetadata); - } - - @Override - public void onIsLoadingChanged(boolean isLoading) { - eventListener.onIsLoadingChanged(isLoading); - } - - @Override - public void onLoadingChanged(boolean isLoading) { - eventListener.onIsLoadingChanged(isLoading); - } - - @Override - public void onAvailableCommandsChanged(Commands availableCommands) { - eventListener.onAvailableCommandsChanged(availableCommands); - } - - @Override - public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { - eventListener.onTrackSelectionParametersChanged(parameters); - } - - @Override - public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { - eventListener.onPlayerStateChanged(playWhenReady, playbackState); - } - - @Override - public void onPlaybackStateChanged(@State int playbackState) { - eventListener.onPlaybackStateChanged(playbackState); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - eventListener.onPlayWhenReadyChanged(playWhenReady, reason); - } - - @Override - public void onPlaybackSuppressionReasonChanged( - @PlayWhenReadyChangeReason int playbackSuppressionReason) { - eventListener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); - } - - @Override - public void onIsPlayingChanged(boolean isPlaying) { - eventListener.onIsPlayingChanged(isPlaying); - } - - @Override - public void onRepeatModeChanged(@RepeatMode int repeatMode) { - eventListener.onRepeatModeChanged(repeatMode); - } - - @Override - public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled); - } - - @Override - public void onPlayerError(PlaybackException error) { - eventListener.onPlayerError(error); - } - - @Override - public void onPlayerErrorChanged(@Nullable PlaybackException error) { - eventListener.onPlayerErrorChanged(error); - } - - @Override - public void onPositionDiscontinuity(@DiscontinuityReason int reason) { - eventListener.onPositionDiscontinuity(reason); - } - - @Override - public void onPositionDiscontinuity( - PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { - eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason); - } - - @Override - public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { - eventListener.onPlaybackParametersChanged(playbackParameters); - } - - @Override - public void onSeekBackIncrementChanged(long seekBackIncrementMs) { - eventListener.onSeekBackIncrementChanged(seekBackIncrementMs); - } - - @Override - public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { - eventListener.onSeekForwardIncrementChanged(seekForwardIncrementMs); - } - - @Override - public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { - eventListener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); - } - - @Override - public void onSeekProcessed() { - eventListener.onSeekProcessed(); + this.listener = listener; } @Override public void onEvents(Player player, Events events) { // Replace player with forwarding player. - eventListener.onEvents(forwardingPlayer, events); + listener.onEvents(forwardingPlayer, events); } @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ForwardingEventListener)) { - return false; - } - - ForwardingEventListener that = (ForwardingEventListener) o; - - if (!forwardingPlayer.equals(that.forwardingPlayer)) { - return false; - } - return eventListener.equals(that.eventListener); + public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) { + listener.onTimelineChanged(timeline, reason); } @Override - public int hashCode() { - int result = forwardingPlayer.hashCode(); - result = 31 * result + eventListener.hashCode(); - return result; + public void onMediaItemTransition( + @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { + listener.onMediaItemTransition(mediaItem, reason); } - } - private static final class ForwardingListener extends ForwardingEventListener - implements Listener { + @Override + @SuppressWarnings("deprecation") + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + listener.onTracksChanged(trackGroups, trackSelections); + } - private final Listener listener; + @Override + public void onTracksInfoChanged(TracksInfo tracksInfo) { + listener.onTracksInfoChanged(tracksInfo); + } - public ForwardingListener(ForwardingPlayer forwardingPlayer, Listener listener) { - super(forwardingPlayer, listener); - this.listener = listener; + @Override + public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { + listener.onMediaMetadataChanged(mediaMetadata); + } + + @Override + public void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) { + listener.onPlaylistMetadataChanged(mediaMetadata); + } + + @Override + public void onIsLoadingChanged(boolean isLoading) { + listener.onIsLoadingChanged(isLoading); + } + + @Override + @SuppressWarnings("deprecation") + public void onLoadingChanged(boolean isLoading) { + listener.onIsLoadingChanged(isLoading); + } + + @Override + public void onAvailableCommandsChanged(Commands availableCommands) { + listener.onAvailableCommandsChanged(availableCommands); + } + + @Override + public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { + listener.onTrackSelectionParametersChanged(parameters); + } + + @Override + @SuppressWarnings("deprecation") + public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { + listener.onPlayerStateChanged(playWhenReady, playbackState); + } + + @Override + public void onPlaybackStateChanged(@State int playbackState) { + listener.onPlaybackStateChanged(playbackState); + } + + @Override + public void onPlayWhenReadyChanged( + boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { + listener.onPlayWhenReadyChanged(playWhenReady, reason); + } + + @Override + public void onPlaybackSuppressionReasonChanged( + @PlayWhenReadyChangeReason int playbackSuppressionReason) { + listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + listener.onIsPlayingChanged(isPlaying); + } + + @Override + public void onRepeatModeChanged(@RepeatMode int repeatMode) { + listener.onRepeatModeChanged(repeatMode); + } + + @Override + public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { + listener.onShuffleModeEnabledChanged(shuffleModeEnabled); + } + + @Override + public void onPlayerError(PlaybackException error) { + listener.onPlayerError(error); + } + + @Override + public void onPlayerErrorChanged(@Nullable PlaybackException error) { + listener.onPlayerErrorChanged(error); + } + + @Override + @SuppressWarnings("deprecation") + public void onPositionDiscontinuity(@DiscontinuityReason int reason) { + listener.onPositionDiscontinuity(reason); + } + + @Override + public void onPositionDiscontinuity( + PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) { + listener.onPositionDiscontinuity(oldPosition, newPosition, reason); + } + + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + listener.onPlaybackParametersChanged(playbackParameters); + } + + @Override + public void onSeekBackIncrementChanged(long seekBackIncrementMs) { + listener.onSeekBackIncrementChanged(seekBackIncrementMs); + } + + @Override + public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { + listener.onSeekForwardIncrementChanged(seekForwardIncrementMs); + } + + @Override + public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { + listener.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs); + } + + @Override + @SuppressWarnings("deprecation") + public void onSeekProcessed() { + listener.onSeekProcessed(); } @Override @@ -1060,5 +1028,27 @@ public class ForwardingPlayer implements Player { public void onDeviceVolumeChanged(int volume, boolean muted) { listener.onDeviceVolumeChanged(volume, muted); } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ForwardingListener)) { + return false; + } + ForwardingListener that = (ForwardingListener) o; + if (!forwardingPlayer.equals(that.forwardingPlayer)) { + return false; + } + return listener.equals(that.listener); + } + + @Override + public int hashCode() { + int result = forwardingPlayer.hashCode(); + result = 31 * result + listener.hashCode(); + return result; + } } } diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 1e7ed137e3..7487910ebb 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -63,343 +63,6 @@ import java.util.List; */ public interface Player { - /** - * Listener of changes in player state. - * - *

    All methods have no-op default implementations to allow selective overrides. - * - *

    Listeners can choose to implement individual events (e.g. {@link - * #onIsPlayingChanged(boolean)}) or {@link #onEvents(Player, Events)}, which is called after one - * or more events occurred together. - * - * @deprecated Use {@link Player.Listener}. - */ - @UnstableApi - @Deprecated - interface EventListener { - - /** - * Called when the timeline has been refreshed. - * - *

    Note that the current {@link MediaItem} or playback position may change as a result of a - * timeline change. If playback can't continue smoothly because of this timeline change, a - * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be - * triggered. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param timeline The latest timeline. Never null, but may be empty. - * @param reason The {@link TimelineChangeReason} responsible for this timeline change. - */ - default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} - - /** - * Called when playback transitions to a media item or starts repeating a media item according - * to the current {@link #getRepeatMode() repeat mode}. - * - *

    Note that this callback is also called when the playlist becomes non-empty or empty as a - * consequence of a playlist change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. - * @param reason The reason for the transition. - */ - default void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} - - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param trackGroups The available tracks. Never null, but may be of length zero. - * @param trackSelections The selected tracks. Never null, but may contain null elements. A - * concrete implementation may include null elements if it has a fixed number of renderer - * components, wishes to report a TrackSelection for each of them, and has one or more - * renderer components that is not assigned any selected tracks. - * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. - */ - @UnstableApi - @Deprecated - default void onTracksChanged( - TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} - - /** - * Called when the available or selected tracks change. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param tracksInfo The available tracks information. Never null, but may be of length zero. - */ - default void onTracksInfoChanged(TracksInfo tracksInfo) {} - - /** - * Called when the combined {@link MediaMetadata} changes. - * - *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} - * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track - * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in - * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from - * static or dynamic metadata. - * - *

    This method may be called multiple times in quick succession. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param mediaMetadata The combined {@link MediaMetadata}. - */ - default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - - /** Called when the playlist {@link MediaMetadata} changes. */ - default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} - - /** - * Called when the player starts or stops loading the source. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isLoading Whether the source is currently being loaded. - */ - default void onIsLoadingChanged(boolean isLoading) {} - - /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ - @Deprecated - default void onLoadingChanged(boolean isLoading) {} - - /** - * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one - * {@link Command}. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param availableCommands The available {@link Commands}. - */ - default void onAvailableCommandsChanged(Commands availableCommands) {} - - /** - * Called when the value returned from {@link #getTrackSelectionParameters()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param parameters The new {@link TrackSelectionParameters}. - */ - default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} - - /** - * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link - * #onPlayWhenReadyChanged(boolean, int)} instead. - */ - @Deprecated - default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} - - /** - * Called when the value returned from {@link #getPlaybackState()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackState The new playback {@link State state}. - */ - default void onPlaybackStateChanged(@State int playbackState) {} - - /** - * Called when the value returned from {@link #getPlayWhenReady()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playWhenReady Whether playback will proceed when ready. - * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. - */ - default void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} - - /** - * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. - */ - default void onPlaybackSuppressionReasonChanged( - @PlaybackSuppressionReason int playbackSuppressionReason) {} - - /** - * Called when the value of {@link #isPlaying()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param isPlaying Whether the player is playing. - */ - default void onIsPlayingChanged(boolean isPlaying) {} - - /** - * Called when the value of {@link #getRepeatMode()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param repeatMode The {@link RepeatMode} used for playback. - */ - default void onRepeatModeChanged(@RepeatMode int repeatMode) {} - - /** - * Called when the value of {@link #getShuffleModeEnabled()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. - */ - default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} - - /** - * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} - * immediately after this method is called. The player instance can still be used, and {@link - * #release()} must still be called on the player should it no longer be required. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The error. - */ - default void onPlayerError(PlaybackException error) {} - - /** - * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} - * to this method in order to include more information about the error. - * - * @param error The new error, or null if the error is being cleared. - */ - default void onPlayerErrorChanged(@Nullable PlaybackException error) {} - - /** - * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. - */ - @Deprecated - default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} - - /** - * Called when a position discontinuity occurs. - * - *

    A position discontinuity occurs when the playing period changes, the playback position - * jumps within the period currently being played, or when the playing period has been skipped - * or removed. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param oldPosition The position before the discontinuity. - * @param newPosition The position after the discontinuity. - * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. - */ - default void onPositionDiscontinuity( - PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} - - /** - * Called when the current playback parameters change. The playback parameters may change due to - * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change - * them (for example, if audio playback switches to passthrough or offload mode, where speed - * adjustment is no longer possible). - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param playbackParameters The playback parameters. - */ - default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} - - /** - * Called when the value of {@link #getSeekBackIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. - */ - default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} - - /** - * Called when the value of {@link #getSeekForwardIncrement()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. - */ - default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} - - /** - * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. - * - *

    {@link #onEvents(Player, Events)} will also be called to report this event along with - * other events that happen in the same {@link Looper} message queue iteration. - * - * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} - * seeks to the previous position, in milliseconds. - */ - default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} - - /** - * @deprecated Seeks are processed without delay. Listen to {@link - * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link - * #DISCONTINUITY_REASON_SEEK} instead. - */ - @Deprecated - default void onSeekProcessed() {} - - /** - * Called when one or more player states changed. - * - *

    State changes and events that happen within one {@link Looper} message queue iteration are - * reported together and only after all individual callbacks were triggered. - * - *

    Only state changes represented by {@link Event events} are reported through this method. - * - *

    Listeners should prefer this method over individual callbacks in the following cases: - * - *

      - *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for - * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, - * int)}). - *
    • They need access to the {@link Player} object to trigger further events (e.g. to call - * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). - *
    • They intend to use multiple state values together or in combination with {@link Player} - * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code - * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from - * within this method. - *
    • They are interested in events that logically happened together (e.g {@link - * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link - * #onMediaItemTransition(MediaItem, int)}). - *
    - * - * @param player The {@link Player} whose state changed. Use the getters to obtain the latest - * states. - * @param events The {@link Events} that happened in this iteration, indicating which player - * states changed. - */ - default void onEvents(Player player, Events events) {} - } - /** A set of {@link Event events}. */ final class Events { @@ -939,62 +602,332 @@ public interface Player { * *

    All methods have no-op default implementations to allow selective overrides. */ - interface Listener extends EventListener { + interface Listener { - @Override + /** + * Called when one or more player states changed. + * + *

    State changes and events that happen within one {@link Looper} message queue iteration are + * reported together and only after all individual callbacks were triggered. + * + *

    Only state changes represented by {@link Event events} are reported through this method. + * + *

    Listeners should prefer this method over individual callbacks in the following cases: + * + *

      + *
    • They intend to trigger the same logic for multiple events (e.g. when updating a UI for + * both {@link #onPlaybackStateChanged(int)} and {@link #onPlayWhenReadyChanged(boolean, + * int)}). + *
    • They need access to the {@link Player} object to trigger further events (e.g. to call + * {@link Player#seekTo(long)} after a {@link #onMediaItemTransition(MediaItem, int)}). + *
    • They intend to use multiple state values together or in combination with {@link Player} + * getter methods. For example using {@link #getCurrentMediaItemIndex()} with the {@code + * timeline} provided in {@link #onTimelineChanged(Timeline, int)} is only safe from + * within this method. + *
    • They are interested in events that logically happened together (e.g {@link + * #onPlaybackStateChanged(int)} to {@link #STATE_BUFFERING} because of {@link + * #onMediaItemTransition(MediaItem, int)}). + *
    + * + * @param player The {@link Player} whose state changed. Use the getters to obtain the latest + * states. + * @param events The {@link Events} that happened in this iteration, indicating which player + * states changed. + */ + default void onEvents(Player player, Events events) {} + + /** + * Called when the timeline has been refreshed. + * + *

    Note that the current {@link MediaItem} or playback position may change as a result of a + * timeline change. If playback can't continue smoothly because of this timeline change, a + * separate {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} callback will be + * triggered. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param timeline The latest timeline. Never null, but may be empty. + * @param reason The {@link TimelineChangeReason} responsible for this timeline change. + */ default void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {} - @Override + /** + * Called when playback transitions to a media item or starts repeating a media item according + * to the current {@link #getRepeatMode() repeat mode}. + * + *

    Note that this callback is also called when the playlist becomes non-empty or empty as a + * consequence of a playlist change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaItem The {@link MediaItem}. May be null if the playlist becomes empty. + * @param reason The reason for the transition. + */ default void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} - @Override + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param trackGroups The available tracks. Never null, but may be of length zero. + * @param trackSelections The selected tracks. Never null, but may contain null elements. A + * concrete implementation may include null elements if it has a fixed number of renderer + * components, wishes to report a TrackSelection for each of them, and has one or more + * renderer components that is not assigned any selected tracks. + * @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead. + */ + @UnstableApi + @Deprecated + default void onTracksChanged( + TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} + + /** + * Called when the available or selected tracks change. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param tracksInfo The available tracks information. Never null, but may be of length zero. + */ default void onTracksInfoChanged(TracksInfo tracksInfo) {} - @Override + /** + * Called when the combined {@link MediaMetadata} changes. + * + *

    The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} + * and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track + * selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in + * the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from + * static or dynamic metadata. + * + *

    This method may be called multiple times in quick succession. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param mediaMetadata The combined {@link MediaMetadata}. + */ + default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} + + /** Called when the playlist {@link MediaMetadata} changes. */ + default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} + + /** + * Called when the player starts or stops loading the source. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isLoading Whether the source is currently being loaded. + */ default void onIsLoadingChanged(boolean isLoading) {} - @Override + /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ + @Deprecated + @UnstableApi + default void onLoadingChanged(boolean isLoading) {} + + /** + * Called when the value returned from {@link #isCommandAvailable(int)} changes for at least one + * {@link Command}. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param availableCommands The available {@link Commands}. + */ default void onAvailableCommandsChanged(Commands availableCommands) {} - @Override + /** + * Called when the value returned from {@link #getTrackSelectionParameters()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param parameters The new {@link TrackSelectionParameters}. + */ + default void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) {} + + /** + * @deprecated Use {@link #onPlaybackStateChanged(int)} and {@link + * #onPlayWhenReadyChanged(boolean, int)} instead. + */ + @Deprecated + @UnstableApi + default void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) {} + + /** + * Called when the value returned from {@link #getPlaybackState()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackState The new playback {@link State state}. + */ default void onPlaybackStateChanged(@State int playbackState) {} - @Override + /** + * Called when the value returned from {@link #getPlayWhenReady()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playWhenReady Whether playback will proceed when ready. + * @param reason The {@link PlayWhenReadyChangeReason reason} for the change. + */ default void onPlayWhenReadyChanged( boolean playWhenReady, @PlayWhenReadyChangeReason int reason) {} - @Override + /** + * Called when the value returned from {@link #getPlaybackSuppressionReason()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackSuppressionReason The current {@link PlaybackSuppressionReason}. + */ default void onPlaybackSuppressionReasonChanged( @PlaybackSuppressionReason int playbackSuppressionReason) {} - @Override + /** + * Called when the value of {@link #isPlaying()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param isPlaying Whether the player is playing. + */ default void onIsPlayingChanged(boolean isPlaying) {} - @Override + /** + * Called when the value of {@link #getRepeatMode()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param repeatMode The {@link RepeatMode} used for playback. + */ default void onRepeatModeChanged(@RepeatMode int repeatMode) {} - @Override + /** + * Called when the value of {@link #getShuffleModeEnabled()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param shuffleModeEnabled Whether shuffling of {@link MediaItem media items} is enabled. + */ default void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {} - @Override + /** + * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} + * immediately after this method is called. The player instance can still be used, and {@link + * #release()} must still be called on the player should it no longer be required. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The error. + */ default void onPlayerError(PlaybackException error) {} - @Override + /** + * Called when the {@link PlaybackException} returned by {@link #getPlayerError()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * + * @param error The new error, or null if the error is being cleared. + */ default void onPlayerErrorChanged(@Nullable PlaybackException error) {} - @Override + /** + * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. + */ + @Deprecated + @UnstableApi + default void onPositionDiscontinuity(@DiscontinuityReason int reason) {} + + /** + * Called when a position discontinuity occurs. + * + *

    A position discontinuity occurs when the playing period changes, the playback position + * jumps within the period currently being played, or when the playing period has been skipped + * or removed. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param oldPosition The position before the discontinuity. + * @param newPosition The position after the discontinuity. + * @param reason The {@link DiscontinuityReason} responsible for the discontinuity. + */ default void onPositionDiscontinuity( PositionInfo oldPosition, PositionInfo newPosition, @DiscontinuityReason int reason) {} - @Override + /** + * Called when the current playback parameters change. The playback parameters may change due to + * a call to {@link #setPlaybackParameters(PlaybackParameters)}, or the player itself may change + * them (for example, if audio playback switches to passthrough or offload mode, where speed + * adjustment is no longer possible). + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param playbackParameters The playback parameters. + */ default void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {} - @Override + /** + * Called when the value of {@link #getSeekBackIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekBackIncrementMs The {@link #seekBack()} increment, in milliseconds. + */ + default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} + + /** + * Called when the value of {@link #getSeekForwardIncrement()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param seekForwardIncrementMs The {@link #seekForward()} increment, in milliseconds. + */ default void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {} - @Override - default void onSeekBackIncrementChanged(long seekBackIncrementMs) {} + /** + * Called when the value of {@link #getMaxSeekToPreviousPosition()} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param maxSeekToPreviousPositionMs The maximum position for which {@link #seekToPrevious()} + * seeks to the previous position, in milliseconds. + */ + default void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {} + + /** + * @deprecated Seeks are processed without delay. Listen to {@link + * #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} with reason {@link + * #DISCONTINUITY_REASON_SEEK} instead. + */ + @Deprecated + @UnstableApi + default void onSeekProcessed() {} /** * Called when the audio session ID changes. @@ -1031,9 +964,6 @@ public interface Player { /** Called when the device volume or mute state changes. */ default void onDeviceVolumeChanged(int volume, boolean muted) {} - @Override - default void onEvents(Player player, Events events) {} - /** * Called each time there's a change in the size of the video being rendered. * @@ -1075,12 +1005,6 @@ public interface Player { */ @UnstableApi default void onMetadata(Metadata metadata) {} - - @Override - default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - - @Override - default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} } /** diff --git a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java index 2973bbb2c2..22b30a8b33 100644 --- a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java @@ -115,21 +115,6 @@ public class ForwardingPlayerTest { } } - @Test - @SuppressWarnings("deprecation") // Testing backwards compatibility with deprecated type. - public void forwardingEventListener_overridesAllEventListenerMethods() throws Exception { - // Check with reflection that ForwardingListener overrides all Listener methods. - Class forwardingListenerClass = getInnerClass("ForwardingEventListener"); - List methods = getPublicMethods(Player.EventListener.class); - for (int i = 0; i < methods.size(); i++) { - Method method = methods.get(i); - assertThat( - forwardingListenerClass.getDeclaredMethod( - method.getName(), method.getParameterTypes())) - .isNotNull(); - } - } - @Test public void forwardingListener_overridesAllListenerMethods() throws Exception { // Check with reflection that ForwardingListener overrides all Listener methods. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index ab755cbc26..36d21c7ad8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -1081,32 +1081,6 @@ public interface ExoPlayer extends Player { @Deprecated DeviceComponent getDeviceComponent(); - /** - * Registers a listener to receive events from the player. - * - *

    The listener's methods will be called on the thread associated with {@link - * #getApplicationLooper()}. - * - * @param listener The listener to register. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @UnstableApi - @Deprecated - @SuppressWarnings("deprecation") - void addListener(EventListener listener); - - /** - * Unregister a listener registered through {@link #addListener(EventListener)}. The listener will - * no longer receive events from the player. - * - * @param listener The listener to unregister. - * @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead. - */ - @UnstableApi - @Deprecated - @SuppressWarnings("deprecation") - void removeListener(EventListener listener); - /** * Adds a listener to receive audio offload events. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index ab7c22607d..621ad87af9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -109,7 +109,6 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.Player.Commands; import androidx.media3.common.Player.DiscontinuityReason; -import androidx.media3.common.Player.EventListener; import androidx.media3.common.Player.Events; import androidx.media3.common.Player.Listener; import androidx.media3.common.Player.PlayWhenReadyChangeReason; @@ -191,9 +190,9 @@ import java.util.concurrent.TimeoutException; private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener; private final ExoPlayerImplInternal internalPlayer; - @SuppressWarnings("deprecation") // TODO(b/187152483): Merge with non-deprecated listeners. - private final ListenerSet eventListeners; - + private final ListenerSet listeners; + // TODO(b/187152483): Remove this once all events are dispatched via ListenerSet. + private final CopyOnWriteArraySet listenerArraySet; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -208,7 +207,6 @@ import java.util.concurrent.TimeoutException; private final Clock clock; private final ComponentListener componentListener; private final FrameMetadataListener frameMetadataListener; - private final CopyOnWriteArraySet listeners; private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; private final StreamVolumeManager streamVolumeManager; @@ -294,7 +292,6 @@ import java.util.concurrent.TimeoutException; detachSurfaceTimeoutMs = builder.detachSurfaceTimeoutMs; componentListener = new ComponentListener(); frameMetadataListener = new FrameMetadataListener(); - listeners = new CopyOnWriteArraySet<>(); Handler eventHandler = new Handler(builder.looper); renderers = builder @@ -318,11 +315,12 @@ import java.util.concurrent.TimeoutException; this.applicationLooper = builder.looper; this.clock = builder.clock; this.wrappingPlayer = wrappingPlayer; - eventListeners = + listeners = new ListenerSet<>( applicationLooper, clock, (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); + listenerArraySet = new CopyOnWriteArraySet<>(); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -410,9 +408,9 @@ import java.util.concurrent.TimeoutException; currentCues = ImmutableList.of(); throwsWhenUsingWrongThread = true; - addEventListener(analyticsCollector); + listeners.add(analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - addEventListener(componentListener); + listeners.add(componentListener); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); @@ -488,18 +486,6 @@ import java.util.concurrent.TimeoutException; return clock; } - @SuppressWarnings("deprecation") // Register deprecated EventListener. - public void addEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.add(eventListener); - } - - @SuppressWarnings("deprecation") // Deregister deprecated EventListener. - public void removeEventListener(Player.EventListener eventListener) { - // Don't verify application thread. We allow calls to this method from any thread. - eventListeners.remove(eventListener); - } - public void addAudioOffloadListener(AudioOffloadListener listener) { // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); @@ -805,10 +791,10 @@ import java.util.concurrent.TimeoutException; if (this.repeatMode != repeatMode) { this.repeatMode = repeatMode; internalPlayer.setRepeatMode(repeatMode); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(repeatMode)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } @@ -822,11 +808,11 @@ import java.util.concurrent.TimeoutException; if (this.shuffleModeEnabled != shuffleModeEnabled) { this.shuffleModeEnabled = shuffleModeEnabled; internalPlayer.setShuffleModeEnabled(shuffleModeEnabled); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, listener -> listener.onShuffleModeEnabledChanged(shuffleModeEnabled)); updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); } } @@ -1028,7 +1014,7 @@ import java.util.concurrent.TimeoutException; audioFocusManager.release(); if (!internalPlayer.release()) { // One of the renderers timed out releasing its resources. - eventListeners.sendEvent( + listeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( @@ -1036,7 +1022,7 @@ import java.util.concurrent.TimeoutException; new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE), PlaybackException.ERROR_CODE_TIMEOUT))); } - eventListeners.release(); + listeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); bandwidthMeter.removeEventListener(analyticsCollector); playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); @@ -1216,7 +1202,7 @@ import java.util.concurrent.TimeoutException; return; } trackSelector.setParameters(parameters); - eventListeners.queueEvent( + listeners.queueEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } @@ -1236,7 +1222,7 @@ import java.util.concurrent.TimeoutException; return; } mediaMetadata = newMediaMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } @@ -1252,7 +1238,7 @@ import java.util.concurrent.TimeoutException; return; } this.playlistMetadata = playlistMetadata; - eventListeners.sendEvent( + listeners.sendEvent( EVENT_PLAYLIST_METADATA_CHANGED, listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } @@ -1409,7 +1395,7 @@ import java.util.concurrent.TimeoutException; streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); analyticsCollector.onAudioAttributesChanged(audioAttributes); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onAudioAttributesChanged(audioAttributes); } } @@ -1447,7 +1433,7 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); analyticsCollector.onAudioSessionIdChanged(audioSessionId); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onAudioSessionIdChanged(audioSessionId); } } @@ -1475,7 +1461,7 @@ import java.util.concurrent.TimeoutException; sendVolumeToRenderers(); analyticsCollector.onVolumeChanged(volume); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onVolumeChanged(volume); } } @@ -1607,14 +1593,14 @@ import java.util.concurrent.TimeoutException; // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.add(listener); - addEventListener(listener); + listenerArraySet.add(listener); } public void removeListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.remove(listener); - removeEventListener(listener); + listenerArraySet.remove(listener); } public void setHandleWakeLock(boolean handleWakeLock) { @@ -1815,7 +1801,7 @@ import java.util.concurrent.TimeoutException; mediaMetadata = newMediaMetadata; if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason)); } @@ -1824,7 +1810,7 @@ import java.util.concurrent.TimeoutException; getPreviousPositionInfo( positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex); PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_POSITION_DISCONTINUITY, listener -> { listener.onPositionDiscontinuity(positionDiscontinuityReason); @@ -1834,16 +1820,16 @@ import java.util.concurrent.TimeoutException; } if (mediaItemTransitioned) { @Nullable final MediaItem finalMediaItem = mediaItem; - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError)); if (newPlaybackInfo.playbackError != null) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError)); } @@ -1852,21 +1838,21 @@ import java.util.concurrent.TimeoutException; trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info); TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo)); } if (metadataChanged) { final MediaMetadata finalMediaMetadata = mediaMetadata; - eventListeners.queueEvent( + listeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { listener.onLoadingChanged(newPlaybackInfo.isLoading); @@ -1875,19 +1861,19 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged( @@ -1895,27 +1881,27 @@ import java.util.concurrent.TimeoutException; } if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged( newPlaybackInfo.playbackSuppressionReason)); } if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo))); } if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters)); } if (seekProcessed) { - eventListeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); + listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed); } updateAvailableCommands(); - eventListeners.flushEvents(); + listeners.flushEvents(); if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) { for (AudioOffloadListener listener : audioOffloadListeners) { @@ -2072,7 +2058,7 @@ import java.util.concurrent.TimeoutException; Commands previousAvailableCommands = availableCommands; availableCommands = Util.getAvailableCommands(wrappingPlayer, permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { - eventListeners.queueEvent( + listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, listener -> listener.onAvailableCommandsChanged(availableCommands)); } @@ -2492,7 +2478,7 @@ import java.util.concurrent.TimeoutException; surfaceHeight = height; analyticsCollector.onSurfaceSizeChanged(width, height); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onSurfaceSizeChanged(width, height); } } @@ -2506,7 +2492,7 @@ import java.util.concurrent.TimeoutException; private void notifySkipSilenceEnabledChanged() { analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); } } @@ -2648,10 +2634,9 @@ import java.util.concurrent.TimeoutException; } } - // TODO(b/204189802): Remove self-listening to deprecated EventListener. - @SuppressWarnings("deprecation") private final class ComponentListener - implements VideoRendererEventListener, + implements Player.Listener, + VideoRendererEventListener, AudioRendererEventListener, TextOutput, MetadataOutput, @@ -2661,7 +2646,6 @@ import java.util.concurrent.TimeoutException; AudioFocusManager.PlayerControl, AudioBecomingNoisyManager.EventListener, StreamVolumeManager.Listener, - Player.EventListener, AudioOffloadListener { // VideoRendererEventListener implementation @@ -2696,7 +2680,7 @@ import java.util.concurrent.TimeoutException; ExoPlayerImpl.this.videoSize = videoSize; analyticsCollector.onVideoSizeChanged(videoSize); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onVideoSizeChanged(videoSize); } } @@ -2706,7 +2690,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); if (videoOutput == output) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onRenderedFirstFrame(); } } @@ -2803,7 +2787,7 @@ import java.util.concurrent.TimeoutException; public void onCues(List cues) { currentCues = cues; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listeners) { + for (Listener listeners : listenerArraySet) { listeners.onCues(cues); } } @@ -2815,7 +2799,7 @@ import java.util.concurrent.TimeoutException; analyticsCollector.onMetadata(metadata); ExoPlayerImpl.this.onMetadata(metadata); // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onMetadata(metadata); } } @@ -2911,7 +2895,7 @@ import java.util.concurrent.TimeoutException; if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); } } @@ -2920,12 +2904,12 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listeners) { + for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); } } - // Player.EventListener implementation. + // Player.Listener implementation. @Override public void onIsLoadingChanged(boolean isLoading) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 6c40e2e8e2..3bd76fc6e6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -33,7 +33,6 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; import androidx.media3.common.PlaybackParameters; -import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroupArray; @@ -672,26 +671,12 @@ public class SimpleExoPlayer extends BasePlayer player.addListener(listener); } - @Deprecated - @Override - public void addListener(Player.EventListener listener) { - blockUntilConstructorFinished(); - player.addEventListener(listener); - } - @Override public void removeListener(Listener listener) { blockUntilConstructorFinished(); player.removeListener(listener); } - @Deprecated - @Override - public void removeListener(Player.EventListener listener) { - blockUntilConstructorFinished(); - player.removeEventListener(listener); - } - @Override public @State int getPlaybackState() { blockUntilConstructorFinished(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index 0f4cd5bdae..baa9047330 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -20,7 +20,6 @@ import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.Format; -import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; @@ -80,16 +79,6 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void addListener(Player.EventListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeListener(Player.EventListener listener) { - throw new UnsupportedOperationException(); - } - @Override public void addAudioOffloadListener(AudioOffloadListener listener) { throw new UnsupportedOperationException(); From c832bf495e6e86bea0e15ae7fada45e19a219dab Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 09:59:43 +0000 Subject: [PATCH 169/251] Add missing calls to AnalyticsCollector PiperOrigin-RevId: 427133919 --- .../src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 621ad87af9..5616e5f773 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -2786,6 +2786,7 @@ import java.util.concurrent.TimeoutException; @Override public void onCues(List cues) { currentCues = cues; + analyticsCollector.onCues(cues); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listeners : listenerArraySet) { listeners.onCues(cues); @@ -2894,6 +2895,7 @@ import java.util.concurrent.TimeoutException; DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; + analyticsCollector.onDeviceInfoChanged(deviceInfo); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); @@ -2903,6 +2905,7 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { + analyticsCollector.onDeviceVolumeChanged(streamVolume, streamMuted); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); From 3ab5437c853ba4694e763d4dbf6c34fac52fd243 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 10:55:32 +0000 Subject: [PATCH 170/251] Fix delayed discontinuity for failed ad insertions. We have logic to not immediately interrupt playback when an ad group fails to load and instead let the current content play and transition at the point where the ad group should have been. This logic was broken by https://github.com/androidx/media/commit/dcbdbe53417d6642f2be98c82ac941d34908bd49 because of one of the conditions used MediaPeriodId.adGroupIndex, which is always -1 for content ids. It still worked for the last ad group because the next ad group index was C.INDEX_UNSET. Fix the issue and amend the test that was meant to catch this to test the ad failures for the last ad and previous ads. Also fix the PositionInfo reported in such a case, which was also wrong. Issue: google/ExoPlayer#9929 #minor-release PiperOrigin-RevId: 427143223 --- .../media3/exoplayer/ExoPlayerImpl.java | 13 +- .../exoplayer/ExoPlayerImplInternal.java | 2 +- .../media3/exoplayer/ExoPlayerTest.java | 179 ++++++++++++++++-- 3 files changed, 166 insertions(+), 28 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 5616e5f773..f69e93ba53 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -1937,8 +1937,6 @@ import java.util.concurrent.TimeoutException; long oldPositionUs; long oldContentPositionUs; if (positionDiscontinuityReason == DISCONTINUITY_REASON_AUTO_TRANSITION) { - oldPositionUs = oldPeriod.positionInWindowUs + oldPeriod.durationUs; - oldContentPositionUs = oldPositionUs; if (oldPlaybackInfo.periodId.isAd()) { // The old position is the end of the previous ad. oldPositionUs = @@ -1946,12 +1944,15 @@ import java.util.concurrent.TimeoutException; oldPlaybackInfo.periodId.adGroupIndex, oldPlaybackInfo.periodId.adIndexInAdGroup); // The ad cue point is stored in the old requested content position. oldContentPositionUs = getRequestedContentPositionUs(oldPlaybackInfo); - } else if (oldPlaybackInfo.periodId.nextAdGroupIndex != C.INDEX_UNSET - && playbackInfo.periodId.isAd()) { - // If it's a transition from content to an ad in the same window, the old position is the - // ad cue point that is the same as current content position. + } else if (oldPlaybackInfo.periodId.nextAdGroupIndex != C.INDEX_UNSET) { + // The old position is the end of a clipped content before an ad group. Use the exact ad + // cue point as the transition position. oldPositionUs = getRequestedContentPositionUs(playbackInfo); oldContentPositionUs = oldPositionUs; + } else { + // The old position is the end of a Timeline period. Use the exact duration. + oldPositionUs = oldPeriod.positionInWindowUs + oldPeriod.durationUs; + oldContentPositionUs = oldPositionUs; } } else if (oldPlaybackInfo.periodId.isAd()) { oldPositionUs = oldPlaybackInfo.positionUs; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 1bbed758c9..cc32805042 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -2672,7 +2672,7 @@ import java.util.concurrent.atomic.AtomicBoolean; boolean earliestCuePointIsUnchangedOrLater = periodIdWithAds.nextAdGroupIndex == C.INDEX_UNSET || (oldPeriodId.nextAdGroupIndex != C.INDEX_UNSET - && periodIdWithAds.adGroupIndex >= oldPeriodId.nextAdGroupIndex); + && periodIdWithAds.nextAdGroupIndex >= oldPeriodId.nextAdGroupIndex); // Drop update if we keep playing the same content (MediaPeriod.periodUid are identical) and // the only change is that MediaPeriodId.nextAdGroupIndex increased. This postpones a potential // discontinuity until we reach the former next ad group position. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 0961f4492d..5b7802c49f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -730,7 +730,7 @@ public final class ExoPlayerTest { } @Test - public void adGroupWithLoadErrorIsSkipped() throws Exception { + public void adGroupWithLoadError_noFurtherAdGroup_isSkipped() throws Exception { AdPlaybackState initialAdPlaybackState = FakeTimeline.createAdPlaybackState( /* adsPerAdGroup= */ 1, /* adGroupTimesUs...= */ @@ -745,11 +745,12 @@ public final class ExoPlayerTest { /* isDynamic= */ false, /* isLive= */ false, /* isPlaceholder= */ false, - /* durationUs= */ C.MICROS_PER_SECOND, + /* durationUs= */ 10 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 0, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, initialAdPlaybackState)); - AdPlaybackState errorAdPlaybackState = initialAdPlaybackState.withAdLoadError(0, 0); + AdPlaybackState errorAdPlaybackState = + initialAdPlaybackState.withAdLoadError(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); final Timeline adErrorTimeline = new FakeTimeline( new TimelineWindowDefinition( @@ -759,30 +760,166 @@ public final class ExoPlayerTest { /* isDynamic= */ false, /* isLive= */ false, /* isPlaceholder= */ false, - /* durationUs= */ C.MICROS_PER_SECOND, + /* durationUs= */ 10 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 0, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, errorAdPlaybackState)); final FakeMediaSource fakeMediaSource = new FakeMediaSource(fakeTimeline, ExoPlayerTestRunner.VIDEO_FORMAT); - ActionSchedule actionSchedule = - new ActionSchedule.Builder(TAG) - .pause() - .waitForPlaybackState(Player.STATE_READY) - .executeRunnable(() -> fakeMediaSource.setNewSourceInfo(adErrorTimeline)) - .waitForTimelineChanged( - adErrorTimeline, /* expectedReason */ Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) - .play() - .build(); - ExoPlayerTestRunner testRunner = - new ExoPlayerTestRunner.Builder(context) - .setMediaSources(fakeMediaSource) - .setActionSchedule(actionSchedule) - .build() - .start() - .blockUntilEnded(TIMEOUT_MS); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + Player.Listener mockListener = mock(Player.Listener.class); + player.addListener(mockListener); + + player.setMediaSource(fakeMediaSource); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + fakeMediaSource.setNewSourceInfo(adErrorTimeline); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + Timeline.Window window = + player.getCurrentTimeline().getWindow(/* windowIndex= */ 0, new Timeline.Window()); + Timeline.Period period = + player + .getCurrentTimeline() + .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); + player.release(); + // There is still one discontinuity from content to content for the failed ad insertion. - testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + PositionInfo positionInfo = + new PositionInfo( + window.uid, + /* mediaItemIndex= */ 0, + window.mediaItem, + period.uid, + /* periodIndex= */ 0, + /* positionMs= */ 5_000, + /* contentPositionMs= */ 5_000, + /* adGroupIndex= */ C.INDEX_UNSET, + /* adIndexInAdGroup= */ C.INDEX_UNSET); + verify(mockListener) + .onPositionDiscontinuity( + positionInfo, positionInfo, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + } + + @Test + public void adGroupWithLoadError_withFurtherAdGroup_isSkipped() throws Exception { + AdPlaybackState initialAdPlaybackState = + FakeTimeline.createAdPlaybackState( + /* adsPerAdGroup= */ 1, /* adGroupTimesUs...= */ + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + 5 * C.MICROS_PER_SECOND, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + 8 * C.MICROS_PER_SECOND); + Timeline fakeTimeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ 0, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* isLive= */ false, + /* isPlaceholder= */ false, + /* durationUs= */ 10 * C.MICROS_PER_SECOND, + /* defaultPositionUs= */ 0, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + initialAdPlaybackState)); + AdPlaybackState errorAdPlaybackState = + initialAdPlaybackState.withAdLoadError(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + final Timeline adErrorTimeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ 0, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* isLive= */ false, + /* isPlaceholder= */ false, + /* durationUs= */ 10 * C.MICROS_PER_SECOND, + /* defaultPositionUs= */ 0, + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + errorAdPlaybackState)); + final FakeMediaSource fakeMediaSource = + new FakeMediaSource(fakeTimeline, ExoPlayerTestRunner.VIDEO_FORMAT); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + Player.Listener mockListener = mock(Player.Listener.class); + player.addListener(mockListener); + + player.setMediaSource(fakeMediaSource); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + fakeMediaSource.setNewSourceInfo(adErrorTimeline); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + Timeline.Window window = + player.getCurrentTimeline().getWindow(/* windowIndex= */ 0, new Timeline.Window()); + Timeline.Period period = + player + .getCurrentTimeline() + .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); + player.release(); + + // There is still one discontinuity from content to content for the failed ad insertion and the + // normal ad transition for the successful ad insertion. + PositionInfo positionInfoFailedAd = + new PositionInfo( + window.uid, + /* mediaItemIndex= */ 0, + window.mediaItem, + period.uid, + /* periodIndex= */ 0, + /* positionMs= */ 5_000, + /* contentPositionMs= */ 5_000, + /* adGroupIndex= */ C.INDEX_UNSET, + /* adIndexInAdGroup= */ C.INDEX_UNSET); + verify(mockListener) + .onPositionDiscontinuity( + positionInfoFailedAd, + positionInfoFailedAd, + Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + PositionInfo positionInfoContentAtSuccessfulAd = + new PositionInfo( + window.uid, + /* mediaItemIndex= */ 0, + window.mediaItem, + period.uid, + /* periodIndex= */ 0, + /* positionMs= */ 8_000, + /* contentPositionMs= */ 8_000, + /* adGroupIndex= */ C.INDEX_UNSET, + /* adIndexInAdGroup= */ C.INDEX_UNSET); + PositionInfo positionInfoSuccessfulAdStart = + new PositionInfo( + window.uid, + /* mediaItemIndex= */ 0, + window.mediaItem, + period.uid, + /* periodIndex= */ 0, + /* positionMs= */ 0, + /* contentPositionMs= */ 8_000, + /* adGroupIndex= */ 1, + /* adIndexInAdGroup= */ 0); + PositionInfo positionInfoSuccessfulAdEnd = + new PositionInfo( + window.uid, + /* mediaItemIndex= */ 0, + window.mediaItem, + period.uid, + /* periodIndex= */ 0, + /* positionMs= */ Util.usToMs( + period.getAdDurationUs(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0)), + /* contentPositionMs= */ 8_000, + /* adGroupIndex= */ 1, + /* adIndexInAdGroup= */ 0); + verify(mockListener) + .onPositionDiscontinuity( + positionInfoContentAtSuccessfulAd, + positionInfoSuccessfulAdStart, + Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + verify(mockListener) + .onPositionDiscontinuity( + positionInfoSuccessfulAdEnd, + positionInfoContentAtSuccessfulAd, + Player.DISCONTINUITY_REASON_AUTO_TRANSITION); } @Test From 9582a9f099e95347720c550801ba2d69aa973936 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 15:37:17 +0000 Subject: [PATCH 171/251] Remove self-listening from ExoPlayerImpl SimpleExoPlayer used to register a listener on ExoPlayerImpl for the old EventListener callbacks. Now both classes are merged, this is no longer needed and should be removed in favor of calling methods directly. #minor-release PiperOrigin-RevId: 427187875 --- .../media3/exoplayer/ExoPlayerImpl.java | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index f69e93ba53..a2ff81b124 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -410,7 +410,6 @@ import java.util.concurrent.TimeoutException; listeners.add(analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); - listeners.add(componentListener); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); @@ -1794,11 +1793,21 @@ import java.util.concurrent.TimeoutException; .buildUpon() .populateFromMetadata(newPlaybackInfo.staticMetadata) .build(); - newMediaMetadata = buildUpdatedMediaMetadata(); } boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata); mediaMetadata = newMediaMetadata; + boolean playWhenReadyChanged = + previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady; + boolean playbackStateChanged = + previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState; + if (playbackStateChanged || playWhenReadyChanged) { + updateWakeAndWifiLock(); + } + boolean isLoadingChanged = previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading; + if (isLoadingChanged) { + updatePriorityTaskManagerForIsLoadingChange(newPlaybackInfo.isLoading); + } if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { listeners.queueEvent( @@ -1851,7 +1860,7 @@ import java.util.concurrent.TimeoutException; EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); } - if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { + if (isLoadingChanged) { listeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, listener -> { @@ -1859,20 +1868,19 @@ import java.util.concurrent.TimeoutException; listener.onIsLoadingChanged(newPlaybackInfo.isLoading); }); } - if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState - || previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { + if (playbackStateChanged || playWhenReadyChanged) { listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onPlayerStateChanged( newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState)); } - if (previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState) { + if (playbackStateChanged) { listeners.queueEvent( Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState)); } - if (previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady) { + if (playWhenReadyChanged) { listeners.queueEvent( Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> @@ -2594,6 +2602,18 @@ import java.util.concurrent.TimeoutException; return keepSessionIdAudioTrack.getAudioSessionId(); } + private void updatePriorityTaskManagerForIsLoadingChange(boolean isLoading) { + if (priorityTaskManager != null) { + if (isLoading && !isPriorityTaskManagerRegistered) { + priorityTaskManager.add(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = true; + } else if (!isLoading && isPriorityTaskManagerRegistered) { + priorityTaskManager.remove(C.PRIORITY_PLAYBACK); + isPriorityTaskManagerRegistered = false; + } + } + } + private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { return new DeviceInfo( DeviceInfo.PLAYBACK_TYPE_LOCAL, @@ -2636,8 +2656,7 @@ import java.util.concurrent.TimeoutException; } private final class ComponentListener - implements Player.Listener, - VideoRendererEventListener, + implements VideoRendererEventListener, AudioRendererEventListener, TextOutput, MetadataOutput, @@ -2913,32 +2932,6 @@ import java.util.concurrent.TimeoutException; } } - // Player.Listener implementation. - - @Override - public void onIsLoadingChanged(boolean isLoading) { - if (priorityTaskManager != null) { - if (isLoading && !isPriorityTaskManagerRegistered) { - priorityTaskManager.add(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = true; - } else if (!isLoading && isPriorityTaskManagerRegistered) { - priorityTaskManager.remove(C.PRIORITY_PLAYBACK); - isPriorityTaskManagerRegistered = false; - } - } - } - - @Override - public void onPlaybackStateChanged(@State int playbackState) { - updateWakeAndWifiLock(); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { - updateWakeAndWifiLock(); - } - // Player.AudioOffloadListener implementation. @Override From a41bdbad315d7aabfd9de257a90379c72c904c4c Mon Sep 17 00:00:00 2001 From: claincly Date: Tue, 8 Feb 2022 15:56:26 +0000 Subject: [PATCH 172/251] Make Codec an interface and introduce DefaultCodec. PiperOrigin-RevId: 427191610 --- .../AudioTranscodingSamplePipeline.java | 6 +- .../androidx/media3/transformer/Codec.java | 376 ++++------------- .../media3/transformer/CodecFactoryUtil.java | 119 ------ .../media3/transformer/DefaultCodec.java | 383 ++++++++++++++++++ .../transformer/DefaultDecoderFactory.java | 13 +- .../transformer/DefaultEncoderFactory.java | 18 +- .../transformer/TransformationException.java | 17 +- .../VideoTranscodingSamplePipeline.java | 8 +- 8 files changed, 486 insertions(+), 454 deletions(-) delete mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/CodecFactoryUtil.java create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java index 0a101e52e6..7ca38a3aed 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/AudioTranscodingSamplePipeline.java @@ -150,7 +150,7 @@ import org.checkerframework.dataflow.qual.Pure; @Override public void releaseOutputBuffer() throws TransformationException { - encoder.releaseOutputBuffer(); + encoder.releaseOutputBuffer(/* render= */ false); } @Override @@ -188,7 +188,7 @@ import org.checkerframework.dataflow.qual.Pure; feedEncoder(decoderOutputBuffer); if (!decoderOutputBuffer.hasRemaining()) { - decoder.releaseOutputBuffer(); + decoder.releaseOutputBuffer(/* render= */ false); } return true; } @@ -243,7 +243,7 @@ import org.checkerframework.dataflow.qual.Pure; speedChangingAudioProcessor.queueInput(decoderOutputBuffer); if (!decoderOutputBuffer.hasRemaining()) { - decoder.releaseOutputBuffer(); + decoder.releaseOutputBuffer(/* render= */ false); } return true; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java index 53ed48bf18..9f821c9f10 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java @@ -16,12 +16,7 @@ package androidx.media3.transformer; -import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.Assertions.checkState; - -import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; -import android.media.MediaFormat; import android.view.Surface; import androidx.annotation.Nullable; import androidx.media3.common.C; @@ -29,24 +24,20 @@ import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; import androidx.media3.decoder.DecoderInputBuffer; -import com.google.common.collect.ImmutableList; import java.nio.ByteBuffer; import java.util.List; -import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * A wrapper around {@link MediaCodec}. + * Provides a layer of abstraction for interacting with decoders and encoders. * - *

    Provides a layer of abstraction for callers that need to interact with {@link MediaCodec}. - * This is done by simplifying the calls needed to queue and dequeue buffers, removing the need to - * track buffer indices and codec events. + *

    {@link DecoderInputBuffer DecoderInputBuffers} are used as both decoders' and encoders' input + * buffers. */ @UnstableApi -public final class Codec { +public interface Codec { /** A factory for {@link Codec decoder} instances. */ - public interface DecoderFactory { + interface DecoderFactory { /** A default {@code DecoderFactory} implementation. */ DecoderFactory DEFAULT = new DefaultDecoderFactory(); @@ -54,28 +45,28 @@ public final class Codec { /** * Returns a {@link Codec} for audio decoding. * - * @param format The {@link Format} (of the input data) used to determine the underlying {@link - * MediaCodec} and its configuration values. - * @return A configured and started decoder wrapper. - * @throws TransformationException If no suitable codec can be created. + * @param format The {@link Format} (of the input data) used to determine the underlying decoder + * and its configuration values. + * @return A {@link Codec} for audio decoding. + * @throws TransformationException If no suitable {@link Codec} can be created. */ Codec createForAudioDecoding(Format format) throws TransformationException; /** * Returns a {@link Codec} for video decoding. * - * @param format The {@link Format} (of the input data) used to determine the underlying {@link - * MediaCodec} and its configuration values. + * @param format The {@link Format} (of the input data) used to determine the underlying decoder + * and its configuration values. * @param outputSurface The {@link Surface} to which the decoder output is rendered. - * @return A configured and started decoder wrapper. - * @throws TransformationException If no suitable codec can be created. + * @return A {@link Codec} for video decoding. + * @throws TransformationException If no suitable {@link Codec} can be created. */ Codec createForVideoDecoding(Format format, Surface outputSurface) throws TransformationException; } /** A factory for {@link Codec encoder} instances. */ - public interface EncoderFactory { + interface EncoderFactory { /** A default {@code EncoderFactory} implementation. */ EncoderFactory DEFAULT = new DefaultEncoderFactory(); @@ -87,12 +78,12 @@ public final class Codec { * {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code * format} is not necessarily allowed. * - * @param format The {@link Format} (of the output data) used to determine the underlying {@link - * MediaCodec} and its configuration values. + * @param format The {@link Format} (of the output data) used to determine the underlying + * encoder and its configuration values. * @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME * types}. - * @return A configured and started encoder wrapper. - * @throws TransformationException If no suitable codec can be created. + * @return A {@link Codec} for audio encoding. + * @throws TransformationException If no suitable {@link Codec} can be created. */ Codec createForAudioEncoding(Format format, List allowedMimeTypes) throws TransformationException; @@ -104,334 +95,123 @@ public final class Codec { * {@code allowedMimeTypes}. The {@link Format#sampleMimeType sample MIME type} given in {@code * format} is not necessarily allowed. * - * @param format The {@link Format} (of the output data) used to determine the underlying {@link - * MediaCodec} and its configuration values. {@link Format#sampleMimeType}, {@link - * Format#width} and {@link Format#height} must be set to those of the desired output video - * format. {@link Format#rotationDegrees} should be 0. The video should always be in - * landscape orientation. + * @param format The {@link Format} (of the output data) used to determine the underlying + * encoder and its configuration values. {@link Format#sampleMimeType}, {@link Format#width} + * and {@link Format#height} must be set to those of the desired output video format. {@link + * Format#rotationDegrees} should be 0. The video should always be in landscape orientation. * @param allowedMimeTypes The non-empty list of allowed output sample {@link MimeTypes MIME * types}. - * @return A configured and started encoder wrapper. - * @throws TransformationException If no suitable codec can be created. + * @return A {@link Codec} for video encoding. + * @throws TransformationException If no suitable {@link Codec} can be created. */ Codec createForVideoEncoding(Format format, List allowedMimeTypes) throws TransformationException; } - // MediaCodec decoders always output 16 bit PCM, unless configured to output PCM float. - // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers. - private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT; - - private final BufferInfo outputBufferInfo; - private final MediaCodec mediaCodec; - private final Format configurationFormat; - @Nullable private final Surface inputSurface; - - private @MonotonicNonNull Format outputFormat; - @Nullable private ByteBuffer outputBuffer; - - private int inputBufferIndex; - private int outputBufferIndex; - private boolean inputStreamEnded; - private boolean outputStreamEnded; - /** - * Creates a {@code Codec} from a configured and started {@link MediaCodec}. - * - * @param mediaCodec The configured and started {@link MediaCodec}. - * @param configurationFormat See {@link #getConfigurationFormat()}. - * @param inputSurface The input {@link Surface} if the {@link MediaCodec} receives input from a - * surface. - */ - public Codec(MediaCodec mediaCodec, Format configurationFormat, @Nullable Surface inputSurface) { - this.mediaCodec = mediaCodec; - this.configurationFormat = configurationFormat; - this.inputSurface = inputSurface; - outputBufferInfo = new BufferInfo(); - inputBufferIndex = C.INDEX_UNSET; - outputBufferIndex = C.INDEX_UNSET; - } - - /** - * Returns the {@link Format} used for configuring the codec. + * Returns the {@link Format} used for configuring the {@code Codec}. * *

    The configuration {@link Format} is the input {@link Format} used by the {@link * DecoderFactory} or output {@link Format} used by the {@link EncoderFactory} for selecting and - * configuring the underlying {@link MediaCodec}. + * configuring the underlying decoder or encoder. */ - public Format getConfigurationFormat() { - return configurationFormat; - } + Format getConfigurationFormat(); - /** Returns the input {@link Surface}, or null if the input is not a surface. */ - @Nullable - public Surface getInputSurface() { - return inputSurface; - } + /** + * Returns the input {@link Surface} of an underlying video encoder. + * + *

    This method must only be called on video encoders because audio/video decoders and audio + * encoders don't use a {@link Surface} as input. + */ + Surface getInputSurface(); /** * Dequeues a writable input buffer, if available. * - * @param inputBuffer The buffer where the dequeued buffer data is stored. + *

    This method must not be called from video encoders because they must use {@link Surface + * surfaces} as inputs. + * + * @param inputBuffer The buffer where the dequeued buffer data is stored, at {@link + * DecoderInputBuffer#data inputBuffer.data}. * @return Whether an input buffer is ready to be used. - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ - @EnsuresNonNullIf(expression = "#1.data", result = true) - public boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) - throws TransformationException { - if (inputStreamEnded) { - return false; - } - if (inputBufferIndex < 0) { - try { - inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - if (inputBufferIndex < 0) { - return false; - } - try { - inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - inputBuffer.clear(); - } - checkNotNull(inputBuffer.data); - return true; - } + boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) throws TransformationException; /** - * Queues an input buffer to the decoder. No buffers may be queued after an {@link + * Queues an input buffer to the {@code Codec}. No buffers may be queued after {@link * DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued. * + *

    This method must not be called from video encoders because they must use {@link Surface + * surfaces} as inputs. + * * @param inputBuffer The {@link DecoderInputBuffer input buffer}. - * @throws IllegalStateException If called again after an {@link - * DecoderInputBuffer#isEndOfStream() end of stream} buffer has been queued. - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ - public void queueInputBuffer(DecoderInputBuffer inputBuffer) throws TransformationException { - checkState( - !inputStreamEnded, "Input buffer can not be queued after the input stream has ended."); - - int offset = 0; - int size = 0; - if (inputBuffer.data != null && inputBuffer.data.hasRemaining()) { - offset = inputBuffer.data.position(); - size = inputBuffer.data.remaining(); - } - int flags = 0; - if (inputBuffer.isEndOfStream()) { - inputStreamEnded = true; - flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; - } - try { - mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - inputBufferIndex = C.INDEX_UNSET; - inputBuffer.data = null; - } + void queueInputBuffer(DecoderInputBuffer inputBuffer) throws TransformationException; /** * Signals end-of-stream on input to a video encoder. * - *

    This method does not need to be called for audio/video decoders or audio encoders. For these - * the {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag should be set on the last input buffer - * {@link #queueInputBuffer(DecoderInputBuffer) queued}. + *

    This method must only be called on video encoders because they must use a {@link Surface} as + * input. For audio/video decoders or audio encoders, the {@link C#BUFFER_FLAG_END_OF_STREAM} flag + * should be set on the last input buffer {@link #queueInputBuffer(DecoderInputBuffer) queued}. * - * @throws IllegalStateException If the codec is not an encoder receiving input from a {@link - * Surface}. - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * @throws TransformationException If the underlying video encoder encounters a problem. */ - public void signalEndOfInputStream() throws TransformationException { - checkState(mediaCodec.getCodecInfo().isEncoder() && inputSurface != null); - try { - mediaCodec.signalEndOfInputStream(); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - } + void signalEndOfInputStream() throws TransformationException; /** - * Returns the current output format, if available. + * Returns the current output format, or {@code null} if unavailable. * - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ @Nullable - public Format getOutputFormat() throws TransformationException { - // The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now. - maybeDequeueOutputBuffer(/* setOutputBuffer= */ false); - return outputFormat; - } + Format getOutputFormat() throws TransformationException; /** - * Returns the current output {@link ByteBuffer}, if available. + * Returns the current output {@link ByteBuffer}, or {@code null} if unavailable. * - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + *

    This method must not be called on video decoders because they must output to a {@link + * Surface}. + * + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ @Nullable - public ByteBuffer getOutputBuffer() throws TransformationException { - return maybeDequeueOutputBuffer(/* setOutputBuffer= */ true) ? outputBuffer : null; - } + ByteBuffer getOutputBuffer() throws TransformationException; /** - * Returns the {@link BufferInfo} associated with the current output buffer, if available. + * Returns the {@link BufferInfo} associated with the current output buffer, or {@code null} if + * there is no output buffer available. * - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + *

    This method returns {@code null} if and only if {@link #getOutputBuffer()} returns null. + * + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ @Nullable - public BufferInfo getOutputBufferInfo() throws TransformationException { - return maybeDequeueOutputBuffer(/* setOutputBuffer= */ false) ? outputBufferInfo : null; - } + BufferInfo getOutputBufferInfo() throws TransformationException; /** * Releases the current output buffer. * - *

    This should be called after the buffer has been processed. The next output buffer will not - * be available until the previous has been released. - * - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. - */ - public void releaseOutputBuffer() throws TransformationException { - releaseOutputBuffer(/* render= */ false); - } - - /** - * Releases the current output buffer. If the {@link MediaCodec} was configured with an output - * surface, setting {@code render} to {@code true} will first send the buffer to the output - * surface. The surface will release the buffer back to the codec once it is no longer + *

    Only set {@code render} to {@code true} when the {@code Codec} is a video decoder. Setting + * {@code render} to {@code true} will first render the buffer to the output surface. In this + * case, the surface will release the buffer back to the {@code Codec} once it is no longer * used/displayed. * *

    This should be called after the buffer has been processed. The next output buffer will not - * be available until the previous has been released. + * be available until the current output buffer has been released. * - * @param render Whether the buffer needs to be sent to the output {@link Surface}. - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * @param render Whether the buffer needs to be rendered to the output {@link Surface}. + * @throws TransformationException If the underlying decoder or encoder encounters a problem. */ - public void releaseOutputBuffer(boolean render) throws TransformationException { - outputBuffer = null; - try { - mediaCodec.releaseOutputBuffer(outputBufferIndex, render); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - outputBufferIndex = C.INDEX_UNSET; - } - - /** Returns whether the codec output stream has ended, and no more data can be dequeued. */ - public boolean isEnded() { - return outputStreamEnded && outputBufferIndex == C.INDEX_UNSET; - } - - /** Releases the underlying codec. */ - public void release() { - outputBuffer = null; - if (inputSurface != null) { - inputSurface.release(); - } - mediaCodec.release(); - } + void releaseOutputBuffer(boolean render) throws TransformationException; /** - * Attempts to dequeue an output buffer if there is no output buffer pending. Does nothing - * otherwise. - * - * @param setOutputBuffer Whether to read the bytes of the dequeued output buffer and copy them - * into {@link #outputBuffer}. - * @return Whether there is an output buffer available. - * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + * Returns whether the {@code Codec}'s output stream has ended, and no more data can be dequeued. */ - private boolean maybeDequeueOutputBuffer(boolean setOutputBuffer) throws TransformationException { - if (outputBufferIndex >= 0) { - return true; - } - if (outputStreamEnded) { - return false; - } + boolean isEnded(); - try { - outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - if (outputBufferIndex < 0) { - if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - outputFormat = getFormat(mediaCodec.getOutputFormat()); - } - return false; - } - if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - outputStreamEnded = true; - if (outputBufferInfo.size == 0) { - releaseOutputBuffer(); - return false; - } - } - if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { - // Encountered a CSD buffer, skip it. - releaseOutputBuffer(); - return false; - } - - if (setOutputBuffer) { - try { - outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex)); - } catch (RuntimeException e) { - throw createTransformationException(e); - } - outputBuffer.position(outputBufferInfo.offset); - outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); - } - return true; - } - - private TransformationException createTransformationException(Exception cause) { - boolean isEncoder = mediaCodec.getCodecInfo().isEncoder(); - boolean isVideo = MimeTypes.isVideo(configurationFormat.sampleMimeType); - String componentName = (isVideo ? "Video" : "Audio") + (isEncoder ? "Encoder" : "Decoder"); - return TransformationException.createForCodec( - cause, - componentName, - configurationFormat, - mediaCodec.getName(), - isEncoder - ? TransformationException.ERROR_CODE_ENCODING_FAILED - : TransformationException.ERROR_CODE_DECODING_FAILED); - } - - private static Format getFormat(MediaFormat mediaFormat) { - ImmutableList.Builder csdBuffers = new ImmutableList.Builder<>(); - int csdIndex = 0; - while (true) { - @Nullable ByteBuffer csdByteBuffer = mediaFormat.getByteBuffer("csd-" + csdIndex); - if (csdByteBuffer == null) { - break; - } - byte[] csdBufferData = new byte[csdByteBuffer.remaining()]; - csdByteBuffer.get(csdBufferData); - csdBuffers.add(csdBufferData); - csdIndex++; - } - String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); - Format.Builder formatBuilder = - new Format.Builder() - .setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME)) - .setInitializationData(csdBuffers.build()); - if (MimeTypes.isVideo(mimeType)) { - formatBuilder - .setWidth(mediaFormat.getInteger(MediaFormat.KEY_WIDTH)) - .setHeight(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)); - } else if (MimeTypes.isAudio(mimeType)) { - // TODO(internal b/178685617): Only set the PCM encoding for audio/raw, once we have a way to - // simulate more realistic codec input/output formats in tests. - formatBuilder - .setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)) - .setSampleRate(mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)) - .setPcmEncoding(MEDIA_CODEC_PCM_ENCODING); - } - return formatBuilder.build(); - } + /** Releases the {@code Codec}. */ + void release(); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/CodecFactoryUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/CodecFactoryUtil.java deleted file mode 100644 index 87fffb66d6..0000000000 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/CodecFactoryUtil.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package androidx.media3.transformer; - -import android.media.MediaCodec; -import android.media.MediaFormat; -import android.view.Surface; -import androidx.annotation.Nullable; -import androidx.media3.common.Format; -import androidx.media3.common.util.TraceUtil; -import java.io.IOException; -import org.checkerframework.checker.nullness.qual.RequiresNonNull; - -/** Utility methods for {@link Codec}'s factory methods. */ -/* package */ final class CodecFactoryUtil { - /** Creates a {@link Codec}. */ - @RequiresNonNull("#1.sampleMimeType") - public static Codec createCodec( - Format format, - MediaFormat mediaFormat, - @Nullable String mediaCodecName, - boolean isVideo, - boolean isDecoder, - @Nullable Surface outputSurface) - throws TransformationException { - @Nullable MediaCodec mediaCodec = null; - @Nullable Surface inputSurface = null; - try { - mediaCodec = - mediaCodecName != null - ? MediaCodec.createByCodecName(mediaCodecName) - : isDecoder - ? MediaCodec.createDecoderByType(format.sampleMimeType) - : MediaCodec.createEncoderByType(format.sampleMimeType); - configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface); - if (isVideo && !isDecoder) { - inputSurface = mediaCodec.createInputSurface(); - } - startCodec(mediaCodec); - } catch (Exception e) { - if (inputSurface != null) { - inputSurface.release(); - } - if (mediaCodec != null) { - mediaCodecName = mediaCodec.getName(); - mediaCodec.release(); - } - throw createTransformationException(e, format, isVideo, isDecoder, mediaCodecName); - } - return new Codec(mediaCodec, format, inputSurface); - } - - /** Creates a {@link TransformationException}. */ - public static TransformationException createTransformationException( - Exception cause, - Format format, - boolean isVideo, - boolean isDecoder, - @Nullable String mediaCodecName) { - String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); - if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { - return TransformationException.createForCodec( - cause, - componentName, - format, - mediaCodecName, - isDecoder - ? TransformationException.ERROR_CODE_DECODER_INIT_FAILED - : TransformationException.ERROR_CODE_ENCODER_INIT_FAILED); - } - if (cause instanceof IllegalArgumentException) { - return TransformationException.createForCodec( - cause, - componentName, - format, - mediaCodecName, - isDecoder - ? TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED - : TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); - } - return TransformationException.createForUnexpected(cause); - } - - private static void configureCodec( - MediaCodec codec, - MediaFormat mediaFormat, - boolean isDecoder, - @Nullable Surface outputSurface) { - TraceUtil.beginSection("configureCodec"); - codec.configure( - mediaFormat, - outputSurface, - /* crypto= */ null, - isDecoder ? 0 : MediaCodec.CONFIGURE_FLAG_ENCODE); - TraceUtil.endSection(); - } - - private static void startCodec(MediaCodec codec) { - TraceUtil.beginSection("startCodec"); - codec.start(); - TraceUtil.endSection(); - } - - private CodecFactoryUtil() {} -} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java new file mode 100644 index 0000000000..3c1c8aea61 --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java @@ -0,0 +1,383 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.media3.transformer; + +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 android.media.MediaCodec; +import android.media.MediaCodec.BufferInfo; +import android.media.MediaFormat; +import android.view.Surface; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.TraceUtil; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.decoder.DecoderInputBuffer; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.ByteBuffer; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** A default {@link Codec} implementation that uses {@link MediaCodec}. */ +@UnstableApi +public final class DefaultCodec implements Codec { + // MediaCodec decoders always output 16 bit PCM, unless configured to output PCM float. + // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers. + private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT; + + private final BufferInfo outputBufferInfo; + private final Format configurationFormat; + private final MediaCodec mediaCodec; + @Nullable private final Surface inputSurface; + + private @MonotonicNonNull Format outputFormat; + @Nullable private ByteBuffer outputBuffer; + + private int inputBufferIndex; + private int outputBufferIndex; + private boolean inputStreamEnded; + private boolean outputStreamEnded; + + /** + * Creates a {@code DefaultCodec}. + * + * @param configurationFormat The {@link Format} to configure the {@code DefaultCodec}. See {@link + * #getConfigurationFormat()}. The {@link Format#sampleMimeType sampleMimeType} must not be + * {@code null}. + * @param mediaFormat The {@link MediaFormat} to configure the underlying {@link MediaCodec}. + * @param mediaCodecName The name of a specific {@link MediaCodec} to instantiate. If {@code + * null}, {@code DefaultCodec} uses {@link Format#sampleMimeType + * configurationFormat.sampleMimeType} to create the underlying {@link MediaCodec codec}. + * @param isDecoder Whether the {@code DefaultCodec} is intended as a decoder. + * @param outputSurface The output {@link Surface} if the {@link MediaCodec} outputs to a surface. + */ + public DefaultCodec( + Format configurationFormat, + MediaFormat mediaFormat, + @Nullable String mediaCodecName, + boolean isDecoder, + @Nullable Surface outputSurface) + throws TransformationException { + this.configurationFormat = configurationFormat; + outputBufferInfo = new BufferInfo(); + inputBufferIndex = C.INDEX_UNSET; + outputBufferIndex = C.INDEX_UNSET; + + String sampleMimeType = checkNotNull(configurationFormat.sampleMimeType); + boolean isVideo = MimeTypes.isVideo(sampleMimeType); + @Nullable MediaCodec mediaCodec = null; + @Nullable Surface inputSurface = null; + try { + mediaCodec = + mediaCodecName != null + ? MediaCodec.createByCodecName(mediaCodecName) + : isDecoder + ? MediaCodec.createDecoderByType(sampleMimeType) + : MediaCodec.createEncoderByType(sampleMimeType); + configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface); + if (isVideo && !isDecoder) { + inputSurface = mediaCodec.createInputSurface(); + } + startCodec(mediaCodec); + } catch (Exception e) { + if (inputSurface != null) { + inputSurface.release(); + } + if (mediaCodec != null) { + mediaCodecName = mediaCodec.getName(); + mediaCodec.release(); + } + + throw createInitializationTransformationException( + e, configurationFormat, isVideo, isDecoder, mediaCodecName); + } + this.mediaCodec = mediaCodec; + this.inputSurface = inputSurface; + } + + @Override + public Format getConfigurationFormat() { + return configurationFormat; + } + + @Override + public Surface getInputSurface() { + return checkStateNotNull(inputSurface); + } + + @Override + @EnsuresNonNullIf(expression = "#1.data", result = true) + public boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) + throws TransformationException { + if (inputStreamEnded) { + return false; + } + if (inputBufferIndex < 0) { + try { + inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + if (inputBufferIndex < 0) { + return false; + } + try { + inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + inputBuffer.clear(); + } + checkNotNull(inputBuffer.data); + return true; + } + + @Override + public void queueInputBuffer(DecoderInputBuffer inputBuffer) throws TransformationException { + checkState( + !inputStreamEnded, "Input buffer can not be queued after the input stream has ended."); + + int offset = 0; + int size = 0; + if (inputBuffer.data != null && inputBuffer.data.hasRemaining()) { + offset = inputBuffer.data.position(); + size = inputBuffer.data.remaining(); + } + int flags = 0; + if (inputBuffer.isEndOfStream()) { + inputStreamEnded = true; + flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; + } + try { + mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + inputBufferIndex = C.INDEX_UNSET; + inputBuffer.data = null; + } + + @Override + public void signalEndOfInputStream() throws TransformationException { + try { + mediaCodec.signalEndOfInputStream(); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + } + + @Override + @Nullable + public Format getOutputFormat() throws TransformationException { + // The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now. + maybeDequeueOutputBuffer(/* setOutputBuffer= */ false); + return outputFormat; + } + + @Override + @Nullable + public ByteBuffer getOutputBuffer() throws TransformationException { + return maybeDequeueOutputBuffer(/* setOutputBuffer= */ true) ? outputBuffer : null; + } + + @Override + @Nullable + public BufferInfo getOutputBufferInfo() throws TransformationException { + return maybeDequeueOutputBuffer(/* setOutputBuffer= */ false) ? outputBufferInfo : null; + } + + @Override + public void releaseOutputBuffer(boolean render) throws TransformationException { + outputBuffer = null; + try { + mediaCodec.releaseOutputBuffer(outputBufferIndex, render); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + outputBufferIndex = C.INDEX_UNSET; + } + + @Override + public boolean isEnded() { + return outputStreamEnded && outputBufferIndex == C.INDEX_UNSET; + } + + @Override + public void release() { + outputBuffer = null; + if (inputSurface != null) { + inputSurface.release(); + } + mediaCodec.release(); + } + + /** + * Attempts to dequeue an output buffer if there is no output buffer pending. Does nothing + * otherwise. + * + * @param setOutputBuffer Whether to read the bytes of the dequeued output buffer and copy them + * into {@link #outputBuffer}. + * @return Whether there is an output buffer available. + * @throws TransformationException If the underlying {@link MediaCodec} encounters a problem. + */ + private boolean maybeDequeueOutputBuffer(boolean setOutputBuffer) throws TransformationException { + if (outputBufferIndex >= 0) { + return true; + } + if (outputStreamEnded) { + return false; + } + + try { + outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + if (outputBufferIndex < 0) { + if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + outputFormat = getFormat(mediaCodec.getOutputFormat()); + } + return false; + } + if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + outputStreamEnded = true; + if (outputBufferInfo.size == 0) { + releaseOutputBuffer(/* render= */ false); + return false; + } + } + if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { + // Encountered a CSD buffer, skip it. + releaseOutputBuffer(/* render= */ false); + return false; + } + + if (setOutputBuffer) { + try { + outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex)); + } catch (RuntimeException e) { + throw createTransformationException(e); + } + outputBuffer.position(outputBufferInfo.offset); + outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); + } + return true; + } + + private TransformationException createTransformationException(Exception cause) { + boolean isDecoder = !mediaCodec.getCodecInfo().isEncoder(); + boolean isVideo = MimeTypes.isVideo(configurationFormat.sampleMimeType); + return TransformationException.createForCodec( + cause, + configurationFormat, + isVideo, + isDecoder, + mediaCodec.getName(), + isDecoder + ? TransformationException.ERROR_CODE_DECODING_FAILED + : TransformationException.ERROR_CODE_ENCODING_FAILED); + } + + private static TransformationException createInitializationTransformationException( + Exception cause, + Format format, + boolean isVideo, + boolean isDecoder, + @Nullable String mediaCodecName) { + if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { + return TransformationException.createForCodec( + cause, + format, + isVideo, + isDecoder, + mediaCodecName, + isDecoder + ? TransformationException.ERROR_CODE_DECODER_INIT_FAILED + : TransformationException.ERROR_CODE_ENCODER_INIT_FAILED); + } + if (cause instanceof IllegalArgumentException) { + return TransformationException.createForCodec( + cause, + format, + isVideo, + isDecoder, + mediaCodecName, + isDecoder + ? TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED + : TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); + } + return TransformationException.createForUnexpected(cause); + } + + private static Format getFormat(MediaFormat mediaFormat) { + ImmutableList.Builder csdBuffers = new ImmutableList.Builder<>(); + int csdIndex = 0; + while (true) { + @Nullable ByteBuffer csdByteBuffer = mediaFormat.getByteBuffer("csd-" + csdIndex); + if (csdByteBuffer == null) { + break; + } + byte[] csdBufferData = new byte[csdByteBuffer.remaining()]; + csdByteBuffer.get(csdBufferData); + csdBuffers.add(csdBufferData); + csdIndex++; + } + String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); + Format.Builder formatBuilder = + new Format.Builder() + .setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME)) + .setInitializationData(csdBuffers.build()); + if (MimeTypes.isVideo(mimeType)) { + formatBuilder + .setWidth(mediaFormat.getInteger(MediaFormat.KEY_WIDTH)) + .setHeight(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)); + } else if (MimeTypes.isAudio(mimeType)) { + // TODO(internal b/178685617): Only set the PCM encoding for audio/raw, once we have a way to + // simulate more realistic codec input/output formats in tests. + formatBuilder + .setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)) + .setSampleRate(mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)) + .setPcmEncoding(MEDIA_CODEC_PCM_ENCODING); + } + return formatBuilder.build(); + } + + private static void configureCodec( + MediaCodec codec, + MediaFormat mediaFormat, + boolean isDecoder, + @Nullable Surface outputSurface) { + TraceUtil.beginSection("configureCodec"); + codec.configure( + mediaFormat, + outputSurface, + /* crypto= */ null, + isDecoder ? 0 : MediaCodec.CONFIGURE_FLAG_ENCODE); + TraceUtil.endSection(); + } + + private static void startCodec(MediaCodec codec) { + TraceUtil.beginSection("startCodec"); + codec.start(); + TraceUtil.endSection(); + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java index b47a18d97c..5e951a6847 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java @@ -18,7 +18,6 @@ package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.SDK_INT; -import static androidx.media3.transformer.CodecFactoryUtil.createCodec; import android.media.MediaFormat; import android.view.Surface; @@ -36,11 +35,10 @@ import androidx.media3.common.util.MediaFormatUtil; mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); - return createCodec( + return new DefaultCodec( format, mediaFormat, /* mediaCodecName= */ null, - /* isVideo= */ false, /* isDecoder= */ true, /* outputSurface= */ null); } @@ -61,12 +59,7 @@ import androidx.media3.common.util.MediaFormatUtil; mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0); } - return createCodec( - format, - mediaFormat, - /* mediaCodecName= */ null, - /* isVideo= */ true, - /* isDecoder= */ true, - outputSurface); + return new DefaultCodec( + format, mediaFormat, /* mediaCodecName= */ null, /* isDecoder= */ true, outputSurface); } } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 9b7d2ba4e1..639e9728bc 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -21,8 +21,6 @@ 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.SDK_INT; -import static androidx.media3.transformer.CodecFactoryUtil.createCodec; -import static androidx.media3.transformer.CodecFactoryUtil.createTransformationException; import static java.lang.Math.abs; import android.media.MediaCodecInfo; @@ -81,12 +79,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { // capabilities limitations. format = format.buildUpon().setSampleMimeType(allowedMimeTypes.get(0)).build(); } else { - throw createTransformationException( + throw TransformationException.createForCodec( new IllegalArgumentException("The requested output format is not supported."), format, /* isVideo= */ false, /* isDecoder= */ false, - /* mediaCodecName= */ null); + /* mediaCodecName= */ null, + TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); } } MediaFormat mediaFormat = @@ -94,11 +93,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { checkNotNull(format.sampleMimeType), format.sampleRate, format.channelCount); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); - return createCodec( + return new DefaultCodec( format, mediaFormat, /* mediaCodecName= */ null, - /* isVideo= */ false, /* isDecoder= */ false, /* outputSurface= */ null); } @@ -121,12 +119,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { findEncoderWithClosestFormatSupport( format, videoEncoderSelector, allowedMimeTypes, disableFallback); if (encoderAndClosestFormatSupport == null) { - throw createTransformationException( + throw TransformationException.createForCodec( new IllegalArgumentException("The requested output format is not supported."), format, /* isVideo= */ true, /* isDecoder= */ false, - /* mediaCodecName= */ null); + /* mediaCodecName= */ null, + TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); } MediaCodecInfo encoderInfo = encoderAndClosestFormatSupport.first; @@ -198,11 +197,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, DEFAULT_COLOR_FORMAT); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, DEFAULT_I_FRAME_INTERVAL_SECS); - return createCodec( + return new DefaultCodec( format, mediaFormat, encoderInfo.getName(), - /* isVideo= */ true, /* isDecoder= */ false, /* outputSurface= */ null); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index 2e1721d53d..5dce35e195 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -207,24 +207,23 @@ public final class TransformationException extends Exception { * Creates an instance for a decoder or encoder related exception. * * @param cause The cause of the failure. - * @param componentName The name of the component used, e.g. 'VideoEncoder'. - * @param configurationFormat The {@link Format} used for configuring the decoder/encoder. + * @param format The {@link Format} used for configuring the decoder/encoder. + * @param isVideo Whether the decoder or encoder is configured for video. + * @param isDecoder Whether the exception is created for a decoder. * @param mediaCodecName The name of the {@link MediaCodec} used, if known. * @param errorCode See {@link #errorCode}. * @return The created instance. */ public static TransformationException createForCodec( Throwable cause, - String componentName, - Format configurationFormat, + Format format, + boolean isVideo, + boolean isDecoder, @Nullable String mediaCodecName, int errorCode) { + String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); return new TransformationException( - componentName - + " error, format = " - + configurationFormat - + ", mediaCodecName=" - + mediaCodecName, + componentName + " error, format = " + format + ", mediaCodecName=" + mediaCodecName, cause, errorCode); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index dee399daa2..843a389882 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -135,7 +135,7 @@ import org.checkerframework.dataflow.qual.Pure; actualOutputFormat.height, inputFormat.pixelWidthHeightRatio, transformationMatrix, - /* outputSurface= */ checkNotNull(encoder.getInputSurface()), + /* outputSurface= */ encoder.getInputSurface(), transformationRequest.enableHdrEditing, debugViewProvider); } else { @@ -145,9 +145,7 @@ import org.checkerframework.dataflow.qual.Pure; decoder = decoderFactory.createForVideoDecoding( inputFormat, - frameEditor == null - ? checkNotNull(encoder.getInputSurface()) - : frameEditor.getInputSurface()); + frameEditor == null ? encoder.getInputSurface() : frameEditor.getInputSurface()); } @Override @@ -262,7 +260,7 @@ import org.checkerframework.dataflow.qual.Pure; @Override public void releaseOutputBuffer() throws TransformationException { - encoder.releaseOutputBuffer(); + encoder.releaseOutputBuffer(/* render= */ false); } @Override From 258925d5f34af4622cdcc1ed3271352ec0e8e6fe Mon Sep 17 00:00:00 2001 From: christosts Date: Tue, 8 Feb 2022 16:02:04 +0000 Subject: [PATCH 173/251] Android 12L: Always set codec max output channels With this change, MediaCodecAudioRenderer always configures MediaCodec with max output channels set to 99 on API 32+. #minor-release PiperOrigin-RevId: 427192801 --- .../audio/MediaCodecAudioRenderer.java | 181 +-------------- .../exoplayer/audio/SpatializerDelegate.java | 212 ------------------ 2 files changed, 5 insertions(+), 388 deletions(-) delete mode 100644 libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 6c929efada..a07750ce25 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -16,7 +16,6 @@ package androidx.media3.exoplayer.audio; import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_MAX_INPUT_SIZE_EXCEEDED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO; import static com.google.common.base.MoreObjects.firstNonNull; @@ -30,9 +29,7 @@ import android.media.MediaCrypto; import android.media.MediaFormat; import android.os.Handler; import androidx.annotation.CallSuper; -import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.media3.common.AudioAttributes; import androidx.media3.common.AuxEffectInfo; import androidx.media3.common.C; @@ -63,10 +60,8 @@ import androidx.media3.exoplayer.mediacodec.MediaCodecSelector; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException; import com.google.common.collect.ImmutableList; -import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.util.List; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Decodes and renders audio using {@link MediaCodec} and an {@link AudioSink}. @@ -103,7 +98,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media private final Context context; private final EventDispatcher eventDispatcher; private final AudioSink audioSink; - private final SpatializationHelper spatializationHelper; private int codecMaxInputSize; private boolean codecNeedsDiscardChannelsWorkaround; @@ -263,7 +257,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media this.context = context; this.audioSink = audioSink; eventDispatcher = new EventDispatcher(eventHandler, eventListener); - spatializationHelper = new SpatializationHelper(context, audioSink.getAudioAttributes()); audioSink.setListener(new AudioSinkListener()); } @@ -421,11 +414,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return audioSink.supportsFormat(format); } - @Override - protected boolean shouldReinitCodec() { - return spatializationHelper.shouldReinitCodec(); - } - @Override protected MediaCodecAdapter.Configuration getMediaCodecConfiguration( MediaCodecInfo codecInfo, @@ -490,7 +478,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media MediaCodecAdapter.Configuration configuration, long initializedTimestampMs, long initializationDurationMs) { - spatializationHelper.onCodecInitialized(configuration); eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs); } @@ -581,7 +568,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media audioSink.disableTunneling(); } audioSink.setPlayerId(getPlayerId()); - spatializationHelper.enable(); } @Override @@ -634,7 +620,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media audioSinkNeedsReset = false; audioSink.reset(); } - spatializationHelper.reset(); } } @@ -759,7 +744,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media case MSG_SET_AUDIO_ATTRIBUTES: AudioAttributes audioAttributes = (AudioAttributes) message; audioSink.setAudioAttributes(audioAttributes); - spatializationHelper.setAudioAttributes(audioSink.getAudioAttributes()); break; case MSG_SET_AUX_EFFECT_INFO: AuxEffectInfo auxEffectInfo = (AuxEffectInfo) message; @@ -871,7 +855,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media == AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY) { mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_FLOAT); } - spatializationHelper.configureForSpatialization(mediaFormat, format); + if (Util.SDK_INT >= 32) { + // TODO[b/190759307] Use MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT once the + // compile SDK target is set to 32. + mediaFormat.setInteger("max-output-channel-count", 99); + } return mediaFormat; } @@ -956,163 +944,4 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media eventDispatcher.audioSinkError(audioSinkError); } } - - /** - * A helper class that signals whether the codec needs to be re-initialized because spatialization - * properties changed. - */ - private static final class SpatializationHelper implements SpatializerDelegate.Listener { - // TODO[b/190759307] Remove and use MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT once the - // compile SDK target is set to 32. - private static final String KEY_MAX_OUTPUT_CHANNEL_COUNT = "max-output-channel-count"; - private static final int SPATIALIZATION_CHANNEL_COUNT = 99; - - @Nullable private final SpatializerDelegate spatializerDelegate; - - private @MonotonicNonNull Handler handler; - @Nullable private AudioAttributes audioAttributes; - @Nullable private Format inputFormat; - private boolean codecConfiguredForSpatialization; - private boolean codecNeedsReinit; - private boolean listenerAdded; - - /** Creates a new instance. */ - public SpatializationHelper(Context context, @Nullable AudioAttributes audioAttributes) { - this.spatializerDelegate = maybeCreateSpatializer(context); - this.audioAttributes = audioAttributes; - } - - /** Enables this helper. Call this method when the renderer is enabled. */ - public void enable() { - maybeAddSpatalizationListener(); - } - - /** Resets the helper and releases any resources. Call this method when renderer is reset. */ - public void reset() { - maybeRemoveSpatalizationListener(); - } - - /** Sets the audio attributes set by the player. */ - public void setAudioAttributes(@Nullable AudioAttributes audioAttributes) { - if (Util.areEqual(this.audioAttributes, audioAttributes)) { - return; - } - - this.audioAttributes = audioAttributes; - updateCodecNeedsReinit(); - } - - /** - * Sets keys for audio spatialization on the {@code mediaFormat} if the platform can apply - * spatialization to this {@code format}. - */ - public void configureForSpatialization(MediaFormat mediaFormat, Format format) { - if (canBeSpatialized(format)) { - mediaFormat.setInteger(KEY_MAX_OUTPUT_CHANNEL_COUNT, SPATIALIZATION_CHANNEL_COUNT); - } - } - - /** Informs the helper that a codec was initialized. */ - public void onCodecInitialized(MediaCodecAdapter.Configuration configuration) { - codecNeedsReinit = false; - inputFormat = configuration.format; - codecConfiguredForSpatialization = - configuration.mediaFormat.containsKey(KEY_MAX_OUTPUT_CHANNEL_COUNT) - && configuration.mediaFormat.getInteger(KEY_MAX_OUTPUT_CHANNEL_COUNT) - == SPATIALIZATION_CHANNEL_COUNT; - } - - /** - * Returns whether the codec should be re-initialized, caused by a change in the spatialization - * properties. - */ - public boolean shouldReinitCodec() { - return codecNeedsReinit; - } - - // SpatializerDelegate.Listener - - @Override - public void onSpatializerEnabledChanged(SpatializerDelegate spatializer, boolean enabled) { - updateCodecNeedsReinit(); - } - - @Override - public void onSpatializerAvailableChanged(SpatializerDelegate spatializer, boolean available) { - updateCodecNeedsReinit(); - } - - // Other internal methods - - /** Returns whether this format can be spatialized by the platform. */ - private boolean canBeSpatialized(@Nullable Format format) { - if (Util.SDK_INT < 32 - || format == null - || audioAttributes == null - || spatializerDelegate == null - || spatializerDelegate.getImmersiveAudioLevel() - != SpatializerDelegate.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL - || !spatializerDelegate.isAvailable() - || !spatializerDelegate.isEnabled()) { - return false; - } - AudioFormat.Builder audioFormatBuilder = - new AudioFormat.Builder() - .setEncoding(AudioFormat.ENCODING_PCM_16BIT) - .setChannelMask(Util.getAudioTrackChannelConfig(format.channelCount)); - if (format.sampleRate != Format.NO_VALUE) { - audioFormatBuilder.setSampleRate(format.sampleRate); - } - return spatializerDelegate.canBeSpatialized( - audioAttributes.getAudioAttributesV21(), audioFormatBuilder.build()); - } - - private void maybeAddSpatalizationListener() { - if (!listenerAdded && spatializerDelegate != null && Util.SDK_INT >= 32) { - if (handler == null) { - // Route callbacks to the playback thread. - handler = Util.createHandlerForCurrentLooper(); - } - spatializerDelegate.addOnSpatializerStateChangedListener(handler::post, this); - listenerAdded = true; - } - } - - private void maybeRemoveSpatalizationListener() { - if (listenerAdded && spatializerDelegate != null && Util.SDK_INT >= 32) { - spatializerDelegate.removeOnSpatializerStateChangedListener(this); - checkStateNotNull(handler).removeCallbacksAndMessages(null); - } - } - - private void updateCodecNeedsReinit() { - codecNeedsReinit = codecConfiguredForSpatialization != canBeSpatialized(inputFormat); - } - - @Nullable - private static SpatializerDelegate maybeCreateSpatializer(Context context) { - if (Util.SDK_INT >= 32) { - return Api32.createSpatializer(context); - } - return null; - } - } - - @RequiresApi(32) - private static final class Api32 { - private Api32() {} - - @DoNotInline - @Nullable - public static SpatializerDelegate createSpatializer(Context context) { - try { - return new SpatializerDelegate(context); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { - // Do nothing for these cases. - } catch (InvocationTargetException e) { - Log.w(TAG, "Failed to load Spatializer with reflection", e); - } - return null; - } - } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java deleted file mode 100644 index 0c1556012c..0000000000 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/SpatializerDelegate.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.audio; - -import static androidx.media3.common.util.Assertions.checkStateNotNull; -import static java.lang.annotation.ElementType.TYPE_USE; - -import android.content.Context; -import android.media.AudioAttributes; -import android.media.AudioFormat; -import android.media.AudioManager; -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.media3.common.util.Assertions; -import androidx.media3.common.util.Util; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * Exposes the android.media.Spatializer API via reflection. This is so that we can use the - * Spatializer while the compile SDK target is set to 31. - */ -@RequiresApi(31) -/* package */ final class SpatializerDelegate { - /** Level of support for audio spatialization. */ - @Documented - @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) - @IntDef({ - SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL, - SPATIALIZER_IMMERSIVE_LEVEL_NONE, - SPATIALIZER_IMMERSIVE_LEVEL_OTHER - }) - @interface ImmersiveAudioLevel {} - - /** See Spatializer#SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL */ - public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; - /** See Spatializer#SPATIALIZER_IMMERSIVE_LEVEL_NONE */ - public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; - /** See Spatializer#SPATIALIZER_IMMERSIVE_LEVEL_OTHER */ - public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1; - - /** Wrapper for Spatializer.OnSpatializerStateChangedListener */ - public interface Listener { - /** See Spatializer.OnSpatializerStateChangedListener.onSpatializerEnabledChanged */ - void onSpatializerEnabledChanged(SpatializerDelegate spatializer, boolean enabled); - - /** See Spatializer.OnSpatializerStateChangedListener.onSpatializerAvailableChanged */ - void onSpatializerAvailableChanged(SpatializerDelegate spatializer, boolean available); - } - - private final Object spatializer; - private final Class spatializerClass; - private final Class spatializerListenerClass; - private final Method isEnabled; - private final Method isAvailable; - private final Method getImmersiveAudioLevel; - private final Method canBeSpatialized; - private final Method addListener; - private final Method removeListener; - private final Map listeners; - - /** Creates an instance. */ - public SpatializerDelegate(Context context) - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - Method getSpatializerMethod = AudioManager.class.getMethod("getSpatializer"); - AudioManager manager = - Assertions.checkNotNull( - (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE)); - spatializer = checkStateNotNull(getSpatializerMethod.invoke(manager)); - spatializerClass = Class.forName("android.media.Spatializer"); - spatializerListenerClass = - Class.forName("android.media.Spatializer$OnSpatializerStateChangedListener"); - isEnabled = spatializerClass.getMethod("isEnabled"); - isAvailable = spatializerClass.getMethod("isAvailable"); - getImmersiveAudioLevel = spatializerClass.getMethod("getImmersiveAudioLevel"); - canBeSpatialized = - spatializerClass.getMethod( - "canBeSpatialized", android.media.AudioAttributes.class, AudioFormat.class); - addListener = - spatializerClass.getMethod( - "addOnSpatializerStateChangedListener", Executor.class, spatializerListenerClass); - removeListener = - spatializerClass.getMethod( - "removeOnSpatializerStateChangedListener", spatializerListenerClass); - listeners = new HashMap<>(); - } - - /** Delegates to Spatializer.isEnabled() */ - public boolean isEnabled() { - try { - return (boolean) Util.castNonNull(isEnabled.invoke(spatializer)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - /** Delegates to Spatializer.isAvailable() */ - public boolean isAvailable() { - try { - return (boolean) Util.castNonNull(isAvailable.invoke(spatializer)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - /** Delegates to Spatializer.getImmersiveAudioLevel() */ - public @ImmersiveAudioLevel int getImmersiveAudioLevel() { - try { - return (int) Util.castNonNull(getImmersiveAudioLevel.invoke(spatializer)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - /** Delegates to Spatializer.canBeSpatialized() */ - public boolean canBeSpatialized(AudioAttributes attributes, AudioFormat format) { - try { - return (boolean) Util.castNonNull(canBeSpatialized.invoke(spatializer, attributes, format)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - /** Delegates to Spatializer.addOnSpatializerStateChangedListener() */ - public void addOnSpatializerStateChangedListener(Executor executor, Listener listener) { - if (listeners.containsKey(listener)) { - return; - } - Object listenerProxy = createSpatializerListenerProxy(listener); - try { - addListener.invoke(spatializer, executor, listenerProxy); - listeners.put(listener, listenerProxy); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - /** Delegates to Spatializer.removeOnSpatializerStateChangedListener() */ - public void removeOnSpatializerStateChangedListener(Listener listener) { - @Nullable Object proxy = listeners.get(listener); - if (proxy == null) { - return; - } - try { - removeListener.invoke(spatializer, proxy); - listeners.remove(listener); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - - private Object createSpatializerListenerProxy(Listener listener) { - return Proxy.newProxyInstance( - spatializerListenerClass.getClassLoader(), - new Class[] {spatializerListenerClass}, - new ProxySpatializerListener(this, listener)); - } - - /** Proxy-based implementation of Spatializer.OnSpatializerStateChangedListener. */ - private static final class ProxySpatializerListener implements InvocationHandler { - private final SpatializerDelegate spatializerDelegate; - private final Listener listener; - - private ProxySpatializerListener(SpatializerDelegate spatializerDelegate, Listener listener) { - this.spatializerDelegate = spatializerDelegate; - this.listener = listener; - } - - @Override - public Object invoke(Object o, Method method, Object[] objects) { - String methodName = method.getName(); - Class[] parameterTypes = method.getParameterTypes(); - if (methodName.equals("onSpatializerAvailableChanged") - && parameterTypes.length == 2 - && spatializerDelegate.spatializerClass.isAssignableFrom(parameterTypes[0]) - && parameterTypes[1].equals(Boolean.TYPE)) { - listener.onSpatializerAvailableChanged(spatializerDelegate, (boolean) objects[1]); - } else if (methodName.equals("onSpatializerEnabledChanged") - && parameterTypes.length == 2 - && spatializerDelegate.spatializerClass.isAssignableFrom(parameterTypes[0]) - && parameterTypes[1].equals(Boolean.TYPE)) { - listener.onSpatializerEnabledChanged(spatializerDelegate, (boolean) objects[1]); - } - return this; - } - } -} From 6dbf9cac8c0b893bf0b81a18b449f2cf868ce6db Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 16:41:10 +0000 Subject: [PATCH 174/251] Avoid special-casing AnalyticsCollector when informing listeners. In some cases (whose where we previously used EventListener), AnalyticsCollector is registered as a listener to receive updates, in other cases it is called directly. Avoid this inconsistent handling by registering it as normal listener and removing all callbacks that are handled by the normal listener flow. The remaining direct usages of AnalyticsCollector calls are those callbacks that have no equivalent in Player.Listener. #minor-release PiperOrigin-RevId: 427201525 --- .../androidx/media3/exoplayer/ExoPlayerImpl.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index a2ff81b124..817bb93d43 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -408,7 +408,7 @@ import java.util.concurrent.TimeoutException; currentCues = ImmutableList.of(); throwsWhenUsingWrongThread = true; - listeners.add(analyticsCollector); + addListener(analyticsCollector); bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { @@ -1392,7 +1392,6 @@ import java.util.concurrent.TimeoutException; this.audioAttributes = audioAttributes; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - analyticsCollector.onAudioAttributesChanged(audioAttributes); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onAudioAttributesChanged(audioAttributes); @@ -1430,7 +1429,6 @@ import java.util.concurrent.TimeoutException; this.audioSessionId = audioSessionId; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - analyticsCollector.onAudioSessionIdChanged(audioSessionId); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onAudioSessionIdChanged(audioSessionId); @@ -1458,7 +1456,6 @@ import java.util.concurrent.TimeoutException; } this.volume = volume; sendVolumeToRenderers(); - analyticsCollector.onVolumeChanged(volume); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onVolumeChanged(volume); @@ -2485,7 +2482,6 @@ import java.util.concurrent.TimeoutException; if (width != surfaceWidth || height != surfaceHeight) { surfaceWidth = width; surfaceHeight = height; - analyticsCollector.onSurfaceSizeChanged(width, height); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onSurfaceSizeChanged(width, height); @@ -2499,7 +2495,6 @@ import java.util.concurrent.TimeoutException; } private void notifySkipSilenceEnabledChanged() { - analyticsCollector.onSkipSilenceEnabledChanged(skipSilenceEnabled); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); @@ -2698,7 +2693,6 @@ import java.util.concurrent.TimeoutException; @Override public void onVideoSizeChanged(VideoSize videoSize) { ExoPlayerImpl.this.videoSize = videoSize; - analyticsCollector.onVideoSizeChanged(videoSize); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onVideoSizeChanged(videoSize); @@ -2806,7 +2800,6 @@ import java.util.concurrent.TimeoutException; @Override public void onCues(List cues) { currentCues = cues; - analyticsCollector.onCues(cues); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listeners : listenerArraySet) { listeners.onCues(cues); @@ -2817,7 +2810,6 @@ import java.util.concurrent.TimeoutException; @Override public void onMetadata(Metadata metadata) { - analyticsCollector.onMetadata(metadata); ExoPlayerImpl.this.onMetadata(metadata); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { @@ -2915,7 +2907,6 @@ import java.util.concurrent.TimeoutException; DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { ExoPlayerImpl.this.deviceInfo = deviceInfo; - analyticsCollector.onDeviceInfoChanged(deviceInfo); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceInfoChanged(deviceInfo); @@ -2925,7 +2916,6 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { - analyticsCollector.onDeviceVolumeChanged(streamVolume, streamMuted); // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onDeviceVolumeChanged(streamVolume, streamMuted); From 9042aa7c7a96a1b07536f5f17f318edc370fe048 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 8 Feb 2022 16:42:01 +0000 Subject: [PATCH 175/251] Misc clean-up in ExoPlayerImpl. This brings listener invocations closer together and removes unnecessary methods. Also fixes a bug where a change in track selection parameters only queued a callback but never flushed it to actually inform the listeners. #minor-release PiperOrigin-RevId: 427201691 --- .../media3/exoplayer/ExoPlayerImpl.java | 69 ++++++++----------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 817bb93d43..bbaa2d85cc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -757,29 +757,6 @@ import java.util.concurrent.TimeoutException; playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); } - public void setPlayWhenReady( - boolean playWhenReady, - @PlaybackSuppressionReason int playbackSuppressionReason, - @PlayWhenReadyChangeReason int playWhenReadyChangeReason) { - if (playbackInfo.playWhenReady == playWhenReady - && playbackInfo.playbackSuppressionReason == playbackSuppressionReason) { - return; - } - pendingOperationAcks++; - PlaybackInfo playbackInfo = - this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason); - internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason); - updatePlaybackInfo( - playbackInfo, - /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - playWhenReadyChangeReason, - /* seekProcessed= */ false, - /* positionDiscontinuity= */ false, - /* ignored */ DISCONTINUITY_REASON_INTERNAL, - /* ignored */ C.TIME_UNSET, - /* ignored */ C.INDEX_UNSET); - } - public boolean getPlayWhenReady() { verifyApplicationThread(); return playbackInfo.playWhenReady; @@ -1201,7 +1178,7 @@ import java.util.concurrent.TimeoutException; return; } trackSelector.setParameters(parameters); - listeners.queueEvent( + listeners.sendEvent( EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, listener -> listener.onTrackSelectionParametersChanged(parameters)); } @@ -1211,20 +1188,6 @@ import java.util.concurrent.TimeoutException; return mediaMetadata; } - private void onMetadata(Metadata metadata) { - staticAndDynamicMediaMetadata = - staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build(); - - MediaMetadata newMediaMetadata = buildUpdatedMediaMetadata(); - - if (newMediaMetadata.equals(mediaMetadata)) { - return; - } - mediaMetadata = newMediaMetadata; - listeners.sendEvent( - EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); - } - public MediaMetadata getPlaylistMetadata() { verifyApplicationThread(); return playlistMetadata; @@ -2371,7 +2334,7 @@ import java.util.concurrent.TimeoutException; * *

    {@link MediaItem} {@link MediaMetadata} is prioritized, with any gaps/missing fields * populated by metadata from static ({@link TrackGroup} {@link Format}) and dynamic ({@link - * #onMetadata(Metadata)}) sources. + * MetadataOutput#onMetadata(Metadata)}) sources. */ private MediaMetadata buildUpdatedMediaMetadata() { Timeline timeline = getCurrentTimeline(); @@ -2511,7 +2474,23 @@ import java.util.concurrent.TimeoutException; playWhenReady && playerCommand != AudioFocusManager.PLAYER_COMMAND_PLAY_WHEN_READY ? Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS : Player.PLAYBACK_SUPPRESSION_REASON_NONE; - setPlayWhenReady(playWhenReady, playbackSuppressionReason, playWhenReadyChangeReason); + if (playbackInfo.playWhenReady == playWhenReady + && playbackInfo.playbackSuppressionReason == playbackSuppressionReason) { + return; + } + pendingOperationAcks++; + PlaybackInfo playbackInfo = + this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason); + internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason); + updatePlaybackInfo( + playbackInfo, + /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + playWhenReadyChangeReason, + /* seekProcessed= */ false, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + /* ignored */ C.TIME_UNSET, + /* ignored */ C.INDEX_UNSET); } private void updateWakeAndWifiLock() { @@ -2810,7 +2789,15 @@ import java.util.concurrent.TimeoutException; @Override public void onMetadata(Metadata metadata) { - ExoPlayerImpl.this.onMetadata(metadata); + staticAndDynamicMediaMetadata = + staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build(); + MediaMetadata newMediaMetadata = buildUpdatedMediaMetadata(); + if (!newMediaMetadata.equals(mediaMetadata)) { + mediaMetadata = newMediaMetadata; + listeners.sendEvent( + EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(mediaMetadata)); + } // TODO(internal b/187152483): Events should be dispatched via ListenerSet for (Listener listener : listenerArraySet) { listener.onMetadata(metadata); From 3d09bc1388669df0a71e8417a3961d945cf972f9 Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 8 Feb 2022 17:46:48 +0000 Subject: [PATCH 176/251] Remove deprecated CronetDataSource constructors #minor-release PiperOrigin-RevId: 427216911 --- .../datasource/cronet/CronetDataSource.java | 124 ------------------ .../cronet/CronetDataSourceFactory.java | 9 +- 2 files changed, 8 insertions(+), 125 deletions(-) diff --git a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java index 6a334c0ae1..74d863301f 100644 --- a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java +++ b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java @@ -432,130 +432,6 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { private volatile long currentConnectTimeoutMs; - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @SuppressWarnings("deprecation") - @Deprecated - public CronetDataSource(CronetEngine cronetEngine, Executor executor) { - this( - cronetEngine, - executor, - DEFAULT_CONNECT_TIMEOUT_MILLIS, - DEFAULT_READ_TIMEOUT_MILLIS, - /* resetTimeoutOnRedirects= */ false, - /* defaultRequestProperties= */ null); - } - - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @Deprecated - public CronetDataSource( - CronetEngine cronetEngine, - Executor executor, - int connectTimeoutMs, - int readTimeoutMs, - boolean resetTimeoutOnRedirects, - @Nullable RequestProperties defaultRequestProperties) { - this( - cronetEngine, - executor, - REQUEST_PRIORITY_MEDIUM, - connectTimeoutMs, - readTimeoutMs, - resetTimeoutOnRedirects, - /* handleSetCookieRequests= */ false, - /* userAgent= */ null, - defaultRequestProperties, - /* contentTypePredicate= */ null, - /* keepPostFor302Redirects */ false); - } - - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @Deprecated - public CronetDataSource( - CronetEngine cronetEngine, - Executor executor, - int connectTimeoutMs, - int readTimeoutMs, - boolean resetTimeoutOnRedirects, - @Nullable RequestProperties defaultRequestProperties, - boolean handleSetCookieRequests) { - this( - cronetEngine, - executor, - REQUEST_PRIORITY_MEDIUM, - connectTimeoutMs, - readTimeoutMs, - resetTimeoutOnRedirects, - handleSetCookieRequests, - /* userAgent= */ null, - defaultRequestProperties, - /* contentTypePredicate= */ null, - /* keepPostFor302Redirects */ false); - } - - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @SuppressWarnings("deprecation") - @Deprecated - public CronetDataSource( - CronetEngine cronetEngine, - Executor executor, - @Nullable Predicate contentTypePredicate) { - this( - cronetEngine, - executor, - contentTypePredicate, - DEFAULT_CONNECT_TIMEOUT_MILLIS, - DEFAULT_READ_TIMEOUT_MILLIS, - /* resetTimeoutOnRedirects= */ false, - /* defaultRequestProperties= */ null); - } - - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @SuppressWarnings("deprecation") - @Deprecated - public CronetDataSource( - CronetEngine cronetEngine, - Executor executor, - @Nullable Predicate contentTypePredicate, - int connectTimeoutMs, - int readTimeoutMs, - boolean resetTimeoutOnRedirects, - @Nullable RequestProperties defaultRequestProperties) { - this( - cronetEngine, - executor, - contentTypePredicate, - connectTimeoutMs, - readTimeoutMs, - resetTimeoutOnRedirects, - defaultRequestProperties, - /* handleSetCookieRequests= */ false); - } - - /** @deprecated Use {@link CronetDataSource.Factory} instead. */ - @Deprecated - public CronetDataSource( - CronetEngine cronetEngine, - Executor executor, - @Nullable Predicate contentTypePredicate, - int connectTimeoutMs, - int readTimeoutMs, - boolean resetTimeoutOnRedirects, - @Nullable RequestProperties defaultRequestProperties, - boolean handleSetCookieRequests) { - this( - cronetEngine, - executor, - REQUEST_PRIORITY_MEDIUM, - connectTimeoutMs, - readTimeoutMs, - resetTimeoutOnRedirects, - handleSetCookieRequests, - /* userAgent= */ null, - defaultRequestProperties, - contentTypePredicate, - /* keepPostFor302Redirects */ false); - } - protected CronetDataSource( CronetEngine cronetEngine, Executor executor, diff --git a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java index d0a7d86574..b1a22e912e 100644 --- a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java +++ b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java @@ -15,6 +15,8 @@ */ package androidx.media3.datasource.cronet; +import static org.chromium.net.UrlRequest.Builder.REQUEST_PRIORITY_MEDIUM; + import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DefaultHttpDataSource; @@ -346,10 +348,15 @@ public final class CronetDataSourceFactory extends BaseFactory { new CronetDataSource( cronetEngine, executor, + REQUEST_PRIORITY_MEDIUM, connectTimeoutMs, readTimeoutMs, resetTimeoutOnRedirects, - defaultRequestProperties); + /* handleSetCookieRequests= */ false, + /* userAgent= */ null, + defaultRequestProperties, + /* contentTypePredicate= */ null, + /* keepPostFor302Redirects */ false); if (transferListener != null) { dataSource.addTransferListener(transferListener); } From 502bb9afdd9018fd84f064ac2f7e093bbd12e3e8 Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 8 Feb 2022 18:06:30 +0000 Subject: [PATCH 177/251] Remove LegacyPlayerView from media3 Apps should migrate to PlayerView (called StyledPlayerView in exoplayer2) before migrating from exoplayer2 to media3. PiperOrigin-RevId: 427222944 --- .../androidx/media3/ui/LegacyPlayerView.java | 1584 ----------------- .../res/values/attrs_legacy_player_view.xml | 54 - 2 files changed, 1638 deletions(-) delete mode 100644 libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java delete mode 100644 libraries/ui/src/main/res/values/attrs_legacy_player_view.xml diff --git a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java deleted file mode 100644 index 2aae797968..0000000000 --- a/libraries/ui/src/main/java/androidx/media3/ui/LegacyPlayerView.java +++ /dev/null @@ -1,1584 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.ui; - -import static androidx.media3.common.Player.COMMAND_GET_TEXT; -import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; -import static java.lang.annotation.ElementType.TYPE_USE; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.opengl.GLSurfaceView; -import android.os.Looper; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.SurfaceView; -import android.view.TextureView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; -import androidx.media3.common.AdOverlayInfo; -import androidx.media3.common.AdViewProvider; -import androidx.media3.common.C; -import androidx.media3.common.ErrorMessageProvider; -import androidx.media3.common.MediaMetadata; -import androidx.media3.common.PlaybackException; -import androidx.media3.common.Player; -import androidx.media3.common.Player.DiscontinuityReason; -import androidx.media3.common.Timeline; -import androidx.media3.common.Timeline.Period; -import androidx.media3.common.TracksInfo; -import androidx.media3.common.VideoSize; -import androidx.media3.common.text.Cue; -import androidx.media3.common.util.Assertions; -import androidx.media3.common.util.RepeatModeUtil; -import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; -import androidx.media3.ui.AspectRatioFrameLayout.ResizeMode; -import com.google.common.collect.ImmutableList; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.List; -import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; -import org.checkerframework.checker.nullness.qual.RequiresNonNull; - -/** - * A high level view for {@link Player} media playbacks. It displays video, subtitles and album art - * during playback, and displays playback controls using a {@link LegacyPlayerControlView}. - * - *

    A LegacyPlayerView can be customized by setting attributes (or calling corresponding methods), - * overriding drawables, overriding the view's layout file, or by specifying a custom view layout - * file. - * - *

    Attributes

    - * - * The following attributes can be set on a LegacyPlayerView when used in a layout XML file: - * - *
      - *
    • {@code use_artwork} - Whether artwork is used if available in audio streams. - *
        - *
      • Corresponding method: {@link #setUseArtwork(boolean)} - *
      • Default: {@code true} - *
      - *
    • {@code default_artwork} - Default artwork to use if no artwork available in audio - * streams. - *
        - *
      • Corresponding method: {@link #setDefaultArtwork(Drawable)} - *
      • Default: {@code null} - *
      - *
    • {@code use_controller} - Whether the playback controls can be shown. - *
        - *
      • Corresponding method: {@link #setUseController(boolean)} - *
      • Default: {@code true} - *
      - *
    • {@code hide_on_touch} - Whether the playback controls are hidden by touch events. - *
        - *
      • Corresponding method: {@link #setControllerHideOnTouch(boolean)} - *
      • Default: {@code true} - *
      - *
    • {@code auto_show} - Whether the playback controls are automatically shown when - * playback starts, pauses, ends, or fails. If set to false, the playback controls can be - * manually operated with {@link #showController()} and {@link #hideController()}. - *
        - *
      • Corresponding method: {@link #setControllerAutoShow(boolean)} - *
      • Default: {@code true} - *
      - *
    • {@code hide_during_ads} - Whether the playback controls are hidden during ads. - * Controls are always shown during ads if they are enabled and the player is paused. - *
        - *
      • Corresponding method: {@link #setControllerHideDuringAds(boolean)} - *
      • Default: {@code true} - *
      - *
    • {@code show_buffering} - Whether the buffering spinner is displayed when the player - * is buffering. Valid values are {@code never}, {@code when_playing} and {@code always}. - *
        - *
      • Corresponding method: {@link #setShowBuffering(int)} - *
      • Default: {@code never} - *
      - *
    • {@code resize_mode} - Controls how video and album art is resized within the view. - * Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height}, {@code fill} and - * {@code zoom}. - *
        - *
      • Corresponding method: {@link #setResizeMode(int)} - *
      • Default: {@code fit} - *
      - *
    • {@code surface_type} - The type of surface view used for video playbacks. Valid - * values are {@code surface_view}, {@code texture_view}, {@code spherical_gl_surface_view}, - * {@code video_decoder_gl_surface_view} and {@code none}. Using {@code none} is recommended - * for audio only applications, since creating the surface can be expensive. Using {@code - * surface_view} is recommended for video applications. Note, TextureView can only be used in - * a hardware accelerated window. When rendered in software, TextureView will draw nothing. - *
        - *
      • Corresponding method: None - *
      • Default: {@code surface_view} - *
      - *
    • {@code shutter_background_color} - The background color of the {@code exo_shutter} - * view. - *
        - *
      • Corresponding method: {@link #setShutterBackgroundColor(int)} - *
      • Default: {@code unset} - *
      - *
    • {@code keep_content_on_player_reset} - Whether the currently displayed video frame - * or media artwork is kept visible when the player is reset. - *
        - *
      • Corresponding method: {@link #setKeepContentOnPlayerReset(boolean)} - *
      • Default: {@code false} - *
      - *
    • {@code player_layout_id} - Specifies the id of the layout to be inflated. See below - * for more details. - *
        - *
      • Corresponding method: None - *
      • Default: {@code R.layout.exo_legacy_player_view} - *
      - *
    • {@code controller_layout_id} - Specifies the id of the layout resource to be - * inflated by the child {@link LegacyPlayerControlView}. See below for more details. - *
        - *
      • Corresponding method: None - *
      • Default: {@code R.layout.exo_legacy_player_control_view} - *
      - *
    • All attributes that can be set on {@link LegacyPlayerControlView} and {@link - * DefaultTimeBar} can also be set on a LegacyPlayerView, and will be propagated to the - * inflated {@link LegacyPlayerControlView} unless the layout is overridden to specify a - * custom {@code exo_controller} (see below). - *
    - * - *

    Overriding drawables

    - * - * The drawables used by {@link LegacyPlayerControlView} (with its default layout file) can be - * overridden by drawables with the same names defined in your application. See the {@link - * LegacyPlayerControlView} documentation for a list of drawables that can be overridden. - * - *

    Overriding the layout file

    - * - * To customize the layout of LegacyPlayerView throughout your app, or just for certain - * configurations, you can define {@code exo_legacy_player_view.xml} layout files in your - * application {@code res/layout*} directories. These layouts will override the one provided by the - * library, and will be inflated for use by LegacyPlayerView. The view identifies and binds its - * children by looking for the following ids: - * - *
      - *
    • {@code exo_content_frame} - A frame whose aspect ratio is resized based on the video - * or album art of the media being played, and the configured {@code resize_mode}. The video - * surface view is inflated into this frame as its first child. - *
        - *
      • Type: {@link AspectRatioFrameLayout} - *
      - *
    • {@code exo_shutter} - A view that's made visible when video should be hidden. This - * view is typically an opaque view that covers the video surface, thereby obscuring it when - * visible. Obscuring the surface in this way also helps to prevent flicker at the start of - * playback when {@code surface_type="surface_view"}. - *
        - *
      • Type: {@link View} - *
      - *
    • {@code exo_buffering} - A view that's made visible when the player is buffering. - * This view typically displays a buffering spinner or animation. - *
        - *
      • Type: {@link View} - *
      - *
    • {@code exo_subtitles} - Displays subtitles. - *
        - *
      • Type: {@link SubtitleView} - *
      - *
    • {@code exo_artwork} - Displays album art. - *
        - *
      • Type: {@link ImageView} - *
      - *
    • {@code exo_error_message} - Displays an error message to the user if playback fails. - *
        - *
      • Type: {@link TextView} - *
      - *
    • {@code exo_controller_placeholder} - A placeholder that's replaced with the inflated - * {@link LegacyPlayerControlView}. Ignored if an {@code exo_controller} view exists. - *
        - *
      • Type: {@link View} - *
      - *
    • {@code exo_controller} - An already inflated {@link LegacyPlayerControlView}. Allows - * use of a custom extension of {@link LegacyPlayerControlView}. {@link - * LegacyPlayerControlView} and {@link DefaultTimeBar} attributes set on the LegacyPlayerView - * will not be automatically propagated through to this instance. If a view exists with this - * id, any {@code exo_controller_placeholder} view will be ignored. - *
        - *
      • Type: {@link LegacyPlayerControlView} - *
      - *
    • {@code exo_ad_overlay} - A {@link FrameLayout} positioned on top of the player which - * is used to show ad UI (if applicable). - *
        - *
      • Type: {@link FrameLayout} - *
      - *
    • {@code exo_overlay} - A {@link FrameLayout} positioned on top of the player which - * the app can access via {@link #getOverlayFrameLayout()}, provided for convenience. - *
        - *
      • Type: {@link FrameLayout} - *
      - *
    - * - *

    All child views are optional and so can be omitted if not required, however where defined they - * must be of the expected type. - * - *

    Specifying a custom layout file

    - * - * Defining your own {@code exo_legacy_player_view.xml} is useful to customize the layout of - * LegacyPlayerView throughout your application. It's also possible to customize the layout for a - * single instance in a layout file. This is achieved by setting the {@code player_layout_id} - * attribute on a LegacyPlayerView. This will cause the specified layout to be inflated instead of - * {@code exo_legacy_player_view.xml} for only the instance on which the attribute is set. - * - * @deprecated Use {@link PlayerView} instead. - */ -@Deprecated -@UnstableApi -public class LegacyPlayerView extends FrameLayout implements AdViewProvider { - - /** - * Determines when the buffering view is shown. One of {@link #SHOW_BUFFERING_NEVER}, {@link - * #SHOW_BUFFERING_WHEN_PLAYING} or {@link #SHOW_BUFFERING_ALWAYS}. - */ - @Documented - @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) - @IntDef({SHOW_BUFFERING_NEVER, SHOW_BUFFERING_WHEN_PLAYING, SHOW_BUFFERING_ALWAYS}) - public @interface ShowBuffering {} - /** The buffering view is never shown. */ - public static final int SHOW_BUFFERING_NEVER = 0; - /** - * The buffering view is shown when the player is in the {@link Player#STATE_BUFFERING buffering} - * state and {@link Player#getPlayWhenReady() playWhenReady} is {@code true}. - */ - public static final int SHOW_BUFFERING_WHEN_PLAYING = 1; - /** - * The buffering view is always shown when the player is in the {@link Player#STATE_BUFFERING - * buffering} state. - */ - public static final int SHOW_BUFFERING_ALWAYS = 2; - - private static final int SURFACE_TYPE_NONE = 0; - private static final int SURFACE_TYPE_SURFACE_VIEW = 1; - private static final int SURFACE_TYPE_TEXTURE_VIEW = 2; - private static final int SURFACE_TYPE_SPHERICAL_GL_SURFACE_VIEW = 3; - private static final int SURFACE_TYPE_VIDEO_DECODER_GL_SURFACE_VIEW = 4; - - private final ComponentListener componentListener; - @Nullable private final AspectRatioFrameLayout contentFrame; - @Nullable private final View shutterView; - @Nullable private final View surfaceView; - private final boolean surfaceViewIgnoresVideoAspectRatio; - @Nullable private final ImageView artworkView; - @Nullable private final SubtitleView subtitleView; - @Nullable private final View bufferingView; - @Nullable private final TextView errorMessageView; - @Nullable private final LegacyPlayerControlView controller; - @Nullable private final FrameLayout adOverlayFrameLayout; - @Nullable private final FrameLayout overlayFrameLayout; - - @Nullable private Player player; - private boolean useController; - @Nullable private LegacyPlayerControlView.VisibilityListener controllerVisibilityListener; - private boolean useArtwork; - @Nullable private Drawable defaultArtwork; - private @ShowBuffering int showBuffering; - private boolean keepContentOnPlayerReset; - @Nullable private ErrorMessageProvider errorMessageProvider; - @Nullable private CharSequence customErrorMessage; - private int controllerShowTimeoutMs; - private boolean controllerAutoShow; - private boolean controllerHideDuringAds; - private boolean controllerHideOnTouch; - private int textureViewRotation; - private boolean isTouching; - private static final int PICTURE_TYPE_FRONT_COVER = 3; - private static final int PICTURE_TYPE_NOT_SET = -1; - - public LegacyPlayerView(Context context) { - this(context, /* attrs= */ null); - } - - public LegacyPlayerView(Context context, @Nullable AttributeSet attrs) { - this(context, attrs, /* defStyleAttr= */ 0); - } - - @SuppressWarnings({"nullness:argument", "nullness:method.invocation"}) - public LegacyPlayerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - componentListener = new ComponentListener(); - - if (isInEditMode()) { - contentFrame = null; - shutterView = null; - surfaceView = null; - surfaceViewIgnoresVideoAspectRatio = false; - artworkView = null; - subtitleView = null; - bufferingView = null; - errorMessageView = null; - controller = null; - adOverlayFrameLayout = null; - overlayFrameLayout = null; - ImageView logo = new ImageView(context); - if (Util.SDK_INT >= 23) { - configureEditModeLogoV23(getResources(), logo); - } else { - configureEditModeLogo(getResources(), logo); - } - addView(logo); - return; - } - - boolean shutterColorSet = false; - int shutterColor = 0; - int playerLayoutId = R.layout.exo_legacy_player_view; - boolean useArtwork = true; - int defaultArtworkId = 0; - boolean useController = true; - int surfaceType = SURFACE_TYPE_SURFACE_VIEW; - int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT; - int controllerShowTimeoutMs = LegacyPlayerControlView.DEFAULT_SHOW_TIMEOUT_MS; - boolean controllerHideOnTouch = true; - boolean controllerAutoShow = true; - boolean controllerHideDuringAds = true; - int showBuffering = SHOW_BUFFERING_NEVER; - if (attrs != null) { - TypedArray a = - context - .getTheme() - .obtainStyledAttributes( - attrs, R.styleable.LegacyPlayerView, defStyleAttr, /* defStyleRes= */ 0); - try { - shutterColorSet = a.hasValue(R.styleable.LegacyPlayerView_shutter_background_color); - shutterColor = - a.getColor(R.styleable.LegacyPlayerView_shutter_background_color, shutterColor); - playerLayoutId = - a.getResourceId(R.styleable.LegacyPlayerView_player_layout_id, playerLayoutId); - useArtwork = a.getBoolean(R.styleable.LegacyPlayerView_use_artwork, useArtwork); - defaultArtworkId = - a.getResourceId(R.styleable.LegacyPlayerView_default_artwork, defaultArtworkId); - useController = a.getBoolean(R.styleable.LegacyPlayerView_use_controller, useController); - surfaceType = a.getInt(R.styleable.LegacyPlayerView_surface_type, surfaceType); - resizeMode = a.getInt(R.styleable.LegacyPlayerView_resize_mode, resizeMode); - controllerShowTimeoutMs = - a.getInt(R.styleable.LegacyPlayerView_show_timeout, controllerShowTimeoutMs); - controllerHideOnTouch = - a.getBoolean(R.styleable.LegacyPlayerView_hide_on_touch, controllerHideOnTouch); - controllerAutoShow = - a.getBoolean(R.styleable.LegacyPlayerView_auto_show, controllerAutoShow); - showBuffering = a.getInteger(R.styleable.LegacyPlayerView_show_buffering, showBuffering); - keepContentOnPlayerReset = - a.getBoolean( - R.styleable.LegacyPlayerView_keep_content_on_player_reset, - keepContentOnPlayerReset); - controllerHideDuringAds = - a.getBoolean(R.styleable.LegacyPlayerView_hide_during_ads, controllerHideDuringAds); - } finally { - a.recycle(); - } - } - - LayoutInflater.from(context).inflate(playerLayoutId, this); - setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); - - // Content frame. - contentFrame = findViewById(R.id.exo_content_frame); - if (contentFrame != null) { - setResizeModeRaw(contentFrame, resizeMode); - } - - // Shutter view. - shutterView = findViewById(R.id.exo_shutter); - if (shutterView != null && shutterColorSet) { - shutterView.setBackgroundColor(shutterColor); - } - - // Create a surface view and insert it into the content frame, if there is one. - boolean surfaceViewIgnoresVideoAspectRatio = false; - if (contentFrame != null && surfaceType != SURFACE_TYPE_NONE) { - ViewGroup.LayoutParams params = - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - switch (surfaceType) { - case SURFACE_TYPE_TEXTURE_VIEW: - surfaceView = new TextureView(context); - break; - case SURFACE_TYPE_SPHERICAL_GL_SURFACE_VIEW: - try { - Class clazz = - Class.forName("androidx.media3.exoplayer.video.spherical.SphericalGLSurfaceView"); - surfaceView = (View) clazz.getConstructor(Context.class).newInstance(context); - } catch (Exception e) { - throw new IllegalStateException( - "spherical_gl_surface_view requires an ExoPlayer dependency", e); - } - surfaceViewIgnoresVideoAspectRatio = true; - break; - case SURFACE_TYPE_VIDEO_DECODER_GL_SURFACE_VIEW: - try { - Class clazz = - Class.forName("androidx.media3.exoplayer.video.VideoDecoderGLSurfaceView"); - surfaceView = (View) clazz.getConstructor(Context.class).newInstance(context); - } catch (Exception e) { - throw new IllegalStateException( - "video_decoder_gl_surface_view requires an ExoPlayer dependency", e); - } - break; - default: - surfaceView = new SurfaceView(context); - break; - } - surfaceView.setLayoutParams(params); - // We don't want surfaceView to be clickable separately to the LegacyPlayerView itself, but we - // do want to register as an OnClickListener so that surfaceView implementations can propagate - // click events up to the LegacyPlayerView by calling their own performClick method. - surfaceView.setOnClickListener(componentListener); - surfaceView.setClickable(false); - contentFrame.addView(surfaceView, 0); - } else { - surfaceView = null; - } - this.surfaceViewIgnoresVideoAspectRatio = surfaceViewIgnoresVideoAspectRatio; - - // Ad overlay frame layout. - adOverlayFrameLayout = findViewById(R.id.exo_ad_overlay); - - // Overlay frame layout. - overlayFrameLayout = findViewById(R.id.exo_overlay); - - // Artwork view. - artworkView = findViewById(R.id.exo_artwork); - this.useArtwork = useArtwork && artworkView != null; - if (defaultArtworkId != 0) { - defaultArtwork = ContextCompat.getDrawable(getContext(), defaultArtworkId); - } - - // Subtitle view. - subtitleView = findViewById(R.id.exo_subtitles); - if (subtitleView != null) { - subtitleView.setUserDefaultStyle(); - subtitleView.setUserDefaultTextSize(); - } - - // Buffering view. - bufferingView = findViewById(R.id.exo_buffering); - if (bufferingView != null) { - bufferingView.setVisibility(View.GONE); - } - this.showBuffering = showBuffering; - - // Error message view. - errorMessageView = findViewById(R.id.exo_error_message); - if (errorMessageView != null) { - errorMessageView.setVisibility(View.GONE); - } - - // Playback control view. - LegacyPlayerControlView customController = findViewById(R.id.exo_controller); - View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder); - if (customController != null) { - this.controller = customController; - } else if (controllerPlaceholder != null) { - // Propagate attrs as playbackAttrs so that LegacyPlayerControlView's custom attributes are - // transferred, but standard attributes (e.g. background) are not. - this.controller = new LegacyPlayerControlView(context, null, 0, attrs); - controller.setId(R.id.exo_controller); - controller.setLayoutParams(controllerPlaceholder.getLayoutParams()); - ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent()); - int controllerIndex = parent.indexOfChild(controllerPlaceholder); - parent.removeView(controllerPlaceholder); - parent.addView(controller, controllerIndex); - } else { - this.controller = null; - } - this.controllerShowTimeoutMs = controller != null ? controllerShowTimeoutMs : 0; - this.controllerHideOnTouch = controllerHideOnTouch; - this.controllerAutoShow = controllerAutoShow; - this.controllerHideDuringAds = controllerHideDuringAds; - this.useController = useController && controller != null; - hideController(); - updateContentDescription(); - if (controller != null) { - controller.addVisibilityListener(/* listener= */ componentListener); - } - } - - /** - * Switches the view targeted by a given {@link Player}. - * - * @param player The player whose target view is being switched. - * @param oldPlayerView The old view to detach from the player. - * @param newPlayerView The new view to attach to the player. - */ - public static void switchTargetView( - Player player, - @Nullable LegacyPlayerView oldPlayerView, - @Nullable LegacyPlayerView newPlayerView) { - if (oldPlayerView == newPlayerView) { - return; - } - // We attach the new view before detaching the old one because this ordering allows the player - // to swap directly from one surface to another, without transitioning through a state where no - // surface is attached. This is significantly more efficient and achieves a more seamless - // transition when using platform provided video decoders. - if (newPlayerView != null) { - newPlayerView.setPlayer(player); - } - if (oldPlayerView != null) { - oldPlayerView.setPlayer(null); - } - } - - /** Returns the player currently set on this view, or null if no player is set. */ - @Nullable - public Player getPlayer() { - return player; - } - - /** - * Sets the {@link Player} to use. - * - *

    To transition a {@link Player} from targeting one view to another, it's recommended to use - * {@link #switchTargetView(Player, LegacyPlayerView, LegacyPlayerView)} rather than this method. - * If you do wish to use this method directly, be sure to attach the player to the new view - * before calling {@code setPlayer(null)} to detach it from the old one. This ordering is - * significantly more efficient and may allow for more seamless transitions. - * - * @param player The {@link Player} to use, or {@code null} to detach the current player. Only - * players which are accessed on the main thread are supported ({@code - * player.getApplicationLooper() == Looper.getMainLooper()}). - */ - public void setPlayer(@Nullable Player player) { - Assertions.checkState(Looper.myLooper() == Looper.getMainLooper()); - Assertions.checkArgument( - player == null || player.getApplicationLooper() == Looper.getMainLooper()); - if (this.player == player) { - return; - } - @Nullable Player oldPlayer = this.player; - if (oldPlayer != null) { - oldPlayer.removeListener(componentListener); - if (oldPlayer.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) { - if (surfaceView instanceof TextureView) { - oldPlayer.clearVideoTextureView((TextureView) surfaceView); - } else if (surfaceView instanceof SurfaceView) { - oldPlayer.clearVideoSurfaceView((SurfaceView) surfaceView); - } - } - } - if (subtitleView != null) { - subtitleView.setCues(null); - } - this.player = player; - if (useController()) { - controller.setPlayer(player); - } - updateBuffering(); - updateErrorMessage(); - updateForCurrentTrackSelections(/* isNewPlayer= */ true); - if (player != null) { - if (player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)) { - if (surfaceView instanceof TextureView) { - player.setVideoTextureView((TextureView) surfaceView); - } else if (surfaceView instanceof SurfaceView) { - player.setVideoSurfaceView((SurfaceView) surfaceView); - } - updateAspectRatio(); - } - if (subtitleView != null && player.isCommandAvailable(COMMAND_GET_TEXT)) { - subtitleView.setCues(player.getCurrentCues()); - } - player.addListener(componentListener); - maybeShowController(false); - } else { - hideController(); - } - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (surfaceView instanceof SurfaceView) { - // Work around https://github.com/google/ExoPlayer/issues/3160. - surfaceView.setVisibility(visibility); - } - } - - /** - * Sets the {@link ResizeMode}. - * - * @param resizeMode The {@link ResizeMode}. - */ - public void setResizeMode(@ResizeMode int resizeMode) { - Assertions.checkStateNotNull(contentFrame); - contentFrame.setResizeMode(resizeMode); - } - - /** Returns the {@link ResizeMode}. */ - public @ResizeMode int getResizeMode() { - Assertions.checkStateNotNull(contentFrame); - return contentFrame.getResizeMode(); - } - - /** Returns whether artwork is displayed if present in the media. */ - public boolean getUseArtwork() { - return useArtwork; - } - - /** - * Sets whether artwork is displayed if present in the media. - * - * @param useArtwork Whether artwork is displayed. - */ - public void setUseArtwork(boolean useArtwork) { - Assertions.checkState(!useArtwork || artworkView != null); - if (this.useArtwork != useArtwork) { - this.useArtwork = useArtwork; - updateForCurrentTrackSelections(/* isNewPlayer= */ false); - } - } - - /** Returns the default artwork to display. */ - @Nullable - public Drawable getDefaultArtwork() { - return defaultArtwork; - } - - /** - * Sets the default artwork to display if {@code useArtwork} is {@code true} and no artwork is - * present in the media. - * - * @param defaultArtwork the default artwork to display - */ - public void setDefaultArtwork(@Nullable Drawable defaultArtwork) { - if (this.defaultArtwork != defaultArtwork) { - this.defaultArtwork = defaultArtwork; - updateForCurrentTrackSelections(/* isNewPlayer= */ false); - } - } - - /** Returns whether the playback controls can be shown. */ - public boolean getUseController() { - return useController; - } - - /** - * Sets whether the playback controls can be shown. If set to {@code false} the playback controls - * are never visible and are disconnected from the player. - * - * @param useController Whether the playback controls can be shown. - */ - public void setUseController(boolean useController) { - Assertions.checkState(!useController || controller != null); - if (this.useController == useController) { - return; - } - this.useController = useController; - if (useController()) { - controller.setPlayer(player); - } else if (controller != null) { - controller.hide(); - controller.setPlayer(/* player= */ null); - } - updateContentDescription(); - } - - /** - * Sets the background color of the {@code exo_shutter} view. - * - * @param color The background color. - */ - public void setShutterBackgroundColor(int color) { - if (shutterView != null) { - shutterView.setBackgroundColor(color); - } - } - - /** - * Sets whether the currently displayed video frame or media artwork is kept visible when the - * player is reset. A player reset is defined to mean the player being re-prepared with different - * media, the player transitioning to unprepared media or an empty list of media items, or the - * player being replaced or cleared by calling {@link #setPlayer(Player)}. - * - *

    If enabled, the currently displayed video frame or media artwork will be kept visible until - * the player set on the view has been successfully prepared with new media and loaded enough of - * it to have determined the available tracks. Hence enabling this option allows transitioning - * from playing one piece of media to another, or from using one player instance to another, - * without clearing the view's content. - * - *

    If disabled, the currently displayed video frame or media artwork will be hidden as soon as - * the player is reset. Note that the video frame is hidden by making {@code exo_shutter} visible. - * Hence the video frame will not be hidden if using a custom layout that omits this view. - * - * @param keepContentOnPlayerReset Whether the currently displayed video frame or media artwork is - * kept visible when the player is reset. - */ - public void setKeepContentOnPlayerReset(boolean keepContentOnPlayerReset) { - if (this.keepContentOnPlayerReset != keepContentOnPlayerReset) { - this.keepContentOnPlayerReset = keepContentOnPlayerReset; - updateForCurrentTrackSelections(/* isNewPlayer= */ false); - } - } - - /** - * Sets whether a buffering spinner is displayed when the player is in the buffering state. The - * buffering spinner is not displayed by default. - * - * @param showBuffering The mode that defines when the buffering spinner is displayed. One of - * {@link #SHOW_BUFFERING_NEVER}, {@link #SHOW_BUFFERING_WHEN_PLAYING} and {@link - * #SHOW_BUFFERING_ALWAYS}. - */ - public void setShowBuffering(@ShowBuffering int showBuffering) { - if (this.showBuffering != showBuffering) { - this.showBuffering = showBuffering; - updateBuffering(); - } - } - - /** - * Sets the optional {@link ErrorMessageProvider}. - * - * @param errorMessageProvider The error message provider. - */ - public void setErrorMessageProvider( - @Nullable ErrorMessageProvider errorMessageProvider) { - if (this.errorMessageProvider != errorMessageProvider) { - this.errorMessageProvider = errorMessageProvider; - updateErrorMessage(); - } - } - - /** - * Sets a custom error message to be displayed by the view. The error message will be displayed - * permanently, unless it is cleared by passing {@code null} to this method. - * - * @param message The message to display, or {@code null} to clear a previously set message. - */ - public void setCustomErrorMessage(@Nullable CharSequence message) { - Assertions.checkState(errorMessageView != null); - customErrorMessage = message; - updateErrorMessage(); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (player != null && player.isPlayingAd()) { - return super.dispatchKeyEvent(event); - } - - boolean isDpadKey = isDpadKey(event.getKeyCode()); - boolean handled = false; - if (isDpadKey && useController() && !controller.isVisible()) { - // Handle the key event by showing the controller. - maybeShowController(true); - handled = true; - } else if (dispatchMediaKeyEvent(event) || super.dispatchKeyEvent(event)) { - // The key event was handled as a media key or by the super class. We should also show the - // controller, or extend its show timeout if already visible. - maybeShowController(true); - handled = true; - } else if (isDpadKey && useController()) { - // The key event wasn't handled, but we should extend the controller's show timeout. - maybeShowController(true); - } - return handled; - } - - /** - * Called to process media key events. Any {@link KeyEvent} can be passed but only media key - * events will be handled. Does nothing if playback controls are disabled. - * - * @param event A key event. - * @return Whether the key event was handled. - */ - public boolean dispatchMediaKeyEvent(KeyEvent event) { - return useController() && controller.dispatchMediaKeyEvent(event); - } - - /** Returns whether the controller is currently visible. */ - public boolean isControllerVisible() { - return controller != null && controller.isVisible(); - } - - /** - * Shows the playback controls. Does nothing if playback controls are disabled. - * - *

    The playback controls are automatically hidden during playback after {{@link - * #getControllerShowTimeoutMs()}}. They are shown indefinitely when playback has not started yet, - * is paused, has ended or failed. - */ - public void showController() { - showController(shouldShowControllerIndefinitely()); - } - - /** Hides the playback controls. Does nothing if playback controls are disabled. */ - public void hideController() { - if (controller != null) { - controller.hide(); - } - } - - /** - * Returns the playback controls timeout. The playback controls are automatically hidden after - * this duration of time has elapsed without user input and with playback or buffering in - * progress. - * - * @return The timeout in milliseconds. A non-positive value will cause the controller to remain - * visible indefinitely. - */ - public int getControllerShowTimeoutMs() { - return controllerShowTimeoutMs; - } - - /** - * Sets the playback controls timeout. The playback controls are automatically hidden after this - * duration of time has elapsed without user input and with playback or buffering in progress. - * - * @param controllerShowTimeoutMs The timeout in milliseconds. A non-positive value will cause the - * controller to remain visible indefinitely. - */ - public void setControllerShowTimeoutMs(int controllerShowTimeoutMs) { - Assertions.checkStateNotNull(controller); - this.controllerShowTimeoutMs = controllerShowTimeoutMs; - if (controller.isVisible()) { - // Update the controller's timeout if necessary. - showController(); - } - } - - /** Returns whether the playback controls are hidden by touch events. */ - public boolean getControllerHideOnTouch() { - return controllerHideOnTouch; - } - - /** - * Sets whether the playback controls are hidden by touch events. - * - * @param controllerHideOnTouch Whether the playback controls are hidden by touch events. - */ - public void setControllerHideOnTouch(boolean controllerHideOnTouch) { - Assertions.checkStateNotNull(controller); - this.controllerHideOnTouch = controllerHideOnTouch; - updateContentDescription(); - } - - /** - * Returns whether the playback controls are automatically shown when playback starts, pauses, - * ends, or fails. If set to false, the playback controls can be manually operated with {@link - * #showController()} and {@link #hideController()}. - */ - public boolean getControllerAutoShow() { - return controllerAutoShow; - } - - /** - * Sets whether the playback controls are automatically shown when playback starts, pauses, ends, - * or fails. If set to false, the playback controls can be manually operated with {@link - * #showController()} and {@link #hideController()}. - * - * @param controllerAutoShow Whether the playback controls are allowed to show automatically. - */ - public void setControllerAutoShow(boolean controllerAutoShow) { - this.controllerAutoShow = controllerAutoShow; - } - - /** - * Sets whether the playback controls are hidden when ads are playing. Controls are always shown - * during ads if they are enabled and the player is paused. - * - * @param controllerHideDuringAds Whether the playback controls are hidden when ads are playing. - */ - public void setControllerHideDuringAds(boolean controllerHideDuringAds) { - this.controllerHideDuringAds = controllerHideDuringAds; - } - - /** - * Sets the {@link LegacyPlayerControlView.VisibilityListener}. - * - * @param listener The listener to be notified about visibility changes, or null to remove the - * current listener. - */ - public void setControllerVisibilityListener( - @Nullable LegacyPlayerControlView.VisibilityListener listener) { - Assertions.checkStateNotNull(controller); - if (this.controllerVisibilityListener == listener) { - return; - } - if (this.controllerVisibilityListener != null) { - controller.removeVisibilityListener(this.controllerVisibilityListener); - } - this.controllerVisibilityListener = listener; - if (listener != null) { - controller.addVisibilityListener(listener); - } - } - - /** - * Sets whether the rewind button is shown. - * - * @param showRewindButton Whether the rewind button is shown. - */ - public void setShowRewindButton(boolean showRewindButton) { - Assertions.checkStateNotNull(controller); - controller.setShowRewindButton(showRewindButton); - } - - /** - * Sets whether the fast forward button is shown. - * - * @param showFastForwardButton Whether the fast forward button is shown. - */ - public void setShowFastForwardButton(boolean showFastForwardButton) { - Assertions.checkStateNotNull(controller); - controller.setShowFastForwardButton(showFastForwardButton); - } - - /** - * Sets whether the previous button is shown. - * - * @param showPreviousButton Whether the previous button is shown. - */ - public void setShowPreviousButton(boolean showPreviousButton) { - Assertions.checkStateNotNull(controller); - controller.setShowPreviousButton(showPreviousButton); - } - - /** - * Sets whether the next button is shown. - * - * @param showNextButton Whether the next button is shown. - */ - public void setShowNextButton(boolean showNextButton) { - Assertions.checkStateNotNull(controller); - controller.setShowNextButton(showNextButton); - } - - /** - * Sets which repeat toggle modes are enabled. - * - * @param repeatToggleModes A set of {@link RepeatModeUtil.RepeatToggleModes}. - */ - public void setRepeatToggleModes(@RepeatModeUtil.RepeatToggleModes int repeatToggleModes) { - Assertions.checkStateNotNull(controller); - controller.setRepeatToggleModes(repeatToggleModes); - } - - /** - * Sets whether the shuffle button is shown. - * - * @param showShuffleButton Whether the shuffle button is shown. - */ - public void setShowShuffleButton(boolean showShuffleButton) { - Assertions.checkStateNotNull(controller); - controller.setShowShuffleButton(showShuffleButton); - } - - /** - * Sets whether the time bar should show all windows, as opposed to just the current one. - * - * @param showMultiWindowTimeBar Whether to show all windows. - */ - public void setShowMultiWindowTimeBar(boolean showMultiWindowTimeBar) { - Assertions.checkStateNotNull(controller); - controller.setShowMultiWindowTimeBar(showMultiWindowTimeBar); - } - - /** - * Sets the millisecond positions of extra ad markers relative to the start of the window (or - * timeline, if in multi-window mode) and whether each extra ad has been played or not. The - * markers are shown in addition to any ad markers for ads in the player's timeline. - * - * @param extraAdGroupTimesMs The millisecond timestamps of the extra ad markers to show, or - * {@code null} to show no extra ad markers. - * @param extraPlayedAdGroups Whether each ad has been played, or {@code null} to show no extra ad - * markers. - */ - public void setExtraAdGroupMarkers( - @Nullable long[] extraAdGroupTimesMs, @Nullable boolean[] extraPlayedAdGroups) { - Assertions.checkStateNotNull(controller); - controller.setExtraAdGroupMarkers(extraAdGroupTimesMs, extraPlayedAdGroups); - } - - /** - * Sets the {@link AspectRatioFrameLayout.AspectRatioListener}. - * - * @param listener The listener to be notified about aspect ratios changes of the video content or - * the content frame. - */ - public void setAspectRatioListener( - @Nullable AspectRatioFrameLayout.AspectRatioListener listener) { - Assertions.checkStateNotNull(contentFrame); - contentFrame.setAspectRatioListener(listener); - } - - /** - * Gets the view onto which video is rendered. This is a: - * - *

      - *
    • {@link SurfaceView} by default, or if the {@code surface_type} attribute is set to {@code - * surface_view}. - *
    • {@link TextureView} if {@code surface_type} is {@code texture_view}. - *
    • {@code SphericalGLSurfaceView} if {@code surface_type} is {@code - * spherical_gl_surface_view}. - *
    • {@code VideoDecoderGLSurfaceView} if {@code surface_type} is {@code - * video_decoder_gl_surface_view}. - *
    • {@code null} if {@code surface_type} is {@code none}. - *
    - * - * @return The {@link SurfaceView}, {@link TextureView}, {@code SphericalGLSurfaceView}, {@code - * VideoDecoderGLSurfaceView} or {@code null}. - */ - @Nullable - public View getVideoSurfaceView() { - return surfaceView; - } - - /** - * Gets the overlay {@link FrameLayout}, which can be populated with UI elements to show on top of - * the player. - * - * @return The overlay {@link FrameLayout}, or {@code null} if the layout has been customized and - * the overlay is not present. - */ - @Nullable - public FrameLayout getOverlayFrameLayout() { - return overlayFrameLayout; - } - - /** - * Gets the {@link SubtitleView}. - * - * @return The {@link SubtitleView}, or {@code null} if the layout has been customized and the - * subtitle view is not present. - */ - @Nullable - public SubtitleView getSubtitleView() { - return subtitleView; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!useController() || player == null) { - return false; - } - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - isTouching = true; - return true; - case MotionEvent.ACTION_UP: - if (isTouching) { - isTouching = false; - performClick(); - return true; - } - return false; - default: - return false; - } - } - - @Override - public boolean performClick() { - super.performClick(); - return toggleControllerVisibility(); - } - - @Override - public boolean onTrackballEvent(MotionEvent ev) { - if (!useController() || player == null) { - return false; - } - maybeShowController(true); - return true; - } - - /** - * Should be called when the player is visible to the user, if the {@code surface_type} extends - * {@link GLSurfaceView}. It is the counterpart to {@link #onPause()}. - * - *

    This method should typically be called in {@code Activity.onStart()}, or {@code - * Activity.onResume()} for API versions <= 23. - */ - public void onResume() { - if (surfaceView instanceof GLSurfaceView) { - ((GLSurfaceView) surfaceView).onResume(); - } - } - - /** - * Should be called when the player is no longer visible to the user, if the {@code surface_type} - * extends {@link GLSurfaceView}. It is the counterpart to {@link #onResume()}. - * - *

    This method should typically be called in {@code Activity.onStop()}, or {@code - * Activity.onPause()} for API versions <= 23. - */ - public void onPause() { - if (surfaceView instanceof GLSurfaceView) { - ((GLSurfaceView) surfaceView).onPause(); - } - } - - /** - * Called when there's a change in the desired aspect ratio of the content frame. The default - * implementation sets the aspect ratio of the content frame to the specified value. - * - * @param contentFrame The content frame, or {@code null}. - * @param aspectRatio The aspect ratio to apply. - */ - protected void onContentAspectRatioChanged( - @Nullable AspectRatioFrameLayout contentFrame, float aspectRatio) { - if (contentFrame != null) { - contentFrame.setAspectRatio(aspectRatio); - } - } - - // AdsLoader.AdViewProvider implementation. - - @Override - public ViewGroup getAdViewGroup() { - return Assertions.checkStateNotNull( - adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback"); - } - - @Override - public List getAdOverlayInfos() { - List overlayViews = new ArrayList<>(); - if (overlayFrameLayout != null) { - overlayViews.add( - new AdOverlayInfo( - overlayFrameLayout, - AdOverlayInfo.PURPOSE_NOT_VISIBLE, - /* detailedReason= */ "Transparent overlay does not impact viewability")); - } - if (controller != null) { - overlayViews.add(new AdOverlayInfo(controller, AdOverlayInfo.PURPOSE_CONTROLS)); - } - return ImmutableList.copyOf(overlayViews); - } - - // Internal methods. - - @EnsuresNonNullIf(expression = "controller", result = true) - private boolean useController() { - if (useController) { - Assertions.checkStateNotNull(controller); - return true; - } - return false; - } - - @EnsuresNonNullIf(expression = "artworkView", result = true) - private boolean useArtwork() { - if (useArtwork) { - Assertions.checkStateNotNull(artworkView); - return true; - } - return false; - } - - private boolean toggleControllerVisibility() { - if (!useController() || player == null) { - return false; - } - if (!controller.isVisible()) { - maybeShowController(true); - } else if (controllerHideOnTouch) { - controller.hide(); - } - return true; - } - - /** Shows the playback controls, but only if forced or shown indefinitely. */ - private void maybeShowController(boolean isForced) { - if (isPlayingAd() && controllerHideDuringAds) { - return; - } - if (useController()) { - boolean wasShowingIndefinitely = controller.isVisible() && controller.getShowTimeoutMs() <= 0; - boolean shouldShowIndefinitely = shouldShowControllerIndefinitely(); - if (isForced || wasShowingIndefinitely || shouldShowIndefinitely) { - showController(shouldShowIndefinitely); - } - } - } - - private boolean shouldShowControllerIndefinitely() { - if (player == null) { - return true; - } - int playbackState = player.getPlaybackState(); - return controllerAutoShow - && (playbackState == Player.STATE_IDLE - || playbackState == Player.STATE_ENDED - || !player.getPlayWhenReady()); - } - - private void showController(boolean showIndefinitely) { - if (!useController()) { - return; - } - controller.setShowTimeoutMs(showIndefinitely ? 0 : controllerShowTimeoutMs); - controller.show(); - } - - private boolean isPlayingAd() { - return player != null && player.isPlayingAd() && player.getPlayWhenReady(); - } - - private void updateForCurrentTrackSelections(boolean isNewPlayer) { - @Nullable Player player = this.player; - if (player == null - || !player.isCommandAvailable(Player.COMMAND_GET_TRACK_INFOS) - || player.getCurrentTracksInfo().getTrackGroupInfos().isEmpty()) { - if (!keepContentOnPlayerReset) { - hideArtwork(); - closeShutter(); - } - return; - } - - if (isNewPlayer && !keepContentOnPlayerReset) { - // Hide any video from the previous player. - closeShutter(); - } - if (player.getCurrentTracksInfo().isTypeSelected(C.TRACK_TYPE_VIDEO)) { - // Video enabled, so artwork must be hidden. If the shutter is closed, it will be opened - // in onRenderedFirstFrame(). - hideArtwork(); - return; - } - - // Video disabled so the shutter must be closed. - closeShutter(); - // Display artwork if enabled and available, else hide it. - if (useArtwork()) { - if (setArtworkFromMediaMetadata(player.getMediaMetadata())) { - return; - } - if (setDrawableArtwork(defaultArtwork)) { - return; - } - } - // Artwork disabled or unavailable. - hideArtwork(); - } - - private void updateAspectRatio() { - VideoSize videoSize = player != null ? player.getVideoSize() : VideoSize.UNKNOWN; - int width = videoSize.width; - int height = videoSize.height; - int unappliedRotationDegrees = videoSize.unappliedRotationDegrees; - float videoAspectRatio = - (height == 0 || width == 0) ? 0 : (width * videoSize.pixelWidthHeightRatio) / height; - - if (surfaceView instanceof TextureView) { - // Try to apply rotation transformation when our surface is a TextureView. - if (videoAspectRatio > 0 - && (unappliedRotationDegrees == 90 || unappliedRotationDegrees == 270)) { - // We will apply a rotation 90/270 degree to the output texture of the TextureView. - // In this case, the output video's width and height will be swapped. - videoAspectRatio = 1 / videoAspectRatio; - } - if (textureViewRotation != 0) { - surfaceView.removeOnLayoutChangeListener(componentListener); - } - textureViewRotation = unappliedRotationDegrees; - if (textureViewRotation != 0) { - // The texture view's dimensions might be changed after layout step. - // So add an OnLayoutChangeListener to apply rotation after layout step. - surfaceView.addOnLayoutChangeListener(componentListener); - } - applyTextureViewRotation((TextureView) surfaceView, textureViewRotation); - } - - onContentAspectRatioChanged( - contentFrame, surfaceViewIgnoresVideoAspectRatio ? 0 : videoAspectRatio); - } - - @RequiresNonNull("artworkView") - private boolean setArtworkFromMediaMetadata(MediaMetadata mediaMetadata) { - if (mediaMetadata.artworkData == null) { - return false; - } - Bitmap bitmap = - BitmapFactory.decodeByteArray( - mediaMetadata.artworkData, /* offset= */ 0, mediaMetadata.artworkData.length); - return setDrawableArtwork(new BitmapDrawable(getResources(), bitmap)); - } - - @RequiresNonNull("artworkView") - private boolean setDrawableArtwork(@Nullable Drawable drawable) { - if (drawable != null) { - int drawableWidth = drawable.getIntrinsicWidth(); - int drawableHeight = drawable.getIntrinsicHeight(); - if (drawableWidth > 0 && drawableHeight > 0) { - float artworkAspectRatio = (float) drawableWidth / drawableHeight; - onContentAspectRatioChanged(contentFrame, artworkAspectRatio); - artworkView.setImageDrawable(drawable); - artworkView.setVisibility(VISIBLE); - return true; - } - } - return false; - } - - private void hideArtwork() { - if (artworkView != null) { - artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference. - artworkView.setVisibility(INVISIBLE); - } - } - - private void closeShutter() { - if (shutterView != null) { - shutterView.setVisibility(View.VISIBLE); - } - } - - private void updateBuffering() { - if (bufferingView != null) { - boolean showBufferingSpinner = - player != null - && player.getPlaybackState() == Player.STATE_BUFFERING - && (showBuffering == SHOW_BUFFERING_ALWAYS - || (showBuffering == SHOW_BUFFERING_WHEN_PLAYING && player.getPlayWhenReady())); - bufferingView.setVisibility(showBufferingSpinner ? View.VISIBLE : View.GONE); - } - } - - private void updateErrorMessage() { - if (errorMessageView != null) { - if (customErrorMessage != null) { - errorMessageView.setText(customErrorMessage); - errorMessageView.setVisibility(View.VISIBLE); - return; - } - @Nullable PlaybackException error = player != null ? player.getPlayerError() : null; - if (error != null && errorMessageProvider != null) { - CharSequence errorMessage = errorMessageProvider.getErrorMessage(error).second; - errorMessageView.setText(errorMessage); - errorMessageView.setVisibility(View.VISIBLE); - } else { - errorMessageView.setVisibility(View.GONE); - } - } - } - - private void updateContentDescription() { - if (controller == null || !useController) { - setContentDescription(/* contentDescription= */ null); - } else if (controller.getVisibility() == View.VISIBLE) { - setContentDescription( - /* contentDescription= */ controllerHideOnTouch - ? getResources().getString(R.string.exo_controls_hide) - : null); - } else { - setContentDescription( - /* contentDescription= */ getResources().getString(R.string.exo_controls_show)); - } - } - - private void updateControllerVisibility() { - if (isPlayingAd() && controllerHideDuringAds) { - hideController(); - } else { - maybeShowController(false); - } - } - - @RequiresApi(23) - private static void configureEditModeLogoV23(Resources resources, ImageView logo) { - logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo, null)); - logo.setBackgroundColor(resources.getColor(R.color.exo_edit_mode_background_color, null)); - } - - private static void configureEditModeLogo(Resources resources, ImageView logo) { - logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo)); - logo.setBackgroundColor(resources.getColor(R.color.exo_edit_mode_background_color)); - } - - @SuppressWarnings("ResourceType") - private static void setResizeModeRaw(AspectRatioFrameLayout aspectRatioFrame, int resizeMode) { - aspectRatioFrame.setResizeMode(resizeMode); - } - - /** Applies a texture rotation to a {@link TextureView}. */ - private static void applyTextureViewRotation(TextureView textureView, int textureViewRotation) { - Matrix transformMatrix = new Matrix(); - float textureViewWidth = textureView.getWidth(); - float textureViewHeight = textureView.getHeight(); - if (textureViewWidth != 0 && textureViewHeight != 0 && textureViewRotation != 0) { - float pivotX = textureViewWidth / 2; - float pivotY = textureViewHeight / 2; - transformMatrix.postRotate(textureViewRotation, pivotX, pivotY); - - // After rotation, scale the rotated texture to fit the TextureView size. - RectF originalTextureRect = new RectF(0, 0, textureViewWidth, textureViewHeight); - RectF rotatedTextureRect = new RectF(); - transformMatrix.mapRect(rotatedTextureRect, originalTextureRect); - transformMatrix.postScale( - textureViewWidth / rotatedTextureRect.width(), - textureViewHeight / rotatedTextureRect.height(), - pivotX, - pivotY); - } - textureView.setTransform(transformMatrix); - } - - @SuppressLint("InlinedApi") - private boolean isDpadKey(int keyCode) { - return keyCode == KeyEvent.KEYCODE_DPAD_UP - || keyCode == KeyEvent.KEYCODE_DPAD_UP_RIGHT - || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT - || keyCode == KeyEvent.KEYCODE_DPAD_DOWN_RIGHT - || keyCode == KeyEvent.KEYCODE_DPAD_DOWN - || keyCode == KeyEvent.KEYCODE_DPAD_DOWN_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_UP_LEFT - || keyCode == KeyEvent.KEYCODE_DPAD_CENTER; - } - - private final class ComponentListener - implements Player.Listener, - OnLayoutChangeListener, - OnClickListener, - LegacyPlayerControlView.VisibilityListener { - - private final Period period; - private @Nullable Object lastPeriodUidWithTracks; - - public ComponentListener() { - period = new Period(); - } - - // Player.Listener implementation - - @Override - public void onCues(List cues) { - if (subtitleView != null) { - subtitleView.setCues(cues); - } - } - - @Override - public void onVideoSizeChanged(VideoSize videoSize) { - updateAspectRatio(); - } - - @Override - public void onRenderedFirstFrame() { - if (shutterView != null) { - shutterView.setVisibility(INVISIBLE); - } - } - - @Override - public void onTracksInfoChanged(TracksInfo tracksInfo) { - // Suppress the update if transitioning to an unprepared period within the same window. This - // is necessary to avoid closing the shutter when such a transition occurs. See: - // https://github.com/google/ExoPlayer/issues/5507. - Player player = Assertions.checkNotNull(LegacyPlayerView.this.player); - Timeline timeline = player.getCurrentTimeline(); - if (timeline.isEmpty()) { - lastPeriodUidWithTracks = null; - } else if (!player.getCurrentTracksInfo().getTrackGroupInfos().isEmpty()) { - lastPeriodUidWithTracks = - timeline.getPeriod(player.getCurrentPeriodIndex(), period, /* setIds= */ true).uid; - } else if (lastPeriodUidWithTracks != null) { - int lastPeriodIndexWithTracks = timeline.getIndexOfPeriod(lastPeriodUidWithTracks); - if (lastPeriodIndexWithTracks != C.INDEX_UNSET) { - int lastWindowIndexWithTracks = - timeline.getPeriod(lastPeriodIndexWithTracks, period).windowIndex; - if (player.getCurrentMediaItemIndex() == lastWindowIndexWithTracks) { - // We're in the same media item. Suppress the update. - return; - } - } - lastPeriodUidWithTracks = null; - } - - updateForCurrentTrackSelections(/* isNewPlayer= */ false); - } - - @Override - public void onPlaybackStateChanged(@Player.State int playbackState) { - updateBuffering(); - updateErrorMessage(); - updateControllerVisibility(); - } - - @Override - public void onPlayWhenReadyChanged( - boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) { - updateBuffering(); - updateControllerVisibility(); - } - - @Override - public void onPositionDiscontinuity( - Player.PositionInfo oldPosition, - Player.PositionInfo newPosition, - @DiscontinuityReason int reason) { - if (isPlayingAd() && controllerHideDuringAds) { - hideController(); - } - } - - // OnLayoutChangeListener implementation - - @Override - public void onLayoutChange( - View view, - int left, - int top, - int right, - int bottom, - int oldLeft, - int oldTop, - int oldRight, - int oldBottom) { - applyTextureViewRotation((TextureView) view, textureViewRotation); - } - - // OnClickListener implementation - - @Override - public void onClick(View view) { - toggleControllerVisibility(); - } - - // LegacyPlayerControlView.VisibilityListener implementation - - @Override - public void onVisibilityChange(int visibility) { - updateContentDescription(); - } - } -} diff --git a/libraries/ui/src/main/res/values/attrs_legacy_player_view.xml b/libraries/ui/src/main/res/values/attrs_legacy_player_view.xml deleted file mode 100644 index 47528a29de..0000000000 --- a/libraries/ui/src/main/res/values/attrs_legacy_player_view.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 64b56cf1840487c514439f1ac4f8e732c14bc257 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 8 Feb 2022 21:02:11 +0000 Subject: [PATCH 178/251] Include opusV2JNI Target In Non-Android Builds Allow opusV2JNI to be built-for & included-in non-Android build targets by ifdef'ing out the liblog dependency. Also removed libz and libandroid dependencies. PiperOrigin-RevId: 427269838 --- libraries/decoder_opus/src/main/jni/opus_jni.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/decoder_opus/src/main/jni/opus_jni.cc b/libraries/decoder_opus/src/main/jni/opus_jni.cc index 00443b988e..ce127d19ab 100644 --- a/libraries/decoder_opus/src/main/jni/opus_jni.cc +++ b/libraries/decoder_opus/src/main/jni/opus_jni.cc @@ -14,17 +14,26 @@ * limitations under the License. */ +#ifdef __ANDROID__ #include +#endif #include +#include #include #include "opus.h" // NOLINT #include "opus_multistream.h" // NOLINT +#ifdef __ANDROID__ #define LOG_TAG "opus_jni" #define LOGE(...) \ ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#else // __ANDROID__ +#define LOGE(...) \ + do { \ + } while (0) +#endif // __ANDROID__ #define DECODER_FUNC(RETURN_TYPE, NAME, ...) \ extern "C" { \ From dc0a293c3f10e235662fef676fa9c1433657f35e Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 9 Feb 2022 10:11:28 +0000 Subject: [PATCH 179/251] Merge listeners in ExoPlayerImpl and add missing constants There are two sets of listeners in ExoPlayerImpl at the moment, which can be merged together to use a single ListenerSet. This has the added advantage that the events that were previously sent through the ArraySet get additional guarantees provided by ListenerSet (e.g. correct event ordering and onEvents triggered). Also add missing constants for onEvents to ensure all Player.Listener methods have an corresponding constant. #minor-release PiperOrigin-RevId: 427415349 --- .../java/androidx/media3/common/Player.java | 89 +++++++++++- .../media3/exoplayer/ExoPlayerImpl.java | 132 ++++++++---------- .../analytics/AnalyticsListener.java | 89 ++++++------ 3 files changed, 191 insertions(+), 119 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 7487910ebb..d37e70869f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -714,7 +714,12 @@ public interface Player { */ default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} - /** Called when the playlist {@link MediaMetadata} changes. */ + /** + * Called when the playlist {@link MediaMetadata} changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + */ default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {} /** @@ -932,6 +937,9 @@ public interface Player { /** * Called when the audio session ID changes. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param audioSessionId The audio session ID. */ @UnstableApi @@ -940,6 +948,9 @@ public interface Player { /** * Called when the audio attributes change. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param audioAttributes The audio attributes. */ default void onAudioAttributesChanged(AudioAttributes audioAttributes) {} @@ -947,6 +958,9 @@ public interface Player { /** * Called when the volume changes. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param volume The new volume, with 0 being silence and 1 being unity gain. */ default void onVolumeChanged(float volume) {} @@ -954,19 +968,40 @@ public interface Player { /** * Called when skipping silences is enabled or disabled in the audio stream. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param skipSilenceEnabled Whether skipping silences in the audio stream is enabled. */ default void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {} - /** Called when the device information changes. */ + /** + * Called when the device information changes + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param deviceInfo The new {@link DeviceInfo}. + */ default void onDeviceInfoChanged(DeviceInfo deviceInfo) {} - /** Called when the device volume or mute state changes. */ + /** + * Called when the device volume or mute state changes. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * + * @param volume The new device volume, with 0 being silence and 1 being unity gain. + * @param muted Whether the device is muted. + */ default void onDeviceVolumeChanged(int volume, boolean muted) {} /** * Called each time there's a change in the size of the video being rendered. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param videoSize The new size of the video. */ default void onVideoSizeChanged(VideoSize videoSize) {} @@ -975,6 +1010,9 @@ public interface Player { * Called each time there's a change in the size of the surface onto which the video is being * rendered. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param width The surface width in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if * the video is not rendered onto a surface. * @param height The surface height in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if @@ -985,6 +1023,9 @@ public interface Player { /** * Called when a frame is rendered for the first time since setting the surface, or since the * renderer was reset, or since the stream being rendered was changed. + * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. */ default void onRenderedFirstFrame() {} @@ -994,6 +1035,9 @@ public interface Player { *

    {@code cues} is in ascending order of priority. If any of the cue boxes overlap when * displayed, the {@link Cue} nearer the end of the list should be shown on top. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param cues The {@link Cue Cues}. May be empty. */ default void onCues(List cues) {} @@ -1001,6 +1045,9 @@ public interface Player { /** * Called when there is metadata associated with the current playback time. * + *

    {@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param metadata The metadata. */ @UnstableApi @@ -1252,6 +1299,17 @@ public interface Player { EVENT_SEEK_FORWARD_INCREMENT_CHANGED, EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, + EVENT_AUDIO_ATTRIBUTES_CHANGED, + EVENT_AUDIO_SESSION_ID, + EVENT_VOLUME_CHANGED, + EVENT_SKIP_SILENCE_ENABLED_CHANGED, + EVENT_SURFACE_SIZE_CHANGED, + EVENT_VIDEO_SIZE_CHANGED, + EVENT_RENDERED_FIRST_FRAME, + EVENT_CUES, + EVENT_METADATA, + EVENT_DEVICE_INFO_CHANGED, + EVENT_DEVICE_VOLUME_CHANGED }) @interface Event {} /** {@link #getCurrentTimeline()} changed. */ @@ -1297,6 +1355,31 @@ public interface Player { int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED = 18; /** {@link #getTrackSelectionParameters()} changed. */ int EVENT_TRACK_SELECTION_PARAMETERS_CHANGED = 19; + /** {@link #getAudioAttributes()} changed. */ + int EVENT_AUDIO_ATTRIBUTES_CHANGED = 20; + /** The audio session id was set. */ + int EVENT_AUDIO_SESSION_ID = 21; + /** {@link #getVolume()} changed. */ + int EVENT_VOLUME_CHANGED = 22; + /** Skipping silences in the audio stream is enabled or disabled. */ + int EVENT_SKIP_SILENCE_ENABLED_CHANGED = 23; + /** The size of the surface onto which the video is being rendered changed. */ + int EVENT_SURFACE_SIZE_CHANGED = 24; + /** {@link #getVideoSize()} changed. */ + int EVENT_VIDEO_SIZE_CHANGED = 25; + /** + * A frame is rendered for the first time since setting the surface, or since the renderer was + * reset, or since the stream being rendered was changed. + */ + int EVENT_RENDERED_FIRST_FRAME = 26; + /** {@link #getCurrentCues()} changed. */ + int EVENT_CUES = 27; + /** Metadata associated with the current playback time changed. */ + int EVENT_METADATA = 28; + /** {@link #getDeviceInfo()} changed. */ + int EVENT_DEVICE_INFO_CHANGED = 29; + /** {@link #getDeviceVolume()} changed. */ + int EVENT_DEVICE_VOLUME_CHANGED = 30; /** * Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index bbaa2d85cc..b3343eec90 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -45,9 +45,20 @@ import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; import static androidx.media3.common.Player.DISCONTINUITY_REASON_REMOVE; import static androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK; +import static androidx.media3.common.Player.EVENT_AUDIO_ATTRIBUTES_CHANGED; +import static androidx.media3.common.Player.EVENT_AUDIO_SESSION_ID; +import static androidx.media3.common.Player.EVENT_CUES; +import static androidx.media3.common.Player.EVENT_DEVICE_INFO_CHANGED; +import static androidx.media3.common.Player.EVENT_DEVICE_VOLUME_CHANGED; import static androidx.media3.common.Player.EVENT_MEDIA_METADATA_CHANGED; +import static androidx.media3.common.Player.EVENT_METADATA; import static androidx.media3.common.Player.EVENT_PLAYLIST_METADATA_CHANGED; +import static androidx.media3.common.Player.EVENT_RENDERED_FIRST_FRAME; +import static androidx.media3.common.Player.EVENT_SKIP_SILENCE_ENABLED_CHANGED; +import static androidx.media3.common.Player.EVENT_SURFACE_SIZE_CHANGED; import static androidx.media3.common.Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED; +import static androidx.media3.common.Player.EVENT_VIDEO_SIZE_CHANGED; +import static androidx.media3.common.Player.EVENT_VOLUME_CHANGED; import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO; import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; @@ -191,8 +202,6 @@ import java.util.concurrent.TimeoutException; private final ExoPlayerImplInternal internalPlayer; private final ListenerSet listeners; - // TODO(b/187152483): Remove this once all events are dispatched via ListenerSet. - private final CopyOnWriteArraySet listenerArraySet; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; private final Timeline.Window window; @@ -320,7 +329,6 @@ import java.util.concurrent.TimeoutException; applicationLooper, clock, (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); - listenerArraySet = new CopyOnWriteArraySet<>(); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -1346,27 +1354,28 @@ import java.util.concurrent.TimeoutException; } } - public void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus) { + public void setAudioAttributes(AudioAttributes newAudioAttributes, boolean handleAudioFocus) { verifyApplicationThread(); if (playerReleased) { return; } - if (!Util.areEqual(this.audioAttributes, audioAttributes)) { - this.audioAttributes = audioAttributes; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onAudioAttributesChanged(audioAttributes); - } + if (!Util.areEqual(this.audioAttributes, newAudioAttributes)) { + this.audioAttributes = newAudioAttributes; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, newAudioAttributes); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(newAudioAttributes.usage)); + // Queue event only and flush after updating playWhenReady in case both events are triggered. + listeners.queueEvent( + EVENT_AUDIO_ATTRIBUTES_CHANGED, + listener -> listener.onAudioAttributesChanged(newAudioAttributes)); } - audioFocusManager.setAudioAttributes(handleAudioFocus ? audioAttributes : null); + audioFocusManager.setAudioAttributes(handleAudioFocus ? newAudioAttributes : null); boolean playWhenReady = getPlayWhenReady(); @AudioFocusManager.PlayerCommand int playerCommand = audioFocusManager.updateAudioFocus(playWhenReady, getPlaybackState()); updatePlayWhenReady( playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); + listeners.flushEvents(); } public AudioAttributes getAudioAttributes() { @@ -1392,10 +1401,9 @@ import java.util.concurrent.TimeoutException; this.audioSessionId = audioSessionId; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onAudioSessionIdChanged(audioSessionId); - } + int finalAudioSessionId = audioSessionId; + listeners.sendEvent( + EVENT_AUDIO_SESSION_ID, listener -> listener.onAudioSessionIdChanged(finalAudioSessionId)); } public int getAudioSessionId() { @@ -1419,10 +1427,8 @@ import java.util.concurrent.TimeoutException; } this.volume = volume; sendVolumeToRenderers(); - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onVolumeChanged(volume); - } + float finalVolume = volume; + listeners.sendEvent(EVENT_VOLUME_CHANGED, listener -> listener.onVolumeChanged(finalVolume)); } public float getVolume() { @@ -1433,14 +1439,16 @@ import java.util.concurrent.TimeoutException; return skipSilenceEnabled; } - public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { + public void setSkipSilenceEnabled(boolean newSkipSilenceEnabled) { verifyApplicationThread(); - if (this.skipSilenceEnabled == skipSilenceEnabled) { + if (skipSilenceEnabled == newSkipSilenceEnabled) { return; } - this.skipSilenceEnabled = skipSilenceEnabled; - sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, skipSilenceEnabled); - notifySkipSilenceEnabledChanged(); + skipSilenceEnabled = newSkipSilenceEnabled; + sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_SKIP_SILENCE_ENABLED, newSkipSilenceEnabled); + listeners.sendEvent( + EVENT_SKIP_SILENCE_ENABLED_CHANGED, + listener -> listener.onSkipSilenceEnabledChanged(newSkipSilenceEnabled)); } public AnalyticsCollector getAnalyticsCollector() { @@ -1552,14 +1560,12 @@ import java.util.concurrent.TimeoutException; // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.add(listener); - listenerArraySet.add(listener); } public void removeListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.remove(listener); - listenerArraySet.remove(listener); } public void setHandleWakeLock(boolean handleWakeLock) { @@ -2445,10 +2451,8 @@ import java.util.concurrent.TimeoutException; if (width != surfaceWidth || height != surfaceHeight) { surfaceWidth = width; surfaceHeight = height; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onSurfaceSizeChanged(width, height); - } + listeners.sendEvent( + EVENT_SURFACE_SIZE_CHANGED, listener -> listener.onSurfaceSizeChanged(width, height)); } } @@ -2457,13 +2461,6 @@ import java.util.concurrent.TimeoutException; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_VOLUME, scaledVolume); } - private void notifySkipSilenceEnabledChanged() { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onSkipSilenceEnabledChanged(skipSilenceEnabled); - } - } - private void updatePlayWhenReady( boolean playWhenReady, @AudioFocusManager.PlayerCommand int playerCommand, @@ -2670,22 +2667,17 @@ import java.util.concurrent.TimeoutException; } @Override - public void onVideoSizeChanged(VideoSize videoSize) { - ExoPlayerImpl.this.videoSize = videoSize; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onVideoSizeChanged(videoSize); - } + public void onVideoSizeChanged(VideoSize newVideoSize) { + videoSize = newVideoSize; + listeners.sendEvent( + EVENT_VIDEO_SIZE_CHANGED, listener -> listener.onVideoSizeChanged(newVideoSize)); } @Override public void onRenderedFirstFrame(Object output, long renderTimeMs) { analyticsCollector.onRenderedFirstFrame(output, renderTimeMs); if (videoOutput == output) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onRenderedFirstFrame(); - } + listeners.sendEvent(EVENT_RENDERED_FIRST_FRAME, Listener::onRenderedFirstFrame); } } @@ -2756,12 +2748,14 @@ import java.util.concurrent.TimeoutException; } @Override - public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { - if (ExoPlayerImpl.this.skipSilenceEnabled == skipSilenceEnabled) { + public void onSkipSilenceEnabledChanged(boolean newSkipSilenceEnabled) { + if (skipSilenceEnabled == newSkipSilenceEnabled) { return; } - ExoPlayerImpl.this.skipSilenceEnabled = skipSilenceEnabled; - notifySkipSilenceEnabledChanged(); + skipSilenceEnabled = newSkipSilenceEnabled; + listeners.sendEvent( + EVENT_SKIP_SILENCE_ENABLED_CHANGED, + listener -> listener.onSkipSilenceEnabledChanged(newSkipSilenceEnabled)); } @Override @@ -2779,10 +2773,7 @@ import java.util.concurrent.TimeoutException; @Override public void onCues(List cues) { currentCues = cues; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listeners : listenerArraySet) { - listeners.onCues(cues); - } + listeners.sendEvent(EVENT_CUES, listener -> listener.onCues(cues)); } // MetadataOutput implementation @@ -2794,14 +2785,12 @@ import java.util.concurrent.TimeoutException; MediaMetadata newMediaMetadata = buildUpdatedMediaMetadata(); if (!newMediaMetadata.equals(mediaMetadata)) { mediaMetadata = newMediaMetadata; - listeners.sendEvent( + listeners.queueEvent( EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onMetadata(metadata); - } + listeners.queueEvent(EVENT_METADATA, listener -> listener.onMetadata(metadata)); + listeners.flushEvents(); } // SurfaceHolder.Callback implementation @@ -2891,22 +2880,19 @@ import java.util.concurrent.TimeoutException; @Override public void onStreamTypeChanged(@C.StreamType int streamType) { - DeviceInfo deviceInfo = createDeviceInfo(streamVolumeManager); - if (!deviceInfo.equals(ExoPlayerImpl.this.deviceInfo)) { - ExoPlayerImpl.this.deviceInfo = deviceInfo; - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onDeviceInfoChanged(deviceInfo); - } + DeviceInfo newDeviceInfo = createDeviceInfo(streamVolumeManager); + if (!newDeviceInfo.equals(deviceInfo)) { + deviceInfo = newDeviceInfo; + listeners.sendEvent( + EVENT_DEVICE_INFO_CHANGED, listener -> listener.onDeviceInfoChanged(newDeviceInfo)); } } @Override public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { - // TODO(internal b/187152483): Events should be dispatched via ListenerSet - for (Listener listener : listenerArraySet) { - listener.onDeviceVolumeChanged(streamVolume, streamMuted); - } + listeners.sendEvent( + EVENT_DEVICE_VOLUME_CHANGED, + listener -> listener.onDeviceVolumeChanged(streamVolume, streamMuted)); } // Player.AudioOffloadListener implementation. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 54b631389e..30fa02b6e3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -272,6 +272,28 @@ public interface AnalyticsListener { /** {@link Player#getMaxSeekToPreviousPosition()} changed. */ int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED = Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED; + /** Audio attributes changed. */ + int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED; + /** An audio session id was set. */ + int EVENT_AUDIO_SESSION_ID = Player.EVENT_AUDIO_SESSION_ID; + /** The volume changed. */ + int EVENT_VOLUME_CHANGED = Player.EVENT_VOLUME_CHANGED; + /** Skipping silences was enabled or disabled in the audio stream. */ + int EVENT_SKIP_SILENCE_ENABLED_CHANGED = Player.EVENT_SKIP_SILENCE_ENABLED_CHANGED; + /** The surface size changed. */ + int EVENT_SURFACE_SIZE_CHANGED = Player.EVENT_SURFACE_SIZE_CHANGED; + /** The video size changed. */ + int EVENT_VIDEO_SIZE_CHANGED = Player.EVENT_VIDEO_SIZE_CHANGED; + /** + * The first frame has been rendered since setting the surface, since the renderer was reset or + * since the stream changed. + */ + int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME; + /** Metadata associated with the current playback time was reported. */ + int EVENT_METADATA = Player.EVENT_METADATA; + + // TODO: Forward EVENT_CUES, EVENT_DEVICE_INFO_CHANGED and EVENT_DEVICE_VOLUME_CHANGED. + /** A source started loading data. */ int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events /** A source started completed loading data. */ @@ -286,73 +308,54 @@ public interface AnalyticsListener { int EVENT_UPSTREAM_DISCARDED = 1005; /** The bandwidth estimate has been updated. */ int EVENT_BANDWIDTH_ESTIMATE = 1006; - /** Metadata associated with the current playback time was reported. */ - int EVENT_METADATA = 1007; /** An audio renderer was enabled. */ - int EVENT_AUDIO_ENABLED = 1008; + int EVENT_AUDIO_ENABLED = 1007; /** An audio renderer created a decoder. */ - int EVENT_AUDIO_DECODER_INITIALIZED = 1009; + int EVENT_AUDIO_DECODER_INITIALIZED = 1008; /** The format consumed by an audio renderer changed. */ - int EVENT_AUDIO_INPUT_FORMAT_CHANGED = 1010; + int EVENT_AUDIO_INPUT_FORMAT_CHANGED = 1009; /** The audio position has increased for the first time since the last pause or position reset. */ - int EVENT_AUDIO_POSITION_ADVANCING = 1011; + int EVENT_AUDIO_POSITION_ADVANCING = 1010; /** An audio underrun occurred. */ - int EVENT_AUDIO_UNDERRUN = 1012; + int EVENT_AUDIO_UNDERRUN = 1011; /** An audio renderer released a decoder. */ - int EVENT_AUDIO_DECODER_RELEASED = 1013; + int EVENT_AUDIO_DECODER_RELEASED = 1012; /** An audio renderer was disabled. */ - int EVENT_AUDIO_DISABLED = 1014; - /** An audio session id was set. */ - int EVENT_AUDIO_SESSION_ID = 1015; - /** Audio attributes changed. */ - int EVENT_AUDIO_ATTRIBUTES_CHANGED = 1016; - /** Skipping silences was enabled or disabled in the audio stream. */ - int EVENT_SKIP_SILENCE_ENABLED_CHANGED = 1017; + int EVENT_AUDIO_DISABLED = 1013; /** The audio sink encountered a non-fatal error. */ - int EVENT_AUDIO_SINK_ERROR = 1018; - /** The volume changed. */ - int EVENT_VOLUME_CHANGED = 1019; + int EVENT_AUDIO_SINK_ERROR = 1014; /** A video renderer was enabled. */ - int EVENT_VIDEO_ENABLED = 1020; + int EVENT_VIDEO_ENABLED = 1015; /** A video renderer created a decoder. */ - int EVENT_VIDEO_DECODER_INITIALIZED = 1021; + int EVENT_VIDEO_DECODER_INITIALIZED = 1016; /** The format consumed by a video renderer changed. */ - int EVENT_VIDEO_INPUT_FORMAT_CHANGED = 1022; + int EVENT_VIDEO_INPUT_FORMAT_CHANGED = 1017; /** Video frames have been dropped. */ - int EVENT_DROPPED_VIDEO_FRAMES = 1023; + int EVENT_DROPPED_VIDEO_FRAMES = 1018; /** A video renderer released a decoder. */ - int EVENT_VIDEO_DECODER_RELEASED = 1024; + int EVENT_VIDEO_DECODER_RELEASED = 1019; /** A video renderer was disabled. */ - int EVENT_VIDEO_DISABLED = 1025; + int EVENT_VIDEO_DISABLED = 1020; /** Video frame processing offset data has been reported. */ - int EVENT_VIDEO_FRAME_PROCESSING_OFFSET = 1026; - /** - * The first frame has been rendered since setting the surface, since the renderer was reset or - * since the stream changed. - */ - int EVENT_RENDERED_FIRST_FRAME = 1027; - /** The video size changed. */ - int EVENT_VIDEO_SIZE_CHANGED = 1028; - /** The surface size changed. */ - int EVENT_SURFACE_SIZE_CHANGED = 1029; + int EVENT_VIDEO_FRAME_PROCESSING_OFFSET = 1021; /** A DRM session has been acquired. */ - int EVENT_DRM_SESSION_ACQUIRED = 1030; + int EVENT_DRM_SESSION_ACQUIRED = 1022; /** DRM keys were loaded. */ - int EVENT_DRM_KEYS_LOADED = 1031; + int EVENT_DRM_KEYS_LOADED = 1023; /** A non-fatal DRM session manager error occurred. */ - int EVENT_DRM_SESSION_MANAGER_ERROR = 1032; + int EVENT_DRM_SESSION_MANAGER_ERROR = 1024; /** DRM keys were restored. */ - int EVENT_DRM_KEYS_RESTORED = 1033; + int EVENT_DRM_KEYS_RESTORED = 1025; /** DRM keys were removed. */ - int EVENT_DRM_KEYS_REMOVED = 1034; + int EVENT_DRM_KEYS_REMOVED = 1026; /** A DRM session has been released. */ - int EVENT_DRM_SESSION_RELEASED = 1035; + int EVENT_DRM_SESSION_RELEASED = 1027; /** The player was released. */ - int EVENT_PLAYER_RELEASED = 1036; + int EVENT_PLAYER_RELEASED = 1028; /** The audio codec encountered an error. */ - int EVENT_AUDIO_CODEC_ERROR = 1037; + int EVENT_AUDIO_CODEC_ERROR = 1029; /** The video codec encountered an error. */ - int EVENT_VIDEO_CODEC_ERROR = 1038; + int EVENT_VIDEO_CODEC_ERROR = 1030; /** Time information of an event. */ final class EventTime { From eb6e25b6fcbb1cfc09e21186edba92a03c930141 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 9 Feb 2022 12:55:59 +0000 Subject: [PATCH 180/251] Handle Choreographer.getInstance throwing RuntimeException #minor-release PiperOrigin-RevId: 427439588 --- .../video/VideoFrameReleaseHelper.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java index 2735852372..00aac0e00e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoFrameReleaseHelper.java @@ -622,21 +622,30 @@ public final class VideoFrameReleaseHelper { } private void createChoreographerInstanceInternal() { - choreographer = Choreographer.getInstance(); + try { + choreographer = Choreographer.getInstance(); + } catch (RuntimeException e) { + // See [Internal: b/213926330]. + Log.w(TAG, "Vsync sampling disabled due to platform error", e); + } } private void addObserverInternal() { - observerCount++; - if (observerCount == 1) { - checkNotNull(choreographer).postFrameCallback(this); + if (choreographer != null) { + observerCount++; + if (observerCount == 1) { + choreographer.postFrameCallback(this); + } } } private void removeObserverInternal() { - observerCount--; - if (observerCount == 0) { - checkNotNull(choreographer).removeFrameCallback(this); - sampledVsyncTimeNs = C.TIME_UNSET; + if (choreographer != null) { + observerCount--; + if (observerCount == 0) { + choreographer.removeFrameCallback(this); + sampledVsyncTimeNs = C.TIME_UNSET; + } } } } From cd3bef24b521811b512d5cc404a868ceaadbb615 Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 9 Feb 2022 13:33:00 +0000 Subject: [PATCH 181/251] Simplify StreamRequest.Builder to an Uri Builder and make it public Right now, the option to build an IMA DAI URI programmatically is still package-private. To simplify the process, we can remove the StreamRequest wrapper and directly provide an URI builder. The same class can provide some package-private helper methods to parse the created URI. #minor-release PiperOrigin-RevId: 427445326 --- .../ImaServerSideAdInsertionMediaSource.java | 42 +- .../ImaServerSideAdInsertionUriBuilder.java | 373 ++++++++++++++ .../ServerSideAdInsertionStreamRequest.java | 477 ------------------ ...maServerSideAdInsertionUriBuilderTest.java | 187 +++++++ ...erverSideAdInsertionStreamRequestTest.java | 148 ------ 5 files changed, 583 insertions(+), 644 deletions(-) create mode 100644 libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java delete mode 100644 libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java create mode 100644 libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilderTest.java delete mode 100644 libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequestTest.java diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 03d99f4afe..bf0f957965 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -323,7 +323,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; @Nullable private final AdEventListener applicationAdEventListener; @Nullable private final AdErrorListener applicationAdErrorListener; - private final ServerSideAdInsertionStreamRequest streamRequest; + private final boolean isLiveStream; + private final String adsId; + private final StreamRequest streamRequest; + private final int loadVideoTimeoutMs; private final StreamPlayer streamPlayer; private final Handler mainHandler; private final ComponentListener componentListener; @@ -354,7 +357,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou adPlaybackState = AdPlaybackState.NONE; mainHandler = Util.createHandlerForCurrentLooper(); Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri; - streamRequest = ServerSideAdInsertionStreamRequest.fromUri(streamRequestUri); + isLiveStream = ImaServerSideAdInsertionUriBuilder.isLiveStream(streamRequestUri); + adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri); + loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri); + streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri); } @Override @@ -371,10 +377,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( adsLoader, - streamRequest.getStreamRequest(), + streamRequest, streamPlayer, applicationAdErrorListener, - streamRequest.loadVideoTimeoutMs); + loadVideoTimeoutMs); loader.startLoading( streamManagerLoadable, new StreamManagerLoadableCallback(), @@ -483,7 +489,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou if (!adPlaybackState.equals(AdPlaybackState.NONE) && contentTimeline != null) { ImmutableMap splitAdPlaybackStates = splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline); - streamPlayer.setAdPlaybackStates(streamRequest.adsId, splitAdPlaybackStates, contentTimeline); + streamPlayer.setAdPlaybackStates(adsId, splitAdPlaybackStates, contentTimeline); checkNotNull(serverSideAdInsertionMediaSource).setAdPlaybackStates(splitAdPlaybackStates); } } @@ -499,9 +505,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou contentMediaSourceFactory.createMediaSource(MediaItem.fromUri(contentUri)), componentListener); this.serverSideAdInsertionMediaSource = serverSideAdInsertionMediaSource; - if (streamRequest.isLiveStream()) { + if (isLiveStream) { AdPlaybackState liveAdPlaybackState = - new AdPlaybackState(streamRequest.adsId) + new AdPlaybackState(adsId) .withNewAdGroup(/* adGroupIndex= */ 0, /* adGroupTimeUs= */ C.TIME_END_OF_SOURCE) .withIsServerSideInserted(/* adGroupIndex= */ 0, true); mainHandler.post(() -> setAdPlaybackState(liveAdPlaybackState)); @@ -614,7 +620,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou if (!mediaItem.equals(oldPosition.mediaItem) || !mediaItem.equals(newPosition.mediaItem) - || !streamRequest.adsId.equals( + || !adsId.equals( player .getCurrentTimeline() .getPeriodByUid(checkNotNull(newPosition.periodUid), new Timeline.Period()) @@ -648,7 +654,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public void onMetadata(Metadata metadata) { - if (!isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) { + if (!isCurrentAdPlaying(player, mediaItem, adsId)) { return; } for (int i = 0; i < metadata.length(); i++) { @@ -668,15 +674,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public void onPlaybackStateChanged(@Player.State int state) { - if (state == Player.STATE_ENDED - && isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) { + if (state == Player.STATE_ENDED && isCurrentAdPlaying(player, mediaItem, adsId)) { streamPlayer.onContentCompleted(); } } @Override public void onVolumeChanged(float volume) { - if (!isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) { + if (!isCurrentAdPlaying(player, mediaItem, adsId)) { return; } int volumePct = (int) Math.floor(volume * 100); @@ -692,15 +697,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou switch (event.getType()) { case CUEPOINTS_CHANGED: // CUEPOINTS_CHANGED event is firing multiple times with the same queue points. - if (!streamRequest.isLiveStream() && newAdPlaybackState.equals(AdPlaybackState.NONE)) { + if (!isLiveStream && newAdPlaybackState.equals(AdPlaybackState.NONE)) { newAdPlaybackState = setVodAdGroupPlaceholders( - checkNotNull(streamManager).getCuePoints(), - new AdPlaybackState(streamRequest.adsId)); + checkNotNull(streamManager).getCuePoints(), new AdPlaybackState(adsId)); } break; case LOADED: - if (streamRequest.isLiveStream()) { + if (isLiveStream) { Timeline timeline = player.getCurrentTimeline(); Timeline.Window window = timeline.getWindow(player.getCurrentMediaItemIndex(), new Timeline.Window()); @@ -717,14 +721,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou event.getAd(), currentPeriodPosition, newAdPlaybackState.equals(AdPlaybackState.NONE) - ? new AdPlaybackState(streamRequest.adsId) + ? new AdPlaybackState(adsId) : newAdPlaybackState); } else { newAdPlaybackState = setVodAdInPlaceholder(event.getAd(), newAdPlaybackState); } break; case SKIPPED: - if (!streamRequest.isLiveStream()) { + if (!isLiveStream) { newAdPlaybackState = skipAd(event.getAd(), newAdPlaybackState); } break; @@ -742,7 +746,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou mainHandler.post(() -> setContentTimeline(contentTimeline)); // Defer source refresh to ad playback state update for VOD. Refresh immediately when live // with single period. - return !streamRequest.isLiveStream() || contentTimeline.getPeriodCount() > 1; + return !isLiveStream || contentTimeline.getPeriodCount() > 1; } } diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java new file mode 100644 index 0000000000..c9cf63973e --- /dev/null +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.ima; + +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 android.net.Uri; +import android.text.TextUtils; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.C.ContentType; +import androidx.media3.common.util.UnstableApi; +import com.google.ads.interactivemedia.v3.api.ImaSdkFactory; +import com.google.ads.interactivemedia.v3.api.StreamRequest; +import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat; +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; + +/** + * Builder for URI for IMA DAI streams. The resulting URI can be used to build a {@link + * androidx.media3.common.MediaItem#fromUri(Uri) media item} that can be played by the {@link + * ImaServerSideAdInsertionMediaSource}. + */ +@UnstableApi +public final class ImaServerSideAdInsertionUriBuilder { + + /** The default timeout for loading the video URI, in milliseconds. */ + public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000; + + private static final String IMA_AUTHORITY = "dai.google.com"; + private static final String ADS_ID = "adsId"; + private static final String ASSET_KEY = "assetKey"; + private static final String API_KEY = "apiKey"; + private static final String CONTENT_SOURCE_ID = "contentSourceId"; + private static final String VIDEO_ID = "videoId"; + private static final String AD_TAG_PARAMETERS = "adTagParameters"; + private static final String MANIFEST_SUFFIX = "manifestSuffix"; + private static final String CONTENT_URL = "contentUrl"; + private static final String AUTH_TOKEN = "authToken"; + private static final String STREAM_ACTIVITY_MONITOR_ID = "streamActivityMonitorId"; + private static final String FORMAT = "format"; + private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs"; + + @Nullable private String adsId; + @Nullable private String assetKey; + @Nullable private String apiKey; + @Nullable private String contentSourceId; + @Nullable private String videoId; + @Nullable private String manifestSuffix; + @Nullable private String contentUrl; + @Nullable private String authToken; + @Nullable private String streamActivityMonitorId; + private ImmutableMap adTagParameters; + public @ContentType int format; + private int loadVideoTimeoutMs; + + /** Creates a new instance. */ + public ImaServerSideAdInsertionUriBuilder() { + adTagParameters = ImmutableMap.of(); + loadVideoTimeoutMs = DEFAULT_LOAD_VIDEO_TIMEOUT_MS; + format = C.TYPE_OTHER; + } + + /** + * An opaque identifier for associated ad playback state, or {@code null} if the {@link + * #setAssetKey(String) asset key} (for live) or {@link #setVideoId(String) video id} (for VOD) + * should be used as the ads identifier. + * + * @param adsId The ads identifier. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setAdsId(String adsId) { + this.adsId = adsId; + return this; + } + + /** + * The stream request asset key used for live streams. + * + * @param assetKey Live stream asset key. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setAssetKey(@Nullable String assetKey) { + this.assetKey = assetKey; + return this; + } + + /** + * Sets the stream request authorization token. Used in place of {@link #setApiKey(String) the API + * key} for stricter content authorization. The publisher can control individual content streams + * authorizations based on this token. + * + * @param authToken Live stream authorization token. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setAuthToken(@Nullable String authToken) { + this.authToken = authToken; + return this; + } + + /** + * The stream request content source ID used for on-demand streams. + * + * @param contentSourceId VOD stream content source id. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setContentSourceId(@Nullable String contentSourceId) { + this.contentSourceId = contentSourceId; + return this; + } + + /** + * The stream request video ID used for on-demand streams. + * + * @param videoId VOD stream video id. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setVideoId(@Nullable String videoId) { + this.videoId = videoId; + return this; + } + + /** + * Sets the format of the stream request. + * + * @param format VOD or live stream type. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setFormat(@ContentType int format) { + checkArgument(format == C.TYPE_DASH || format == C.TYPE_HLS); + this.format = format; + return this; + } + + /** + * The stream request API key. This is used for content authentication. The API key is provided to + * the publisher to unlock their content. It's a security measure used to verify the applications + * that are attempting to access the content. + * + * @param apiKey Stream api key. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setApiKey(@Nullable String apiKey) { + this.apiKey = apiKey; + return this; + } + + /** + * Sets the ID to be used to debug the stream with the stream activity monitor. This is used to + * provide a convenient way to allow publishers to find a stream log in the stream activity + * monitor tool. + * + * @param streamActivityMonitorId ID for debugging the stream with the stream activity monitor. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setStreamActivityMonitorId( + @Nullable String streamActivityMonitorId) { + this.streamActivityMonitorId = streamActivityMonitorId; + return this; + } + + /** + * Sets the overridable ad tag parameters on the stream request. Supply targeting parameters to your + * stream provides more information. + * + *

    You can use the dai-ot and dai-ov parameters for stream variant preference. See Override Stream Variant Parameters + * for more information. + * + * @param adTagParameters A map of extra parameters to pass to the ad server. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setAdTagParameters( + Map adTagParameters) { + this.adTagParameters = ImmutableMap.copyOf(adTagParameters); + return this; + } + + /** + * Sets the optional stream manifest's suffix, which will be appended to the stream manifest's + * URL. The provided string must be URL-encoded and must not include a leading question mark. + * + * @param manifestSuffix Stream manifest's suffix. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setManifestSuffix(@Nullable String manifestSuffix) { + this.manifestSuffix = manifestSuffix; + return this; + } + + /** + * Specifies the deep link to the content's screen. If provided, this parameter is passed to the + * OM SDK. See Android + * documentation for more information. + * + * @param contentUrl Deep link to the content's screen. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setContentUrl(@Nullable String contentUrl) { + this.contentUrl = contentUrl; + return this; + } + + /** + * Sets the duration after which resolving the video URI should time out, in milliseconds. + * + *

    The default is {@link #DEFAULT_LOAD_VIDEO_TIMEOUT_MS} milliseconds. + * + * @param loadVideoTimeoutMs The timeout after which to give up resolving the video URI. + * @return This instance, for convenience. + */ + public ImaServerSideAdInsertionUriBuilder setLoadVideoTimeoutMs(int loadVideoTimeoutMs) { + this.loadVideoTimeoutMs = loadVideoTimeoutMs; + return this; + } + + /** + * Builds a URI with the builder's current values. + * + * @return The build {@link Uri}. + * @throws IllegalStateException If the builder has missing or invalid inputs. + */ + public Uri build() { + checkState( + (TextUtils.isEmpty(assetKey) + && !TextUtils.isEmpty(contentSourceId) + && !TextUtils.isEmpty(videoId)) + || (!TextUtils.isEmpty(assetKey) + && TextUtils.isEmpty(contentSourceId) + && TextUtils.isEmpty(videoId))); + checkState(format != C.TYPE_OTHER); + @Nullable String adsId = this.adsId; + if (adsId == null) { + adsId = assetKey != null ? assetKey : checkNotNull(videoId); + } + Uri.Builder dataUriBuilder = new Uri.Builder(); + dataUriBuilder.scheme(C.SSAI_SCHEME); + dataUriBuilder.authority(IMA_AUTHORITY); + dataUriBuilder.appendQueryParameter(ADS_ID, adsId); + if (loadVideoTimeoutMs != DEFAULT_LOAD_VIDEO_TIMEOUT_MS) { + dataUriBuilder.appendQueryParameter( + LOAD_VIDEO_TIMEOUT_MS, String.valueOf(loadVideoTimeoutMs)); + } + if (assetKey != null) { + dataUriBuilder.appendQueryParameter(ASSET_KEY, assetKey); + } + if (apiKey != null) { + dataUriBuilder.appendQueryParameter(API_KEY, apiKey); + } + if (contentSourceId != null) { + dataUriBuilder.appendQueryParameter(CONTENT_SOURCE_ID, contentSourceId); + } + if (videoId != null) { + dataUriBuilder.appendQueryParameter(VIDEO_ID, videoId); + } + if (manifestSuffix != null) { + dataUriBuilder.appendQueryParameter(MANIFEST_SUFFIX, manifestSuffix); + } + if (contentUrl != null) { + dataUriBuilder.appendQueryParameter(CONTENT_URL, contentUrl); + } + if (authToken != null) { + dataUriBuilder.appendQueryParameter(AUTH_TOKEN, authToken); + } + if (streamActivityMonitorId != null) { + dataUriBuilder.appendQueryParameter(STREAM_ACTIVITY_MONITOR_ID, streamActivityMonitorId); + } + if (!adTagParameters.isEmpty()) { + Uri.Builder adTagParametersUriBuilder = new Uri.Builder(); + for (Map.Entry entry : adTagParameters.entrySet()) { + adTagParametersUriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); + } + dataUriBuilder.appendQueryParameter( + AD_TAG_PARAMETERS, adTagParametersUriBuilder.build().toString()); + } + dataUriBuilder.appendQueryParameter(FORMAT, String.valueOf(format)); + return dataUriBuilder.build(); + } + + /** Returns whether the provided request is for a live stream or false if it is a VOD stream. */ + /* package */ static boolean isLiveStream(Uri uri) { + return !TextUtils.isEmpty(uri.getQueryParameter(ASSET_KEY)); + } + + /** Returns the opaque adsId for this stream. */ + /* package */ static String getAdsId(Uri uri) { + return checkNotNull(uri.getQueryParameter(ADS_ID)); + } + + /** Returns the video load timeout in milliseconds. */ + /* package */ static int getLoadVideoTimeoutMs(Uri uri) { + @Nullable String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS); + return TextUtils.isEmpty(adsLoaderTimeoutUs) + ? DEFAULT_LOAD_VIDEO_TIMEOUT_MS + : Integer.parseInt(adsLoaderTimeoutUs); + } + + /** Returns the corresponding {@link StreamRequest}. */ + @SuppressWarnings("nullness") // Required for making nullness test pass for library_with_ima_sdk. + /* package */ static StreamRequest createStreamRequest(Uri uri) { + if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) { + throw new IllegalArgumentException("Invalid URI scheme or authority."); + } + StreamRequest streamRequest; + // Required params. + @Nullable String assetKey = uri.getQueryParameter(ASSET_KEY); + @Nullable String apiKey = uri.getQueryParameter(API_KEY); + @Nullable String contentSourceId = uri.getQueryParameter(CONTENT_SOURCE_ID); + @Nullable String videoId = uri.getQueryParameter(VIDEO_ID); + if (!TextUtils.isEmpty(assetKey)) { + streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey); + } else { + streamRequest = + ImaSdkFactory.getInstance() + .createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey); + } + int format = Integer.parseInt(uri.getQueryParameter(FORMAT)); + if (format == C.TYPE_DASH) { + streamRequest.setFormat(StreamFormat.DASH); + } else if (format == C.TYPE_HLS) { + streamRequest.setFormat(StreamFormat.HLS); + } else { + throw new IllegalArgumentException("Unsupported stream format:" + format); + } + // Optional params. + @Nullable String adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS); + if (!TextUtils.isEmpty(adTagParametersValue)) { + Map adTagParameters = new HashMap<>(); + Uri adTagParametersUri = Uri.parse(adTagParametersValue); + for (String paramName : adTagParametersUri.getQueryParameterNames()) { + String singleAdTagParameterValue = adTagParametersUri.getQueryParameter(paramName); + if (!TextUtils.isEmpty(singleAdTagParameterValue)) { + adTagParameters.put(paramName, singleAdTagParameterValue); + } + } + streamRequest.setAdTagParameters(adTagParameters); + } + @Nullable String manifestSuffix = uri.getQueryParameter(MANIFEST_SUFFIX); + if (manifestSuffix != null) { + streamRequest.setManifestSuffix(manifestSuffix); + } + @Nullable String contentUrl = uri.getQueryParameter(CONTENT_URL); + if (contentUrl != null) { + streamRequest.setContentUrl(contentUrl); + } + @Nullable String authToken = uri.getQueryParameter(AUTH_TOKEN); + if (authToken != null) { + streamRequest.setAuthToken(authToken); + } + @Nullable String streamActivityMonitorId = uri.getQueryParameter(STREAM_ACTIVITY_MONITOR_ID); + if (streamActivityMonitorId != null) { + streamRequest.setStreamActivityMonitorId(streamActivityMonitorId); + } + return streamRequest; + } +} diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java deleted file mode 100644 index d8a2345210..0000000000 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequest.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.ima; - -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 android.net.Uri; -import android.text.TextUtils; -import androidx.annotation.Nullable; -import androidx.media3.common.C; -import androidx.media3.common.C.ContentType; -import com.google.ads.interactivemedia.v3.api.ImaSdkFactory; -import com.google.ads.interactivemedia.v3.api.StreamRequest; -import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableMap; -import java.util.HashMap; -import java.util.Map; - -/** Stream request data for an IMA DAI stream. */ -/* package */ final class ServerSideAdInsertionStreamRequest { - - /** The default timeout for loading the video URI, in milliseconds. */ - public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000; - - /** Builds a {@link ServerSideAdInsertionStreamRequest}. */ - public static final class Builder { - - @Nullable private String adsId; - @Nullable private String assetKey; - @Nullable private String apiKey; - @Nullable private String contentSourceId; - @Nullable private String videoId; - @Nullable private String manifestSuffix; - @Nullable private String contentUrl; - @Nullable private String authToken; - @Nullable private String streamActivityMonitorId; - private ImmutableMap adTagParameters; - public @ContentType int format = C.TYPE_HLS; - private int loadVideoTimeoutMs; - - /** Creates a new instance. */ - public Builder() { - adTagParameters = ImmutableMap.of(); - loadVideoTimeoutMs = DEFAULT_LOAD_VIDEO_TIMEOUT_MS; - } - - /** - * An opaque identifier for associated ad playback state, or {@code null} if the {@link - * #setAssetKey(String) asset key} (for live) or {@link #setVideoId(String) video id} (for VOD) - * should be used as the ads identifier. - * - * @param adsId The ads identifier. - * @return This instance, for convenience. - */ - public Builder setAdsId(String adsId) { - this.adsId = adsId; - return this; - } - - /** - * The stream request asset key used for live streams. - * - * @param assetKey Live stream asset key. - * @return This instance, for convenience. - */ - public Builder setAssetKey(@Nullable String assetKey) { - this.assetKey = assetKey; - return this; - } - - /** - * Sets the stream request authorization token. Used in place of {@link #setApiKey(String) the - * API key} for stricter content authorization. The publisher can control individual content - * streams authorizations based on this token. - * - * @param authToken Live stream authorization token. - * @return This instance, for convenience. - */ - public Builder setAuthToken(@Nullable String authToken) { - this.authToken = authToken; - return this; - } - - /** - * The stream request content source ID used for on-demand streams. - * - * @param contentSourceId VOD stream content source id. - * @return This instance, for convenience. - */ - public Builder setContentSourceId(@Nullable String contentSourceId) { - this.contentSourceId = contentSourceId; - return this; - } - - /** - * The stream request video ID used for on-demand streams. - * - * @param videoId VOD stream video id. - * @return This instance, for convenience. - */ - public Builder setVideoId(@Nullable String videoId) { - this.videoId = videoId; - return this; - } - - /** - * Sets the format of the stream request. - * - * @param format VOD or live stream type. - * @return This instance, for convenience. - */ - public Builder setFormat(@ContentType int format) { - checkArgument(format == C.TYPE_DASH || format == C.TYPE_HLS); - this.format = format; - return this; - } - - /** - * The stream request API key. This is used for content authentication. The API key is provided - * to the publisher to unlock their content. It's a security measure used to verify the - * applications that are attempting to access the content. - * - * @param apiKey Stream api key. - * @return This instance, for convenience. - */ - public Builder setApiKey(@Nullable String apiKey) { - this.apiKey = apiKey; - return this; - } - - /** - * Sets the ID to be used to debug the stream with the stream activity monitor. This is used to - * provide a convenient way to allow publishers to find a stream log in the stream activity - * monitor tool. - * - * @param streamActivityMonitorId ID for debugging the stream with the stream activity monitor. - * @return This instance, for convenience. - */ - public Builder setStreamActivityMonitorId(@Nullable String streamActivityMonitorId) { - this.streamActivityMonitorId = streamActivityMonitorId; - return this; - } - - /** - * Sets the overridable ad tag parameters on the stream request. Supply targeting parameters to your - * stream provides more information. - * - *

    You can use the dai-ot and dai-ov parameters for stream variant preference. See Override Stream Variant Parameters - * for more information. - * - * @param adTagParameters A map of extra parameters to pass to the ad server. - * @return This instance, for convenience. - */ - public Builder setAdTagParameters(Map adTagParameters) { - this.adTagParameters = ImmutableMap.copyOf(adTagParameters); - return this; - } - - /** - * Sets the optional stream manifest's suffix, which will be appended to the stream manifest's - * URL. The provided string must be URL-encoded and must not include a leading question mark. - * - * @param manifestSuffix Stream manifest's suffix. - * @return This instance, for convenience. - */ - public Builder setManifestSuffix(@Nullable String manifestSuffix) { - this.manifestSuffix = manifestSuffix; - return this; - } - - /** - * Specifies the deep link to the content's screen. If provided, this parameter is passed to the - * OM SDK. See Android - * documentation for more information. - * - * @param contentUrl Deep link to the content's screen. - * @return This instance, for convenience. - */ - public Builder setContentUrl(@Nullable String contentUrl) { - this.contentUrl = contentUrl; - return this; - } - - /** - * Sets the duration after which resolving the video URI should time out, in milliseconds. - * - *

    The default is {@link #DEFAULT_LOAD_VIDEO_TIMEOUT_MS} milliseconds. - * - * @param loadVideoTimeoutMs The timeout after which to give up resolving the video URI. - * @return This instance, for convenience. - */ - public Builder setLoadVideoTimeoutMs(int loadVideoTimeoutMs) { - this.loadVideoTimeoutMs = loadVideoTimeoutMs; - return this; - } - - /** - * Builds a {@link ServerSideAdInsertionStreamRequest} with the builder's current values. - * - * @return The build {@link ServerSideAdInsertionStreamRequest}. - * @throws IllegalStateException If request has missing or invalid inputs. - */ - public ServerSideAdInsertionStreamRequest build() { - checkState( - (TextUtils.isEmpty(assetKey) - && !TextUtils.isEmpty(contentSourceId) - && !TextUtils.isEmpty(videoId)) - || (!TextUtils.isEmpty(assetKey) - && TextUtils.isEmpty(contentSourceId) - && TextUtils.isEmpty(videoId))); - @Nullable String adsId = this.adsId; - if (adsId == null) { - adsId = assetKey != null ? assetKey : checkNotNull(videoId); - } - return new ServerSideAdInsertionStreamRequest( - adsId, - assetKey, - apiKey, - contentSourceId, - videoId, - adTagParameters, - manifestSuffix, - contentUrl, - authToken, - streamActivityMonitorId, - format, - loadVideoTimeoutMs); - } - } - - private static final String IMA_AUTHORITY = "dai.google.com"; - private static final String ADS_ID = "adsId"; - private static final String ASSET_KEY = "assetKey"; - private static final String API_KEY = "apiKey"; - private static final String CONTENT_SOURCE_ID = "contentSourceId"; - private static final String VIDEO_ID = "videoId"; - private static final String AD_TAG_PARAMETERS = "adTagParameters"; - private static final String MANIFEST_SUFFIX = "manifestSuffix"; - private static final String CONTENT_URL = "contentUrl"; - private static final String AUTH_TOKEN = "authToken"; - private static final String STREAM_ACTIVITY_MONITOR_ID = "streamActivityMonitorId"; - private static final String FORMAT = "format"; - private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs"; - - public final String adsId; - @Nullable public final String assetKey; - @Nullable public final String apiKey; - @Nullable public final String contentSourceId; - @Nullable public final String videoId; - public final ImmutableMap adTagParameters; - @Nullable public final String manifestSuffix; - @Nullable public final String contentUrl; - @Nullable public final String authToken; - @Nullable public final String streamActivityMonitorId; - public @ContentType int format = C.TYPE_HLS; - public final int loadVideoTimeoutMs; - - private ServerSideAdInsertionStreamRequest( - String adsId, - @Nullable String assetKey, - @Nullable String apiKey, - @Nullable String contentSourceId, - @Nullable String videoId, - ImmutableMap adTagParameters, - @Nullable String manifestSuffix, - @Nullable String contentUrl, - @Nullable String authToken, - @Nullable String streamActivityMonitorId, - @ContentType int format, - int loadVideoTimeoutMs) { - this.adsId = adsId; - this.assetKey = assetKey; - this.apiKey = apiKey; - this.contentSourceId = contentSourceId; - this.videoId = videoId; - this.adTagParameters = adTagParameters; - this.manifestSuffix = manifestSuffix; - this.contentUrl = contentUrl; - this.authToken = authToken; - this.streamActivityMonitorId = streamActivityMonitorId; - this.format = format; - this.loadVideoTimeoutMs = loadVideoTimeoutMs; - } - - /** Returns whether this request is for a live stream or false if it is a VOD stream. */ - public boolean isLiveStream() { - return !TextUtils.isEmpty(assetKey); - } - - /** Returns the corresponding {@link StreamRequest}. */ - @SuppressWarnings("nullness") // Required for making nullness test pass for library_with_ima_sdk. - public StreamRequest getStreamRequest() { - StreamRequest streamRequest; - if (!TextUtils.isEmpty(assetKey)) { - streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey); - } else { - streamRequest = - ImaSdkFactory.getInstance() - .createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey); - } - if (format == C.TYPE_DASH) { - streamRequest.setFormat(StreamFormat.DASH); - } else if (format == C.TYPE_HLS) { - streamRequest.setFormat(StreamFormat.HLS); - } - // Optional params. - streamRequest.setAdTagParameters(adTagParameters); - if (manifestSuffix != null) { - streamRequest.setManifestSuffix(manifestSuffix); - } - if (contentUrl != null) { - streamRequest.setContentUrl(contentUrl); - } - if (authToken != null) { - streamRequest.setAuthToken(authToken); - } - if (streamActivityMonitorId != null) { - streamRequest.setStreamActivityMonitorId(streamActivityMonitorId); - } - return streamRequest; - } - - /** Returns a corresponding {@link Uri}. */ - public Uri toUri() { - Uri.Builder dataUriBuilder = new Uri.Builder(); - dataUriBuilder.scheme(C.SSAI_SCHEME); - dataUriBuilder.authority(IMA_AUTHORITY); - dataUriBuilder.appendQueryParameter(ADS_ID, adsId); - if (loadVideoTimeoutMs != DEFAULT_LOAD_VIDEO_TIMEOUT_MS) { - dataUriBuilder.appendQueryParameter( - LOAD_VIDEO_TIMEOUT_MS, String.valueOf(loadVideoTimeoutMs)); - } - if (assetKey != null) { - dataUriBuilder.appendQueryParameter(ASSET_KEY, assetKey); - } - if (apiKey != null) { - dataUriBuilder.appendQueryParameter(API_KEY, apiKey); - } - if (contentSourceId != null) { - dataUriBuilder.appendQueryParameter(CONTENT_SOURCE_ID, contentSourceId); - } - if (videoId != null) { - dataUriBuilder.appendQueryParameter(VIDEO_ID, videoId); - } - if (manifestSuffix != null) { - dataUriBuilder.appendQueryParameter(MANIFEST_SUFFIX, manifestSuffix); - } - if (contentUrl != null) { - dataUriBuilder.appendQueryParameter(CONTENT_URL, contentUrl); - } - if (authToken != null) { - dataUriBuilder.appendQueryParameter(AUTH_TOKEN, authToken); - } - if (streamActivityMonitorId != null) { - dataUriBuilder.appendQueryParameter(STREAM_ACTIVITY_MONITOR_ID, streamActivityMonitorId); - } - if (!adTagParameters.isEmpty()) { - Uri.Builder adTagParametersUriBuilder = new Uri.Builder(); - for (Map.Entry entry : adTagParameters.entrySet()) { - adTagParametersUriBuilder.appendQueryParameter(entry.getKey(), entry.getValue()); - } - dataUriBuilder.appendQueryParameter( - AD_TAG_PARAMETERS, adTagParametersUriBuilder.build().toString()); - } - dataUriBuilder.appendQueryParameter(FORMAT, String.valueOf(format)); - return dataUriBuilder.build(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ServerSideAdInsertionStreamRequest)) { - return false; - } - ServerSideAdInsertionStreamRequest that = (ServerSideAdInsertionStreamRequest) o; - return format == that.format - && loadVideoTimeoutMs == that.loadVideoTimeoutMs - && Objects.equal(adsId, that.adsId) - && Objects.equal(assetKey, that.assetKey) - && Objects.equal(apiKey, that.apiKey) - && Objects.equal(contentSourceId, that.contentSourceId) - && Objects.equal(videoId, that.videoId) - && Objects.equal(adTagParameters, that.adTagParameters) - && Objects.equal(manifestSuffix, that.manifestSuffix) - && Objects.equal(contentUrl, that.contentUrl) - && Objects.equal(authToken, that.authToken) - && Objects.equal(streamActivityMonitorId, that.streamActivityMonitorId); - } - - @Override - public int hashCode() { - return Objects.hashCode( - adsId, - assetKey, - apiKey, - contentSourceId, - videoId, - adTagParameters, - manifestSuffix, - contentUrl, - authToken, - streamActivityMonitorId, - loadVideoTimeoutMs, - format); - } - - /** - * Creates a {@link ServerSideAdInsertionStreamRequest} for the given URI. - * - * @param uri The URI. - * @return An {@link ServerSideAdInsertionStreamRequest} for the given URI. - * @throws IllegalStateException If uri has missing or invalid inputs. - */ - public static ServerSideAdInsertionStreamRequest fromUri(Uri uri) { - ServerSideAdInsertionStreamRequest.Builder request = - new ServerSideAdInsertionStreamRequest.Builder(); - if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) { - throw new IllegalArgumentException("Invalid URI scheme or authority."); - } - request.setAdsId(checkNotNull(uri.getQueryParameter(ADS_ID))); - request.setAssetKey(uri.getQueryParameter(ASSET_KEY)); - request.setApiKey(uri.getQueryParameter(API_KEY)); - request.setContentSourceId(uri.getQueryParameter(CONTENT_SOURCE_ID)); - request.setVideoId(uri.getQueryParameter(VIDEO_ID)); - request.setManifestSuffix(uri.getQueryParameter(MANIFEST_SUFFIX)); - request.setContentUrl(uri.getQueryParameter(CONTENT_URL)); - request.setAuthToken(uri.getQueryParameter(AUTH_TOKEN)); - request.setStreamActivityMonitorId(uri.getQueryParameter(STREAM_ACTIVITY_MONITOR_ID)); - String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS); - request.setLoadVideoTimeoutMs( - TextUtils.isEmpty(adsLoaderTimeoutUs) - ? DEFAULT_LOAD_VIDEO_TIMEOUT_MS - : Integer.parseInt(adsLoaderTimeoutUs)); - String formatValue = uri.getQueryParameter(FORMAT); - if (!TextUtils.isEmpty(formatValue)) { - request.setFormat(Integer.parseInt(formatValue)); - } - Map adTagParameters; - String adTagParametersValue; - String singleAdTagParameterValue; - if (uri.getQueryParameter(AD_TAG_PARAMETERS) != null) { - adTagParameters = new HashMap<>(); - adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS); - if (!TextUtils.isEmpty(adTagParametersValue)) { - Uri adTagParametersUri = Uri.parse(adTagParametersValue); - for (String paramName : adTagParametersUri.getQueryParameterNames()) { - singleAdTagParameterValue = adTagParametersUri.getQueryParameter(paramName); - if (!TextUtils.isEmpty(singleAdTagParameterValue)) { - adTagParameters.put(paramName, singleAdTagParameterValue); - } - } - } - request.setAdTagParameters(adTagParameters); - } - return request.build(); - } -} diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilderTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilderTest.java new file mode 100644 index 0000000000..e021332f63 --- /dev/null +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilderTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.ima; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; +import androidx.media3.common.C; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.ads.interactivemedia.v3.api.StreamRequest; +import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat; +import java.util.HashMap; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link ImaServerSideAdInsertionUriBuilder}. */ +@RunWith(AndroidJUnit4.class) +public final class ImaServerSideAdInsertionUriBuilderTest { + + private static final String ADS_ID = "testAdsId"; + private static final String ASSET_KEY = "testAssetKey"; + private static final String API_KEY = "testApiKey"; + private static final String CONTENT_SOURCE_ID = "testContentSourceId"; + private static final String VIDEO_ID = "testVideoId"; + private static final String MANIFEST_SUFFIX = "testManifestSuffix"; + private static final String CONTENT_URL = + "http://google.com/contentUrl?queryParamName=queryParamValue"; + private static final String AUTH_TOKEN = "testAuthToken"; + private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId"; + private static final int ADS_LOADER_TIMEOUT_MS = 2; + private static final Map adTagParameters = new HashMap<>(); + + static { + adTagParameters.put("param1", "value1"); + adTagParameters.put("param2", "value2"); + } + + @Test + public void build_live_correctUriParsing() { + ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder(); + builder.setAdsId(ADS_ID); + builder.setAssetKey(ASSET_KEY); + builder.setApiKey(API_KEY); + builder.setManifestSuffix(MANIFEST_SUFFIX); + builder.setContentUrl(CONTENT_URL); + builder.setAuthToken(AUTH_TOKEN); + builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); + builder.setFormat(C.TYPE_HLS); + builder.setAdTagParameters(adTagParameters); + builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); + Uri uri = builder.build(); + + StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri); + assertThat(streamRequest.getAssetKey()).isEqualTo(ASSET_KEY); + assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY); + assertThat(streamRequest.getManifestSuffix()).isEqualTo(MANIFEST_SUFFIX); + assertThat(streamRequest.getContentUrl()).isEqualTo(CONTENT_URL); + assertThat(streamRequest.getAuthToken()).isEqualTo(AUTH_TOKEN); + assertThat(streamRequest.getStreamActivityMonitorId()).isEqualTo(STREAM_ACTIVITY_MONITOR_ID); + assertThat(streamRequest.getFormat()).isEqualTo(StreamFormat.HLS); + assertThat(streamRequest.getAdTagParameters()).isEqualTo(adTagParameters); + + boolean isLive = ImaServerSideAdInsertionUriBuilder.isLiveStream(uri); + assertThat(isLive).isTrue(); + + String adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(uri); + assertThat(adsId).isEqualTo(ADS_ID); + + int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri); + assertThat(loadVideoTimeoutMs).isEqualTo(ADS_LOADER_TIMEOUT_MS); + } + + @Test + public void build_vod_correctUriParsing() { + ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder(); + builder.setAdsId(ADS_ID); + builder.setApiKey(API_KEY); + builder.setContentSourceId(CONTENT_SOURCE_ID); + builder.setVideoId(VIDEO_ID); + builder.setManifestSuffix(MANIFEST_SUFFIX); + builder.setContentUrl(CONTENT_URL); + builder.setAuthToken(AUTH_TOKEN); + builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); + builder.setFormat(C.TYPE_DASH); + builder.setAdTagParameters(adTagParameters); + builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); + Uri uri = builder.build(); + + StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri); + assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY); + assertThat(streamRequest.getContentSourceId()).isEqualTo(CONTENT_SOURCE_ID); + assertThat(streamRequest.getVideoId()).isEqualTo(VIDEO_ID); + assertThat(streamRequest.getManifestSuffix()).isEqualTo(MANIFEST_SUFFIX); + assertThat(streamRequest.getContentUrl()).isEqualTo(CONTENT_URL); + assertThat(streamRequest.getAuthToken()).isEqualTo(AUTH_TOKEN); + assertThat(streamRequest.getStreamActivityMonitorId()).isEqualTo(STREAM_ACTIVITY_MONITOR_ID); + assertThat(streamRequest.getFormat()).isEqualTo(StreamFormat.DASH); + assertThat(streamRequest.getAdTagParameters()).isEqualTo(adTagParameters); + + boolean isLive = ImaServerSideAdInsertionUriBuilder.isLiveStream(uri); + assertThat(isLive).isFalse(); + + String adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(uri); + assertThat(adsId).isEqualTo(ADS_ID); + + int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri); + assertThat(loadVideoTimeoutMs).isEqualTo(ADS_LOADER_TIMEOUT_MS); + } + + @Test + public void build_vodWithNoAdsId_usesVideoIdAsDefault() { + ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder(); + builder.setContentSourceId(CONTENT_SOURCE_ID); + builder.setVideoId(VIDEO_ID); + builder.setFormat(C.TYPE_DASH); + + Uri streamRequest = builder.build(); + + assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(VIDEO_ID); + assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(VIDEO_ID); + } + + @Test + public void build_liveWithNoAdsId_usesAssetKeyAsDefault() { + ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder(); + builder.setAssetKey(ASSET_KEY); + builder.setFormat(C.TYPE_DASH); + + Uri streamRequest = builder.build(); + + assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(ASSET_KEY); + assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(ASSET_KEY); + } + + @Test + public void build_assetKeyWithVideoId_throwsIllegalStateException() { + ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder(); + requestBuilder.setAssetKey(ASSET_KEY); + requestBuilder.setVideoId(VIDEO_ID); + + Assert.assertThrows(IllegalStateException.class, requestBuilder::build); + } + + @Test + public void build_assetKeyWithContentSource_throwsIllegalStateException() { + ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder(); + requestBuilder.setAssetKey(ASSET_KEY); + requestBuilder.setContentSourceId(CONTENT_SOURCE_ID); + + Assert.assertThrows(IllegalStateException.class, requestBuilder::build); + } + + @Test + public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() { + ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder(); + + Assert.assertThrows(IllegalStateException.class, requestBuilder::build); + } + + @Test + public void build_withoutLoadVideoTimeoutMs_usesDefaultTimeout() { + Uri uri = + new ImaServerSideAdInsertionUriBuilder() + .setAssetKey(ASSET_KEY) + .setFormat(C.TYPE_DASH) + .build(); + + int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri); + assertThat(loadVideoTimeoutMs) + .isEqualTo(ImaServerSideAdInsertionUriBuilder.DEFAULT_LOAD_VIDEO_TIMEOUT_MS); + } +} diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequestTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequestTest.java deleted file mode 100644 index 78b71c2f06..0000000000 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ServerSideAdInsertionStreamRequestTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.ima; - -import static com.google.common.truth.Truth.assertThat; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import java.util.HashMap; -import java.util.Map; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link ServerSideAdInsertionStreamRequest}. */ -@RunWith(AndroidJUnit4.class) -public final class ServerSideAdInsertionStreamRequestTest { - - private static final String ADS_ID = "testAdsId"; - private static final String ASSET_KEY = "testAssetKey"; - private static final String API_KEY = "testApiKey"; - private static final String CONTENT_SOURCE_ID = "testContentSourceId"; - private static final String VIDEO_ID = "testVideoId"; - private static final String MANIFEST_SUFFIX = "testManifestSuffix"; - private static final String CONTENT_URL = - "http://google.com/contentUrl?queryParamName=queryParamValue"; - private static final String AUTH_TOKEN = "testAuthToken"; - private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId"; - private static final int ADS_LOADER_TIMEOUT_MS = 2; - private static final int FORMAT_DASH = 0; - private static final int FORMAT_HLS = 2; - private static final Map adTagParameters = new HashMap<>(); - - static { - adTagParameters.put("param1", "value1"); - adTagParameters.put("param2", "value2"); - } - - @Test - public void build_live_correctUriAndParsing() { - ServerSideAdInsertionStreamRequest.Builder builder = - new ServerSideAdInsertionStreamRequest.Builder(); - builder.setAdsId(ADS_ID); - builder.setAssetKey(ASSET_KEY); - builder.setApiKey(API_KEY); - builder.setManifestSuffix(MANIFEST_SUFFIX); - builder.setContentUrl(CONTENT_URL); - builder.setAuthToken(AUTH_TOKEN); - builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); - builder.setFormat(FORMAT_HLS); - builder.setAdTagParameters(adTagParameters); - builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); - ServerSideAdInsertionStreamRequest streamRequest = builder.build(); - - ServerSideAdInsertionStreamRequest requestAfterConversions = - ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri()); - - assertThat(streamRequest).isEqualTo(requestAfterConversions); - } - - @Test - public void build_vod_correctUriAndParsing() { - ServerSideAdInsertionStreamRequest.Builder builder = - new ServerSideAdInsertionStreamRequest.Builder(); - builder.setAdsId(ADS_ID); - builder.setApiKey(API_KEY); - builder.setContentSourceId(CONTENT_SOURCE_ID); - builder.setVideoId(VIDEO_ID); - builder.setManifestSuffix(MANIFEST_SUFFIX); - builder.setContentUrl(CONTENT_URL); - builder.setAuthToken(AUTH_TOKEN); - builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); - builder.setFormat(FORMAT_DASH); - builder.setAdTagParameters(adTagParameters); - builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); - ServerSideAdInsertionStreamRequest streamRequest = builder.build(); - - ServerSideAdInsertionStreamRequest requestAfterConversions = - ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri()); - - assertThat(requestAfterConversions).isEqualTo(streamRequest); - } - - @Test - public void build_vodWithNoAdsId_usesVideoIdAsDefault() { - ServerSideAdInsertionStreamRequest.Builder builder = - new ServerSideAdInsertionStreamRequest.Builder(); - builder.setContentSourceId(CONTENT_SOURCE_ID); - builder.setVideoId(VIDEO_ID); - - ServerSideAdInsertionStreamRequest streamRequest = builder.build(); - - assertThat(streamRequest.adsId).isEqualTo(VIDEO_ID); - assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(VIDEO_ID); - } - - @Test - public void build_liveWithNoAdsId_usesAssetKeyAsDefault() { - ServerSideAdInsertionStreamRequest.Builder builder = - new ServerSideAdInsertionStreamRequest.Builder(); - builder.setAssetKey(ASSET_KEY); - - ServerSideAdInsertionStreamRequest streamRequest = builder.build(); - - assertThat(streamRequest.adsId).isEqualTo(ASSET_KEY); - assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(ASSET_KEY); - } - - @Test - public void build_assetKeyWithVideoId_throwsIllegalStateException() { - ServerSideAdInsertionStreamRequest.Builder requestBuilder = - new ServerSideAdInsertionStreamRequest.Builder(); - requestBuilder.setAssetKey(ASSET_KEY); - requestBuilder.setVideoId(VIDEO_ID); - - Assert.assertThrows(IllegalStateException.class, requestBuilder::build); - } - - @Test - public void build_assetKeyWithContentSource_throwsIllegalStateException() { - ServerSideAdInsertionStreamRequest.Builder requestBuilder = - new ServerSideAdInsertionStreamRequest.Builder(); - requestBuilder.setAssetKey(ASSET_KEY); - requestBuilder.setContentSourceId(CONTENT_SOURCE_ID); - - Assert.assertThrows(IllegalStateException.class, requestBuilder::build); - } - - @Test - public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() { - ServerSideAdInsertionStreamRequest.Builder requestBuilder = - new ServerSideAdInsertionStreamRequest.Builder(); - - Assert.assertThrows(IllegalStateException.class, requestBuilder::build); - } -} From f36c94e1042f4384a22d8106136f17c2298c359f Mon Sep 17 00:00:00 2001 From: kimvde Date: Wed, 9 Feb 2022 15:46:35 +0000 Subject: [PATCH 182/251] Transformer: format test analysis file to JSON PiperOrigin-RevId: 427469350 --- .../media3/transformer/AndroidTestUtil.java | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 82f3c29f56..6fbff6917f 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -32,6 +32,8 @@ import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.compatqual.NullableType; +import org.json.JSONException; +import org.json.JSONObject; /** Utilities for instrumentation tests. */ public final class AndroidTestUtil { @@ -107,43 +109,21 @@ public final class AndroidTestUtil { return result; } - private static void writeResultToFile(Context context, String testId, TransformationResult result) - throws IOException { + private static void writeResultToFile( + Context context, String testId, TransformationResult transformationResult) + throws IOException, JSONException { File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt"); + String analysisContent = + new JSONObject() + .put("testId", testId) + .put("device", getDeviceJson()) + .put("transformationResult", getTransformationResultJson(transformationResult)) + .toString(/* indentSpaces= */ 2); try (FileWriter fileWriter = new FileWriter(analysisFile)) { - String fileContents = - "test=" - + testId - + ", " - + getFormattedResult(result) - + ", deviceFingerprint=" - + Build.FINGERPRINT - + ", deviceBrand=" - + Build.MANUFACTURER - + ", deviceModel=" - + Build.MODEL - + ", sdkVersion=" - + Build.VERSION.SDK_INT; - fileWriter.write(fileContents); + fileWriter.write(analysisContent); } } - /** Formats a {@link TransformationResult} into a comma separated String. */ - public static String getFormattedResult(TransformationResult result) { - String analysis = ""; - if (result.fileSizeBytes != C.LENGTH_UNSET) { - analysis += "fileSizeBytes=" + result.fileSizeBytes; - } - if (result.averageAudioBitrate != C.RATE_UNSET_INT) { - analysis += ", averageAudioBitrate=" + result.averageAudioBitrate; - } - if (result.averageVideoBitrate != C.RATE_UNSET_INT) { - analysis += ", averageVideoBitrate=" + result.averageVideoBitrate; - } - - return analysis; - } - private static File createExternalCacheFile(Context context, String fileName) throws IOException { File file = new File(context.getExternalCacheDir(), fileName); checkState(!file.exists() || file.delete(), "Could not delete file: " + file.getAbsolutePath()); @@ -151,5 +131,28 @@ public final class AndroidTestUtil { return file; } + private static JSONObject getDeviceJson() throws JSONException { + return new JSONObject() + .put("manufacturer", Build.MANUFACTURER) + .put("model", Build.MODEL) + .put("sdkVersion", Build.VERSION.SDK_INT) + .put("fingerprint", Build.FINGERPRINT); + } + + private static JSONObject getTransformationResultJson(TransformationResult transformationResult) + throws JSONException { + JSONObject transformationResultJson = new JSONObject(); + if (transformationResult.fileSizeBytes != C.LENGTH_UNSET) { + transformationResultJson.put("fileSizeBytes", transformationResult.fileSizeBytes); + } + if (transformationResult.averageAudioBitrate != C.RATE_UNSET_INT) { + transformationResultJson.put("averageAudioBitrate", transformationResult.averageAudioBitrate); + } + if (transformationResult.averageVideoBitrate != C.RATE_UNSET_INT) { + transformationResultJson.put("averageVideoBitrate", transformationResult.averageVideoBitrate); + } + return transformationResultJson; + } + private AndroidTestUtil() {} } From 018631320bcb23091f88c1ed54d72646d133251d Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 9 Feb 2022 16:12:57 +0000 Subject: [PATCH 183/251] Fix incorrect parameter name PiperOrigin-RevId: 427474975 --- .../cast/src/main/java/androidx/media3/cast/CastPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 347d27904e..07c86e68b8 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -527,7 +527,7 @@ public final class CastPlayer extends BasePlayer { setRepeatModeAndNotifyIfChanged(repeatMode); listeners.flushEvents(); PendingResult pendingResult = - remoteMediaClient.queueSetRepeatMode(getCastRepeatMode(repeatMode), /* jsonObject= */ null); + remoteMediaClient.queueSetRepeatMode(getCastRepeatMode(repeatMode), /* customData= */ null); this.repeatMode.pendingResultCallback = new ResultCallback() { @Override From c3cb2f7cfb5fc456e1fe7ffaf9094a426fe845f0 Mon Sep 17 00:00:00 2001 From: tonihei Date: Wed, 9 Feb 2022 16:27:00 +0000 Subject: [PATCH 184/251] Add missing events to AnalyticsListener. And also add a test that all Player.Listener events are forwarded to AnalyticsListener. The AnalyticsCollector also needlessly implemented Video/AudioRendererEventListener, which is not needed because all of the equivalent methods are called directly and never through the interface. #minor-release PiperOrigin-RevId: 427478000 --- .../media3/common/ForwardingPlayerTest.java | 20 +- .../analytics/AnalyticsCollector.java | 374 +++++++++++++----- .../analytics/AnalyticsListener.java | 68 +++- .../audio/AudioRendererEventListener.java | 2 +- .../analytics/AnalyticsCollectorTest.java | 23 +- 5 files changed, 376 insertions(+), 111 deletions(-) diff --git a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java index 22b30a8b33..02b6a3e023 100644 --- a/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/ForwardingPlayerTest.java @@ -106,12 +106,12 @@ public class ForwardingPlayerTest { public void forwardingPlayer_overridesAllPlayerMethods() throws Exception { // Check with reflection that ForwardingPlayer overrides all Player methods. List methods = getPublicMethods(Player.class); - for (int i = 0; i < methods.size(); i++) { - Method method = methods.get(i); + for (Method method : methods) { assertThat( - ForwardingPlayer.class.getDeclaredMethod( - method.getName(), method.getParameterTypes())) - .isNotNull(); + ForwardingPlayer.class + .getDeclaredMethod(method.getName(), method.getParameterTypes()) + .getDeclaringClass()) + .isEqualTo(ForwardingPlayer.class); } } @@ -120,10 +120,12 @@ public class ForwardingPlayerTest { // Check with reflection that ForwardingListener overrides all Listener methods. Class forwardingListenerClass = getInnerClass("ForwardingListener"); List methods = getPublicMethods(Player.Listener.class); - for (int i = 0; i < methods.size(); i++) { - Method method = methods.get(i); - assertThat(forwardingListenerClass.getMethod(method.getName(), method.getParameterTypes())) - .isNotNull(); + for (Method method : methods) { + assertThat( + forwardingListenerClass + .getMethod(method.getName(), method.getParameterTypes()) + .getDeclaringClass()) + .isEqualTo(forwardingListenerClass); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java index 620d971217..72334349d7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java @@ -19,12 +19,18 @@ 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 android.media.AudioTrack; +import android.media.MediaCodec; +import android.media.MediaCodec.CodecException; import android.os.Looper; +import android.os.SystemClock; import android.util.SparseArray; +import android.view.Surface; import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; @@ -32,24 +38,28 @@ import androidx.media3.common.Metadata; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; +import androidx.media3.common.Player.DiscontinuityReason; import androidx.media3.common.Player.PlaybackSuppressionReason; import androidx.media3.common.Timeline; import androidx.media3.common.Timeline.Period; import androidx.media3.common.Timeline.Window; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelectionArray; +import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; import androidx.media3.common.VideoSize; +import androidx.media3.common.text.Cue; import androidx.media3.common.util.Clock; import androidx.media3.common.util.HandlerWrapper; import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.decoder.DecoderException; import androidx.media3.exoplayer.DecoderCounters; import androidx.media3.exoplayer.DecoderReuseEvaluation; import androidx.media3.exoplayer.ExoPlaybackException; import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime; -import androidx.media3.exoplayer.audio.AudioRendererEventListener; +import androidx.media3.exoplayer.audio.AudioSink; import androidx.media3.exoplayer.drm.DrmSession; import androidx.media3.exoplayer.drm.DrmSessionEventListener; import androidx.media3.exoplayer.source.LoadEventInfo; @@ -57,7 +67,7 @@ import androidx.media3.exoplayer.source.MediaLoadData; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSourceEventListener; import androidx.media3.exoplayer.upstream.BandwidthMeter; -import androidx.media3.exoplayer.video.VideoRendererEventListener; +import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -73,8 +83,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; @UnstableApi public class AnalyticsCollector implements Player.Listener, - AudioRendererEventListener, - VideoRendererEventListener, MediaSourceEventListener, BandwidthMeter.EventListener, DrmSessionEventListener { @@ -185,10 +193,15 @@ public class AnalyticsCollector } } - // AudioRendererEventListener implementation. + // Audio events. + /** + * Called when the audio renderer is enabled. + * + * @param counters {@link DecoderCounters} that will be updated by the audio renderer for as long + * as it remains enabled. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onAudioEnabled(DecoderCounters counters) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -200,8 +213,15 @@ public class AnalyticsCollector }); } + /** + * Called when a audio decoder is created. + * + * @param decoderName The audio decoder that was created. + * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization + * finished. + * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onAudioDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); @@ -217,8 +237,15 @@ public class AnalyticsCollector }); } + /** + * Called when the format of the media being consumed by the audio renderer changes. + * + * @param format The new format. + * @param decoderReuseEvaluation The result of the evaluation to determine whether an existing + * decoder instance can be reused for the new format, or {@code null} if the renderer did not + * have a decoder. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onAudioInputFormatChanged( Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { EventTime eventTime = generateReadingMediaPeriodEventTime(); @@ -232,7 +259,13 @@ public class AnalyticsCollector }); } - @Override + /** + * Called when the audio position has increased for the first time since the last pause or + * position reset. + * + * @param playoutStartSystemTimeMs The approximate derived {@link System#currentTimeMillis()} at + * which playout started. + */ public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -241,7 +274,14 @@ public class AnalyticsCollector listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs)); } - @Override + /** + * Called when an audio underrun occurs. + * + * @param bufferSize The size of the audio output buffer, in bytes. + * @param bufferSizeMs The size of the audio output buffer, in milliseconds, if it contains PCM + * encoded audio. {@link C#TIME_UNSET} if the output buffer contains non-PCM encoded audio. + * @param elapsedSinceLastFeedMs The time since audio was last written to the output buffer. + */ public final void onAudioUnderrun( int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); @@ -252,7 +292,11 @@ public class AnalyticsCollector listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs)); } - @Override + /** + * Called when a audio decoder is released. + * + * @param decoderName The audio decoder that was released. + */ public final void onAudioDecoderReleased(String decoderName) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -261,8 +305,12 @@ public class AnalyticsCollector listener -> listener.onAudioDecoderReleased(eventTime, decoderName)); } + /** + * Called when the audio renderer is disabled. + * + * @param counters {@link DecoderCounters} that were updated by the audio renderer. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onAudioDisabled(DecoderCounters counters) { EventTime eventTime = generatePlayingMediaPeriodEventTime(); sendEvent( @@ -274,16 +322,16 @@ public class AnalyticsCollector }); } - @Override - public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED, - listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled)); - } - - @Override + /** + * Called when {@link AudioSink} has encountered an error. + * + *

    If the sink writes to a platform {@link AudioTrack}, this will be called for all {@link + * AudioTrack} errors. + * + * @param audioSinkError The error that occurred. Typically an {@link + * AudioSink.InitializationException}, a {@link AudioSink.WriteException}, or an {@link + * AudioSink.UnexpectedDiscontinuityException}. + */ public final void onAudioSinkError(Exception audioSinkError) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -292,7 +340,12 @@ public class AnalyticsCollector listener -> listener.onAudioSinkError(eventTime, audioSinkError)); } - @Override + /** + * Called when an audio decoder encounters an error. + * + * @param audioCodecError The error. Typically a {@link CodecException} if the renderer uses + * {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder. + */ public final void onAudioCodecError(Exception audioCodecError) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -301,34 +354,6 @@ public class AnalyticsCollector listener -> listener.onAudioCodecError(eventTime, audioCodecError)); } - // Additional audio events. - - /** - * Called when the audio session ID changes. - * - * @param audioSessionId The audio session ID. - */ - public final void onAudioSessionIdChanged(int audioSessionId) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_SESSION_ID, - listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId)); - } - - /** - * Called when the audio attributes change. - * - * @param audioAttributes The audio attributes. - */ - public final void onAudioAttributesChanged(AudioAttributes audioAttributes) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED, - listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes)); - } - /** * Called when the volume changes. * @@ -342,10 +367,15 @@ public class AnalyticsCollector listener -> listener.onVolumeChanged(eventTime, volume)); } - // VideoRendererEventListener implementation. + // Video events. + /** + * Called when the video renderer is enabled. + * + * @param counters {@link DecoderCounters} that will be updated by the video renderer for as long + * as it remains enabled. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onVideoEnabled(DecoderCounters counters) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -357,8 +387,15 @@ public class AnalyticsCollector }); } + /** + * Called when a video decoder is created. + * + * @param decoderName The decoder that was created. + * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization + * finished. + * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onVideoDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); @@ -374,8 +411,15 @@ public class AnalyticsCollector }); } + /** + * Called when the format of the media being consumed by the video renderer changes. + * + * @param format The new format. + * @param decoderReuseEvaluation The result of the evaluation to determine whether an existing + * decoder instance can be reused for the new format, or {@code null} if the renderer did not + * have a decoder. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onVideoInputFormatChanged( Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { EventTime eventTime = generateReadingMediaPeriodEventTime(); @@ -389,7 +433,16 @@ public class AnalyticsCollector }); } - @Override + /** + * Called to report the number of frames dropped by the video renderer. Dropped frames are + * reported whenever the renderer is stopped having dropped frames, and optionally, whenever the + * count reaches a specified threshold whilst the renderer is started. + * + * @param count The number of dropped frames. + * @param elapsedMs The duration in milliseconds over which the frames were dropped. This duration + * is timed from when the renderer was started or from when dropped frames were last reported + * (whichever was more recent), and not from when the first of the reported drops occurred. + */ public final void onDroppedFrames(int count, long elapsedMs) { EventTime eventTime = generatePlayingMediaPeriodEventTime(); sendEvent( @@ -398,7 +451,11 @@ public class AnalyticsCollector listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs)); } - @Override + /** + * Called when a video decoder is released. + * + * @param decoderName The video decoder that was released. + */ public final void onVideoDecoderReleased(String decoderName) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -407,8 +464,12 @@ public class AnalyticsCollector listener -> listener.onVideoDecoderReleased(eventTime, decoderName)); } + /** + * Called when the video renderer is disabled. + * + * @param counters {@link DecoderCounters} that were updated by the video renderer. + */ @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override public final void onVideoDisabled(DecoderCounters counters) { EventTime eventTime = generatePlayingMediaPeriodEventTime(); sendEvent( @@ -420,25 +481,14 @@ public class AnalyticsCollector }); } - @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override - public final void onVideoSizeChanged(VideoSize videoSize) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED, - listener -> { - listener.onVideoSizeChanged(eventTime, videoSize); - listener.onVideoSizeChanged( - eventTime, - videoSize.width, - videoSize.height, - videoSize.unappliedRotationDegrees, - videoSize.pixelWidthHeightRatio); - }); - } - - @Override + /** + * Called when a frame is rendered for the first time since setting the output, or since the + * renderer was reset, or since the stream being rendered was changed. + * + * @param output The output of the video renderer. Normally a {@link Surface}, however some video + * renderers may have other output types (e.g., a {@link VideoDecoderOutputBufferRenderer}). + * @param renderTimeMs The {@link SystemClock#elapsedRealtime()} when the frame was rendered. + */ public final void onRenderedFirstFrame(Object output, long renderTimeMs) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -447,7 +497,24 @@ public class AnalyticsCollector listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs)); } - @Override + /** + * Called to report the video processing offset of video frames processed by the video renderer. + * + *

    Video processing offset represents how early a video frame is processed compared to the + * player's current position. For each video frame, the offset is calculated as Pvf + * - Ppl where Pvf is the presentation timestamp of the video + * frame and Ppl is the current position of the player. Positive values + * indicate the frame was processed early enough whereas negative values indicate that the + * player's position had progressed beyond the frame's timestamp when the frame was processed (and + * the frame was probably dropped). + * + *

    The renderer reports the sum of video processing offset samples (one sample per processed + * video frame: dropped, skipped or rendered) and the total number of samples. + * + * @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the + * video frames processed by the renderer in microseconds. + * @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}. + */ public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { EventTime eventTime = generatePlayingMediaPeriodEventTime(); sendEvent( @@ -457,7 +524,19 @@ public class AnalyticsCollector listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount)); } - @Override + /** + * Called when a video decoder encounters an error. + * + *

    This method being called does not indicate that playback has failed, or that it will fail. + * The player may be able to recover from the error. Hence applications should not + * implement this method to display a user visible error or initiate an application level retry. + * {@link Player.Listener#onPlayerError} is the appropriate place to implement such behavior. This + * method is called to provide the application with an opportunity to log the error if it wishes + * to do so. + * + * @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses + * {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder. + */ public final void onVideoCodecError(Exception videoCodecError) { EventTime eventTime = generateReadingMediaPeriodEventTime(); sendEvent( @@ -466,8 +545,6 @@ public class AnalyticsCollector listener -> listener.onVideoCodecError(eventTime, videoCodecError)); } - // Additional video events. - /** * Called each time there's a change in the size of the surface onto which the video is being * rendered. @@ -608,6 +685,12 @@ public class AnalyticsCollector listener -> listener.onTracksInfoChanged(eventTime, tracksInfo)); } + @SuppressWarnings("deprecation") // Implementing deprecated method. + @Override + public void onLoadingChanged(boolean isLoading) { + // Do nothing. Handled by non-deprecated onIsLoadingChanged. + } + @SuppressWarnings("deprecation") // Calling deprecated listener method. @Override public final void onIsLoadingChanged(boolean isLoading) { @@ -699,21 +782,26 @@ public class AnalyticsCollector @Override public final void onPlayerError(PlaybackException error) { - @Nullable EventTime eventTime = null; - if (error instanceof ExoPlaybackException) { - ExoPlaybackException exoError = (ExoPlaybackException) error; - if (exoError.mediaPeriodId != null) { - eventTime = generateEventTime(new MediaPeriodId(exoError.mediaPeriodId)); - } - } - if (eventTime == null) { - eventTime = generateCurrentPlayerMediaPeriodEventTime(); - } - EventTime finalEventTime = eventTime; + EventTime eventTime = getEventTimeForErrorEvent(error); sendEvent( eventTime, AnalyticsListener.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerError(finalEventTime, error)); + listener -> listener.onPlayerError(eventTime, error)); + } + + @Override + public void onPlayerErrorChanged(@Nullable PlaybackException error) { + EventTime eventTime = getEventTimeForErrorEvent(error); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYER_ERROR, + listener -> listener.onPlayerErrorChanged(eventTime, error)); + } + + @SuppressWarnings("deprecation") // Implementing deprecated method. + @Override + public void onPositionDiscontinuity(@DiscontinuityReason int reason) { + // Do nothing. Handled by non-deprecated onPositionDiscontinuity. } // Calling deprecated callback. @@ -801,6 +889,13 @@ public class AnalyticsCollector listener -> listener.onMetadata(eventTime, metadata)); } + @Override + public void onCues(List cues) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, AnalyticsListener.EVENT_CUES, listener -> listener.onCues(eventTime, cues)); + } + @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. @Override public final void onSeekProcessed() { @@ -809,6 +904,89 @@ public class AnalyticsCollector eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime)); } + @Override + public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED, + listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled)); + } + + @Override + public final void onAudioSessionIdChanged(int audioSessionId) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_SESSION_ID, + listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId)); + } + + @Override + public final void onAudioAttributesChanged(AudioAttributes audioAttributes) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED, + listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes)); + } + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onVideoSizeChanged(VideoSize videoSize) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED, + listener -> { + listener.onVideoSizeChanged(eventTime, videoSize); + listener.onVideoSizeChanged( + eventTime, + videoSize.width, + videoSize.height, + videoSize.unappliedRotationDegrees, + videoSize.pixelWidthHeightRatio); + }); + } + + @Override + public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, + listener -> listener.onTrackSelectionParametersChanged(eventTime, parameters)); + } + + @Override + public void onDeviceInfoChanged(DeviceInfo deviceInfo) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DEVICE_INFO_CHANGED, + listener -> listener.onDeviceInfoChanged(eventTime, deviceInfo)); + } + + @Override + public void onDeviceVolumeChanged(int volume, boolean muted) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DEVICE_VOLUME_CHANGED, + listener -> listener.onDeviceVolumeChanged(eventTime, volume, muted)); + } + + @SuppressWarnings("UngroupedOverloads") // Grouped by interface. + @Override + public void onRenderedFirstFrame() { + // Do nothing. Handled by onRenderedFirstFrame call with additional parameters. + } + + @Override + public void onEvents(Player player, Player.Events events) { + // Do nothing. AnalyticsCollector issues its own onEvents. + } + // BandwidthMeter.EventListener implementation. @Override @@ -1002,6 +1180,16 @@ public class AnalyticsCollector windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null); } + private EventTime getEventTimeForErrorEvent(@Nullable PlaybackException error) { + if (error instanceof ExoPlaybackException) { + ExoPlaybackException exoError = (ExoPlaybackException) error; + if (exoError.mediaPeriodId != null) { + return generateEventTime(new MediaPeriodId(exoError.mediaPeriodId)); + } + } + return generateCurrentPlayerMediaPeriodEventTime(); + } + /** Keeps track of the active media periods and currently playing and reading media period. */ private static final class MediaPeriodQueueTracker { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 30fa02b6e3..f99c2b6e4a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -32,6 +32,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; import androidx.media3.common.FlagSet; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; @@ -47,8 +48,10 @@ import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelection; import androidx.media3.common.TrackSelectionArray; +import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; import androidx.media3.common.VideoSize; +import androidx.media3.common.text.Cue; import androidx.media3.common.util.UnstableApi; import androidx.media3.decoder.DecoderException; import androidx.media3.exoplayer.DecoderCounters; @@ -66,6 +69,7 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; /** * A listener for analytics events. @@ -184,6 +188,10 @@ public interface AnalyticsListener { EVENT_PLAYLIST_METADATA_CHANGED, EVENT_SEEK_BACK_INCREMENT_CHANGED, EVENT_SEEK_FORWARD_INCREMENT_CHANGED, + EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, + EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, + EVENT_DEVICE_INFO_CHANGED, + EVENT_DEVICE_VOLUME_CHANGED, EVENT_LOAD_STARTED, EVENT_LOAD_COMPLETED, EVENT_LOAD_CANCELED, @@ -192,6 +200,7 @@ public interface AnalyticsListener { EVENT_UPSTREAM_DISCARDED, EVENT_BANDWIDTH_ESTIMATE, EVENT_METADATA, + EVENT_CUES, EVENT_AUDIO_ENABLED, EVENT_AUDIO_DECODER_INITIALIZED, EVENT_AUDIO_INPUT_FORMAT_CHANGED, @@ -272,6 +281,8 @@ public interface AnalyticsListener { /** {@link Player#getMaxSeekToPreviousPosition()} changed. */ int EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED = Player.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED; + /** {@link Player#getTrackSelectionParameters()} changed. */ + int EVENT_TRACK_SELECTION_PARAMETERS_CHANGED = Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED; /** Audio attributes changed. */ int EVENT_AUDIO_ATTRIBUTES_CHANGED = Player.EVENT_AUDIO_ATTRIBUTES_CHANGED; /** An audio session id was set. */ @@ -291,9 +302,12 @@ public interface AnalyticsListener { int EVENT_RENDERED_FIRST_FRAME = Player.EVENT_RENDERED_FIRST_FRAME; /** Metadata associated with the current playback time was reported. */ int EVENT_METADATA = Player.EVENT_METADATA; - - // TODO: Forward EVENT_CUES, EVENT_DEVICE_INFO_CHANGED and EVENT_DEVICE_VOLUME_CHANGED. - + /** {@link Player#getCurrentCues()} changed. */ + int EVENT_CUES = Player.EVENT_CUES; + /** {@link Player#getDeviceInfo()} changed. */ + int EVENT_DEVICE_INFO_CHANGED = Player.EVENT_DEVICE_INFO_CHANGED; + /** {@link Player#getDeviceVolume()} changed. */ + int EVENT_DEVICE_VOLUME_CHANGED = Player.EVENT_DEVICE_VOLUME_CHANGED; /** A source started loading data. */ int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events /** A source started completed loading data. */ @@ -683,6 +697,17 @@ public interface AnalyticsListener { */ default void onPlayerError(EventTime eventTime, PlaybackException error) {} + /** + * Called when the {@link PlaybackException} returned by {@link Player#getPlayerError()} changes. + * + *

    Implementations of Player may pass an instance of a subclass of {@link PlaybackException} to + * this method in order to include more information about the error. + * + * @param eventTime The event time. + * @param error The new error, or null if the error is being cleared. + */ + default void onPlayerErrorChanged(EventTime eventTime, @Nullable PlaybackException error) {} + /** * Called when the available or selected tracks for the renderers changed. * @@ -703,6 +728,15 @@ public interface AnalyticsListener { */ default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {} + /** + * Called when track selection parameters change. + * + * @param eventTime The event time. + * @param trackSelectionParameters The new {@link TrackSelectionParameters}. + */ + default void onTrackSelectionParametersChanged( + EventTime eventTime, TrackSelectionParameters trackSelectionParameters) {} + /** * Called when the combined {@link MediaMetadata} changes. * @@ -812,6 +846,17 @@ public interface AnalyticsListener { */ default void onMetadata(EventTime eventTime, Metadata metadata) {} + /** + * Called when there is a change in the {@link Cue Cues}. + * + *

    {@code cues} is in ascending order of priority. If any of the cue boxes overlap when + * displayed, the {@link Cue} nearer the end of the list should be shown on top. + * + * @param eventTime The event time. + * @param cues The {@link Cue Cues}. May be empty. + */ + default void onCues(EventTime eventTime, List cues) {} + /** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */ @Deprecated default void onDecoderEnabled( @@ -989,6 +1034,23 @@ public interface AnalyticsListener { */ default void onVolumeChanged(EventTime eventTime, float volume) {} + /** + * Called when the device information changes + * + * @param eventTime The event time. + * @param deviceInfo The new {@link DeviceInfo}. + */ + default void onDeviceInfoChanged(EventTime eventTime, DeviceInfo deviceInfo) {} + + /** + * Called when the device volume or mute state changes. + * + * @param eventTime The event time. + * @param volume The new device volume, with 0 being silence and 1 being unity gain. + * @param muted Whether the device is muted. + */ + default void onDeviceVolumeChanged(EventTime eventTime, int volume, boolean muted) {} + /** * Called when a video renderer is enabled. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java index 9002de6c6f..a01dcf614b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java @@ -132,7 +132,7 @@ public interface AudioRendererEventListener { /** * Called when {@link AudioSink} has encountered an error. * - *

    If the sink writes to a platform {@link AudioTrack}, this will called for all {@link + *

    If the sink writes to a platform {@link AudioTrack}, this will be called for all {@link * AudioTrack} errors. * *

    This method being called does not indicate that playback has failed, or that it will fail. diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java index b34c2b19a9..35e59c9740 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java @@ -121,6 +121,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -191,6 +192,18 @@ public final class AnalyticsCollectorTest { private EventWindowAndPeriodId window0Period1Seq0; private EventWindowAndPeriodId window1Period0Seq1; + @Test + public void analyticsCollector_overridesAllPlayerListenerMethods() throws Exception { + // Verify that AnalyticsCollector forwards all Player.Listener methods to AnalyticsListener. + for (Method method : Player.Listener.class.getDeclaredMethods()) { + assertThat( + AnalyticsCollector.class + .getMethod(method.getName(), method.getParameterTypes()) + .getDeclaringClass()) + .isEqualTo(AnalyticsCollector.class); + } + } + @Test public void emptyTimeline() throws Exception { FakeMediaSource mediaSource = @@ -434,7 +447,7 @@ public final class AnalyticsCollectorTest { // Wait until second period has fully loaded to assert loading events without flakiness. .waitForIsLoading(true) .waitForIsLoading(false) - .seek(/* windowIndex= */ 1, /* positionMs= */ 0) + .seek(/* mediaItemIndex= */ 1, /* positionMs= */ 0) .play() .build(); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); @@ -527,7 +540,7 @@ public final class AnalyticsCollectorTest { new ActionSchedule.Builder(TAG) .pause() .waitForPlaybackState(Player.STATE_READY) - .playUntilPosition(/* windowIndex= */ 0, periodDurationMs) + .playUntilPosition(/* mediaItemIndex= */ 0, periodDurationMs) .seekAndWait(/* positionMs= */ 0) .play() .build(); @@ -821,7 +834,7 @@ public final class AnalyticsCollectorTest { .pause() .waitForPlaybackState(Player.STATE_READY) // Ensure second period is already being read from. - .playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ periodDurationMs) + .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ periodDurationMs) .executeRunnable( () -> concatenatedMediaSource.moveMediaSource( @@ -1086,9 +1099,9 @@ public final class AnalyticsCollectorTest { .waitForPlaybackState(Player.STATE_READY) // Wait in each content part to ensure previously triggered events get a chance to be // delivered. This prevents flakiness caused by playback progressing too fast. - .playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 3_000) + .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 3_000) .waitForPendingPlayerCommands() - .playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 8_000) + .playUntilPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 8_000) .waitForPendingPlayerCommands() .play() .waitForPlaybackState(Player.STATE_ENDED) From 5107be6f7f7286409d288c7923d53755e909fc65 Mon Sep 17 00:00:00 2001 From: krocard Date: Wed, 9 Feb 2022 16:48:18 +0000 Subject: [PATCH 185/251] Fix DefaultAudioTrackBufferSizeProvider test And move them in separate top level classes so that presubmit runs them. #minor-release PiperOrigin-RevId: 427482430 --- ...ltAudioTrackBufferSizeProviderAC3Test.java | 57 ++++ ...dioTrackBufferSizeProviderEncodedTest.java | 64 +++++ ...ltAudioTrackBufferSizeProviderPcmTest.java | 213 ++++++++++++++ ...faultAudioTrackBufferSizeProviderTest.java | 268 ------------------ 4 files changed, 334 insertions(+), 268 deletions(-) create mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderAC3Test.java create mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderEncodedTest.java create mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderPcmTest.java delete mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderAC3Test.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderAC3Test.java new file mode 100644 index 0000000000..7f6f41314f --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderAC3Test.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.audio; + +import static androidx.media3.common.C.MICROS_PER_SECOND; +import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH; +import static androidx.media3.exoplayer.audio.DefaultAudioTrackBufferSizeProvider.getMaximumEncodedRateBytesPerSecond; +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.C; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link DefaultAudioTrackBufferSizeProvider} AC3 audio. */ +@RunWith(AndroidJUnit4.class) +public class DefaultAudioTrackBufferSizeProviderAC3Test { + + private static final DefaultAudioTrackBufferSizeProvider DEFAULT = + new DefaultAudioTrackBufferSizeProvider.Builder().build(); + + @Test + public void + getBufferSizeInBytes_passthroughAC3_isPassthroughBufferSizeTimesMultiplicationFactor() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 0, + /* encoding= */ C.ENCODING_AC3, + /* outputMode= */ OUTPUT_MODE_PASSTHROUGH, + /* pcmFrameSize= */ 1, + /* sampleRate= */ 0, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo( + durationUsToAc3MaxBytes(DEFAULT.passthroughBufferDurationUs) + * DEFAULT.ac3BufferMultiplicationFactor); + } + + private static int durationUsToAc3MaxBytes(long durationUs) { + return (int) + (durationUs * getMaximumEncodedRateBytesPerSecond(C.ENCODING_AC3) / MICROS_PER_SECOND); + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderEncodedTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderEncodedTest.java new file mode 100644 index 0000000000..638dbf5661 --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderEncodedTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.audio; + +import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH; +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.C; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.ParameterizedRobolectricTestRunner; + +/** + * Tests for {@link DefaultAudioTrackBufferSizeProvider} for encoded audio except {@link + * C#ENCODING_AC3}. + */ +@RunWith(ParameterizedRobolectricTestRunner.class) +public class DefaultAudioTrackBufferSizeProviderEncodedTest { + + private static final DefaultAudioTrackBufferSizeProvider DEFAULT = + new DefaultAudioTrackBufferSizeProvider.Builder().build(); + + @ParameterizedRobolectricTestRunner.Parameter(0) + public @C.Encoding int encoding; + + @ParameterizedRobolectricTestRunner.Parameters(name = "{index}: encoding={0}") + public static ImmutableList data() { + return ImmutableList.of( + C.ENCODING_MP3, + C.ENCODING_AAC_LC, + C.ENCODING_AAC_HE_V1, + C.ENCODING_AC4, + C.ENCODING_DTS, + C.ENCODING_DOLBY_TRUEHD); + } + + @Test + public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 123456789, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PASSTHROUGH, + /* pcmFrameSize= */ 1, + /* sampleRate= */ 0, + /* maxAudioTrackPlaybackSpeed= */ 0); + + assertThat(bufferSize).isEqualTo(123456789); + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderPcmTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderPcmTest.java new file mode 100644 index 0000000000..0b922a9c3e --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderPcmTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.audio; + +import static androidx.media3.common.C.MICROS_PER_SECOND; +import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PCM; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.ceil; + +import androidx.media3.common.C; +import androidx.media3.common.util.Util; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.ParameterizedRobolectricTestRunner; + +/** Tests for {@link DefaultAudioTrackBufferSizeProvider} for PCM audio. */ +@RunWith(ParameterizedRobolectricTestRunner.class) +public class DefaultAudioTrackBufferSizeProviderPcmTest { + + private static final DefaultAudioTrackBufferSizeProvider DEFAULT = + new DefaultAudioTrackBufferSizeProvider.Builder().build(); + + @ParameterizedRobolectricTestRunner.Parameter(0) + public @C.PcmEncoding int encoding; + + @ParameterizedRobolectricTestRunner.Parameter(1) + public int channelCount; + + @ParameterizedRobolectricTestRunner.Parameter(2) + public int sampleRate; + + @ParameterizedRobolectricTestRunner.Parameters( + name = "{index}: encoding={0}, channelCount={1}, sampleRate={2}") + public static List data() { + return Sets.cartesianProduct( + ImmutableList.of( + /* encoding */ ImmutableSet.of( + C.ENCODING_PCM_8BIT, + C.ENCODING_PCM_16BIT, + C.ENCODING_PCM_16BIT_BIG_ENDIAN, + C.ENCODING_PCM_24BIT, + C.ENCODING_PCM_32BIT, + C.ENCODING_PCM_FLOAT), + /* channelCount */ ImmutableSet.of(1, 2, 3, 4, 6, 8), + /* sampleRate*/ ImmutableSet.of( + 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000))) + .stream() + .map(s -> s.toArray(new Integer[0])) + .collect(Collectors.toList()); + } + + private int getPcmFrameSize() { + return Util.getPcmFrameSize(encoding, channelCount); + } + + private int roundUpToFrame(int buffer) { + int pcmFrameSize = getPcmFrameSize(); + return (int) ceil((double) buffer / pcmFrameSize) * pcmFrameSize; + } + + private int durationUsToBytes(int durationUs) { + return (int) ((long) durationUs * getPcmFrameSize() * sampleRate / MICROS_PER_SECOND); + } + + @Test + public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 1234567890, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize).isEqualTo(roundUpToFrame(1234567890)); + } + + @Test + public void getBufferSizeInBytes_noMinBufferSize_isMinBufferDuration() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 0, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(durationUsToBytes(DEFAULT.minPcmBufferDurationUs))); + } + + @Test + public void getBufferSizeInBytes_tooSmallMinBufferSize_isMinBufferDuration() { + int minBufferSizeInBytes = + durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) + - 1; + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ minBufferSizeInBytes, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(durationUsToBytes(DEFAULT.minPcmBufferDurationUs))); + } + + @Test + public void getBufferSizeInBytes_lowMinBufferSize_multipliesAudioTrackMinBuffer() { + int minBufferSizeInBytes = + durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) + + 1; + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ minBufferSizeInBytes, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor)); + } + + @Test + public void getBufferSizeInBytes_highMinBufferSize_multipliesAudioTrackMinBuffer() { + int minBufferSizeInBytes = + durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) + - 1; + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ minBufferSizeInBytes, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor)); + } + + @Test + public void getBufferSizeInBytes_tooHighMinBufferSize_isMaxBufferDuration() { + int minBufferSizeInBytes = + durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) + + 1; + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ minBufferSizeInBytes, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(durationUsToBytes(DEFAULT.maxPcmBufferDurationUs))); + } + + @Test + public void getBufferSizeInBytes_lowPlaybackSpeed_isScaledByPlaybackSpeed() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 0, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 1 / 5F); + + assertThat(bufferSize) + .isEqualTo(roundUpToFrame(durationUsToBytes(DEFAULT.minPcmBufferDurationUs) / 5)); + } + + @Test + public void getBufferSizeInBytes_highPlaybackSpeed_isScaledByPlaybackSpeed() { + int bufferSize = + DEFAULT.getBufferSizeInBytes( + /* minBufferSizeInBytes= */ 0, + /* encoding= */ encoding, + /* outputMode= */ OUTPUT_MODE_PCM, + /* pcmFrameSize= */ getPcmFrameSize(), + /* sampleRate= */ sampleRate, + /* maxAudioTrackPlaybackSpeed= */ 8F); + + int expected = roundUpToFrame(durationUsToBytes(DEFAULT.minPcmBufferDurationUs) * 8); + assertThat(bufferSize).isEqualTo(expected); + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java deleted file mode 100644 index 59b41fdc0e..0000000000 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer.audio; - -import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH; -import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PCM; -import static androidx.media3.exoplayer.audio.DefaultAudioTrackBufferSizeProvider.getMaximumEncodedRateBytesPerSecond; -import static com.google.common.truth.Truth.assertThat; - -import androidx.media3.common.C; -import androidx.media3.common.util.Util; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import java.util.List; -import java.util.stream.Collectors; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.junit.runners.Parameterized; - -/** Tests for {@link DefaultAudioTrackBufferSizeProvider}. */ -@RunWith(JUnit4.class) -public class DefaultAudioTrackBufferSizeProviderTest { - - private static final DefaultAudioTrackBufferSizeProvider DEFAULT = - new DefaultAudioTrackBufferSizeProvider.Builder().build(); - - /** Tests for {@link DefaultAudioTrackBufferSizeProvider} for PCM audio. */ - @RunWith(Parameterized.class) - public static class PcmTest { - - @Parameterized.Parameter(0) - public @C.PcmEncoding int encoding; - - @Parameterized.Parameter(1) - public int channelCount; - - @Parameterized.Parameter(2) - public int sampleRate; - - @Parameterized.Parameters(name = "{index}: encoding={0}, channelCount={1}, sampleRate={2}") - public static List data() { - return Sets.cartesianProduct( - ImmutableList.of( - /* encoding */ ImmutableSet.of( - C.ENCODING_PCM_8BIT, - C.ENCODING_PCM_16BIT, - C.ENCODING_PCM_16BIT_BIG_ENDIAN, - C.ENCODING_PCM_24BIT, - C.ENCODING_PCM_32BIT, - C.ENCODING_PCM_FLOAT), - /* channelCount */ ImmutableSet.of(1, 2, 3, 4, 6, 8), - /* sampleRate*/ ImmutableSet.of( - 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000))) - .stream() - .map(s -> s.toArray(new Integer[0])) - .collect(Collectors.toList()); - } - - private int getPcmFrameSize() { - return Util.getPcmFrameSize(encoding, channelCount); - } - - private int durationUsToBytes(int durationUs) { - return (int) (((long) durationUs * getPcmFrameSize() * sampleRate) / C.MICROS_PER_SECOND); - } - - @Test - public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 123456789, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize).isEqualTo(123456789); - } - - @Test - public void getBufferSizeInBytes_noMinBufferSize_isMinBufferDuration() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 0, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs)); - assertThat(bufferSize % getPcmFrameSize()).isEqualTo(0); - } - - @Test - public void getBufferSizeInBytes_tooSmallMinBufferSize_isMinBufferDuration() { - int minBufferSizeInBytes = - durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) - - 1; - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ minBufferSizeInBytes, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs)); - } - - @Test - public void getBufferSizeInBytes_lowMinBufferSize_multipliesAudioTrackMinBuffer() { - int minBufferSizeInBytes = - durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) - + 1; - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ minBufferSizeInBytes, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize) - .isEqualTo(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor); - } - - @Test - public void getBufferSizeInBytes_highMinBufferSize_multipliesAudioTrackMinBuffer() { - int minBufferSizeInBytes = - durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) - - 1; - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ minBufferSizeInBytes, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize) - .isEqualTo(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor); - } - - @Test - public void getBufferSizeInBytes_tooHighMinBufferSize_isMaxBufferDuration() { - int minBufferSizeInBytes = - durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor) - + 1; - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ minBufferSizeInBytes, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.maxPcmBufferDurationUs)); - assertThat(bufferSize % getPcmFrameSize()).isEqualTo(0); - } - - @Test - public void getBufferSizeInBytes_lowPlaybackSpeed_isScaledByPlaybackSpeed() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 0, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 1 / 5F); - - assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs / 5)); - } - - @Test - public void getBufferSizeInBytes_highPlaybackSpeed_isScaledByPlaybackSpeed() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 0, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PCM, - /* pcmFrameSize= */ getPcmFrameSize(), - /* sampleRate= */ sampleRate, - /* maxAudioTrackPlaybackSpeed= */ 8F); - - assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs * 8)); - } - } - /** - * Tests for {@link DefaultAudioTrackBufferSizeProvider} for encoded audio except {@link - * C#ENCODING_AC3}. - */ - @RunWith(Parameterized.class) - public static class EncodedTest { - - @Parameterized.Parameter(0) - public @C.Encoding int encoding; - - @Parameterized.Parameters(name = "{index}: encoding={0}") - public static ImmutableList data() { - return ImmutableList.of( - C.ENCODING_MP3, - C.ENCODING_AAC_LC, - C.ENCODING_AAC_HE_V1, - C.ENCODING_AC4, - C.ENCODING_DTS, - C.ENCODING_DOLBY_TRUEHD); - } - - @Test - public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 123456789, - /* encoding= */ encoding, - /* outputMode= */ OUTPUT_MODE_PASSTHROUGH, - /* pcmFrameSize= */ 1, - /* sampleRate= */ 0, - /* maxAudioTrackPlaybackSpeed= */ 0); - - assertThat(bufferSize).isEqualTo(123456789); - } - } - - @Test - public void - getBufferSizeInBytes_passthroughAC3_isPassthroughBufferSizeTimesMultiplicationFactor() { - int bufferSize = - DEFAULT.getBufferSizeInBytes( - /* minBufferSizeInBytes= */ 0, - /* encoding= */ C.ENCODING_AC3, - /* outputMode= */ OUTPUT_MODE_PASSTHROUGH, - /* pcmFrameSize= */ 1, - /* sampleRate= */ 0, - /* maxAudioTrackPlaybackSpeed= */ 1); - - assertThat(bufferSize) - .isEqualTo( - durationUsToAc3MaxBytes(DEFAULT.passthroughBufferDurationUs) - * DEFAULT.ac3BufferMultiplicationFactor); - } - - private static int durationUsToAc3MaxBytes(long durationUs) { - return (int) - (durationUs * getMaximumEncodedRateBytesPerSecond(C.ENCODING_AC3) / C.MICROS_PER_SECOND); - } -} From ad5f2387e039364500c5f5bdf2f1eaad84e4e348 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 10 Feb 2022 12:20:28 +0000 Subject: [PATCH 186/251] Add missing Javadoc for AudioAttributes. #minor-release PiperOrigin-RevId: 427712140 --- .../media3/common/AudioAttributes.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java index 82ba84f652..cbcc0298d7 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java +++ b/libraries/common/src/main/java/androidx/media3/common/AudioAttributes.java @@ -43,6 +43,11 @@ import java.lang.reflect.Method; */ public final class AudioAttributes implements Bundleable { + /** + * The default audio attributes, where the content type is {@link C#CONTENT_TYPE_UNKNOWN}, usage + * is {@link C#USAGE_MEDIA}, capture policy is {@link C#ALLOW_CAPTURE_BY_ALL} and no flags are + * set. + */ public static final AudioAttributes DEFAULT = new Builder().build(); /** Builder for {@link AudioAttributes}. */ @@ -68,19 +73,19 @@ public final class AudioAttributes implements Bundleable { spatializationBehavior = C.SPATIALIZATION_BEHAVIOR_AUTO; } - /** @see android.media.AudioAttributes.Builder#setContentType(int) */ + /** See {@link android.media.AudioAttributes.Builder#setContentType(int)} */ public Builder setContentType(@C.AudioContentType int contentType) { this.contentType = contentType; return this; } - /** @see android.media.AudioAttributes.Builder#setFlags(int) */ + /** See {@link android.media.AudioAttributes.Builder#setFlags(int)} */ public Builder setFlags(@C.AudioFlags int flags) { this.flags = flags; return this; } - /** @see android.media.AudioAttributes.Builder#setUsage(int) */ + /** See {@link android.media.AudioAttributes.Builder#setUsage(int)} */ public Builder setUsage(@C.AudioUsage int usage) { this.usage = usage; return this; @@ -94,7 +99,7 @@ public final class AudioAttributes implements Bundleable { // TODO[b/190759307] Update javadoc to link to AudioAttributes.Builder#setSpatializationBehavior // once compile SDK target is set to 32. - /** See AudioAttributes.Builder#setSpatializationBehavior(int). */ + /** See {@code android.media.AudioAttributes.Builder.setSpatializationBehavior(int)}. */ public Builder setSpatializationBehavior(@C.SpatializationBehavior int spatializationBehavior) { this.spatializationBehavior = spatializationBehavior; return this; @@ -107,10 +112,15 @@ public final class AudioAttributes implements Bundleable { } } + /** The {@link C.AudioContentType}. */ public final @C.AudioContentType int contentType; + /** The {@link C.AudioFlags}. */ public final @C.AudioFlags int flags; + /** The {@link C.AudioUsage}. */ public final @C.AudioUsage int usage; + /** The {@link C.AudioAllowedCapturePolicy}. */ public final @C.AudioAllowedCapturePolicy int allowedCapturePolicy; + /** The {@link C.SpatializationBehavior}. */ public final @C.SpatializationBehavior int spatializationBehavior; @Nullable private android.media.AudioAttributes audioAttributesV21; From 12ce9f7c0ff31e45e677e549f636b0c66de1b1d5 Mon Sep 17 00:00:00 2001 From: claincly Date: Thu, 10 Feb 2022 13:59:54 +0000 Subject: [PATCH 187/251] Fix the logic to get the max supported encoding level. On some Android devices, the return value of ``` MediaCodecInfo.getCapabilitiesForType(mimeType).profileLevels ``` contains one entry for each encoding profile, like but on some other devices, there are multiple entries for the same profile, like , , , where we need to iterate through all the entries and find the max. PiperOrigin-RevId: 427727030 --- .../main/java/androidx/media3/transformer/EncoderUtil.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java index 3a6c9bc63d..970644433a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java @@ -16,6 +16,7 @@ package androidx.media3.transformer; +import static java.lang.Math.max; import static java.lang.Math.round; import android.media.MediaCodec; @@ -127,12 +128,13 @@ public final class EncoderUtil { MediaCodecInfo.CodecProfileLevel[] profileLevels = encoderInfo.getCapabilitiesForType(mimeType).profileLevels; + int maxSupportedLevel = LEVEL_UNSET; for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) { if (profileLevel.profile == profile) { - return profileLevel.level; + maxSupportedLevel = max(maxSupportedLevel, profileLevel.level); } } - return LEVEL_UNSET; + return maxSupportedLevel; } /** From b9cb87153f960de241a35a86276cf63746084c40 Mon Sep 17 00:00:00 2001 From: claincly Date: Thu, 10 Feb 2022 14:05:17 +0000 Subject: [PATCH 188/251] Add logging for exceptions thrown while configuring MediaCodec. PiperOrigin-RevId: 427728320 --- .../media3/transformer/DefaultCodec.java | 5 ++- .../transformer/TransformationException.java | 43 ++++++++++++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java index 3c1c8aea61..9482014983 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java @@ -108,7 +108,7 @@ public final class DefaultCodec implements Codec { } throw createInitializationTransformationException( - e, configurationFormat, isVideo, isDecoder, mediaCodecName); + e, mediaFormat, configurationFormat, isVideo, isDecoder, mediaCodecName); } this.mediaCodec = mediaCodec; this.inputSurface = inputSurface; @@ -299,6 +299,7 @@ public final class DefaultCodec implements Codec { private static TransformationException createInitializationTransformationException( Exception cause, + MediaFormat mediaFormat, Format format, boolean isVideo, boolean isDecoder, @@ -306,6 +307,7 @@ public final class DefaultCodec implements Codec { if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { return TransformationException.createForCodec( cause, + mediaFormat, format, isVideo, isDecoder, @@ -317,6 +319,7 @@ public final class DefaultCodec implements Codec { if (cause instanceof IllegalArgumentException) { return TransformationException.createForCodec( cause, + mediaFormat, format, isVideo, isDecoder, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index 5dce35e195..0a57e3edfa 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -18,6 +18,7 @@ package androidx.media3.transformer; import static java.lang.annotation.ElementType.TYPE_USE; import android.media.MediaCodec; +import android.media.MediaFormat; import android.os.SystemClock; import androidx.annotation.IntDef; import androidx.annotation.Nullable; @@ -207,7 +208,40 @@ public final class TransformationException extends Exception { * Creates an instance for a decoder or encoder related exception. * * @param cause The cause of the failure. - * @param format The {@link Format} used for configuring the decoder/encoder. + * @param mediaFormat The {@link MediaFormat} used for configuring the underlying {@link + * MediaCodec}, if known. + * @param format The {@link Format} used for configuring the {@link Codec}. + * @param isVideo Whether the decoder or encoder is configured for video. + * @param isDecoder Whether the exception is created for a decoder. + * @param mediaCodecName The name of the {@link MediaCodec} used, if known. + * @param errorCode See {@link #errorCode}. + * @return The created instance. + */ + public static TransformationException createForCodec( + Throwable cause, + @Nullable MediaFormat mediaFormat, + Format format, + boolean isVideo, + boolean isDecoder, + @Nullable String mediaCodecName, + int errorCode) { + String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); + String errorMessage = + componentName + + " error, format=" + + format + + ", mediaCodecName=" + + mediaCodecName + + ", mediaFormat=" + + (mediaFormat == null ? "no configured MediaFormat" : mediaFormat.toString()); + return new TransformationException(errorMessage, cause, errorCode); + } + + /** + * Creates an instance for a decoder or encoder related exception. + * + * @param cause The cause of the failure. + * @param format The {@link Format} used for configuring the {@link Codec}. * @param isVideo Whether the decoder or encoder is configured for video. * @param isDecoder Whether the exception is created for a decoder. * @param mediaCodecName The name of the {@link MediaCodec} used, if known. @@ -221,11 +255,8 @@ public final class TransformationException extends Exception { boolean isDecoder, @Nullable String mediaCodecName, int errorCode) { - String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); - return new TransformationException( - componentName + " error, format = " + format + ", mediaCodecName=" + mediaCodecName, - cause, - errorCode); + return createForCodec( + cause, /* mediaFormat= */ null, format, isVideo, isDecoder, mediaCodecName, errorCode); } /** From ba3558c0c6f45add5fcfaebd8b55fa0d112e760e Mon Sep 17 00:00:00 2001 From: claincly Date: Thu, 10 Feb 2022 18:50:14 +0000 Subject: [PATCH 189/251] Disable setting profile/level for API level < 24. We have seen devices running on API21/23 fail transcoding because of setting encoding profile/level. Some devices (ale-123/nexus 7) on API21 returns ENOSYS (Function not implemented) when being configured with a profile setting. (although API21 introduced the capability of setting encoding profile) Some devices (nexus 5) on API23 fails configuration with a specific parameter set, despite advertising support for it. Not setting the baseline profile has no effect on encoding, because when not set, the encoding will pick a suitable profile to use. Since baseline is the lowest possible profile, the auto-picked value can't be worse than baseline. Ref: b/218696352 PiperOrigin-RevId: 427792124 --- .../media3/transformer/DefaultEncoderFactory.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 639e9728bc..f901efe5e0 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -176,7 +176,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { // in-app muxing. mediaFormat.setInteger(MediaFormat.KEY_LATENCY, 1); } - } else if (Util.SDK_INT >= 23) { + } else if (Util.SDK_INT >= 24) { int supportedLevel = EncoderUtil.findHighestSupportedEncodingLevel( encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); @@ -186,12 +186,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { mediaFormat.setInteger( MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedLevel); - } else { - // Use the baseline profile for safest results, as encoding in baseline is required per - // https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding - mediaFormat.setInteger( - MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); } + // For API levels below 24, setting profile and level can lead to failures in MediaCodec + // configuration. The encoder selects the profile/level when we don't set them. } mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, DEFAULT_COLOR_FORMAT); From 1af8d570321a3c638b03d39499747a98ed32b7eb Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 10 Feb 2022 19:50:11 +0000 Subject: [PATCH 190/251] Fix Sample Size For Supplemental Data In MatroskaExtractor For when a track is both encrypted and has supplemental data, the sample size will be equal to `block sample size - encryption data size`. PiperOrigin-RevId: 427807612 --- .../media3/extractor/mkv/MatroskaExtractor.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 5f4d246a9f..63ded1ed1d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -1552,12 +1552,13 @@ public class MatroskaExtractor implements Extractor { blockFlags |= C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA; blockAdditionalData.reset(/* limit= */ 0); // If there is supplemental data, the structure of the sample data is: - // sample size (4 bytes) || sample data || supplemental data + // encryption data (if any) || sample size (4 bytes) || sample data || supplemental data + int sampleSize = size + sampleStrippedBytes.limit() - sampleBytesRead; scratch.reset(/* limit= */ 4); - scratch.getData()[0] = (byte) ((size >> 24) & 0xFF); - scratch.getData()[1] = (byte) ((size >> 16) & 0xFF); - scratch.getData()[2] = (byte) ((size >> 8) & 0xFF); - scratch.getData()[3] = (byte) (size & 0xFF); + scratch.getData()[0] = (byte) ((sampleSize >> 24) & 0xFF); + scratch.getData()[1] = (byte) ((sampleSize >> 16) & 0xFF); + scratch.getData()[2] = (byte) ((sampleSize >> 8) & 0xFF); + scratch.getData()[3] = (byte) (sampleSize & 0xFF); output.sampleData(scratch, 4, TrackOutput.SAMPLE_DATA_PART_SUPPLEMENTAL); sampleBytesWritten += 4; } From 8d09da4abf9a83221a6db60bbefbb8a83a1f6176 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 10 Feb 2022 20:37:25 +0000 Subject: [PATCH 191/251] Trim CodecDelay/PreSkip In New OpusDecoder Instances By Default PiperOrigin-RevId: 427819209 --- .../media3/decoder/opus/OpusDecoder.java | 1 + .../media3/decoder/opus/OpusDecoderTest.java | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java index 62fb9c04b1..47f4c7a758 100644 --- a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java +++ b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java @@ -99,6 +99,7 @@ public final class OpusDecoder } preSkipSamples = getPreSkipSamples(initializationData); seekPreRollSamples = getSeekPreRollSamples(initializationData); + skipSamples = preSkipSamples; byte[] headerBytes = initializationData.get(0); if (headerBytes.length < 19) { diff --git a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java index f1994d6d0e..63cc0d072c 100644 --- a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java +++ b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java @@ -17,11 +17,16 @@ package androidx.media3.decoder.opus; import static com.google.common.truth.Truth.assertThat; +import androidx.annotation.Nullable; import androidx.media3.common.C; +import androidx.media3.common.util.LibraryLoader; +import androidx.media3.decoder.DecoderInputBuffer; +import androidx.media3.decoder.SimpleDecoderOutputBuffer; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,9 +34,14 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class OpusDecoderTest { + private static final LibraryLoader LOADER = new LibraryLoader("opusV2JNI"); + private static final byte[] HEADER = new byte[] {79, 112, 117, 115, 72, 101, 97, 100, 0, 2, 1, 56, 0, 0, -69, -128, 0, 0, 0}; + private static final byte[] ENCODED_DATA = new byte[] {-4}; + private static final int DECODED_DATA_SIZE = 3840; + private static final int HEADER_PRE_SKIP_SAMPLES = 14337; private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840; @@ -39,6 +49,7 @@ public final class OpusDecoderTest { private static final ImmutableList HEADER_ONLY_INITIALIZATION_DATA = ImmutableList.of(HEADER); + private static final long PRE_SKIP_NANOS = 6_500_000; private static final long CUSTOM_PRE_SKIP_SAMPLES = 28674; private static final byte[] CUSTOM_PRE_SKIP_BYTES = buildNativeOrderByteArray(sampleCountToNanoseconds(CUSTOM_PRE_SKIP_SAMPLES)); @@ -50,6 +61,11 @@ public final class OpusDecoderTest { private static final ImmutableList FULL_INITIALIZATION_DATA = ImmutableList.of(HEADER, CUSTOM_PRE_SKIP_BYTES, CUSTOM_SEEK_PRE_ROLL_BYTES); + @Before + public void setUp() { + assertThat(LOADER.isAvailable()).isTrue(); + } + @Test public void getChannelCount() { int channelCount = OpusDecoder.getChannelCount(HEADER); @@ -80,11 +96,56 @@ public final class OpusDecoderTest { assertThat(seekPreRollSamples).isEqualTo(DEFAULT_SEEK_PRE_ROLL_SAMPLES); } + @Test + public void decode_removesPreSkipFromOutput() throws OpusDecoderException { + OpusDecoder decoder = + new OpusDecoder( + /* numInputBuffers= */ 0, + /* numOutputBuffers= */ 0, + /* initialInputBufferSize= */ 0, + createInitializationData(/* preSkipNanos= */ PRE_SKIP_NANOS), + /* cryptoConfig= */ null, + /* outputFloat= */ false); + DecoderInputBuffer input = + createInputBuffer(decoder, ENCODED_DATA, /* supplementalData= */ null); + SimpleDecoderOutputBuffer output = decoder.createOutputBuffer(); + assertThat(decoder.decode(input, output, false)).isNull(); + assertThat(output.data.remaining()) + .isEqualTo(DECODED_DATA_SIZE - nanosecondsToBytes(PRE_SKIP_NANOS)); + } + private static long sampleCountToNanoseconds(long sampleCount) { return (sampleCount * C.NANOS_PER_SECOND) / OpusDecoder.SAMPLE_RATE; } + private static long nanosecondsToSampleCount(long nanoseconds) { + return (nanoseconds * OpusDecoder.SAMPLE_RATE) / C.NANOS_PER_SECOND; + } + + private static long nanosecondsToBytes(long nanoseconds) { + return nanosecondsToSampleCount(nanoseconds) * 4; + } + private static byte[] buildNativeOrderByteArray(long value) { return ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()).putLong(value).array(); } + + private static ImmutableList createInitializationData(long preSkipNanos) { + byte[] preSkip = buildNativeOrderByteArray(preSkipNanos); + return ImmutableList.of(HEADER, preSkip, CUSTOM_SEEK_PRE_ROLL_BYTES); + } + + private static DecoderInputBuffer createInputBuffer( + OpusDecoder decoder, byte[] data, @Nullable byte[] supplementalData) { + DecoderInputBuffer input = decoder.createInputBuffer(); + input.ensureSpaceForWrite(data.length); + input.data.put(data); + input.data.position(0).limit(data.length); + if (supplementalData != null) { + input.resetSupplementalData(supplementalData.length); + input.supplementalData.put(supplementalData).rewind(); + input.addFlag(C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA); + } + return input; + } } From 27383068bda4bc88994165bd142f9732dbcc26b9 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 11 Feb 2022 09:28:34 +0000 Subject: [PATCH 192/251] Make ExoPlayerImpl an ExoPlayer implementation All the functionality of SimpleExoPlayer has moved to ExoPlayerImpl. Hence, ExoPlayerImpl can fulfil its own name and become an ExoPlayer implementation. As a result, ExoPlayer.Builder can return ExoPlayerImpl directly without using SimpleExoPlayer at all. #minor-release PiperOrigin-RevId: 427947028 --- .../androidx/media3/exoplayer/ExoPlayer.java | 4 +- .../media3/exoplayer/ExoPlayerImpl.java | 347 +++++++++++------- 2 files changed, 209 insertions(+), 142 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 36d21c7ad8..488237fd82 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -1018,7 +1018,9 @@ public interface ExoPlayer extends Player { * @throws IllegalStateException If this method has already been called. */ public ExoPlayer build() { - return buildSimpleExoPlayer(); + checkState(!buildCalled); + buildCalled = true; + return new ExoPlayerImpl(/* builder= */ this, /* wrappingPlayer= */ null); } /* package */ SimpleExoPlayer buildSimpleExoPlayer() { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index b3343eec90..14a8b20162 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -18,59 +18,6 @@ package androidx.media3.exoplayer; import static androidx.media3.common.C.TRACK_TYPE_AUDIO; import static androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION; import static androidx.media3.common.C.TRACK_TYPE_VIDEO; -import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; -import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; -import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; -import static androidx.media3.common.Player.COMMAND_GET_TEXT; -import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; -import static androidx.media3.common.Player.COMMAND_GET_TRACK_INFOS; -import static androidx.media3.common.Player.COMMAND_GET_VOLUME; -import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; -import static androidx.media3.common.Player.COMMAND_PREPARE; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; -import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; -import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME; -import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA; -import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; -import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; -import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; -import static androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS; -import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; -import static androidx.media3.common.Player.COMMAND_SET_VOLUME; -import static androidx.media3.common.Player.COMMAND_STOP; -import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; -import static androidx.media3.common.Player.DISCONTINUITY_REASON_INTERNAL; -import static androidx.media3.common.Player.DISCONTINUITY_REASON_REMOVE; -import static androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK; -import static androidx.media3.common.Player.EVENT_AUDIO_ATTRIBUTES_CHANGED; -import static androidx.media3.common.Player.EVENT_AUDIO_SESSION_ID; -import static androidx.media3.common.Player.EVENT_CUES; -import static androidx.media3.common.Player.EVENT_DEVICE_INFO_CHANGED; -import static androidx.media3.common.Player.EVENT_DEVICE_VOLUME_CHANGED; -import static androidx.media3.common.Player.EVENT_MEDIA_METADATA_CHANGED; -import static androidx.media3.common.Player.EVENT_METADATA; -import static androidx.media3.common.Player.EVENT_PLAYLIST_METADATA_CHANGED; -import static androidx.media3.common.Player.EVENT_RENDERED_FIRST_FRAME; -import static androidx.media3.common.Player.EVENT_SKIP_SILENCE_ENABLED_CHANGED; -import static androidx.media3.common.Player.EVENT_SURFACE_SIZE_CHANGED; -import static androidx.media3.common.Player.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED; -import static androidx.media3.common.Player.EVENT_VIDEO_SIZE_CHANGED; -import static androidx.media3.common.Player.EVENT_VOLUME_CHANGED; -import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO; -import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; -import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT; -import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_SEEK; -import static androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE; -import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS; -import static androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST; -import static androidx.media3.common.Player.STATE_BUFFERING; -import static androidx.media3.common.Player.STATE_ENDED; -import static androidx.media3.common.Player.STATE_IDLE; -import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED; -import static androidx.media3.common.Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.castNonNull; @@ -107,6 +54,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.media3.common.AudioAttributes; import androidx.media3.common.AuxEffectInfo; +import androidx.media3.common.BasePlayer; import androidx.media3.common.C; import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; @@ -118,16 +66,6 @@ import androidx.media3.common.Metadata; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; -import androidx.media3.common.Player.Commands; -import androidx.media3.common.Player.DiscontinuityReason; -import androidx.media3.common.Player.Events; -import androidx.media3.common.Player.Listener; -import androidx.media3.common.Player.PlayWhenReadyChangeReason; -import androidx.media3.common.Player.PlaybackSuppressionReason; -import androidx.media3.common.Player.PositionInfo; -import androidx.media3.common.Player.RepeatMode; -import androidx.media3.common.Player.State; -import androidx.media3.common.Player.TimelineChangeReason; import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; @@ -144,7 +82,6 @@ import androidx.media3.common.util.HandlerWrapper; import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; -import androidx.media3.exoplayer.ExoPlayer.AudioOffloadListener; import androidx.media3.exoplayer.PlayerMessage.Target; import androidx.media3.exoplayer.Renderer.MessageType; import androidx.media3.exoplayer.analytics.AnalyticsCollector; @@ -173,8 +110,13 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeoutException; -/** A helper class for the {@link SimpleExoPlayer} implementation of {@link ExoPlayer}. */ -/* package */ final class ExoPlayerImpl { +/** The default implementation of {@link ExoPlayer}. */ +/* package */ final class ExoPlayerImpl extends BasePlayer + implements ExoPlayer, + ExoPlayer.AudioComponent, + ExoPlayer.VideoComponent, + ExoPlayer.TextComponent, + ExoPlayer.DeviceComponent { static { MediaLibraryInfo.registerModule("media3.exoplayer"); @@ -204,7 +146,6 @@ import java.util.concurrent.TimeoutException; private final ListenerSet listeners; private final CopyOnWriteArraySet audioOffloadListeners; private final Timeline.Period period; - private final Timeline.Window window; private final List mediaSourceHolderSnapshots; private final boolean useLazyPreparation; private final MediaSource.Factory mediaSourceFactory; @@ -279,7 +220,7 @@ import java.util.concurrent.TimeoutException; private long maskingWindowPositionMs; @SuppressLint("HandlerLeak") - public ExoPlayerImpl(ExoPlayer.Builder builder, Player wrappingPlayer) { + public ExoPlayerImpl(ExoPlayer.Builder builder, @Nullable Player wrappingPlayer) { constructorFinished = new ConditionVariable(); try { Log.i( @@ -323,12 +264,12 @@ import java.util.concurrent.TimeoutException; this.pauseAtEndOfMediaItems = builder.pauseAtEndOfMediaItems; this.applicationLooper = builder.looper; this.clock = builder.clock; - this.wrappingPlayer = wrappingPlayer; + this.wrappingPlayer = wrappingPlayer == null ? this : wrappingPlayer; listeners = new ListenerSet<>( applicationLooper, clock, - (listener, flags) -> listener.onEvents(wrappingPlayer, new Events(flags))); + (listener, flags) -> listener.onEvents(this.wrappingPlayer, new Events(flags))); audioOffloadListeners = new CopyOnWriteArraySet<>(); mediaSourceHolderSnapshots = new ArrayList<>(); shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0); @@ -339,7 +280,6 @@ import java.util.concurrent.TimeoutException; TracksInfo.EMPTY, /* info= */ null); period = new Timeline.Period(); - window = new Timeline.Window(); permanentAvailableCommands = new Commands.Builder() .addAll( @@ -377,7 +317,7 @@ import java.util.concurrent.TimeoutException; playbackInfoUpdate -> playbackInfoUpdateHandler.post(() -> handlePlaybackInfo(playbackInfoUpdate)); playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult); - analyticsCollector.setPlayer(wrappingPlayer, applicationLooper); + analyticsCollector.setPlayer(this.wrappingPlayer, applicationLooper); PlayerId playerId = Util.SDK_INT < 31 ? new PlayerId() @@ -420,7 +360,7 @@ import java.util.concurrent.TimeoutException; bandwidthMeter.addEventListener(new Handler(applicationLooper), analyticsCollector); addAudioOffloadListener(componentListener); if (builder.foregroundModeTimeoutMs > 0) { - experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); + internalPlayer.experimentalSetForegroundModeTimeoutMs(builder.foregroundModeTimeoutMs); } audioBecomingNoisyManager = @@ -454,82 +394,108 @@ import java.util.concurrent.TimeoutException; } } - /** - * Sets a limit on the time a call to {@link #setForegroundMode} can spend. If a call to {@link - * #setForegroundMode} takes more than {@code timeoutMs} milliseconds to complete, the player will - * raise an error via {@link Player.Listener#onPlayerError}. - * - *

    This method is experimental, and will be renamed or removed in a future release. It should - * only be called before the player is used. - * - * @param timeoutMs The time limit in milliseconds. - */ - public void experimentalSetForegroundModeTimeoutMs(long timeoutMs) { - internalPlayer.experimentalSetForegroundModeTimeoutMs(timeoutMs); + @SuppressWarnings("deprecation") // Returning deprecated class. + @Override + @Deprecated + public AudioComponent getAudioComponent() { + return this; } + @SuppressWarnings("deprecation") // Returning deprecated class. + @Override + @Deprecated + public VideoComponent getVideoComponent() { + return this; + } + + @SuppressWarnings("deprecation") // Returning deprecated class. + @Override + @Deprecated + public TextComponent getTextComponent() { + return this; + } + + @SuppressWarnings("deprecation") // Returning deprecated class. + @Override + @Deprecated + public DeviceComponent getDeviceComponent() { + return this; + } + + @Override public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) { verifyApplicationThread(); internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled); } + @Override public boolean experimentalIsSleepingForOffload() { verifyApplicationThread(); return playbackInfo.sleepingForOffload; } + @Override public Looper getPlaybackLooper() { // Don't verify application thread. We allow calls to this method from any thread. return internalPlayer.getPlaybackLooper(); } + @Override public Looper getApplicationLooper() { // Don't verify application thread. We allow calls to this method from any thread. return applicationLooper; } + @Override public Clock getClock() { // Don't verify application thread. We allow calls to this method from any thread. return clock; } + @Override public void addAudioOffloadListener(AudioOffloadListener listener) { // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.add(listener); } + @Override public void removeAudioOffloadListener(AudioOffloadListener listener) { // Don't verify application thread. We allow calls to this method from any thread. audioOffloadListeners.remove(listener); } + @Override public Commands getAvailableCommands() { verifyApplicationThread(); return availableCommands; } + @Override public @State int getPlaybackState() { verifyApplicationThread(); return playbackInfo.playbackState; } + @Override public @PlaybackSuppressionReason int getPlaybackSuppressionReason() { verifyApplicationThread(); return playbackInfo.playbackSuppressionReason; } + @Override @Nullable public ExoPlaybackException getPlayerError() { verifyApplicationThread(); return playbackInfo.playbackError; } - /** @deprecated Use {@link #prepare()} instead. */ + @Override @Deprecated public void retry() { prepare(); } + @Override public void prepare() { verifyApplicationThread(); boolean playWhenReady = getPlayWhenReady(); @@ -561,9 +527,7 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } - /** - * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link ExoPlayer#prepare()} instead. - */ + @Override @Deprecated public void prepare(MediaSource mediaSource) { verifyApplicationThread(); @@ -571,10 +535,7 @@ import java.util.concurrent.TimeoutException; prepare(); } - /** - * @deprecated Use {@link #setMediaSource(MediaSource, boolean)} and {@link ExoPlayer#prepare()} - * instead. - */ + @Override @Deprecated public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { verifyApplicationThread(); @@ -582,37 +543,44 @@ import java.util.concurrent.TimeoutException; prepare(); } + @Override public void setMediaItems(List mediaItems, boolean resetPosition) { verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), resetPosition); } + @Override public void setMediaItems(List mediaItems, int startIndex, long startPositionMs) { verifyApplicationThread(); setMediaSources(createMediaSources(mediaItems), startIndex, startPositionMs); } + @Override public void setMediaSource(MediaSource mediaSource) { verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource)); } + @Override public void setMediaSource(MediaSource mediaSource, long startPositionMs) { verifyApplicationThread(); setMediaSources( Collections.singletonList(mediaSource), /* startWindowIndex= */ 0, startPositionMs); } + @Override public void setMediaSource(MediaSource mediaSource, boolean resetPosition) { verifyApplicationThread(); setMediaSources(Collections.singletonList(mediaSource), resetPosition); } + @Override public void setMediaSources(List mediaSources) { verifyApplicationThread(); setMediaSources(mediaSources, /* resetPosition= */ true); } + @Override public void setMediaSources(List mediaSources, boolean resetPosition) { verifyApplicationThread(); setMediaSourcesInternal( @@ -622,6 +590,7 @@ import java.util.concurrent.TimeoutException; /* resetToDefaultPosition= */ resetPosition); } + @Override public void setMediaSources( List mediaSources, int startWindowIndex, long startPositionMs) { verifyApplicationThread(); @@ -629,27 +598,32 @@ import java.util.concurrent.TimeoutException; mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false); } + @Override public void addMediaItems(int index, List mediaItems) { verifyApplicationThread(); index = min(index, mediaSourceHolderSnapshots.size()); addMediaSources(index, createMediaSources(mediaItems)); } + @Override public void addMediaSource(MediaSource mediaSource) { verifyApplicationThread(); addMediaSources(Collections.singletonList(mediaSource)); } + @Override public void addMediaSource(int index, MediaSource mediaSource) { verifyApplicationThread(); addMediaSources(index, Collections.singletonList(mediaSource)); } + @Override public void addMediaSources(List mediaSources) { verifyApplicationThread(); addMediaSources(/* index= */ mediaSourceHolderSnapshots.size(), mediaSources); } + @Override public void addMediaSources(int index, List mediaSources) { verifyApplicationThread(); Assertions.checkArgument(index >= 0); @@ -674,6 +648,7 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } + @Override public void removeMediaItems(int fromIndex, int toIndex) { verifyApplicationThread(); toIndex = min(toIndex, mediaSourceHolderSnapshots.size()); @@ -691,6 +666,7 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } + @Override public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) { verifyApplicationThread(); Assertions.checkArgument( @@ -720,6 +696,7 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } + @Override public void setShuffleOrder(ShuffleOrder shuffleOrder) { verifyApplicationThread(); Timeline timeline = createMaskingTimeline(); @@ -743,6 +720,7 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } + @Override public void setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { verifyApplicationThread(); if (this.pauseAtEndOfMediaItems == pauseAtEndOfMediaItems) { @@ -752,11 +730,13 @@ import java.util.concurrent.TimeoutException; internalPlayer.setPauseAtEndOfWindow(pauseAtEndOfMediaItems); } + @Override public boolean getPauseAtEndOfMediaItems() { verifyApplicationThread(); return pauseAtEndOfMediaItems; } + @Override public void setPlayWhenReady(boolean playWhenReady) { verifyApplicationThread(); @AudioFocusManager.PlayerCommand @@ -765,11 +745,13 @@ import java.util.concurrent.TimeoutException; playWhenReady, playerCommand, getPlayWhenReadyChangeReason(playWhenReady, playerCommand)); } + @Override public boolean getPlayWhenReady() { verifyApplicationThread(); return playbackInfo.playWhenReady; } + @Override public void setRepeatMode(@RepeatMode int repeatMode) { verifyApplicationThread(); if (this.repeatMode != repeatMode) { @@ -782,11 +764,13 @@ import java.util.concurrent.TimeoutException; } } + @Override public @RepeatMode int getRepeatMode() { verifyApplicationThread(); return repeatMode; } + @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { verifyApplicationThread(); if (this.shuffleModeEnabled != shuffleModeEnabled) { @@ -800,16 +784,19 @@ import java.util.concurrent.TimeoutException; } } + @Override public boolean getShuffleModeEnabled() { verifyApplicationThread(); return shuffleModeEnabled; } + @Override public boolean isLoading() { verifyApplicationThread(); return playbackInfo.isLoading; } + @Override public void seekTo(int mediaItemIndex, long positionMs) { verifyApplicationThread(); analyticsCollector.notifySeekStarted(); @@ -852,21 +839,25 @@ import java.util.concurrent.TimeoutException; oldMaskingMediaItemIndex); } + @Override public long getSeekBackIncrement() { verifyApplicationThread(); return seekBackIncrementMs; } + @Override public long getSeekForwardIncrement() { verifyApplicationThread(); return seekForwardIncrementMs; } + @Override public long getMaxSeekToPreviousPosition() { verifyApplicationThread(); return C.DEFAULT_MAX_SEEK_TO_PREVIOUS_POSITION_MS; } + @Override public void setPlaybackParameters(PlaybackParameters playbackParameters) { verifyApplicationThread(); if (playbackParameters == null) { @@ -889,11 +880,13 @@ import java.util.concurrent.TimeoutException; /* ignored */ C.INDEX_UNSET); } + @Override public PlaybackParameters getPlaybackParameters() { verifyApplicationThread(); return playbackInfo.playbackParameters; } + @Override public void setSeekParameters(@Nullable SeekParameters seekParameters) { verifyApplicationThread(); if (seekParameters == null) { @@ -905,18 +898,20 @@ import java.util.concurrent.TimeoutException; } } + @Override public SeekParameters getSeekParameters() { verifyApplicationThread(); return seekParameters; } + @Override public void setForegroundMode(boolean foregroundMode) { verifyApplicationThread(); if (this.foregroundMode != foregroundMode) { this.foregroundMode = foregroundMode; if (!internalPlayer.setForegroundMode(foregroundMode)) { // One of the renderers timed out releasing its resources. - stop( + stopInternal( /* reset= */ false, ExoPlaybackException.createForUnexpected( new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_SET_FOREGROUND_MODE), @@ -925,55 +920,20 @@ import java.util.concurrent.TimeoutException; } } + @Override public void stop() { stop(/* reset= */ false); } + @Override public void stop(boolean reset) { verifyApplicationThread(); audioFocusManager.updateAudioFocus(getPlayWhenReady(), Player.STATE_IDLE); - stop(reset, /* error= */ null); + stopInternal(reset, /* error= */ null); currentCues = ImmutableList.of(); } - /** - * Stops the player. - * - * @param reset Whether the playlist should be cleared and whether the playback position and - * playback error should be reset. - * @param error An optional {@link ExoPlaybackException} to set. - */ - public void stop(boolean reset, @Nullable ExoPlaybackException error) { - PlaybackInfo playbackInfo; - if (reset) { - playbackInfo = - removeMediaItemsInternal( - /* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size()); - playbackInfo = playbackInfo.copyWithPlaybackError(null); - } else { - playbackInfo = this.playbackInfo.copyWithLoadingMediaPeriodId(this.playbackInfo.periodId); - playbackInfo.bufferedPositionUs = playbackInfo.positionUs; - playbackInfo.totalBufferedDurationUs = 0; - } - playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); - if (error != null) { - playbackInfo = playbackInfo.copyWithPlaybackError(error); - } - pendingOperationAcks++; - internalPlayer.stop(); - boolean positionDiscontinuity = - playbackInfo.timeline.isEmpty() && !this.playbackInfo.timeline.isEmpty(); - updatePlaybackInfo( - playbackInfo, - TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* seekProcessed= */ false, - positionDiscontinuity, - DISCONTINUITY_REASON_REMOVE, - /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(playbackInfo), - /* ignored */ C.INDEX_UNSET); - } - + @Override public void release() { Log.i( TAG, @@ -1027,11 +987,13 @@ import java.util.concurrent.TimeoutException; playerReleased = true; } + @Override public PlayerMessage createMessage(Target target) { verifyApplicationThread(); return createMessageInternal(target); } + @Override public int getCurrentPeriodIndex() { verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { @@ -1041,12 +1003,14 @@ import java.util.concurrent.TimeoutException; } } + @Override public int getCurrentMediaItemIndex() { verifyApplicationThread(); int currentWindowIndex = getCurrentWindowIndexInternal(); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } + @Override public long getDuration() { verifyApplicationThread(); if (isPlayingAd()) { @@ -1058,18 +1022,13 @@ import java.util.concurrent.TimeoutException; return getContentDuration(); } - private long getContentDuration() { - Timeline timeline = getCurrentTimeline(); - return timeline.isEmpty() - ? C.TIME_UNSET - : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); - } - + @Override public long getCurrentPosition() { verifyApplicationThread(); return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } + @Override public long getBufferedPosition() { verifyApplicationThread(); if (isPlayingAd()) { @@ -1080,26 +1039,31 @@ import java.util.concurrent.TimeoutException; return getContentBufferedPosition(); } + @Override public long getTotalBufferedDuration() { verifyApplicationThread(); return Util.usToMs(playbackInfo.totalBufferedDurationUs); } + @Override public boolean isPlayingAd() { verifyApplicationThread(); return playbackInfo.periodId.isAd(); } + @Override public int getCurrentAdGroupIndex() { verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET; } + @Override public int getCurrentAdIndexInAdGroup() { verifyApplicationThread(); return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET; } + @Override public long getContentPosition() { verifyApplicationThread(); if (isPlayingAd()) { @@ -1115,6 +1079,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public long getContentBufferedPosition() { verifyApplicationThread(); if (playbackInfo.timeline.isEmpty()) { @@ -1139,46 +1104,55 @@ import java.util.concurrent.TimeoutException; playbackInfo.timeline, playbackInfo.loadingMediaPeriodId, contentBufferedPositionUs)); } + @Override public int getRendererCount() { verifyApplicationThread(); return renderers.length; } + @Override public @C.TrackType int getRendererType(int index) { verifyApplicationThread(); return renderers[index].getTrackType(); } + @Override public Renderer getRenderer(int index) { verifyApplicationThread(); return renderers[index]; } + @Override public TrackSelector getTrackSelector() { verifyApplicationThread(); return trackSelector; } + @Override public TrackGroupArray getCurrentTrackGroups() { verifyApplicationThread(); return playbackInfo.trackGroups; } + @Override public TrackSelectionArray getCurrentTrackSelections() { verifyApplicationThread(); return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections); } + @Override public TracksInfo getCurrentTracksInfo() { verifyApplicationThread(); return playbackInfo.trackSelectorResult.tracksInfo; } + @Override public TrackSelectionParameters getTrackSelectionParameters() { verifyApplicationThread(); return trackSelector.getParameters(); } + @Override public void setTrackSelectionParameters(TrackSelectionParameters parameters) { verifyApplicationThread(); if (!trackSelector.isSetParametersSupported() @@ -1191,16 +1165,19 @@ import java.util.concurrent.TimeoutException; listener -> listener.onTrackSelectionParametersChanged(parameters)); } + @Override public MediaMetadata getMediaMetadata() { verifyApplicationThread(); return mediaMetadata; } + @Override public MediaMetadata getPlaylistMetadata() { verifyApplicationThread(); return playlistMetadata; } + @Override public void setPlaylistMetadata(MediaMetadata playlistMetadata) { verifyApplicationThread(); checkNotNull(playlistMetadata); @@ -1213,21 +1190,25 @@ import java.util.concurrent.TimeoutException; listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } + @Override public Timeline getCurrentTimeline() { verifyApplicationThread(); return playbackInfo.timeline; } + @Override public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { verifyApplicationThread(); this.videoScalingMode = videoScalingMode; sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_SCALING_MODE, videoScalingMode); } + @Override public @C.VideoScalingMode int getVideoScalingMode() { return videoScalingMode; } + @Override public void setVideoChangeFrameRateStrategy( @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { verifyApplicationThread(); @@ -1239,14 +1220,17 @@ import java.util.concurrent.TimeoutException; TRACK_TYPE_VIDEO, MSG_SET_CHANGE_FRAME_RATE_STRATEGY, videoChangeFrameRateStrategy); } + @Override public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { return videoChangeFrameRateStrategy; } + @Override public VideoSize getVideoSize() { return videoSize; } + @Override public void clearVideoSurface() { verifyApplicationThread(); removeSurfaceCallbacks(); @@ -1254,6 +1238,7 @@ import java.util.concurrent.TimeoutException; maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); } + @Override public void clearVideoSurface(@Nullable Surface surface) { verifyApplicationThread(); if (surface != null && surface == videoOutput) { @@ -1261,6 +1246,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public void setVideoSurface(@Nullable Surface surface) { verifyApplicationThread(); removeSurfaceCallbacks(); @@ -1269,6 +1255,7 @@ import java.util.concurrent.TimeoutException; maybeNotifySurfaceSizeChanged(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize); } + @Override public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { verifyApplicationThread(); if (surfaceHolder == null) { @@ -1290,6 +1277,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) { verifyApplicationThread(); if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) { @@ -1297,6 +1285,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) { verifyApplicationThread(); if (surfaceView instanceof VideoDecoderOutputBufferRenderer) { @@ -1318,11 +1307,13 @@ import java.util.concurrent.TimeoutException; } } + @Override public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) { verifyApplicationThread(); clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder()); } + @Override public void setVideoTextureView(@Nullable TextureView textureView) { verifyApplicationThread(); if (textureView == null) { @@ -1347,6 +1338,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public void clearVideoTextureView(@Nullable TextureView textureView) { verifyApplicationThread(); if (textureView != null && textureView == this.textureView) { @@ -1354,6 +1346,7 @@ import java.util.concurrent.TimeoutException; } } + @Override public void setAudioAttributes(AudioAttributes newAudioAttributes, boolean handleAudioFocus) { verifyApplicationThread(); if (playerReleased) { @@ -1378,10 +1371,12 @@ import java.util.concurrent.TimeoutException; listeners.flushEvents(); } + @Override public AudioAttributes getAudioAttributes() { return audioAttributes; } + @Override public void setAudioSessionId(int audioSessionId) { verifyApplicationThread(); if (this.audioSessionId == audioSessionId) { @@ -1406,19 +1401,23 @@ import java.util.concurrent.TimeoutException; EVENT_AUDIO_SESSION_ID, listener -> listener.onAudioSessionIdChanged(finalAudioSessionId)); } + @Override public int getAudioSessionId() { return audioSessionId; } + @Override public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) { verifyApplicationThread(); sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUX_EFFECT_INFO, auxEffectInfo); } + @Override public void clearAuxEffectInfo() { setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); } + @Override public void setVolume(float volume) { verifyApplicationThread(); volume = Util.constrainValue(volume, /* min= */ 0, /* max= */ 1); @@ -1431,14 +1430,17 @@ import java.util.concurrent.TimeoutException; listeners.sendEvent(EVENT_VOLUME_CHANGED, listener -> listener.onVolumeChanged(finalVolume)); } + @Override public float getVolume() { return volume; } + @Override public boolean getSkipSilenceEnabled() { return skipSilenceEnabled; } + @Override public void setSkipSilenceEnabled(boolean newSkipSilenceEnabled) { verifyApplicationThread(); if (skipSilenceEnabled == newSkipSilenceEnabled) { @@ -1451,21 +1453,25 @@ import java.util.concurrent.TimeoutException; listener -> listener.onSkipSilenceEnabledChanged(newSkipSilenceEnabled)); } + @Override public AnalyticsCollector getAnalyticsCollector() { return analyticsCollector; } + @Override public void addAnalyticsListener(AnalyticsListener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); analyticsCollector.addListener(listener); } + @Override public void removeAnalyticsListener(AnalyticsListener listener) { // Don't verify application thread. We allow calls to this method from any thread. analyticsCollector.removeListener(listener); } + @Override public void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { verifyApplicationThread(); if (playerReleased) { @@ -1474,6 +1480,7 @@ import java.util.concurrent.TimeoutException; audioBecomingNoisyManager.setEnabled(handleAudioBecomingNoisy); } + @Override public void setPriorityTaskManager(@Nullable PriorityTaskManager priorityTaskManager) { verifyApplicationThread(); if (Util.areEqual(this.priorityTaskManager, priorityTaskManager)) { @@ -1491,26 +1498,31 @@ import java.util.concurrent.TimeoutException; this.priorityTaskManager = priorityTaskManager; } + @Override @Nullable public Format getVideoFormat() { return videoFormat; } + @Override @Nullable public Format getAudioFormat() { return audioFormat; } + @Override @Nullable public DecoderCounters getVideoDecoderCounters() { return videoDecoderCounters; } + @Override @Nullable public DecoderCounters getAudioDecoderCounters() { return audioDecoderCounters; } + @Override public void setVideoFrameMetadataListener(VideoFrameMetadataListener listener) { verifyApplicationThread(); videoFrameMetadataListener = listener; @@ -1520,6 +1532,7 @@ import java.util.concurrent.TimeoutException; .send(); } + @Override public void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener) { verifyApplicationThread(); if (videoFrameMetadataListener != listener) { @@ -1531,6 +1544,7 @@ import java.util.concurrent.TimeoutException; .send(); } + @Override public void setCameraMotionListener(CameraMotionListener listener) { verifyApplicationThread(); cameraMotionListener = listener; @@ -1540,6 +1554,7 @@ import java.util.concurrent.TimeoutException; .send(); } + @Override public void clearCameraMotionListener(CameraMotionListener listener) { verifyApplicationThread(); if (cameraMotionListener != listener) { @@ -1551,27 +1566,32 @@ import java.util.concurrent.TimeoutException; .send(); } + @Override public List getCurrentCues() { verifyApplicationThread(); return currentCues; } + @Override public void addListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.add(listener); } + @Override public void removeListener(Listener listener) { // Don't verify application thread. We allow calls to this method from any thread. checkNotNull(listener); listeners.remove(listener); } + @Override public void setHandleWakeLock(boolean handleWakeLock) { setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); } + @Override public void setWakeMode(@C.WakeMode int wakeMode) { verifyApplicationThread(); switch (wakeMode) { @@ -1592,36 +1612,43 @@ import java.util.concurrent.TimeoutException; } } + @Override public DeviceInfo getDeviceInfo() { verifyApplicationThread(); return deviceInfo; } + @Override public int getDeviceVolume() { verifyApplicationThread(); return streamVolumeManager.getVolume(); } + @Override public boolean isDeviceMuted() { verifyApplicationThread(); return streamVolumeManager.isMuted(); } + @Override public void setDeviceVolume(int volume) { verifyApplicationThread(); streamVolumeManager.setVolume(volume); } + @Override public void increaseDeviceVolume() { verifyApplicationThread(); streamVolumeManager.increaseVolume(); } + @Override public void decreaseDeviceVolume() { verifyApplicationThread(); streamVolumeManager.decreaseVolume(); } + @Override public void setDeviceMuted(boolean muted) { verifyApplicationThread(); streamVolumeManager.setMuted(muted); @@ -1631,6 +1658,44 @@ import java.util.concurrent.TimeoutException; this.throwsWhenUsingWrongThread = throwsWhenUsingWrongThread; } + /** + * Stops the player. + * + * @param reset Whether the playlist should be cleared and whether the playback position and + * playback error should be reset. + * @param error An optional {@link ExoPlaybackException} to set. + */ + private void stopInternal(boolean reset, @Nullable ExoPlaybackException error) { + PlaybackInfo playbackInfo; + if (reset) { + playbackInfo = + removeMediaItemsInternal( + /* fromIndex= */ 0, /* toIndex= */ mediaSourceHolderSnapshots.size()); + playbackInfo = playbackInfo.copyWithPlaybackError(null); + } else { + playbackInfo = this.playbackInfo.copyWithLoadingMediaPeriodId(this.playbackInfo.periodId); + playbackInfo.bufferedPositionUs = playbackInfo.positionUs; + playbackInfo.totalBufferedDurationUs = 0; + } + playbackInfo = playbackInfo.copyWithPlaybackState(Player.STATE_IDLE); + if (error != null) { + playbackInfo = playbackInfo.copyWithPlaybackError(error); + } + pendingOperationAcks++; + internalPlayer.stop(); + boolean positionDiscontinuity = + playbackInfo.timeline.isEmpty() && !this.playbackInfo.timeline.isEmpty(); + updatePlaybackInfo( + playbackInfo, + TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false, + positionDiscontinuity, + DISCONTINUITY_REASON_REMOVE, + /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(playbackInfo), + /* ignored */ C.INDEX_UNSET); + } + private int getCurrentWindowIndexInternal() { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; @@ -2415,7 +2480,7 @@ import java.util.concurrent.TimeoutException; } this.videoOutput = videoOutput; if (messageDeliveryTimedOut) { - stop( + stopInternal( /* reset= */ false, ExoPlaybackException.createForUnexpected( new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE), From f33dff64d624bf47152b964afd57802c474c1835 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 11 Feb 2022 10:07:48 +0000 Subject: [PATCH 193/251] Rollback of https://github.com/androidx/media/commit/2848240a8c5edd447882fdb9d70ffb53a2b54e10 *** Original commit *** Update gradle wrapper To fix transformer demo + Nexus emulator bug. #minor-release *** PiperOrigin-RevId: 427952642 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0440a40b90..1e62b66061 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip From 8b180eb04052ab24fd7756925b4c8e7438feb4d6 Mon Sep 17 00:00:00 2001 From: kimvde Date: Fri, 11 Feb 2022 11:43:55 +0000 Subject: [PATCH 194/251] Remove Transformer deprecated methods that were never released #minor-release PiperOrigin-RevId: 427965501 --- .../media3/transformer/Transformer.java | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 3b88fff41d..0e1572a8c7 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -26,7 +26,6 @@ import static java.lang.Math.min; import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; -import android.graphics.Matrix; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; @@ -225,24 +224,6 @@ public final class Transformer { return this; } - /** @deprecated Use {@link TransformationRequest.Builder#setResolution(int)} instead. */ - @Deprecated - public Builder setResolution(int outputHeight) { - transformationRequest = transformationRequest.buildUpon().setResolution(outputHeight).build(); - return this; - } - - /** - * @deprecated Use {@link TransformationRequest.Builder#setTransformationMatrix(Matrix)} - * instead. - */ - @Deprecated - public Builder setTransformationMatrix(Matrix transformationMatrix) { - transformationRequest = - transformationRequest.buildUpon().setTransformationMatrix(transformationMatrix).build(); - return this; - } - /** * @deprecated This feature will be removed in a following release and the MIME type of the * output will always be MP4. @@ -253,22 +234,6 @@ public final class Transformer { return this; } - /** @deprecated Use {@link TransformationRequest.Builder#setVideoMimeType(String)} instead. */ - @Deprecated - public Builder setVideoMimeType(String videoMimeType) { - transformationRequest = - transformationRequest.buildUpon().setVideoMimeType(videoMimeType).build(); - return this; - } - - /** @deprecated Use {@link TransformationRequest.Builder#setAudioMimeType(String)} instead. */ - @Deprecated - public Builder setAudioMimeType(String audioMimeType) { - transformationRequest = - transformationRequest.buildUpon().setAudioMimeType(audioMimeType).build(); - return this; - } - /** * @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link * #removeAllListeners()} instead. From b5ed01d479499b557fe968b0b81862a4b34ace8e Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 11 Feb 2022 13:47:20 +0000 Subject: [PATCH 195/251] Transformer GL: Implement auto-scaling to preserve input frame. PiperOrigin-RevId: 427982223 --- .../transformer/ConfigurationActivity.java | 2 +- .../transformer/DefaultEncoderFactory.java | 2 +- .../transformer/TransformationRequest.java | 20 +-- .../VideoTranscodingSamplePipeline.java | 122 +++++++++++------- 4 files changed, 82 insertions(+), 64 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java index 97fdc6e6d2..1200d6b8c6 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java @@ -152,7 +152,7 @@ public final class ConfigurationActivity extends AppCompatActivity { rotateAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotateSpinner = findViewById(R.id.rotate_spinner); rotateSpinner.setAdapter(rotateAdapter); - rotateAdapter.addAll(SAME_AS_INPUT_OPTION, "0", "10", "45", "90", "180"); + rotateAdapter.addAll(SAME_AS_INPUT_OPTION, "0", "10", "45", "60", "90", "180"); enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index f901efe5e0..1eafb15ec3 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -107,7 +107,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { checkArgument(format.width != Format.NO_VALUE); checkArgument(format.height != Format.NO_VALUE); // According to interface Javadoc, format.rotationDegrees should be 0. The video should always - // be in landscape orientation. + // be encoded in landscape orientation. checkArgument(format.height <= format.width); checkArgument(format.rotationDegrees == 0); checkNotNull(format.sampleMimeType); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java index 0c0f37f0d9..a74c1ca0fc 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java @@ -26,7 +26,6 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.extractor.mp4.Mp4Extractor; -import com.google.common.collect.ImmutableSet; /** A media transformation request. */ @UnstableApi @@ -35,9 +34,6 @@ public final class TransformationRequest { /** A builder for {@link TransformationRequest} instances. */ public static final class Builder { - private static final ImmutableSet SUPPORTED_OUTPUT_HEIGHTS = - ImmutableSet.of(144, 240, 360, 480, 720, 1080, 1440, 2160); - private Matrix transformationMatrix; private boolean flattenForSlowMotion; private int outputHeight; @@ -124,27 +120,17 @@ public final class TransformationRequest { /** * Sets the output resolution using the output height. The default value {@link C#LENGTH_UNSET} - * corresponds to using the same height as the input. Output width will scale to preserve the - * input video's aspect ratio. - * - *

    For now, only "popular" heights like 144, 240, 360, 480, 720, 1080, 1440, or 2160 are - * supported, to ensure compatibility on different devices. + * corresponds to using the same height as the input. Output width of the displayed video will + * scale to preserve the video's aspect ratio after other transformations. * *

    For example, a 1920x1440 video can be scaled to 640x480 by calling setResolution(480). * - * @param outputHeight The output height in pixels. + * @param outputHeight The output height of the displayed video, in pixels. * @return This builder. * @throws IllegalArgumentException If the {@code outputHeight} is not supported. */ public Builder setResolution(int outputHeight) { - // TODO(b/209781577): Define outputHeight in the javadoc as height can be ambiguous for videos - // where rotationDegrees is set in the Format. // TODO(b/201293185): Restructure to input a Presentation class. - // TODO(b/201293185): Check encoder codec capabilities in order to allow arbitrary - // resolutions and reasonable fallbacks. - checkArgument( - outputHeight == C.LENGTH_UNSET || SUPPORTED_OUTPUT_HEIGHTS.contains(outputHeight), - "Unsupported outputHeight: " + outputHeight); this.outputHeight = outputHeight; return this; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 843a389882..80bbeea2e6 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -18,6 +18,8 @@ package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.SDK_INT; +import static java.lang.Math.max; +import static java.lang.Math.min; import android.content.Context; import android.graphics.Matrix; @@ -63,76 +65,106 @@ import org.checkerframework.dataflow.qual.Pure; encoderOutputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); - // Scale width and height to desired transformationRequest.outputHeight, preserving aspect - // ratio. - // TODO(b/209781577): Think about which edge length should be set for portrait videos. - float inputFormatAspectRatio = (float) inputFormat.width / inputFormat.height; - int outputWidth = inputFormat.width; - int outputHeight = inputFormat.height; + // The decoder rotates encoded frames for display by inputFormat.rotationDegrees. + int decodedWidth = + (inputFormat.rotationDegrees % 180 == 0) ? inputFormat.width : inputFormat.height; + int decodedHeight = + (inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width; + float decodedAspectRatio = (float) decodedWidth / decodedHeight; + + Matrix transformationMatrix = new Matrix(transformationRequest.transformationMatrix); + + int outputWidth = decodedWidth; + int outputHeight = decodedHeight; + if (!transformationMatrix.isIdentity()) { + // Scale frames by decodedAspectRatio, to account for FrameEditor's normalized device + // coordinates (NDC) (a square from -1 to 1 for both x and y) and preserve rectangular display + // of input pixels during transformations (ex. rotations). With scaling, transformationMatrix + // operations operate on a rectangle for x from -decodedAspectRatio to decodedAspectRatio, and + // y from -1 to 1. + transformationMatrix.preScale(/* sx= */ decodedAspectRatio, /* sy= */ 1f); + transformationMatrix.postScale(/* sx= */ 1f / decodedAspectRatio, /* sy= */ 1f); + + float[][] transformOnNdcPoints = {{-1, -1, 0, 1}, {-1, 1, 0, 1}, {1, -1, 0, 1}, {1, 1, 0, 1}}; + float xMin = Float.MAX_VALUE; + float xMax = Float.MIN_VALUE; + float yMin = Float.MAX_VALUE; + float yMax = Float.MIN_VALUE; + for (float[] transformOnNdcPoint : transformOnNdcPoints) { + transformationMatrix.mapPoints(transformOnNdcPoint); + xMin = min(xMin, transformOnNdcPoint[0]); + xMax = max(xMax, transformOnNdcPoint[0]); + yMin = min(yMin, transformOnNdcPoint[1]); + yMax = max(yMax, transformOnNdcPoint[1]); + } + + float xCenter = (xMax + xMin) / 2f; + float yCenter = (yMax + yMin) / 2f; + transformationMatrix.postTranslate(-xCenter, -yCenter); + + float ndcWidthAndHeight = 2f; // Length from -1 to 1. + float xScale = (xMax - xMin) / ndcWidthAndHeight; + float yScale = (yMax - yMin) / ndcWidthAndHeight; + transformationMatrix.postScale(1f / xScale, 1f / yScale); + outputWidth = Math.round(decodedWidth * xScale); + outputHeight = Math.round(decodedHeight * yScale); + } + // Scale width and height to desired transformationRequest.outputHeight, preserving + // aspect ratio. if (transformationRequest.outputHeight != C.LENGTH_UNSET - && transformationRequest.outputHeight != inputFormat.height) { - outputWidth = Math.round(inputFormatAspectRatio * transformationRequest.outputHeight); + && transformationRequest.outputHeight != outputHeight) { + outputWidth = + Math.round((float) transformationRequest.outputHeight * outputWidth / outputHeight); outputHeight = transformationRequest.outputHeight; } - // The encoder may not support encoding in portrait orientation, so the decoded video is - // rotated to landscape orientation and a rotation is added back later to the output format. - boolean swapEncodingDimensions = inputFormat.height > inputFormat.width; + // Encoders commonly support higher maximum widths than maximum heights. Rotate the decoded + // video before encoding, so the encoded video's width >= height, and set outputRotationDegrees + // to ensure the video is displayed in the correct orientation. + int requestedEncoderWidth; + int requestedEncoderHeight; + boolean swapEncodingDimensions = outputHeight > outputWidth; if (swapEncodingDimensions) { - outputRotationDegrees = (inputFormat.rotationDegrees + 90) % 360; - int temp = outputWidth; - outputWidth = outputHeight; - outputHeight = temp; + outputRotationDegrees = 90; + requestedEncoderWidth = outputHeight; + requestedEncoderHeight = outputWidth; + // TODO(b/201293185): After fragment shader transformations are implemented, put + // postRotate in a later vertex shader. + transformationMatrix.postRotate(outputRotationDegrees); } else { - outputRotationDegrees = inputFormat.rotationDegrees; + outputRotationDegrees = 0; + requestedEncoderWidth = outputWidth; + requestedEncoderHeight = outputHeight; } - float displayAspectRatio = - (inputFormat.rotationDegrees % 180) == 0 - ? inputFormatAspectRatio - : 1.0f / inputFormatAspectRatio; - Matrix transformationMatrix = new Matrix(transformationRequest.transformationMatrix); - // Scale frames by input aspect ratio, to account for FrameEditor's square normalized device - // coordinates (-1 to 1) and preserve frame relative dimensions during transformations - // (ex. rotations). After this scaling, transformationMatrix operations operate on a rectangle - // for x from -displayAspectRatio to displayAspectRatio, and y from -1 to 1 - transformationMatrix.preScale(displayAspectRatio, 1); - transformationMatrix.postScale(1.0f / displayAspectRatio, 1); - - // The decoder rotates videos to their intended display orientation. The frameEditor rotates - // them back for improved encoder compatibility. - // TODO(b/201293185): After fragment shader transformations are implemented, put - // postRotate in a later vertex shader. - transformationMatrix.postRotate(outputRotationDegrees); - - Format requestedOutputFormat = + Format requestedEncoderFormat = new Format.Builder() - .setWidth(outputWidth) - .setHeight(outputHeight) + .setWidth(requestedEncoderWidth) + .setHeight(requestedEncoderHeight) .setRotationDegrees(0) .setSampleMimeType( transformationRequest.videoMimeType != null ? transformationRequest.videoMimeType : inputFormat.sampleMimeType) .build(); - encoder = encoderFactory.createForVideoEncoding(requestedOutputFormat, allowedOutputMimeTypes); - Format actualOutputFormat = encoder.getConfigurationFormat(); + encoder = encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes); + Format encoderSupportedFormat = encoder.getConfigurationFormat(); fallbackListener.onTransformationRequestFinalized( createFallbackTransformationRequest( transformationRequest, !swapEncodingDimensions, - requestedOutputFormat, - actualOutputFormat)); + requestedEncoderFormat, + encoderSupportedFormat)); if (transformationRequest.enableHdrEditing - || inputFormat.height != actualOutputFormat.height - || inputFormat.width != actualOutputFormat.width + || inputFormat.height != encoderSupportedFormat.height + || inputFormat.width != encoderSupportedFormat.width || !transformationMatrix.isIdentity()) { frameEditor = FrameEditor.create( context, - actualOutputFormat.width, - actualOutputFormat.height, + encoderSupportedFormat.width, + encoderSupportedFormat.height, inputFormat.pixelWidthHeightRatio, transformationMatrix, /* outputSurface= */ encoder.getInputSurface(), From 8f7c91a1ad03716224963d9bced66a8de90c1762 Mon Sep 17 00:00:00 2001 From: krocard Date: Fri, 11 Feb 2022 13:58:30 +0000 Subject: [PATCH 196/251] Split AnalyticsCollector in interface and default Impl This will allow R8 to strip out the implementation if it is not needed for an app. #minor-release PiperOrigin-RevId: 427983730 --- .../androidx/media3/exoplayer/ExoPlayer.java | 3 +- .../analytics/AnalyticsCollector.java | 1176 +--------------- .../analytics/DefaultAnalyticsCollector.java | 1215 +++++++++++++++++ .../exoplayer/MediaPeriodQueueTest.java | 3 +- .../media3/exoplayer/MediaSourceListTest.java | 3 +- ...ava => DefaultAnalyticsCollectorTest.java} | 18 +- .../test/utils/TestExoPlayerBuilder.java | 4 +- 7 files changed, 1264 insertions(+), 1158 deletions(-) create mode 100644 libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java rename libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/{AnalyticsCollectorTest.java => DefaultAnalyticsCollectorTest.java} (99%) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 488237fd82..e05289e8c0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -47,6 +47,7 @@ import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsListener; +import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.audio.AudioSink; import androidx.media3.exoplayer.audio.DefaultAudioSink; import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer; @@ -580,7 +581,7 @@ public interface ExoPlayer extends Player { this.analyticsCollectorSupplier = analyticsCollectorSupplier != null ? analyticsCollectorSupplier - : () -> new AnalyticsCollector(checkNotNull(clock)); + : () -> new DefaultAnalyticsCollector(checkNotNull(clock)); looper = Util.getCurrentOrMainLooper(); audioAttributes = AudioAttributes.DEFAULT; wakeMode = C.WAKE_MODE_NONE; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java index 72334349d7..03751eed2f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsCollector.java @@ -15,123 +15,52 @@ */ package androidx.media3.exoplayer.analytics; -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 android.media.AudioTrack; import android.media.MediaCodec; import android.media.MediaCodec.CodecException; import android.os.Looper; import android.os.SystemClock; -import android.util.SparseArray; import android.view.Surface; -import androidx.annotation.CallSuper; import androidx.annotation.Nullable; -import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; -import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; -import androidx.media3.common.MediaItem; -import androidx.media3.common.MediaMetadata; -import androidx.media3.common.Metadata; -import androidx.media3.common.PlaybackException; -import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; -import androidx.media3.common.Player.DiscontinuityReason; -import androidx.media3.common.Player.PlaybackSuppressionReason; -import androidx.media3.common.Timeline; -import androidx.media3.common.Timeline.Period; -import androidx.media3.common.Timeline.Window; -import androidx.media3.common.TrackGroupArray; -import androidx.media3.common.TrackSelectionArray; -import androidx.media3.common.TrackSelectionParameters; -import androidx.media3.common.TracksInfo; -import androidx.media3.common.VideoSize; -import androidx.media3.common.text.Cue; -import androidx.media3.common.util.Clock; -import androidx.media3.common.util.HandlerWrapper; -import androidx.media3.common.util.ListenerSet; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderException; import androidx.media3.exoplayer.DecoderCounters; import androidx.media3.exoplayer.DecoderReuseEvaluation; -import androidx.media3.exoplayer.ExoPlaybackException; -import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime; import androidx.media3.exoplayer.audio.AudioSink; -import androidx.media3.exoplayer.drm.DrmSession; import androidx.media3.exoplayer.drm.DrmSessionEventListener; -import androidx.media3.exoplayer.source.LoadEventInfo; -import androidx.media3.exoplayer.source.MediaLoadData; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSourceEventListener; import androidx.media3.exoplayer.upstream.BandwidthMeter; import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import java.io.IOException; import java.util.List; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** - * Data collector that forwards analytics events to {@link AnalyticsListener AnalyticsListeners}. + * Interface for data collectors that forward analytics events to {@link AnalyticsListener + * AnalyticsListeners}. */ @UnstableApi -public class AnalyticsCollector - implements Player.Listener, +public interface AnalyticsCollector + extends Player.Listener, MediaSourceEventListener, BandwidthMeter.EventListener, DrmSessionEventListener { - private final Clock clock; - private final Period period; - private final Window window; - private final MediaPeriodQueueTracker mediaPeriodQueueTracker; - private final SparseArray eventTimes; - - private ListenerSet listeners; - private @MonotonicNonNull Player player; - private @MonotonicNonNull HandlerWrapper handler; - private boolean isSeeking; - - /** - * Creates an analytics collector. - * - * @param clock A {@link Clock} used to generate timestamps. - */ - public AnalyticsCollector(Clock clock) { - this.clock = checkNotNull(clock); - listeners = new ListenerSet<>(Util.getCurrentOrMainLooper(), clock, (listener, flags) -> {}); - period = new Period(); - window = new Window(); - mediaPeriodQueueTracker = new MediaPeriodQueueTracker(period); - eventTimes = new SparseArray<>(); - } - /** * Adds a listener for analytics events. * * @param listener The listener to add. */ - @CallSuper - public void addListener(AnalyticsListener listener) { - checkNotNull(listener); - listeners.add(listener); - } + void addListener(AnalyticsListener listener); /** * Removes a previously added analytics event listener. * * @param listener The listener to remove. */ - @CallSuper - public void removeListener(AnalyticsListener listener) { - listeners.remove(listener); - } + void removeListener(AnalyticsListener listener); /** * Sets the player for which data will be collected. Must only be called if no player has been set @@ -140,28 +69,13 @@ public class AnalyticsCollector * @param player The {@link Player} for which data will be collected. * @param looper The {@link Looper} used for listener callbacks. */ - @CallSuper - public void setPlayer(Player player, Looper looper) { - checkState(this.player == null || mediaPeriodQueueTracker.mediaPeriodQueue.isEmpty()); - this.player = checkNotNull(player); - handler = clock.createHandler(looper, null); - listeners = - listeners.copy( - looper, - (listener, flags) -> - listener.onEvents(player, new AnalyticsListener.Events(flags, eventTimes))); - } + void setPlayer(Player player, Looper looper); /** * Releases the collector. Must be called after the player for which data is collected has been * released. */ - @CallSuper - public void release() { - // Release lazily so that all events that got triggered as part of player.release() - // are still delivered to all listeners and onPlayerReleased() is delivered last. - checkStateNotNull(handler).post(this::releaseInternal); - } + void release(); /** * Updates the playback queue information used for event association. @@ -172,26 +86,13 @@ public class AnalyticsCollector * @param readingPeriod The media period in the queue that is currently being read by renderers, * or null if the queue is empty. */ - public final void updateMediaPeriodQueueInfo( - List queue, @Nullable MediaPeriodId readingPeriod) { - mediaPeriodQueueTracker.onQueueUpdated(queue, readingPeriod, checkNotNull(player)); - } - - // External events. + void updateMediaPeriodQueueInfo(List queue, @Nullable MediaPeriodId readingPeriod); /** * Notify analytics collector that a seek operation will start. Should be called before the player * adjusts its state and position to the seek. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void notifySeekStarted() { - if (!isSeeking) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - isSeeking = true; - sendEvent( - eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekStarted(eventTime)); - } - } + void notifySeekStarted(); // Audio events. @@ -201,17 +102,7 @@ public class AnalyticsCollector * @param counters {@link DecoderCounters} that will be updated by the audio renderer for as long * as it remains enabled. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onAudioEnabled(DecoderCounters counters) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_ENABLED, - listener -> { - listener.onAudioEnabled(eventTime, counters); - listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_AUDIO, counters); - }); - } + void onAudioEnabled(DecoderCounters counters); /** * Called when a audio decoder is created. @@ -221,21 +112,8 @@ public class AnalyticsCollector * finished. * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onAudioDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_DECODER_INITIALIZED, - listener -> { - listener.onAudioDecoderInitialized(eventTime, decoderName, initializationDurationMs); - listener.onAudioDecoderInitialized( - eventTime, decoderName, initializedTimestampMs, initializationDurationMs); - listener.onDecoderInitialized( - eventTime, C.TRACK_TYPE_AUDIO, decoderName, initializationDurationMs); - }); - } + void onAudioDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs); /** * Called when the format of the media being consumed by the audio renderer changes. @@ -245,19 +123,8 @@ public class AnalyticsCollector * decoder instance can be reused for the new format, or {@code null} if the renderer did not * have a decoder. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onAudioInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_INPUT_FORMAT_CHANGED, - listener -> { - listener.onAudioInputFormatChanged(eventTime, format); - listener.onAudioInputFormatChanged(eventTime, format, decoderReuseEvaluation); - listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_AUDIO, format); - }); - } + void onAudioInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation); /** * Called when the audio position has increased for the first time since the last pause or @@ -266,13 +133,7 @@ public class AnalyticsCollector * @param playoutStartSystemTimeMs The approximate derived {@link System#currentTimeMillis()} at * which playout started. */ - public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_POSITION_ADVANCING, - listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs)); - } + void onAudioPositionAdvancing(long playoutStartSystemTimeMs); /** * Called when an audio underrun occurs. @@ -282,45 +143,21 @@ public class AnalyticsCollector * encoded audio. {@link C#TIME_UNSET} if the output buffer contains non-PCM encoded audio. * @param elapsedSinceLastFeedMs The time since audio was last written to the output buffer. */ - public final void onAudioUnderrun( - int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_UNDERRUN, - listener -> - listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs)); - } + void onAudioUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); /** * Called when a audio decoder is released. * * @param decoderName The audio decoder that was released. */ - public final void onAudioDecoderReleased(String decoderName) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_DECODER_RELEASED, - listener -> listener.onAudioDecoderReleased(eventTime, decoderName)); - } + void onAudioDecoderReleased(String decoderName); /** * Called when the audio renderer is disabled. * * @param counters {@link DecoderCounters} that were updated by the audio renderer. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onAudioDisabled(DecoderCounters counters) { - EventTime eventTime = generatePlayingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_DISABLED, - listener -> { - listener.onAudioDisabled(eventTime, counters); - listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_AUDIO, counters); - }); - } + void onAudioDisabled(DecoderCounters counters); /** * Called when {@link AudioSink} has encountered an error. @@ -332,13 +169,7 @@ public class AnalyticsCollector * AudioSink.InitializationException}, a {@link AudioSink.WriteException}, or an {@link * AudioSink.UnexpectedDiscontinuityException}. */ - public final void onAudioSinkError(Exception audioSinkError) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_SINK_ERROR, - listener -> listener.onAudioSinkError(eventTime, audioSinkError)); - } + void onAudioSinkError(Exception audioSinkError); /** * Called when an audio decoder encounters an error. @@ -346,26 +177,7 @@ public class AnalyticsCollector * @param audioCodecError The error. Typically a {@link CodecException} if the renderer uses * {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder. */ - public final void onAudioCodecError(Exception audioCodecError) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_CODEC_ERROR, - listener -> listener.onAudioCodecError(eventTime, audioCodecError)); - } - - /** - * Called when the volume changes. - * - * @param volume The new volume, with 0 being silence and 1 being unity gain. - */ - public final void onVolumeChanged(float volume) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VOLUME_CHANGED, - listener -> listener.onVolumeChanged(eventTime, volume)); - } + void onAudioCodecError(Exception audioCodecError); // Video events. @@ -375,17 +187,7 @@ public class AnalyticsCollector * @param counters {@link DecoderCounters} that will be updated by the video renderer for as long * as it remains enabled. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onVideoEnabled(DecoderCounters counters) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_ENABLED, - listener -> { - listener.onVideoEnabled(eventTime, counters); - listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_VIDEO, counters); - }); - } + void onVideoEnabled(DecoderCounters counters); /** * Called when a video decoder is created. @@ -395,21 +197,8 @@ public class AnalyticsCollector * finished. * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onVideoDecoderInitialized( - String decoderName, long initializedTimestampMs, long initializationDurationMs) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_DECODER_INITIALIZED, - listener -> { - listener.onVideoDecoderInitialized(eventTime, decoderName, initializationDurationMs); - listener.onVideoDecoderInitialized( - eventTime, decoderName, initializedTimestampMs, initializationDurationMs); - listener.onDecoderInitialized( - eventTime, C.TRACK_TYPE_VIDEO, decoderName, initializationDurationMs); - }); - } + void onVideoDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs); /** * Called when the format of the media being consumed by the video renderer changes. @@ -419,19 +208,8 @@ public class AnalyticsCollector * decoder instance can be reused for the new format, or {@code null} if the renderer did not * have a decoder. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onVideoInputFormatChanged( - Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_INPUT_FORMAT_CHANGED, - listener -> { - listener.onVideoInputFormatChanged(eventTime, format); - listener.onVideoInputFormatChanged(eventTime, format, decoderReuseEvaluation); - listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_VIDEO, format); - }); - } + void onVideoInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation); /** * Called to report the number of frames dropped by the video renderer. Dropped frames are @@ -443,43 +221,21 @@ public class AnalyticsCollector * is timed from when the renderer was started or from when dropped frames were last reported * (whichever was more recent), and not from when the first of the reported drops occurred. */ - public final void onDroppedFrames(int count, long elapsedMs) { - EventTime eventTime = generatePlayingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES, - listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs)); - } + void onDroppedFrames(int count, long elapsedMs); /** * Called when a video decoder is released. * * @param decoderName The video decoder that was released. */ - public final void onVideoDecoderReleased(String decoderName) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_DECODER_RELEASED, - listener -> listener.onVideoDecoderReleased(eventTime, decoderName)); - } + void onVideoDecoderReleased(String decoderName); /** * Called when the video renderer is disabled. * * @param counters {@link DecoderCounters} that were updated by the video renderer. */ - @SuppressWarnings("deprecation") // Calling deprecated listener method. - public final void onVideoDisabled(DecoderCounters counters) { - EventTime eventTime = generatePlayingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_DISABLED, - listener -> { - listener.onVideoDisabled(eventTime, counters); - listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters); - }); - } + void onVideoDisabled(DecoderCounters counters); /** * Called when a frame is rendered for the first time since setting the output, or since the @@ -489,13 +245,7 @@ public class AnalyticsCollector * renderers may have other output types (e.g., a {@link VideoDecoderOutputBufferRenderer}). * @param renderTimeMs The {@link SystemClock#elapsedRealtime()} when the frame was rendered. */ - public final void onRenderedFirstFrame(Object output, long renderTimeMs) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_RENDERED_FIRST_FRAME, - listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs)); - } + void onRenderedFirstFrame(Object output, long renderTimeMs); /** * Called to report the video processing offset of video frames processed by the video renderer. @@ -515,14 +265,7 @@ public class AnalyticsCollector * video frames processed by the renderer in microseconds. * @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}. */ - public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { - EventTime eventTime = generatePlayingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_FRAME_PROCESSING_OFFSET, - listener -> - listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount)); - } + void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount); /** * Called when a video decoder encounters an error. @@ -537,860 +280,5 @@ public class AnalyticsCollector * @param videoCodecError The error. Typically a {@link CodecException} if the renderer uses * {@link MediaCodec}, or a {@link DecoderException} if the renderer uses a software decoder. */ - public final void onVideoCodecError(Exception videoCodecError) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_CODEC_ERROR, - listener -> listener.onVideoCodecError(eventTime, videoCodecError)); - } - - /** - * Called each time there's a change in the size of the surface onto which the video is being - * rendered. - * - * @param width The surface width in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if the - * video is not rendered onto a surface. - * @param height The surface height in pixels. May be {@link C#LENGTH_UNSET} if unknown, or 0 if - * the video is not rendered onto a surface. - */ - public void onSurfaceSizeChanged(int width, int height) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SURFACE_SIZE_CHANGED, - listener -> listener.onSurfaceSizeChanged(eventTime, width, height)); - } - - // MediaSourceEventListener implementation. - - @Override - public final void onLoadStarted( - int windowIndex, - @Nullable MediaPeriodId mediaPeriodId, - LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_LOAD_STARTED, - listener -> listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData)); - } - - @Override - public final void onLoadCompleted( - int windowIndex, - @Nullable MediaPeriodId mediaPeriodId, - LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_LOAD_COMPLETED, - listener -> listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData)); - } - - @Override - public final void onLoadCanceled( - int windowIndex, - @Nullable MediaPeriodId mediaPeriodId, - LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_LOAD_CANCELED, - listener -> listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData)); - } - - @Override - public final void onLoadError( - int windowIndex, - @Nullable MediaPeriodId mediaPeriodId, - LoadEventInfo loadEventInfo, - MediaLoadData mediaLoadData, - IOException error, - boolean wasCanceled) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_LOAD_ERROR, - listener -> - listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled)); - } - - @Override - public final void onUpstreamDiscarded( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_UPSTREAM_DISCARDED, - listener -> listener.onUpstreamDiscarded(eventTime, mediaLoadData)); - } - - @Override - public final void onDownstreamFormatChanged( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DOWNSTREAM_FORMAT_CHANGED, - listener -> listener.onDownstreamFormatChanged(eventTime, mediaLoadData)); - } - - // Player.Listener implementation. - - // TODO: Use Player.Listener.onEvents to know when a set of simultaneous callbacks finished. - // This helps to assign exactly the same EventTime to all of them instead of having slightly - // different real times. - - @Override - public final void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) { - mediaPeriodQueueTracker.onTimelineChanged(checkNotNull(player)); - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_TIMELINE_CHANGED, - listener -> listener.onTimelineChanged(eventTime, reason)); - } - - @Override - public final void onMediaItemTransition( - @Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_MEDIA_ITEM_TRANSITION, - listener -> listener.onMediaItemTransition(eventTime, mediaItem, reason)); - } - - @Override - @SuppressWarnings("deprecation") // Implementing and calling deprecate listener method - public final void onTracksChanged( - TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_TRACKS_CHANGED, - listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections)); - } - - @Override - public void onTracksInfoChanged(TracksInfo tracksInfo) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_TRACKS_CHANGED, - listener -> listener.onTracksInfoChanged(eventTime, tracksInfo)); - } - - @SuppressWarnings("deprecation") // Implementing deprecated method. - @Override - public void onLoadingChanged(boolean isLoading) { - // Do nothing. Handled by non-deprecated onIsLoadingChanged. - } - - @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override - public final void onIsLoadingChanged(boolean isLoading) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_IS_LOADING_CHANGED, - listener -> { - listener.onLoadingChanged(eventTime, isLoading); - listener.onIsLoadingChanged(eventTime, isLoading); - }); - } - - @Override - public void onAvailableCommandsChanged(Player.Commands availableCommands) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AVAILABLE_COMMANDS_CHANGED, - listener -> listener.onAvailableCommandsChanged(eventTime, availableCommands)); - } - - @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. - @Override - public final void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - /* eventFlag= */ C.INDEX_UNSET, - listener -> listener.onPlayerStateChanged(eventTime, playWhenReady, playbackState)); - } - - @Override - public final void onPlaybackStateChanged(@Player.State int playbackState) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYBACK_STATE_CHANGED, - listener -> listener.onPlaybackStateChanged(eventTime, playbackState)); - } - - @Override - public final void onPlayWhenReadyChanged( - boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAY_WHEN_READY_CHANGED, - listener -> listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason)); - } - - @Override - public final void onPlaybackSuppressionReasonChanged( - @PlaybackSuppressionReason int playbackSuppressionReason) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, - listener -> - listener.onPlaybackSuppressionReasonChanged(eventTime, playbackSuppressionReason)); - } - - @Override - public void onIsPlayingChanged(boolean isPlaying) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_IS_PLAYING_CHANGED, - listener -> listener.onIsPlayingChanged(eventTime, isPlaying)); - } - - @Override - public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_REPEAT_MODE_CHANGED, - listener -> listener.onRepeatModeChanged(eventTime, repeatMode)); - } - - @Override - public final void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, - listener -> listener.onShuffleModeChanged(eventTime, shuffleModeEnabled)); - } - - @Override - public final void onPlayerError(PlaybackException error) { - EventTime eventTime = getEventTimeForErrorEvent(error); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerError(eventTime, error)); - } - - @Override - public void onPlayerErrorChanged(@Nullable PlaybackException error) { - EventTime eventTime = getEventTimeForErrorEvent(error); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerErrorChanged(eventTime, error)); - } - - @SuppressWarnings("deprecation") // Implementing deprecated method. - @Override - public void onPositionDiscontinuity(@DiscontinuityReason int reason) { - // Do nothing. Handled by non-deprecated onPositionDiscontinuity. - } - - // Calling deprecated callback. - @SuppressWarnings("deprecation") - @Override - public final void onPositionDiscontinuity( - Player.PositionInfo oldPosition, - Player.PositionInfo newPosition, - @Player.DiscontinuityReason int reason) { - if (reason == Player.DISCONTINUITY_REASON_SEEK) { - isSeeking = false; - } - mediaPeriodQueueTracker.onPositionDiscontinuity(checkNotNull(player)); - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_POSITION_DISCONTINUITY, - listener -> { - listener.onPositionDiscontinuity(eventTime, reason); - listener.onPositionDiscontinuity(eventTime, oldPosition, newPosition, reason); - }); - } - - @Override - public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYBACK_PARAMETERS_CHANGED, - listener -> listener.onPlaybackParametersChanged(eventTime, playbackParameters)); - } - - @Override - public void onSeekBackIncrementChanged(long seekBackIncrementMs) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SEEK_BACK_INCREMENT_CHANGED, - listener -> listener.onSeekBackIncrementChanged(eventTime, seekBackIncrementMs)); - } - - @Override - public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SEEK_FORWARD_INCREMENT_CHANGED, - listener -> listener.onSeekForwardIncrementChanged(eventTime, seekForwardIncrementMs)); - } - - @Override - public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, - listener -> - listener.onMaxSeekToPreviousPositionChanged(eventTime, maxSeekToPreviousPositionMs)); - } - - @Override - public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_MEDIA_METADATA_CHANGED, - listener -> listener.onMediaMetadataChanged(eventTime, mediaMetadata)); - } - - @Override - public void onPlaylistMetadataChanged(MediaMetadata playlistMetadata) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYLIST_METADATA_CHANGED, - listener -> listener.onPlaylistMetadataChanged(eventTime, playlistMetadata)); - } - - @Override - public final void onMetadata(Metadata metadata) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_METADATA, - listener -> listener.onMetadata(eventTime, metadata)); - } - - @Override - public void onCues(List cues) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, AnalyticsListener.EVENT_CUES, listener -> listener.onCues(eventTime, cues)); - } - - @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. - @Override - public final void onSeekProcessed() { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime)); - } - - @Override - public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED, - listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled)); - } - - @Override - public final void onAudioSessionIdChanged(int audioSessionId) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_SESSION_ID, - listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId)); - } - - @Override - public final void onAudioAttributesChanged(AudioAttributes audioAttributes) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED, - listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes)); - } - - @SuppressWarnings("deprecation") // Calling deprecated listener method. - @Override - public final void onVideoSizeChanged(VideoSize videoSize) { - EventTime eventTime = generateReadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED, - listener -> { - listener.onVideoSizeChanged(eventTime, videoSize); - listener.onVideoSizeChanged( - eventTime, - videoSize.width, - videoSize.height, - videoSize.unappliedRotationDegrees, - videoSize.pixelWidthHeightRatio); - }); - } - - @Override - public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, - listener -> listener.onTrackSelectionParametersChanged(eventTime, parameters)); - } - - @Override - public void onDeviceInfoChanged(DeviceInfo deviceInfo) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DEVICE_INFO_CHANGED, - listener -> listener.onDeviceInfoChanged(eventTime, deviceInfo)); - } - - @Override - public void onDeviceVolumeChanged(int volume, boolean muted) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DEVICE_VOLUME_CHANGED, - listener -> listener.onDeviceVolumeChanged(eventTime, volume, muted)); - } - - @SuppressWarnings("UngroupedOverloads") // Grouped by interface. - @Override - public void onRenderedFirstFrame() { - // Do nothing. Handled by onRenderedFirstFrame call with additional parameters. - } - - @Override - public void onEvents(Player player, Player.Events events) { - // Do nothing. AnalyticsCollector issues its own onEvents. - } - - // BandwidthMeter.EventListener implementation. - - @Override - public final void onBandwidthSample(int elapsedMs, long bytesTransferred, long bitrateEstimate) { - EventTime eventTime = generateLoadingMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_BANDWIDTH_ESTIMATE, - listener -> - listener.onBandwidthEstimate(eventTime, elapsedMs, bytesTransferred, bitrateEstimate)); - } - - // DrmSessionEventListener implementation. - - @Override - @SuppressWarnings("deprecation") // Calls deprecated listener method. - public final void onDrmSessionAcquired( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId, @DrmSession.State int state) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED, - listener -> { - listener.onDrmSessionAcquired(eventTime); - listener.onDrmSessionAcquired(eventTime, state); - }); - } - - @Override - public final void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_KEYS_LOADED, - listener -> listener.onDrmKeysLoaded(eventTime)); - } - - @Override - public final void onDrmSessionManagerError( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_SESSION_MANAGER_ERROR, - listener -> listener.onDrmSessionManagerError(eventTime, error)); - } - - @Override - public final void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_KEYS_RESTORED, - listener -> listener.onDrmKeysRestored(eventTime)); - } - - @Override - public final void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_KEYS_REMOVED, - listener -> listener.onDrmKeysRemoved(eventTime)); - } - - @Override - public final void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); - sendEvent( - eventTime, - AnalyticsListener.EVENT_DRM_SESSION_RELEASED, - listener -> listener.onDrmSessionReleased(eventTime)); - } - - // Internal methods. - - /** - * Sends an event to registered listeners. - * - * @param eventTime The {@link EventTime} to report. - * @param eventFlag An integer flag indicating the type of the event, or {@link C#INDEX_UNSET} to - * report this event without flag. - * @param eventInvocation The event. - */ - protected final void sendEvent( - EventTime eventTime, int eventFlag, ListenerSet.Event eventInvocation) { - eventTimes.put(eventFlag, eventTime); - listeners.sendEvent(eventFlag, eventInvocation); - } - - /** Generates an {@link EventTime} for the currently playing item in the player. */ - protected final EventTime generateCurrentPlayerMediaPeriodEventTime() { - return generateEventTime(mediaPeriodQueueTracker.getCurrentPlayerMediaPeriod()); - } - - /** Returns a new {@link EventTime} for the specified timeline, window and media period id. */ - @RequiresNonNull("player") - protected final EventTime generateEventTime( - Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - if (timeline.isEmpty()) { - // Ensure media period id is only reported together with a valid timeline. - mediaPeriodId = null; - } - long realtimeMs = clock.elapsedRealtime(); - long eventPositionMs; - boolean isInCurrentWindow = - timeline.equals(player.getCurrentTimeline()) - && windowIndex == player.getCurrentMediaItemIndex(); - if (mediaPeriodId != null && mediaPeriodId.isAd()) { - boolean isCurrentAd = - isInCurrentWindow - && player.getCurrentAdGroupIndex() == mediaPeriodId.adGroupIndex - && player.getCurrentAdIndexInAdGroup() == mediaPeriodId.adIndexInAdGroup; - // Assume start position of 0 for future ads. - eventPositionMs = isCurrentAd ? player.getCurrentPosition() : 0; - } else if (isInCurrentWindow) { - eventPositionMs = player.getContentPosition(); - } else { - // Assume default start position for future content windows. If timeline is not available yet, - // assume start position of 0. - eventPositionMs = - timeline.isEmpty() ? 0 : timeline.getWindow(windowIndex, window).getDefaultPositionMs(); - } - @Nullable - MediaPeriodId currentMediaPeriodId = mediaPeriodQueueTracker.getCurrentPlayerMediaPeriod(); - return new EventTime( - realtimeMs, - timeline, - windowIndex, - mediaPeriodId, - eventPositionMs, - player.getCurrentTimeline(), - player.getCurrentMediaItemIndex(), - currentMediaPeriodId, - player.getCurrentPosition(), - player.getTotalBufferedDuration()); - } - - private void releaseInternal() { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_PLAYER_RELEASED, - listener -> listener.onPlayerReleased(eventTime)); - listeners.release(); - } - - private EventTime generateEventTime(@Nullable MediaPeriodId mediaPeriodId) { - checkNotNull(player); - @Nullable - Timeline knownTimeline = - mediaPeriodId == null - ? null - : mediaPeriodQueueTracker.getMediaPeriodIdTimeline(mediaPeriodId); - if (mediaPeriodId == null || knownTimeline == null) { - int windowIndex = player.getCurrentMediaItemIndex(); - Timeline timeline = player.getCurrentTimeline(); - boolean windowIsInTimeline = windowIndex < timeline.getWindowCount(); - return generateEventTime( - windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null); - } - int windowIndex = knownTimeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex; - return generateEventTime(knownTimeline, windowIndex, mediaPeriodId); - } - - private EventTime generatePlayingMediaPeriodEventTime() { - return generateEventTime(mediaPeriodQueueTracker.getPlayingMediaPeriod()); - } - - private EventTime generateReadingMediaPeriodEventTime() { - return generateEventTime(mediaPeriodQueueTracker.getReadingMediaPeriod()); - } - - private EventTime generateLoadingMediaPeriodEventTime() { - return generateEventTime(mediaPeriodQueueTracker.getLoadingMediaPeriod()); - } - - private EventTime generateMediaPeriodEventTime( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { - checkNotNull(player); - if (mediaPeriodId != null) { - boolean isInKnownTimeline = - mediaPeriodQueueTracker.getMediaPeriodIdTimeline(mediaPeriodId) != null; - return isInKnownTimeline - ? generateEventTime(mediaPeriodId) - : generateEventTime(Timeline.EMPTY, windowIndex, mediaPeriodId); - } - Timeline timeline = player.getCurrentTimeline(); - boolean windowIsInTimeline = windowIndex < timeline.getWindowCount(); - return generateEventTime( - windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null); - } - - private EventTime getEventTimeForErrorEvent(@Nullable PlaybackException error) { - if (error instanceof ExoPlaybackException) { - ExoPlaybackException exoError = (ExoPlaybackException) error; - if (exoError.mediaPeriodId != null) { - return generateEventTime(new MediaPeriodId(exoError.mediaPeriodId)); - } - } - return generateCurrentPlayerMediaPeriodEventTime(); - } - - /** Keeps track of the active media periods and currently playing and reading media period. */ - private static final class MediaPeriodQueueTracker { - - // TODO: Investigate reporting MediaPeriodId in renderer events. - - private final Period period; - - private ImmutableList mediaPeriodQueue; - private ImmutableMap mediaPeriodTimelines; - @Nullable private MediaPeriodId currentPlayerMediaPeriod; - private @MonotonicNonNull MediaPeriodId playingMediaPeriod; - private @MonotonicNonNull MediaPeriodId readingMediaPeriod; - - public MediaPeriodQueueTracker(Period period) { - this.period = period; - mediaPeriodQueue = ImmutableList.of(); - mediaPeriodTimelines = ImmutableMap.of(); - } - - /** - * Returns the {@link MediaPeriodId} of the media period corresponding the current position of - * the player. - * - *

    May be null if no matching media period has been created yet. - */ - @Nullable - public MediaPeriodId getCurrentPlayerMediaPeriod() { - return currentPlayerMediaPeriod; - } - - /** - * Returns the {@link MediaPeriodId} of the media period at the front of the queue. If the queue - * is empty, this is the last media period which was at the front of the queue. - * - *

    May be null, if no media period has been created yet. - */ - @Nullable - public MediaPeriodId getPlayingMediaPeriod() { - return playingMediaPeriod; - } - - /** - * Returns the {@link MediaPeriodId} of the media period currently being read by the player. If - * the queue is empty, this is the last media period which was read by the player. - * - *

    May be null, if no media period has been created yet. - */ - @Nullable - public MediaPeriodId getReadingMediaPeriod() { - return readingMediaPeriod; - } - - /** - * Returns the {@link MediaPeriodId} of the media period at the end of the queue which is - * currently loading or will be the next one loading. - * - *

    May be null, if no media period is active yet. - */ - @Nullable - public MediaPeriodId getLoadingMediaPeriod() { - return mediaPeriodQueue.isEmpty() ? null : Iterables.getLast(mediaPeriodQueue); - } - - /** - * Returns the most recent {@link Timeline} for the given {@link MediaPeriodId}, or null if no - * timeline is available. - */ - @Nullable - public Timeline getMediaPeriodIdTimeline(MediaPeriodId mediaPeriodId) { - return mediaPeriodTimelines.get(mediaPeriodId); - } - - /** Updates the queue tracker with a reported position discontinuity. */ - public void onPositionDiscontinuity(Player player) { - currentPlayerMediaPeriod = - findCurrentPlayerMediaPeriodInQueue(player, mediaPeriodQueue, playingMediaPeriod, period); - } - - /** Updates the queue tracker with a reported timeline change. */ - public void onTimelineChanged(Player player) { - currentPlayerMediaPeriod = - findCurrentPlayerMediaPeriodInQueue(player, mediaPeriodQueue, playingMediaPeriod, period); - updateMediaPeriodTimelines(/* preferredTimeline= */ player.getCurrentTimeline()); - } - - /** Updates the queue tracker to a new queue of media periods. */ - public void onQueueUpdated( - List queue, @Nullable MediaPeriodId readingPeriod, Player player) { - mediaPeriodQueue = ImmutableList.copyOf(queue); - if (!queue.isEmpty()) { - playingMediaPeriod = queue.get(0); - readingMediaPeriod = checkNotNull(readingPeriod); - } - if (currentPlayerMediaPeriod == null) { - currentPlayerMediaPeriod = - findCurrentPlayerMediaPeriodInQueue( - player, mediaPeriodQueue, playingMediaPeriod, period); - } - updateMediaPeriodTimelines(/* preferredTimeline= */ player.getCurrentTimeline()); - } - - private void updateMediaPeriodTimelines(Timeline preferredTimeline) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - if (mediaPeriodQueue.isEmpty()) { - addTimelineForMediaPeriodId(builder, playingMediaPeriod, preferredTimeline); - if (!Objects.equal(readingMediaPeriod, playingMediaPeriod)) { - addTimelineForMediaPeriodId(builder, readingMediaPeriod, preferredTimeline); - } - if (!Objects.equal(currentPlayerMediaPeriod, playingMediaPeriod) - && !Objects.equal(currentPlayerMediaPeriod, readingMediaPeriod)) { - addTimelineForMediaPeriodId(builder, currentPlayerMediaPeriod, preferredTimeline); - } - } else { - for (int i = 0; i < mediaPeriodQueue.size(); i++) { - addTimelineForMediaPeriodId(builder, mediaPeriodQueue.get(i), preferredTimeline); - } - if (!mediaPeriodQueue.contains(currentPlayerMediaPeriod)) { - addTimelineForMediaPeriodId(builder, currentPlayerMediaPeriod, preferredTimeline); - } - } - mediaPeriodTimelines = builder.buildOrThrow(); - } - - private void addTimelineForMediaPeriodId( - ImmutableMap.Builder mediaPeriodTimelinesBuilder, - @Nullable MediaPeriodId mediaPeriodId, - Timeline preferredTimeline) { - if (mediaPeriodId == null) { - return; - } - if (preferredTimeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET) { - mediaPeriodTimelinesBuilder.put(mediaPeriodId, preferredTimeline); - } else { - @Nullable Timeline existingTimeline = mediaPeriodTimelines.get(mediaPeriodId); - if (existingTimeline != null) { - mediaPeriodTimelinesBuilder.put(mediaPeriodId, existingTimeline); - } - } - } - - @Nullable - private static MediaPeriodId findCurrentPlayerMediaPeriodInQueue( - Player player, - ImmutableList mediaPeriodQueue, - @Nullable MediaPeriodId playingMediaPeriod, - Period period) { - Timeline playerTimeline = player.getCurrentTimeline(); - int playerPeriodIndex = player.getCurrentPeriodIndex(); - @Nullable - Object playerPeriodUid = - playerTimeline.isEmpty() ? null : playerTimeline.getUidOfPeriod(playerPeriodIndex); - int playerNextAdGroupIndex = - player.isPlayingAd() || playerTimeline.isEmpty() - ? C.INDEX_UNSET - : playerTimeline - .getPeriod(playerPeriodIndex, period) - .getAdGroupIndexAfterPositionUs( - Util.msToUs(player.getCurrentPosition()) - period.getPositionInWindowUs()); - for (int i = 0; i < mediaPeriodQueue.size(); i++) { - MediaPeriodId mediaPeriodId = mediaPeriodQueue.get(i); - if (isMatchingMediaPeriod( - mediaPeriodId, - playerPeriodUid, - player.isPlayingAd(), - player.getCurrentAdGroupIndex(), - player.getCurrentAdIndexInAdGroup(), - playerNextAdGroupIndex)) { - return mediaPeriodId; - } - } - if (mediaPeriodQueue.isEmpty() && playingMediaPeriod != null) { - if (isMatchingMediaPeriod( - playingMediaPeriod, - playerPeriodUid, - player.isPlayingAd(), - player.getCurrentAdGroupIndex(), - player.getCurrentAdIndexInAdGroup(), - playerNextAdGroupIndex)) { - return playingMediaPeriod; - } - } - return null; - } - - private static boolean isMatchingMediaPeriod( - MediaPeriodId mediaPeriodId, - @Nullable Object playerPeriodUid, - boolean isPlayingAd, - int playerAdGroupIndex, - int playerAdIndexInAdGroup, - int playerNextAdGroupIndex) { - if (!mediaPeriodId.periodUid.equals(playerPeriodUid)) { - return false; - } - // Timeline period matches. Still need to check ad information. - return (isPlayingAd - && mediaPeriodId.adGroupIndex == playerAdGroupIndex - && mediaPeriodId.adIndexInAdGroup == playerAdIndexInAdGroup) - || (!isPlayingAd - && mediaPeriodId.adGroupIndex == C.INDEX_UNSET - && mediaPeriodId.nextAdGroupIndex == playerNextAdGroupIndex); - } - } + void onVideoCodecError(Exception videoCodecError); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java new file mode 100644 index 0000000000..b3d9c3669d --- /dev/null +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java @@ -0,0 +1,1215 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.analytics; + +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 android.os.Looper; +import android.util.SparseArray; +import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; +import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; +import androidx.media3.common.Format; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.Metadata; +import androidx.media3.common.PlaybackException; +import androidx.media3.common.PlaybackParameters; +import androidx.media3.common.Player; +import androidx.media3.common.Player.DiscontinuityReason; +import androidx.media3.common.Player.PlaybackSuppressionReason; +import androidx.media3.common.Timeline; +import androidx.media3.common.Timeline.Period; +import androidx.media3.common.Timeline.Window; +import androidx.media3.common.TrackGroupArray; +import androidx.media3.common.TrackSelectionArray; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.TracksInfo; +import androidx.media3.common.VideoSize; +import androidx.media3.common.text.Cue; +import androidx.media3.common.util.Clock; +import androidx.media3.common.util.HandlerWrapper; +import androidx.media3.common.util.ListenerSet; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; +import androidx.media3.exoplayer.DecoderCounters; +import androidx.media3.exoplayer.DecoderReuseEvaluation; +import androidx.media3.exoplayer.ExoPlaybackException; +import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime; +import androidx.media3.exoplayer.drm.DrmSession; +import androidx.media3.exoplayer.source.LoadEventInfo; +import androidx.media3.exoplayer.source.MediaLoadData; +import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import java.io.IOException; +import java.util.List; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.RequiresNonNull; + +/** + * Data collector that forwards analytics events to {@link AnalyticsListener AnalyticsListeners}. + */ +@UnstableApi +public class DefaultAnalyticsCollector implements AnalyticsCollector { + + private final Clock clock; + private final Period period; + private final Window window; + private final MediaPeriodQueueTracker mediaPeriodQueueTracker; + private final SparseArray eventTimes; + + private ListenerSet listeners; + private @MonotonicNonNull Player player; + private @MonotonicNonNull HandlerWrapper handler; + private boolean isSeeking; + + /** + * Creates an analytics collector. + * + * @param clock A {@link Clock} used to generate timestamps. + */ + public DefaultAnalyticsCollector(Clock clock) { + this.clock = checkNotNull(clock); + listeners = new ListenerSet<>(Util.getCurrentOrMainLooper(), clock, (listener, flags) -> {}); + period = new Period(); + window = new Window(); + mediaPeriodQueueTracker = new MediaPeriodQueueTracker(period); + eventTimes = new SparseArray<>(); + } + + @Override + @CallSuper + public void addListener(AnalyticsListener listener) { + checkNotNull(listener); + listeners.add(listener); + } + + @Override + @CallSuper + public void removeListener(AnalyticsListener listener) { + listeners.remove(listener); + } + + @Override + @CallSuper + public void setPlayer(Player player, Looper looper) { + checkState(this.player == null || mediaPeriodQueueTracker.mediaPeriodQueue.isEmpty()); + this.player = checkNotNull(player); + handler = clock.createHandler(looper, null); + listeners = + listeners.copy( + looper, + (listener, flags) -> + listener.onEvents(player, new AnalyticsListener.Events(flags, eventTimes))); + } + + @Override + @CallSuper + public void release() { + // Release lazily so that all events that got triggered as part of player.release() + // are still delivered to all listeners and onPlayerReleased() is delivered last. + checkStateNotNull(handler).post(this::releaseInternal); + } + + @Override + public final void updateMediaPeriodQueueInfo( + List queue, @Nullable MediaPeriodId readingPeriod) { + mediaPeriodQueueTracker.onQueueUpdated(queue, readingPeriod, checkNotNull(player)); + } + + // External events. + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void notifySeekStarted() { + if (!isSeeking) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + isSeeking = true; + sendEvent( + eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekStarted(eventTime)); + } + } + + // Audio events. + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onAudioEnabled(DecoderCounters counters) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_ENABLED, + listener -> { + listener.onAudioEnabled(eventTime, counters); + listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_AUDIO, counters); + }); + } + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onAudioDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_DECODER_INITIALIZED, + listener -> { + listener.onAudioDecoderInitialized(eventTime, decoderName, initializationDurationMs); + listener.onAudioDecoderInitialized( + eventTime, decoderName, initializedTimestampMs, initializationDurationMs); + listener.onDecoderInitialized( + eventTime, C.TRACK_TYPE_AUDIO, decoderName, initializationDurationMs); + }); + } + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onAudioInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_INPUT_FORMAT_CHANGED, + listener -> { + listener.onAudioInputFormatChanged(eventTime, format); + listener.onAudioInputFormatChanged(eventTime, format, decoderReuseEvaluation); + listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_AUDIO, format); + }); + } + + @Override + public final void onAudioPositionAdvancing(long playoutStartSystemTimeMs) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_POSITION_ADVANCING, + listener -> listener.onAudioPositionAdvancing(eventTime, playoutStartSystemTimeMs)); + } + + @Override + public final void onAudioUnderrun( + int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_UNDERRUN, + listener -> + listener.onAudioUnderrun(eventTime, bufferSize, bufferSizeMs, elapsedSinceLastFeedMs)); + } + + @Override + public final void onAudioDecoderReleased(String decoderName) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_DECODER_RELEASED, + listener -> listener.onAudioDecoderReleased(eventTime, decoderName)); + } + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void onAudioDisabled(DecoderCounters counters) { + EventTime eventTime = generatePlayingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_DISABLED, + listener -> { + listener.onAudioDisabled(eventTime, counters); + listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_AUDIO, counters); + }); + } + + @Override + public final void onAudioSinkError(Exception audioSinkError) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_SINK_ERROR, + listener -> listener.onAudioSinkError(eventTime, audioSinkError)); + } + + @Override + public final void onAudioCodecError(Exception audioCodecError) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_CODEC_ERROR, + listener -> listener.onAudioCodecError(eventTime, audioCodecError)); + } + + @Override + public final void onVolumeChanged(float volume) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VOLUME_CHANGED, + listener -> listener.onVolumeChanged(eventTime, volume)); + } + + // Video events. + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void onVideoEnabled(DecoderCounters counters) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_ENABLED, + listener -> { + listener.onVideoEnabled(eventTime, counters); + listener.onDecoderEnabled(eventTime, C.TRACK_TYPE_VIDEO, counters); + }); + } + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void onVideoDecoderInitialized( + String decoderName, long initializedTimestampMs, long initializationDurationMs) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_DECODER_INITIALIZED, + listener -> { + listener.onVideoDecoderInitialized(eventTime, decoderName, initializationDurationMs); + listener.onVideoDecoderInitialized( + eventTime, decoderName, initializedTimestampMs, initializationDurationMs); + listener.onDecoderInitialized( + eventTime, C.TRACK_TYPE_VIDEO, decoderName, initializationDurationMs); + }); + } + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void onVideoInputFormatChanged( + Format format, @Nullable DecoderReuseEvaluation decoderReuseEvaluation) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_INPUT_FORMAT_CHANGED, + listener -> { + listener.onVideoInputFormatChanged(eventTime, format); + listener.onVideoInputFormatChanged(eventTime, format, decoderReuseEvaluation); + listener.onDecoderInputFormatChanged(eventTime, C.TRACK_TYPE_VIDEO, format); + }); + } + + @Override + public final void onDroppedFrames(int count, long elapsedMs) { + EventTime eventTime = generatePlayingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DROPPED_VIDEO_FRAMES, + listener -> listener.onDroppedVideoFrames(eventTime, count, elapsedMs)); + } + + @Override + public final void onVideoDecoderReleased(String decoderName) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_DECODER_RELEASED, + listener -> listener.onVideoDecoderReleased(eventTime, decoderName)); + } + + @Override + @SuppressWarnings("deprecation") // Calling deprecated listener method. + public final void onVideoDisabled(DecoderCounters counters) { + EventTime eventTime = generatePlayingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_DISABLED, + listener -> { + listener.onVideoDisabled(eventTime, counters); + listener.onDecoderDisabled(eventTime, C.TRACK_TYPE_VIDEO, counters); + }); + } + + @Override + public final void onRenderedFirstFrame(Object output, long renderTimeMs) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_RENDERED_FIRST_FRAME, + listener -> listener.onRenderedFirstFrame(eventTime, output, renderTimeMs)); + } + + @Override + public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) { + EventTime eventTime = generatePlayingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_FRAME_PROCESSING_OFFSET, + listener -> + listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount)); + } + + @Override + public final void onVideoCodecError(Exception videoCodecError) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_CODEC_ERROR, + listener -> listener.onVideoCodecError(eventTime, videoCodecError)); + } + + @Override + public final void onSurfaceSizeChanged(int width, int height) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SURFACE_SIZE_CHANGED, + listener -> listener.onSurfaceSizeChanged(eventTime, width, height)); + } + + // MediaSourceEventListener implementation. + + @Override + public final void onLoadStarted( + int windowIndex, + @Nullable MediaPeriodId mediaPeriodId, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_LOAD_STARTED, + listener -> listener.onLoadStarted(eventTime, loadEventInfo, mediaLoadData)); + } + + @Override + public final void onLoadCompleted( + int windowIndex, + @Nullable MediaPeriodId mediaPeriodId, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_LOAD_COMPLETED, + listener -> listener.onLoadCompleted(eventTime, loadEventInfo, mediaLoadData)); + } + + @Override + public final void onLoadCanceled( + int windowIndex, + @Nullable MediaPeriodId mediaPeriodId, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_LOAD_CANCELED, + listener -> listener.onLoadCanceled(eventTime, loadEventInfo, mediaLoadData)); + } + + @Override + public final void onLoadError( + int windowIndex, + @Nullable MediaPeriodId mediaPeriodId, + LoadEventInfo loadEventInfo, + MediaLoadData mediaLoadData, + IOException error, + boolean wasCanceled) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_LOAD_ERROR, + listener -> + listener.onLoadError(eventTime, loadEventInfo, mediaLoadData, error, wasCanceled)); + } + + @Override + public final void onUpstreamDiscarded( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_UPSTREAM_DISCARDED, + listener -> listener.onUpstreamDiscarded(eventTime, mediaLoadData)); + } + + @Override + public final void onDownstreamFormatChanged( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DOWNSTREAM_FORMAT_CHANGED, + listener -> listener.onDownstreamFormatChanged(eventTime, mediaLoadData)); + } + + // Player.Listener implementation. + + // TODO: Use Player.Listener.onEvents to know when a set of simultaneous callbacks finished. + // This helps to assign exactly the same EventTime to all of them instead of having slightly + // different real times. + + @Override + public final void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) { + mediaPeriodQueueTracker.onTimelineChanged(checkNotNull(player)); + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_TIMELINE_CHANGED, + listener -> listener.onTimelineChanged(eventTime, reason)); + } + + @Override + public final void onMediaItemTransition( + @Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_MEDIA_ITEM_TRANSITION, + listener -> listener.onMediaItemTransition(eventTime, mediaItem, reason)); + } + + @Override + @SuppressWarnings("deprecation") // Implementing and calling deprecate listener method + public final void onTracksChanged( + TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_TRACKS_CHANGED, + listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections)); + } + + @Override + public void onTracksInfoChanged(TracksInfo tracksInfo) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_TRACKS_CHANGED, + listener -> listener.onTracksInfoChanged(eventTime, tracksInfo)); + } + + @SuppressWarnings("deprecation") // Implementing deprecated method. + @Override + public void onLoadingChanged(boolean isLoading) { + // Do nothing. Handled by non-deprecated onIsLoadingChanged. + } + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onIsLoadingChanged(boolean isLoading) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_IS_LOADING_CHANGED, + listener -> { + listener.onLoadingChanged(eventTime, isLoading); + listener.onIsLoadingChanged(eventTime, isLoading); + }); + } + + @Override + public void onAvailableCommandsChanged(Player.Commands availableCommands) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AVAILABLE_COMMANDS_CHANGED, + listener -> listener.onAvailableCommandsChanged(eventTime, availableCommands)); + } + + @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. + @Override + public final void onPlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + /* eventFlag= */ C.INDEX_UNSET, + listener -> listener.onPlayerStateChanged(eventTime, playWhenReady, playbackState)); + } + + @Override + public final void onPlaybackStateChanged(@Player.State int playbackState) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYBACK_STATE_CHANGED, + listener -> listener.onPlaybackStateChanged(eventTime, playbackState)); + } + + @Override + public final void onPlayWhenReadyChanged( + boolean playWhenReady, @Player.PlayWhenReadyChangeReason int reason) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAY_WHEN_READY_CHANGED, + listener -> listener.onPlayWhenReadyChanged(eventTime, playWhenReady, reason)); + } + + @Override + public final void onPlaybackSuppressionReasonChanged( + @PlaybackSuppressionReason int playbackSuppressionReason) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, + listener -> + listener.onPlaybackSuppressionReasonChanged(eventTime, playbackSuppressionReason)); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_IS_PLAYING_CHANGED, + listener -> listener.onIsPlayingChanged(eventTime, isPlaying)); + } + + @Override + public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_REPEAT_MODE_CHANGED, + listener -> listener.onRepeatModeChanged(eventTime, repeatMode)); + } + + @Override + public final void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, + listener -> listener.onShuffleModeChanged(eventTime, shuffleModeEnabled)); + } + + @Override + public final void onPlayerError(PlaybackException error) { + EventTime eventTime = getEventTimeForErrorEvent(error); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYER_ERROR, + listener -> listener.onPlayerError(eventTime, error)); + } + + @Override + public void onPlayerErrorChanged(@Nullable PlaybackException error) { + EventTime eventTime = getEventTimeForErrorEvent(error); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYER_ERROR, + listener -> listener.onPlayerErrorChanged(eventTime, error)); + } + + @SuppressWarnings("deprecation") // Implementing deprecated method. + @Override + public void onPositionDiscontinuity(@DiscontinuityReason int reason) { + // Do nothing. Handled by non-deprecated onPositionDiscontinuity. + } + + // Calling deprecated callback. + @SuppressWarnings("deprecation") + @Override + public final void onPositionDiscontinuity( + Player.PositionInfo oldPosition, + Player.PositionInfo newPosition, + @Player.DiscontinuityReason int reason) { + if (reason == Player.DISCONTINUITY_REASON_SEEK) { + isSeeking = false; + } + mediaPeriodQueueTracker.onPositionDiscontinuity(checkNotNull(player)); + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_POSITION_DISCONTINUITY, + listener -> { + listener.onPositionDiscontinuity(eventTime, reason); + listener.onPositionDiscontinuity(eventTime, oldPosition, newPosition, reason); + }); + } + + @Override + public final void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYBACK_PARAMETERS_CHANGED, + listener -> listener.onPlaybackParametersChanged(eventTime, playbackParameters)); + } + + @Override + public void onSeekBackIncrementChanged(long seekBackIncrementMs) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SEEK_BACK_INCREMENT_CHANGED, + listener -> listener.onSeekBackIncrementChanged(eventTime, seekBackIncrementMs)); + } + + @Override + public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SEEK_FORWARD_INCREMENT_CHANGED, + listener -> listener.onSeekForwardIncrementChanged(eventTime, seekForwardIncrementMs)); + } + + @Override + public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_MAX_SEEK_TO_PREVIOUS_POSITION_CHANGED, + listener -> + listener.onMaxSeekToPreviousPositionChanged(eventTime, maxSeekToPreviousPositionMs)); + } + + @Override + public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(eventTime, mediaMetadata)); + } + + @Override + public void onPlaylistMetadataChanged(MediaMetadata playlistMetadata) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYLIST_METADATA_CHANGED, + listener -> listener.onPlaylistMetadataChanged(eventTime, playlistMetadata)); + } + + @Override + public final void onMetadata(Metadata metadata) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_METADATA, + listener -> listener.onMetadata(eventTime, metadata)); + } + + @Override + public void onCues(List cues) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, AnalyticsListener.EVENT_CUES, listener -> listener.onCues(eventTime, cues)); + } + + @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. + @Override + public final void onSeekProcessed() { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onSeekProcessed(eventTime)); + } + + @Override + public final void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_SKIP_SILENCE_ENABLED_CHANGED, + listener -> listener.onSkipSilenceEnabledChanged(eventTime, skipSilenceEnabled)); + } + + @Override + public final void onAudioSessionIdChanged(int audioSessionId) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_SESSION_ID, + listener -> listener.onAudioSessionIdChanged(eventTime, audioSessionId)); + } + + @Override + public final void onAudioAttributesChanged(AudioAttributes audioAttributes) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_AUDIO_ATTRIBUTES_CHANGED, + listener -> listener.onAudioAttributesChanged(eventTime, audioAttributes)); + } + + @SuppressWarnings("deprecation") // Calling deprecated listener method. + @Override + public final void onVideoSizeChanged(VideoSize videoSize) { + EventTime eventTime = generateReadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED, + listener -> { + listener.onVideoSizeChanged(eventTime, videoSize); + listener.onVideoSizeChanged( + eventTime, + videoSize.width, + videoSize.height, + videoSize.unappliedRotationDegrees, + videoSize.pixelWidthHeightRatio); + }); + } + + @Override + public void onTrackSelectionParametersChanged(TrackSelectionParameters parameters) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_TRACK_SELECTION_PARAMETERS_CHANGED, + listener -> listener.onTrackSelectionParametersChanged(eventTime, parameters)); + } + + @Override + public void onDeviceInfoChanged(DeviceInfo deviceInfo) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DEVICE_INFO_CHANGED, + listener -> listener.onDeviceInfoChanged(eventTime, deviceInfo)); + } + + @Override + public void onDeviceVolumeChanged(int volume, boolean muted) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DEVICE_VOLUME_CHANGED, + listener -> listener.onDeviceVolumeChanged(eventTime, volume, muted)); + } + + @SuppressWarnings("UngroupedOverloads") // Grouped by interface. + @Override + public void onRenderedFirstFrame() { + // Do nothing. Handled by onRenderedFirstFrame call with additional parameters. + } + + @Override + public void onEvents(Player player, Player.Events events) { + // Do nothing. AnalyticsCollector issues its own onEvents. + } + + // BandwidthMeter.EventListener implementation. + + @Override + public final void onBandwidthSample(int elapsedMs, long bytesTransferred, long bitrateEstimate) { + EventTime eventTime = generateLoadingMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_BANDWIDTH_ESTIMATE, + listener -> + listener.onBandwidthEstimate(eventTime, elapsedMs, bytesTransferred, bitrateEstimate)); + } + + // DrmSessionEventListener implementation. + + @Override + @SuppressWarnings("deprecation") // Calls deprecated listener method. + public final void onDrmSessionAcquired( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId, @DrmSession.State int state) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_SESSION_ACQUIRED, + listener -> { + listener.onDrmSessionAcquired(eventTime); + listener.onDrmSessionAcquired(eventTime, state); + }); + } + + @Override + public final void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_KEYS_LOADED, + listener -> listener.onDrmKeysLoaded(eventTime)); + } + + @Override + public final void onDrmSessionManagerError( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_SESSION_MANAGER_ERROR, + listener -> listener.onDrmSessionManagerError(eventTime, error)); + } + + @Override + public final void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_KEYS_RESTORED, + listener -> listener.onDrmKeysRestored(eventTime)); + } + + @Override + public final void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_KEYS_REMOVED, + listener -> listener.onDrmKeysRemoved(eventTime)); + } + + @Override + public final void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + EventTime eventTime = generateMediaPeriodEventTime(windowIndex, mediaPeriodId); + sendEvent( + eventTime, + AnalyticsListener.EVENT_DRM_SESSION_RELEASED, + listener -> listener.onDrmSessionReleased(eventTime)); + } + + // Internal methods. + + /** + * Sends an event to registered listeners. + * + * @param eventTime The {@link EventTime} to report. + * @param eventFlag An integer flag indicating the type of the event, or {@link C#INDEX_UNSET} to + * report this event without flag. + * @param eventInvocation The event. + */ + protected final void sendEvent( + EventTime eventTime, int eventFlag, ListenerSet.Event eventInvocation) { + eventTimes.put(eventFlag, eventTime); + listeners.sendEvent(eventFlag, eventInvocation); + } + + /** Generates an {@link EventTime} for the currently playing item in the player. */ + protected final EventTime generateCurrentPlayerMediaPeriodEventTime() { + return generateEventTime(mediaPeriodQueueTracker.getCurrentPlayerMediaPeriod()); + } + + /** Returns a new {@link EventTime} for the specified timeline, window and media period id. */ + @RequiresNonNull("player") + protected final EventTime generateEventTime( + Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + if (timeline.isEmpty()) { + // Ensure media period id is only reported together with a valid timeline. + mediaPeriodId = null; + } + long realtimeMs = clock.elapsedRealtime(); + long eventPositionMs; + boolean isInCurrentWindow = + timeline.equals(player.getCurrentTimeline()) + && windowIndex == player.getCurrentMediaItemIndex(); + if (mediaPeriodId != null && mediaPeriodId.isAd()) { + boolean isCurrentAd = + isInCurrentWindow + && player.getCurrentAdGroupIndex() == mediaPeriodId.adGroupIndex + && player.getCurrentAdIndexInAdGroup() == mediaPeriodId.adIndexInAdGroup; + // Assume start position of 0 for future ads. + eventPositionMs = isCurrentAd ? player.getCurrentPosition() : 0; + } else if (isInCurrentWindow) { + eventPositionMs = player.getContentPosition(); + } else { + // Assume default start position for future content windows. If timeline is not available yet, + // assume start position of 0. + eventPositionMs = + timeline.isEmpty() ? 0 : timeline.getWindow(windowIndex, window).getDefaultPositionMs(); + } + @Nullable + MediaPeriodId currentMediaPeriodId = mediaPeriodQueueTracker.getCurrentPlayerMediaPeriod(); + return new EventTime( + realtimeMs, + timeline, + windowIndex, + mediaPeriodId, + eventPositionMs, + player.getCurrentTimeline(), + player.getCurrentMediaItemIndex(), + currentMediaPeriodId, + player.getCurrentPosition(), + player.getTotalBufferedDuration()); + } + + private void releaseInternal() { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYER_RELEASED, + listener -> listener.onPlayerReleased(eventTime)); + listeners.release(); + } + + private EventTime generateEventTime(@Nullable MediaPeriodId mediaPeriodId) { + checkNotNull(player); + @Nullable + Timeline knownTimeline = + mediaPeriodId == null + ? null + : mediaPeriodQueueTracker.getMediaPeriodIdTimeline(mediaPeriodId); + if (mediaPeriodId == null || knownTimeline == null) { + int windowIndex = player.getCurrentMediaItemIndex(); + Timeline timeline = player.getCurrentTimeline(); + boolean windowIsInTimeline = windowIndex < timeline.getWindowCount(); + return generateEventTime( + windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null); + } + int windowIndex = knownTimeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex; + return generateEventTime(knownTimeline, windowIndex, mediaPeriodId); + } + + private EventTime generatePlayingMediaPeriodEventTime() { + return generateEventTime(mediaPeriodQueueTracker.getPlayingMediaPeriod()); + } + + private EventTime generateReadingMediaPeriodEventTime() { + return generateEventTime(mediaPeriodQueueTracker.getReadingMediaPeriod()); + } + + private EventTime generateLoadingMediaPeriodEventTime() { + return generateEventTime(mediaPeriodQueueTracker.getLoadingMediaPeriod()); + } + + private EventTime generateMediaPeriodEventTime( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + checkNotNull(player); + if (mediaPeriodId != null) { + boolean isInKnownTimeline = + mediaPeriodQueueTracker.getMediaPeriodIdTimeline(mediaPeriodId) != null; + return isInKnownTimeline + ? generateEventTime(mediaPeriodId) + : generateEventTime(Timeline.EMPTY, windowIndex, mediaPeriodId); + } + Timeline timeline = player.getCurrentTimeline(); + boolean windowIsInTimeline = windowIndex < timeline.getWindowCount(); + return generateEventTime( + windowIsInTimeline ? timeline : Timeline.EMPTY, windowIndex, /* mediaPeriodId= */ null); + } + + private EventTime getEventTimeForErrorEvent(@Nullable PlaybackException error) { + if (error instanceof ExoPlaybackException) { + ExoPlaybackException exoError = (ExoPlaybackException) error; + if (exoError.mediaPeriodId != null) { + return generateEventTime(new MediaPeriodId(exoError.mediaPeriodId)); + } + } + return generateCurrentPlayerMediaPeriodEventTime(); + } + + /** Keeps track of the active media periods and currently playing and reading media period. */ + private static final class MediaPeriodQueueTracker { + + // TODO: Investigate reporting MediaPeriodId in renderer events. + + private final Period period; + + private ImmutableList mediaPeriodQueue; + private ImmutableMap mediaPeriodTimelines; + @Nullable private MediaPeriodId currentPlayerMediaPeriod; + private @MonotonicNonNull MediaPeriodId playingMediaPeriod; + private @MonotonicNonNull MediaPeriodId readingMediaPeriod; + + public MediaPeriodQueueTracker(Period period) { + this.period = period; + mediaPeriodQueue = ImmutableList.of(); + mediaPeriodTimelines = ImmutableMap.of(); + } + + /** + * Returns the {@link MediaPeriodId} of the media period corresponding the current position of + * the player. + * + *

    May be null if no matching media period has been created yet. + */ + @Nullable + public MediaPeriodId getCurrentPlayerMediaPeriod() { + return currentPlayerMediaPeriod; + } + + /** + * Returns the {@link MediaPeriodId} of the media period at the front of the queue. If the queue + * is empty, this is the last media period which was at the front of the queue. + * + *

    May be null, if no media period has been created yet. + */ + @Nullable + public MediaPeriodId getPlayingMediaPeriod() { + return playingMediaPeriod; + } + + /** + * Returns the {@link MediaPeriodId} of the media period currently being read by the player. If + * the queue is empty, this is the last media period which was read by the player. + * + *

    May be null, if no media period has been created yet. + */ + @Nullable + public MediaPeriodId getReadingMediaPeriod() { + return readingMediaPeriod; + } + + /** + * Returns the {@link MediaPeriodId} of the media period at the end of the queue which is + * currently loading or will be the next one loading. + * + *

    May be null, if no media period is active yet. + */ + @Nullable + public MediaPeriodId getLoadingMediaPeriod() { + return mediaPeriodQueue.isEmpty() ? null : Iterables.getLast(mediaPeriodQueue); + } + + /** + * Returns the most recent {@link Timeline} for the given {@link MediaPeriodId}, or null if no + * timeline is available. + */ + @Nullable + public Timeline getMediaPeriodIdTimeline(MediaPeriodId mediaPeriodId) { + return mediaPeriodTimelines.get(mediaPeriodId); + } + + /** Updates the queue tracker with a reported position discontinuity. */ + public void onPositionDiscontinuity(Player player) { + currentPlayerMediaPeriod = + findCurrentPlayerMediaPeriodInQueue(player, mediaPeriodQueue, playingMediaPeriod, period); + } + + /** Updates the queue tracker with a reported timeline change. */ + public void onTimelineChanged(Player player) { + currentPlayerMediaPeriod = + findCurrentPlayerMediaPeriodInQueue(player, mediaPeriodQueue, playingMediaPeriod, period); + updateMediaPeriodTimelines(/* preferredTimeline= */ player.getCurrentTimeline()); + } + + /** Updates the queue tracker to a new queue of media periods. */ + public void onQueueUpdated( + List queue, @Nullable MediaPeriodId readingPeriod, Player player) { + mediaPeriodQueue = ImmutableList.copyOf(queue); + if (!queue.isEmpty()) { + playingMediaPeriod = queue.get(0); + readingMediaPeriod = checkNotNull(readingPeriod); + } + if (currentPlayerMediaPeriod == null) { + currentPlayerMediaPeriod = + findCurrentPlayerMediaPeriodInQueue( + player, mediaPeriodQueue, playingMediaPeriod, period); + } + updateMediaPeriodTimelines(/* preferredTimeline= */ player.getCurrentTimeline()); + } + + private void updateMediaPeriodTimelines(Timeline preferredTimeline) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + if (mediaPeriodQueue.isEmpty()) { + addTimelineForMediaPeriodId(builder, playingMediaPeriod, preferredTimeline); + if (!Objects.equal(readingMediaPeriod, playingMediaPeriod)) { + addTimelineForMediaPeriodId(builder, readingMediaPeriod, preferredTimeline); + } + if (!Objects.equal(currentPlayerMediaPeriod, playingMediaPeriod) + && !Objects.equal(currentPlayerMediaPeriod, readingMediaPeriod)) { + addTimelineForMediaPeriodId(builder, currentPlayerMediaPeriod, preferredTimeline); + } + } else { + for (int i = 0; i < mediaPeriodQueue.size(); i++) { + addTimelineForMediaPeriodId(builder, mediaPeriodQueue.get(i), preferredTimeline); + } + if (!mediaPeriodQueue.contains(currentPlayerMediaPeriod)) { + addTimelineForMediaPeriodId(builder, currentPlayerMediaPeriod, preferredTimeline); + } + } + mediaPeriodTimelines = builder.buildOrThrow(); + } + + private void addTimelineForMediaPeriodId( + ImmutableMap.Builder mediaPeriodTimelinesBuilder, + @Nullable MediaPeriodId mediaPeriodId, + Timeline preferredTimeline) { + if (mediaPeriodId == null) { + return; + } + if (preferredTimeline.getIndexOfPeriod(mediaPeriodId.periodUid) != C.INDEX_UNSET) { + mediaPeriodTimelinesBuilder.put(mediaPeriodId, preferredTimeline); + } else { + @Nullable Timeline existingTimeline = mediaPeriodTimelines.get(mediaPeriodId); + if (existingTimeline != null) { + mediaPeriodTimelinesBuilder.put(mediaPeriodId, existingTimeline); + } + } + } + + @Nullable + private static MediaPeriodId findCurrentPlayerMediaPeriodInQueue( + Player player, + ImmutableList mediaPeriodQueue, + @Nullable MediaPeriodId playingMediaPeriod, + Period period) { + Timeline playerTimeline = player.getCurrentTimeline(); + int playerPeriodIndex = player.getCurrentPeriodIndex(); + @Nullable + Object playerPeriodUid = + playerTimeline.isEmpty() ? null : playerTimeline.getUidOfPeriod(playerPeriodIndex); + int playerNextAdGroupIndex = + player.isPlayingAd() || playerTimeline.isEmpty() + ? C.INDEX_UNSET + : playerTimeline + .getPeriod(playerPeriodIndex, period) + .getAdGroupIndexAfterPositionUs( + Util.msToUs(player.getCurrentPosition()) - period.getPositionInWindowUs()); + for (int i = 0; i < mediaPeriodQueue.size(); i++) { + MediaPeriodId mediaPeriodId = mediaPeriodQueue.get(i); + if (isMatchingMediaPeriod( + mediaPeriodId, + playerPeriodUid, + player.isPlayingAd(), + player.getCurrentAdGroupIndex(), + player.getCurrentAdIndexInAdGroup(), + playerNextAdGroupIndex)) { + return mediaPeriodId; + } + } + if (mediaPeriodQueue.isEmpty() && playingMediaPeriod != null) { + if (isMatchingMediaPeriod( + playingMediaPeriod, + playerPeriodUid, + player.isPlayingAd(), + player.getCurrentAdGroupIndex(), + player.getCurrentAdIndexInAdGroup(), + playerNextAdGroupIndex)) { + return playingMediaPeriod; + } + } + return null; + } + + private static boolean isMatchingMediaPeriod( + MediaPeriodId mediaPeriodId, + @Nullable Object playerPeriodUid, + boolean isPlayingAd, + int playerAdGroupIndex, + int playerAdIndexInAdGroup, + int playerNextAdGroupIndex) { + if (!mediaPeriodId.periodUid.equals(playerPeriodUid)) { + return false; + } + // Timeline period matches. Still need to check ad information. + return (isPlayingAd + && mediaPeriodId.adGroupIndex == playerAdGroupIndex + && mediaPeriodId.adIndexInAdGroup == playerAdIndexInAdGroup) + || (!isPlayingAd + && mediaPeriodId.adGroupIndex == C.INDEX_UNSET + && mediaPeriodId.nextAdGroupIndex == playerNextAdGroupIndex); + } + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index bf5f152a6f..ef8b0477f0 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -31,6 +31,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.TracksInfo; import androidx.media3.common.util.Clock; import androidx.media3.exoplayer.analytics.AnalyticsCollector; +import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSource.MediaSourceCaller; @@ -83,7 +84,7 @@ public final class MediaPeriodQueueTest { @Before public void setUp() { - AnalyticsCollector analyticsCollector = new AnalyticsCollector(Clock.DEFAULT); + AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(Clock.DEFAULT); analyticsCollector.setPlayer( new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(), Looper.getMainLooper()); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaSourceListTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaSourceListTest.java index 32052e2228..42a65f24d7 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaSourceListTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaSourceListTest.java @@ -32,6 +32,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.util.Clock; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.analytics.AnalyticsCollector; +import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.ShuffleOrder; @@ -58,7 +59,7 @@ public class MediaSourceListTest { @Before public void setUp() { - AnalyticsCollector analyticsCollector = new AnalyticsCollector(Clock.DEFAULT); + AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(Clock.DEFAULT); analyticsCollector.setPlayer( new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(), Looper.getMainLooper()); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java similarity index 99% rename from libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java rename to libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java index 35e59c9740..7aff4de60f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/AnalyticsCollectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java @@ -55,12 +55,12 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.argThat; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.same; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -134,11 +134,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.robolectric.shadows.ShadowLooper; -/** Integration test for {@link AnalyticsCollector}. */ +/** Integration test for {@link DefaultAnalyticsCollector}. */ @RunWith(AndroidJUnit4.class) -public final class AnalyticsCollectorTest { +public final class DefaultAnalyticsCollectorTest { - private static final String TAG = "AnalyticsCollectorTest"; + private static final String TAG = "DefaultAnalyticsCollectorTest"; // Deprecated event constants. private static final long EVENT_PLAYER_STATE_CHANGED = 1L << 63; @@ -193,14 +193,14 @@ public final class AnalyticsCollectorTest { private EventWindowAndPeriodId window1Period0Seq1; @Test - public void analyticsCollector_overridesAllPlayerListenerMethods() throws Exception { + public void defaultAnalyticsCollector_overridesAllPlayerListenerMethods() throws Exception { // Verify that AnalyticsCollector forwards all Player.Listener methods to AnalyticsListener. for (Method method : Player.Listener.class.getDeclaredMethods()) { assertThat( - AnalyticsCollector.class + DefaultAnalyticsCollector.class .getMethod(method.getName(), method.getParameterTypes()) .getDeclaringClass()) - .isEqualTo(AnalyticsCollector.class); + .isEqualTo(DefaultAnalyticsCollector.class); } } @@ -1964,7 +1964,7 @@ public final class AnalyticsCollectorTest { @Test public void recursiveListenerInvocation_arrivesInCorrectOrder() { - AnalyticsCollector analyticsCollector = new AnalyticsCollector(Clock.DEFAULT); + AnalyticsCollector analyticsCollector = new DefaultAnalyticsCollector(Clock.DEFAULT); analyticsCollector.setPlayer( new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(), Looper.myLooper()); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index caa7887a42..3917a4bf24 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -29,7 +29,7 @@ import androidx.media3.exoplayer.LoadControl; import androidx.media3.exoplayer.Renderer; import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.SimpleExoPlayer; -import androidx.media3.exoplayer.analytics.AnalyticsCollector; +import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.upstream.BandwidthMeter; @@ -302,7 +302,7 @@ public class TestExoPlayerBuilder { .setTrackSelector(trackSelector) .setLoadControl(loadControl) .setBandwidthMeter(bandwidthMeter) - .setAnalyticsCollector(new AnalyticsCollector(clock)) + .setAnalyticsCollector(new DefaultAnalyticsCollector(clock)) .setClock(clock) .setUseLazyPreparation(useLazyPreparation) .setLooper(looper) From 0314d1473734805b40e408421dab4be86e9e78ff Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 11 Feb 2022 15:30:26 +0000 Subject: [PATCH 197/251] Set character limit for accessability strings to NONE They are not normally displayed in any size-restricted UI element and don't need a character limit. #minor-release PiperOrigin-RevId: 427998164 --- libraries/ui/src/main/res/values/strings.xml | 52 ++++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/ui/src/main/res/values/strings.xml b/libraries/ui/src/main/res/values/strings.xml index 515767c8ef..7f65a4bb4b 100644 --- a/libraries/ui/src/main/res/values/strings.xml +++ b/libraries/ui/src/main/res/values/strings.xml @@ -14,63 +14,63 @@ limitations under the License. --> - + Show player controls - + Hide player controls - + Playback progress - + Settings - + Hide additional settings - + Show additional settings - + Enter fullscreen - + Exit fullscreen - + Previous - + Next - + Pause - + Play - + Stop - + Rewind - + Rewind %d second Rewind %d seconds - + Fast forward - + Fast forward %d second Fast forward %d seconds - + Current mode: Repeat none. Toggle repeat mode. - + Current mode: Repeat one. Toggle repeat mode. - + Current mode: Repeat all. Toggle repeat mode. - + Disable shuffle mode - + Enable shuffle mode - + VR mode - + Disable subtitles - + Enable subtitles - + Speed From 6dae8746ad503520bf076e9d744fbdafd7ac6a52 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 11 Feb 2022 16:25:29 +0000 Subject: [PATCH 198/251] Stop using SimpleExoPlayer for tests The class is deprecated and all tests should preferably use the non-deprecated code paths. PiperOrigin-RevId: 428007986 --- .../media3/exoplayer/ExoPlayerTest.java | 122 ++++++++++++- .../media3/exoplayer/SimpleExoPlayerTest.java | 170 ------------------ .../test/utils/TestExoPlayerBuilder.java | 13 +- 3 files changed, 127 insertions(+), 178 deletions(-) delete mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 5b7802c49f..d99bbeeb2f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -65,9 +65,11 @@ import static org.junit.Assert.fail; import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -180,7 +182,9 @@ import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mockito; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowAudioManager; +import org.robolectric.shadows.ShadowLooper; /** Unit test for {@link ExoPlayer}. */ @RunWith(AndroidJUnit4.class) @@ -11468,6 +11472,122 @@ public final class ExoPlayerTest { assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata); } + @Test + @Config(sdk = Config.ALL_SDKS) + public void builder_inBackgroundThread_doesNotThrow() throws Exception { + Thread builderThread = + new Thread( + () -> new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build()); + AtomicReference builderThrow = new AtomicReference<>(); + builderThread.setUncaughtExceptionHandler((thread, throwable) -> builderThrow.set(throwable)); + + builderThread.start(); + builderThread.join(); + + assertThat(builderThrow.get()).isNull(); + } + + @Test + public void onPlaylistMetadataChanged_calledWhenPlaylistMetadataSet() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build(); + Player.Listener playerListener = mock(Player.Listener.class); + player.addListener(playerListener); + AnalyticsListener analyticsListener = mock(AnalyticsListener.class); + player.addAnalyticsListener(analyticsListener); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("test").build(); + player.setPlaylistMetadata(mediaMetadata); + + verify(playerListener).onPlaylistMetadataChanged(mediaMetadata); + verify(analyticsListener).onPlaylistMetadataChanged(any(), eq(mediaMetadata)); + } + + @Test + public void release_triggersAllPendingEventsInAnalyticsListeners() throws Exception { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setRenderersFactory( + (handler, videoListener, audioListener, textOutput, metadataOutput) -> + new Renderer[] {new FakeVideoRenderer(handler, videoListener)}) + .build(); + AnalyticsListener listener = mock(AnalyticsListener.class); + player.addAnalyticsListener(listener); + // Do something that requires clean-up callbacks like decoder disabling. + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); + player.prepare(); + player.play(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.release(); + ShadowLooper.runMainLooperToNextTask(); + + verify(listener).onVideoDisabled(any(), any()); + verify(listener).onPlayerReleased(any()); + } + + @Test + public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception { + Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0)); + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setRenderersFactory( + (handler, videoListener, audioListener, textOutput, metadataOutput) -> + new Renderer[] {new FakeVideoRenderer(handler, videoListener)}) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); + player.setVideoSurface(surface); + player.prepare(); + player.play(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.release(); + surface.release(); + ShadowLooper.runMainLooperToNextTask(); + + verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener + verify(listener).onRenderedFirstFrame(); // VideoListener + } + + @Test + public void releaseAfterVolumeChanges_triggerPendingVolumeEventInListener() throws Exception { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.setVolume(0F); + player.release(); + ShadowLooper.runMainLooperToNextTask(); + + verify(listener).onVolumeChanged(anyFloat()); + } + + @Test + public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + int deviceVolume = player.getDeviceVolume(); + try { + player.setDeviceVolume(deviceVolume + 1); // No-op if at max volume. + player.setDeviceVolume(deviceVolume - 1); // No-op if at min volume. + } finally { + player.setDeviceVolume(deviceVolume); // Restore original volume. + } + + player.release(); + ShadowLooper.runMainLooperToNextTask(); + + verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + // Internal methods. private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) { @@ -11587,7 +11707,7 @@ public final class ExoPlayerTest { @Override public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { super.render(positionUs, elapsedRealtimeUs); - if (sleepOnNextRender.compareAndSet(/* expect= */ true, /* update= */ false)) { + if (sleepOnNextRender.compareAndSet(/* expectedValue= */ true, /* newValue= */ false)) { wakeupListenerReceiver.get().onSleep(WAKEUP_DEADLINE_MS); } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java deleted file mode 100644 index 9a1dbb7e62..0000000000 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/SimpleExoPlayerTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.exoplayer; - -import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilPlaybackState; -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.graphics.SurfaceTexture; -import android.view.Surface; -import androidx.media3.common.MediaMetadata; -import androidx.media3.common.Player; -import androidx.media3.exoplayer.analytics.AnalyticsListener; -import androidx.media3.test.utils.ExoPlayerTestRunner; -import androidx.media3.test.utils.FakeClock; -import androidx.media3.test.utils.FakeMediaSource; -import androidx.media3.test.utils.FakeTimeline; -import androidx.media3.test.utils.FakeVideoRenderer; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import java.util.concurrent.atomic.AtomicReference; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLooper; - -/** Unit test for {@link SimpleExoPlayer}. */ -@SuppressWarnings("deprecation") // Testing deprecated type. -@RunWith(AndroidJUnit4.class) -public class SimpleExoPlayerTest { - - @Test - @Config(sdk = Config.ALL_SDKS) - public void builder_inBackgroundThread_doesNotThrow() throws Exception { - Thread builderThread = - new Thread( - () -> new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build()); - AtomicReference builderThrow = new AtomicReference<>(); - builderThread.setUncaughtExceptionHandler((thread, throwable) -> builderThrow.set(throwable)); - - builderThread.start(); - builderThread.join(); - - assertThat(builderThrow.get()).isNull(); - } - - @Test - public void onPlaylistMetadataChanged_calledWhenPlaylistMetadataSet() { - SimpleExoPlayer player = - new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(); - Player.Listener playerListener = mock(Player.Listener.class); - player.addListener(playerListener); - AnalyticsListener analyticsListener = mock(AnalyticsListener.class); - player.addAnalyticsListener(analyticsListener); - - MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("test").build(); - player.setPlaylistMetadata(mediaMetadata); - - verify(playerListener).onPlaylistMetadataChanged(mediaMetadata); - verify(analyticsListener).onPlaylistMetadataChanged(any(), eq(mediaMetadata)); - } - - @Test - public void release_triggersAllPendingEventsInAnalyticsListeners() throws Exception { - SimpleExoPlayer player = - new SimpleExoPlayer.Builder( - ApplicationProvider.getApplicationContext(), - (handler, videoListener, audioListener, textOutput, metadataOutput) -> - new Renderer[] {new FakeVideoRenderer(handler, videoListener)}) - .setClock(new FakeClock(/* isAutoAdvancing= */ true)) - .build(); - AnalyticsListener listener = mock(AnalyticsListener.class); - player.addAnalyticsListener(listener); - // Do something that requires clean-up callbacks like decoder disabling. - player.setMediaSource( - new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); - player.prepare(); - player.play(); - runUntilPlaybackState(player, Player.STATE_READY); - - player.release(); - ShadowLooper.runMainLooperToNextTask(); - - verify(listener).onVideoDisabled(any(), any()); - verify(listener).onPlayerReleased(any()); - } - - @Test - public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception { - Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0)); - SimpleExoPlayer player = - new SimpleExoPlayer.Builder( - ApplicationProvider.getApplicationContext(), - (handler, videoListener, audioListener, textOutput, metadataOutput) -> - new Renderer[] {new FakeVideoRenderer(handler, videoListener)}) - .setClock(new FakeClock(/* isAutoAdvancing= */ true)) - .build(); - Player.Listener listener = mock(Player.Listener.class); - player.addListener(listener); - player.setMediaSource( - new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); - player.setVideoSurface(surface); - player.prepare(); - player.play(); - runUntilPlaybackState(player, Player.STATE_READY); - - player.release(); - surface.release(); - ShadowLooper.runMainLooperToNextTask(); - - verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener - verify(listener).onRenderedFirstFrame(); // VideoListener - } - - @Test - public void releaseAfterVolumeChanges_triggerPendingVolumeEventInListener() throws Exception { - SimpleExoPlayer player = - new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(); - Player.Listener listener = mock(Player.Listener.class); - player.addListener(listener); - - player.setVolume(0F); - player.release(); - ShadowLooper.runMainLooperToNextTask(); - - verify(listener).onVolumeChanged(anyFloat()); - } - - @Test - public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() { - SimpleExoPlayer player = - new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(); - Player.Listener listener = mock(Player.Listener.class); - player.addListener(listener); - - int deviceVolume = player.getDeviceVolume(); - try { - player.setDeviceVolume(deviceVolume + 1); // No-op if at max volume. - player.setDeviceVolume(deviceVolume - 1); // No-op if at min volume. - } finally { - player.setDeviceVolume(deviceVolume); // Restore original volume. - } - - player.release(); - ShadowLooper.runMainLooperToNextTask(); - - verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean()); - } -} diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index 3917a4bf24..ef866067fc 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -25,10 +25,10 @@ import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.DefaultLoadControl; +import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.LoadControl; import androidx.media3.exoplayer.Renderer; import androidx.media3.exoplayer.RenderersFactory; -import androidx.media3.exoplayer.SimpleExoPlayer; import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; @@ -36,8 +36,7 @@ import androidx.media3.exoplayer.upstream.BandwidthMeter; import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -/** A builder of {@link SimpleExoPlayer} instances for testing. */ -@SuppressWarnings("deprecation") // Returning deprecated type for backwards compatibility. +/** A builder of {@link ExoPlayer} instances for testing. */ @UnstableApi public class TestExoPlayerBuilder { @@ -275,8 +274,8 @@ public class TestExoPlayerBuilder { return seekForwardIncrementMs; } - /** Builds an {@link SimpleExoPlayer} using the provided values or their defaults. */ - public SimpleExoPlayer build() { + /** Builds an {@link ExoPlayer} using the provided values or their defaults. */ + public ExoPlayer build() { Assertions.checkNotNull( looper, "TestExoPlayer builder run on a thread without Looper and no Looper specified."); // Do not update renderersFactory and renderers here, otherwise their getters may @@ -297,8 +296,8 @@ public class TestExoPlayerBuilder { }; } - SimpleExoPlayer.Builder builder = - new SimpleExoPlayer.Builder(context, playerRenderersFactory) + ExoPlayer.Builder builder = + new ExoPlayer.Builder(context, playerRenderersFactory) .setTrackSelector(trackSelector) .setLoadControl(loadControl) .setBandwidthMeter(bandwidthMeter) From 4ef007cae736c48773406c7c1c3d2f97888f5e47 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 11 Feb 2022 16:33:29 +0000 Subject: [PATCH 199/251] Add some missing thread verifications This adds some missing calls to verifyApplicationThread to ExoPlayerImpl. Now all public methods start with this call, except listener registrations because registration after construction on a background thread is allowed and supported. PiperOrigin-RevId: 428009498 --- .../media3/exoplayer/ExoPlayerImpl.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 14a8b20162..1280192eee 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -398,6 +398,7 @@ import java.util.concurrent.TimeoutException; @Override @Deprecated public AudioComponent getAudioComponent() { + verifyApplicationThread(); return this; } @@ -405,6 +406,7 @@ import java.util.concurrent.TimeoutException; @Override @Deprecated public VideoComponent getVideoComponent() { + verifyApplicationThread(); return this; } @@ -412,6 +414,7 @@ import java.util.concurrent.TimeoutException; @Override @Deprecated public TextComponent getTextComponent() { + verifyApplicationThread(); return this; } @@ -419,6 +422,7 @@ import java.util.concurrent.TimeoutException; @Override @Deprecated public DeviceComponent getDeviceComponent() { + verifyApplicationThread(); return this; } @@ -492,6 +496,7 @@ import java.util.concurrent.TimeoutException; @Override @Deprecated public void retry() { + verifyApplicationThread(); prepare(); } @@ -922,6 +927,7 @@ import java.util.concurrent.TimeoutException; @Override public void stop() { + verifyApplicationThread(); stop(/* reset= */ false); } @@ -1205,6 +1211,7 @@ import java.util.concurrent.TimeoutException; @Override public @C.VideoScalingMode int getVideoScalingMode() { + verifyApplicationThread(); return videoScalingMode; } @@ -1222,11 +1229,13 @@ import java.util.concurrent.TimeoutException; @Override public @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy() { + verifyApplicationThread(); return videoChangeFrameRateStrategy; } @Override public VideoSize getVideoSize() { + verifyApplicationThread(); return videoSize; } @@ -1373,6 +1382,7 @@ import java.util.concurrent.TimeoutException; @Override public AudioAttributes getAudioAttributes() { + verifyApplicationThread(); return audioAttributes; } @@ -1403,6 +1413,7 @@ import java.util.concurrent.TimeoutException; @Override public int getAudioSessionId() { + verifyApplicationThread(); return audioSessionId; } @@ -1414,6 +1425,7 @@ import java.util.concurrent.TimeoutException; @Override public void clearAuxEffectInfo() { + verifyApplicationThread(); setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f)); } @@ -1432,11 +1444,13 @@ import java.util.concurrent.TimeoutException; @Override public float getVolume() { + verifyApplicationThread(); return volume; } @Override public boolean getSkipSilenceEnabled() { + verifyApplicationThread(); return skipSilenceEnabled; } @@ -1455,6 +1469,7 @@ import java.util.concurrent.TimeoutException; @Override public AnalyticsCollector getAnalyticsCollector() { + verifyApplicationThread(); return analyticsCollector; } @@ -1501,24 +1516,28 @@ import java.util.concurrent.TimeoutException; @Override @Nullable public Format getVideoFormat() { + verifyApplicationThread(); return videoFormat; } @Override @Nullable public Format getAudioFormat() { + verifyApplicationThread(); return audioFormat; } @Override @Nullable public DecoderCounters getVideoDecoderCounters() { + verifyApplicationThread(); return videoDecoderCounters; } @Override @Nullable public DecoderCounters getAudioDecoderCounters() { + verifyApplicationThread(); return audioDecoderCounters; } @@ -1588,6 +1607,7 @@ import java.util.concurrent.TimeoutException; @Override public void setHandleWakeLock(boolean handleWakeLock) { + verifyApplicationThread(); setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); } From a72f04f9b00299e10de09d18146309656a5a9a02 Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 11 Feb 2022 16:41:36 +0000 Subject: [PATCH 200/251] Resolve media period ids in multi-period windows Ignorable ad periods are skipped to resolve the media period id with the ad playback state of the resulting period. In case of a change in the period position un-played ad periods are rolled forward to be played. PiperOrigin-RevId: 428011116 --- .../androidx/media3/common/TimelineTest.java | 5 +- .../exoplayer/ExoPlayerImplInternal.java | 15 +- .../media3/exoplayer/MediaPeriodQueue.java | 111 ++++- .../media3/exoplayer/ExoPlayerTest.java | 465 +++++++++++++++++- .../exoplayer/MediaPeriodQueueTest.java | 319 +++++++++++- .../ImaServerSideAdInsertionMediaSource.java | 1 + .../media3/exoplayer/ima/ImaUtil.java | 5 +- .../exoplayer/ima/ImaAdsLoaderTest.java | 10 +- .../media3/exoplayer/ima/ImaUtilTest.java | 46 ++ .../test/utils/FakeMediaSourceFactory.java | 3 +- .../media3/test/utils/FakeTimeline.java | 159 +++++- .../media3/test/utils/FakeTimelineTest.java | 106 ++++ 12 files changed, 1177 insertions(+), 68 deletions(-) create mode 100644 libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java diff --git a/libraries/common/src/test/java/androidx/media3/common/TimelineTest.java b/libraries/common/src/test/java/androidx/media3/common/TimelineTest.java index b9452a1be4..f2de751193 100644 --- a/libraries/common/src/test/java/androidx/media3/common/TimelineTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/TimelineTest.java @@ -23,6 +23,7 @@ import androidx.media3.test.utils.FakeTimeline; import androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition; import androidx.media3.test.utils.TimelineAsserts; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; import org.junit.Test; import org.junit.runner.RunWith; @@ -221,7 +222,7 @@ public class TimelineTest { /* durationUs= */ 2, /* defaultPositionUs= */ 22, /* windowOffsetInFirstPeriodUs= */ 222, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder().setMediaId("mediaId2").build()), new TimelineWindowDefinition( /* periodCount= */ 3, @@ -233,7 +234,7 @@ public class TimelineTest { /* durationUs= */ 3, /* defaultPositionUs= */ 33, /* windowOffsetInFirstPeriodUs= */ 333, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder().setMediaId("mediaId3").build())); Timeline restoredTimeline = Timeline.CREATOR.fromBundle(timeline.toBundle()); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index cc32805042..14d3f9af17 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -1178,7 +1178,7 @@ import java.util.concurrent.atomic.AtomicBoolean; requestedContentPositionUs = seekPosition.windowPositionUs == C.TIME_UNSET ? C.TIME_UNSET : resolvedContentPositionUs; periodId = - queue.resolveMediaPeriodIdForAds( + queue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( playbackInfo.timeline, periodUid, resolvedContentPositionUs); if (periodId.isAd()) { playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period); @@ -1492,7 +1492,7 @@ import java.util.concurrent.atomic.AtomicBoolean; window, period, firstWindowIndex, /* windowPositionUs= */ C.TIME_UNSET); // Add ad metadata if any and propagate the window sequence number to new period id. MediaPeriodId firstPeriodId = - queue.resolveMediaPeriodIdForAds( + queue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( timeline, firstPeriodAndPositionUs.first, /* positionUs= */ 0); long positionUs = firstPeriodAndPositionUs.second; if (firstPeriodId.isAd()) { @@ -2354,7 +2354,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private PlaybackInfo handlePositionDiscontinuity( MediaPeriodId mediaPeriodId, long positionUs, - long contentPositionUs, + long requestedContentPositionUs, long discontinuityStartPositionUs, boolean reportDiscontinuity, @DiscontinuityReason int discontinuityReason) { @@ -2379,9 +2379,9 @@ import java.util.concurrent.atomic.AtomicBoolean; staticMetadata = extractMetadataFromTrackSelectionArray(trackSelectorResult.selections); // Ensure the media period queue requested content position matches the new playback info. if (playingPeriodHolder != null - && playingPeriodHolder.info.requestedContentPositionUs != contentPositionUs) { + && playingPeriodHolder.info.requestedContentPositionUs != requestedContentPositionUs) { playingPeriodHolder.info = - playingPeriodHolder.info.copyWithRequestedContentPositionUs(contentPositionUs); + playingPeriodHolder.info.copyWithRequestedContentPositionUs(requestedContentPositionUs); } } else if (!mediaPeriodId.equals(playbackInfo.periodId)) { // Reset previously kept track info if unprepared and the period changes. @@ -2395,7 +2395,7 @@ import java.util.concurrent.atomic.AtomicBoolean; return playbackInfo.copyWithNewPosition( mediaPeriodId, positionUs, - contentPositionUs, + requestedContentPositionUs, discontinuityStartPositionUs, getTotalBufferedDurationUs(), trackGroupArray, @@ -2668,7 +2668,8 @@ import java.util.concurrent.atomic.AtomicBoolean; // Ensure ad insertion metadata is up to date. MediaPeriodId periodIdWithAds = - queue.resolveMediaPeriodIdForAds(timeline, newPeriodUid, contentPositionForAdResolutionUs); + queue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, newPeriodUid, contentPositionForAdResolutionUs); boolean earliestCuePointIsUnchangedOrLater = periodIdWithAds.nextAdGroupIndex == C.INDEX_UNSET || (oldPeriodId.nextAdGroupIndex != C.INDEX_UNSET diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java index 774ff9d4cc..12edf0605c 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.util.Assertions.checkNotNull; import static java.lang.Math.max; import android.os.Handler; @@ -446,21 +447,7 @@ import com.google.common.collect.ImmutableList; Timeline timeline, Object periodUid, long positionUs) { long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(timeline, periodUid); return resolveMediaPeriodIdForAds( - timeline, periodUid, positionUs, windowSequenceNumber, period); - } - - // Internal methods. - - private void notifyQueueUpdate() { - ImmutableList.Builder builder = ImmutableList.builder(); - @Nullable MediaPeriodHolder period = playing; - while (period != null) { - builder.add(period.info.id); - period = period.getNext(); - } - @Nullable MediaPeriodId readingPeriodId = reading == null ? null : reading.info.id; - analyticsCollectorHandler.post( - () -> analyticsCollector.updateMediaPeriodQueueInfo(builder.build(), readingPeriodId)); + timeline, periodUid, positionUs, windowSequenceNumber, window, period); } /** @@ -481,8 +468,21 @@ import com.google.common.collect.ImmutableList; Object periodUid, long positionUs, long windowSequenceNumber, + Timeline.Window window, Timeline.Period period) { timeline.getPeriodByUid(periodUid, period); + timeline.getWindow(period.windowIndex, window); + int periodIndex = timeline.getIndexOfPeriod(periodUid); + // Skip ignorable server side inserted ad periods. + while ((period.durationUs == 0 + && period.getAdGroupCount() > 0 + && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount()) + && period.getAdGroupIndexForPositionUs(0) == C.INDEX_UNSET) + && periodIndex++ < window.lastPeriodIndex) { + timeline.getPeriod(periodIndex, period, /* setIds= */ true); + periodUid = checkNotNull(period.uid); + } + timeline.getPeriodByUid(periodUid, period); int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs); if (adGroupIndex == C.INDEX_UNSET) { int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs); @@ -493,6 +493,55 @@ import com.google.common.collect.ImmutableList; } } + /** + * Resolves the specified timeline period and position to a {@link MediaPeriodId} that should be + * played after a period position change, returning an identifier for an ad group if one needs to + * be played before the specified position, or an identifier for a content media period if not. + * + * @param timeline The timeline the period is part of. + * @param periodUid The uid of the timeline period to play. + * @param positionUs The next content position in the period to play. + * @return The identifier for the first media period to play, taking into account unplayed ads. + */ + public MediaPeriodId resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + Timeline timeline, Object periodUid, long positionUs) { + long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(timeline, periodUid); + // Check for preceding ad periods in multi-period window. + timeline.getPeriodByUid(periodUid, period); + timeline.getWindow(period.windowIndex, window); + Object periodUidToPlay = periodUid; + boolean seenAdPeriod = false; + for (int i = timeline.getIndexOfPeriod(periodUid); i >= window.firstPeriodIndex; i--) { + timeline.getPeriod(/* periodIndex= */ i, period, /* setIds= */ true); + boolean isAdPeriod = period.getAdGroupCount() > 0; + seenAdPeriod |= isAdPeriod; + if (period.getAdGroupIndexForPositionUs(period.durationUs) != C.INDEX_UNSET) { + // Roll forward to preceding un-played ad period. + periodUidToPlay = checkNotNull(period.uid); + } + if (seenAdPeriod && (!isAdPeriod || period.durationUs != 0)) { + // Stop for any periods except un-played ads with no content. + break; + } + } + return resolveMediaPeriodIdForAds( + timeline, periodUidToPlay, positionUs, windowSequenceNumber, window, period); + } + + // Internal methods. + + private void notifyQueueUpdate() { + ImmutableList.Builder builder = ImmutableList.builder(); + @Nullable MediaPeriodHolder period = playing; + while (period != null) { + builder.add(period.info.id); + period = period.getNext(); + } + @Nullable MediaPeriodId readingPeriodId = reading == null ? null : reading.info.id; + analyticsCollectorHandler.post( + () -> analyticsCollector.updateMediaPeriodQueueInfo(builder.build(), readingPeriodId)); + } + /** * Resolves the specified period uid to a corresponding window sequence number. Either by reusing * the window sequence number of an existing matching media period or by creating a new window @@ -647,12 +696,12 @@ import com.google.common.collect.ImmutableList; // We can't create a next period yet. return null; } - - long startPositionUs; - long contentPositionUs; + // We either start a new period in the same window or the first period in the next window. + long startPositionUs = 0; + long contentPositionUs = 0; int nextWindowIndex = timeline.getPeriod(nextPeriodIndex, period, /* setIds= */ true).windowIndex; - Object nextPeriodUid = period.uid; + Object nextPeriodUid = checkNotNull(period.uid); long windowSequenceNumber = mediaPeriodInfo.id.windowSequenceNumber; if (timeline.getWindow(nextWindowIndex, window).firstPeriodIndex == nextPeriodIndex) { // We're starting to buffer a new window. When playback transitions to this window we'll @@ -672,20 +721,32 @@ import com.google.common.collect.ImmutableList; } nextPeriodUid = defaultPositionUs.first; startPositionUs = defaultPositionUs.second; - MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext(); + @Nullable MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext(); if (nextMediaPeriodHolder != null && nextMediaPeriodHolder.uid.equals(nextPeriodUid)) { windowSequenceNumber = nextMediaPeriodHolder.info.id.windowSequenceNumber; } else { windowSequenceNumber = nextWindowSequenceNumber++; } - } else { - // We're starting to buffer a new period within the same window. - startPositionUs = 0; - contentPositionUs = 0; } + + @Nullable MediaPeriodId periodId = resolveMediaPeriodIdForAds( - timeline, nextPeriodUid, startPositionUs, windowSequenceNumber, period); + timeline, nextPeriodUid, startPositionUs, windowSequenceNumber, window, period); + if (contentPositionUs != C.TIME_UNSET + && mediaPeriodInfo.requestedContentPositionUs != C.TIME_UNSET) { + boolean isPrecedingPeriodAnAd = + timeline.getPeriodByUid(mediaPeriodInfo.id.periodUid, period).getAdGroupCount() > 0 + && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount()); + // Handle the requested content position for period transitions within the same window. + if (periodId.isAd() && isPrecedingPeriodAnAd) { + // Propagate the requested position to the following ad period in the same window. + contentPositionUs = mediaPeriodInfo.requestedContentPositionUs; + } else if (isPrecedingPeriodAnAd) { + // Use the requested content position of the preceding ad period as the start position. + startPositionUs = mediaPeriodInfo.requestedContentPositionUs; + } + } return getMediaPeriodInfo(timeline, periodId, contentPositionUs, startPositionUs); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index d99bbeeb2f..0d48e0d7cd 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -124,6 +124,7 @@ import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSourceEventListener; import androidx.media3.exoplayer.source.SinglePeriodTimeline; +import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource; import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.upstream.Allocation; import androidx.media3.exoplayer.upstream.Allocator; @@ -4984,6 +4985,436 @@ public final class ExoPlayerTest { runUntilPlaybackState(player, Player.STATE_ENDED); } + @Test + public void seekTo_beyondSSAIMidRolls_seekAdjustedAndRequestedContentPositionKept() + throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + true, + true, + false); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.pause(); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.seekTo(/* positionMs= */ 4000); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(6)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); + // seek discontinuities + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).periodIndex).isEqualTo(3); + assertThat(newPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).positionMs).isEqualTo(4000); + // seek adjustment + assertThat(oldPositions.get(1).periodIndex).isEqualTo(3); + assertThat(oldPositions.get(1).adGroupIndex).isEqualTo(-1); + assertThat(oldPositions.get(1).positionMs).isEqualTo(4000); + assertThat(newPositions.get(1).periodIndex).isEqualTo(1); + assertThat(newPositions.get(1).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(1).adIndexInAdGroup).isEqualTo(0); + assertThat(newPositions.get(1).positionMs).isEqualTo(0); + assertThat(newPositions.get(1).contentPositionMs).isEqualTo(4000); + // auto transition from ad to end of period + assertThat(oldPositions.get(2).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(2).adGroupIndex).isEqualTo(0); + assertThat(oldPositions.get(2).adIndexInAdGroup).isEqualTo(0); + assertThat(oldPositions.get(2).positionMs).isEqualTo(2500); + assertThat(oldPositions.get(2).contentPositionMs).isEqualTo(4000); + assertThat(newPositions.get(2).periodIndex).isEqualTo(1); + assertThat(newPositions.get(2).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(2).positionMs).isEqualTo(2500); + // auto transition to next ad period + assertThat(oldPositions.get(3).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(3).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(3).periodIndex).isEqualTo(2); + assertThat(newPositions.get(3).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(3).adIndexInAdGroup).isEqualTo(0); + assertThat(newPositions.get(3).contentPositionMs).isEqualTo(4000); + // auto transition from ad to end of period + assertThat(oldPositions.get(4).periodIndex).isEqualTo(2); + assertThat(oldPositions.get(4).adGroupIndex).isEqualTo(0); + assertThat(oldPositions.get(4).adIndexInAdGroup).isEqualTo(0); + assertThat(newPositions.get(4).periodIndex).isEqualTo(2); + assertThat(newPositions.get(4).adGroupIndex).isEqualTo(-1); + // auto transition to final content period with seek position + assertThat(oldPositions.get(5).periodIndex).isEqualTo(2); + assertThat(oldPositions.get(5).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(5).periodIndex).isEqualTo(3); + assertThat(newPositions.get(5).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(5).contentPositionMs).isEqualTo(4000); + } + + @Test + public void seekTo_beyondSSAIMidRollsConsecutiveContentPeriods_seekAdjusted() throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + true, + false, + false); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.pause(); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.seekTo(/* positionMs= */ 7000); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(5)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(1, 2, 0, 0, 0).inOrder(); + // seek + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).periodIndex).isEqualTo(3); + assertThat(newPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).positionMs).isEqualTo(7000); + // seek adjustment + assertThat(oldPositions.get(1).periodIndex).isEqualTo(3); + assertThat(oldPositions.get(1).adGroupIndex).isEqualTo(-1); + assertThat(oldPositions.get(1).positionMs).isEqualTo(7000); + assertThat(newPositions.get(1).periodIndex).isEqualTo(1); + assertThat(newPositions.get(1).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(1).positionMs).isEqualTo(0); + } + + @Test + public void seekTo_beforeSSAIMidRolls_requestedContentPositionNotPropagatedIntoAds() + throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + true, + true, + false); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.pause(); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + player.play(); + + player.seekTo(1600); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(6)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(1, 0, 0, 0, 0, 0).inOrder(); + // seek discontinuity + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(newPositions.get(0).periodIndex).isEqualTo(0); + assertThat(newPositions.get(0).positionMs).isEqualTo(1600); + assertThat(newPositions.get(0).contentPositionMs).isEqualTo(1600); + // auto discontinuities through ads has correct content position that is not the seek position. + assertThat(newPositions.get(1).periodIndex).isEqualTo(1); + assertThat(newPositions.get(1).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(1).adIndexInAdGroup).isEqualTo(0); + assertThat(newPositions.get(1).positionMs).isEqualTo(0); + assertThat(newPositions.get(1).contentPositionMs).isEqualTo(2500); + assertThat(newPositions.get(2).contentPositionMs).isEqualTo(2500); + assertThat(newPositions.get(3).contentPositionMs).isEqualTo(2500); + assertThat(newPositions.get(4).contentPositionMs).isEqualTo(2500); + // Content resumes at expected position that is not the seek position. + assertThat(newPositions.get(5).periodIndex).isEqualTo(3); + assertThat(newPositions.get(5).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(5).positionMs).isEqualTo(2500); + assertThat(newPositions.get(5).contentPositionMs).isEqualTo(2500); + } + + @Test + public void seekTo_toSAIMidRolls_playsMidRolls() throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + true, + true, + false); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.pause(); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + player.seekTo(2500); + player.play(); + + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(6)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); + // seek discontinuity + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).periodIndex).isEqualTo(1); + assertThat(newPositions.get(0).adGroupIndex).isEqualTo(-1); + // seek adjustment discontinuity + assertThat(oldPositions.get(1).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(1).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(1).periodIndex).isEqualTo(1); + assertThat(newPositions.get(1).adGroupIndex).isEqualTo(0); + // auto transition to last frame of first ad period + assertThat(oldPositions.get(2).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(2).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(2).periodIndex).isEqualTo(1); + assertThat(newPositions.get(2).adGroupIndex).isEqualTo(-1); + // auto transition to second ad period + assertThat(oldPositions.get(3).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(3).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(3).periodIndex).isEqualTo(2); + assertThat(newPositions.get(3).adGroupIndex).isEqualTo(0); + // auto transition to last frame of second ad period + assertThat(oldPositions.get(4).periodIndex).isEqualTo(2); + assertThat(oldPositions.get(4).adGroupIndex).isEqualTo(0); + assertThat(newPositions.get(4).periodIndex).isEqualTo(2); + assertThat(newPositions.get(4).adGroupIndex).isEqualTo(-1); + // auto transition to the final content period + assertThat(oldPositions.get(5).periodIndex).isEqualTo(2); + assertThat(oldPositions.get(5).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(5).periodIndex).isEqualTo(3); + assertThat(newPositions.get(5).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(5).positionMs).isEqualTo(2500); + assertThat(newPositions.get(5).contentPositionMs).isEqualTo(2500); + } + + @Test + public void seekTo_toPlayedSAIMidRolls_requestedContentPositionNotPropagatedIntoAds() + throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ 2, + /* isAdPeriodFlags...= */ false, + true, + true, + false); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.pause(); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + player.seekTo(2500); + player.play(); + + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(1)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(1).inOrder(); + // seek discontinuity + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); + // TODO(bachinger): Incorrect masking. Skipped played prerolls not taken into account by masking + assertThat(newPositions.get(0).periodIndex).isEqualTo(1); + assertThat(newPositions.get(0).adGroupIndex).isEqualTo(-1); + } + + @Test + public void play_playedSSAIPreMidPostRolls_skipsAllAds() throws Exception { + ArgumentCaptor oldPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor newPositionArgumentCaptor = + ArgumentCaptor.forClass(PositionInfo.class); + ArgumentCaptor reasonArgumentCaptor = ArgumentCaptor.forClass(Integer.class); + FakeTimeline adTimeline = + FakeTimeline.createMultiPeriodAdTimeline( + "windowId", + /* numberOfPlayedAds= */ Integer.MAX_VALUE, + /* isAdPeriodFlags...= */ true, + false, + true, + true, + false, + true, + true, + true); + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.prepare(); + + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(listener, times(3)) + .onPositionDiscontinuity( + oldPositionArgumentCaptor.capture(), + newPositionArgumentCaptor.capture(), + reasonArgumentCaptor.capture()); + List oldPositions = oldPositionArgumentCaptor.getAllValues(); + List newPositions = newPositionArgumentCaptor.getAllValues(); + List reasons = reasonArgumentCaptor.getAllValues(); + assertThat(reasons).containsExactly(0, 0, 0).inOrder(); + // Auto discontinuity from the empty ad period to the first content period. + assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); + assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(oldPositions.get(0).positionMs).isEqualTo(0); + assertThat(newPositions.get(0).periodIndex).isEqualTo(1); + assertThat(newPositions.get(0).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(0).positionMs).isEqualTo(0); + // Auto discontinuity from the first content to the second content period. + assertThat(oldPositions.get(1).periodIndex).isEqualTo(1); + assertThat(oldPositions.get(1).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(1).periodIndex).isEqualTo(4); + assertThat(newPositions.get(1).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(1).positionMs).isEqualTo(1250); + // Auto discontinuity from the second content period to the last frame of the last postroll. + assertThat(oldPositions.get(2).periodIndex).isEqualTo(4); + assertThat(oldPositions.get(2).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(2).periodIndex).isEqualTo(7); + assertThat(newPositions.get(2).adGroupIndex).isEqualTo(-1); + assertThat(newPositions.get(2).positionMs).isEqualTo(2500); + } + @Test public void becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled() throws Exception { ExoPlayer player = new TestExoPlayerBuilder(context).build(); @@ -8036,7 +8467,7 @@ public final class ExoPlayerTest { /* durationUs = */ 100_000, /* defaultPositionUs = */ 0, /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), MediaItem.fromUri("http://foo.bar/fake1")); FakeMediaSource fakeMediaSource1 = new FakeMediaSource(new FakeTimeline(window1)); TimelineWindowDefinition window2 = @@ -8050,7 +8481,7 @@ public final class ExoPlayerTest { /* durationUs = */ 100_000, /* defaultPositionUs = */ 0, /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), MediaItem.fromUri("http://foo.bar/fake2")); FakeMediaSource fakeMediaSource2 = new FakeMediaSource(new FakeTimeline(window2)); TimelineWindowDefinition window3 = @@ -8064,7 +8495,7 @@ public final class ExoPlayerTest { /* durationUs = */ 100_000, /* defaultPositionUs = */ 0, /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), MediaItem.fromUri("http://foo.bar/fake3")); FakeMediaSource fakeMediaSource3 = new FakeMediaSource(new FakeTimeline(window3)); final Player[] playerHolder = {null}; @@ -8422,7 +8853,7 @@ public final class ExoPlayerTest { /* durationUs= */ 10_000_000, /* defaultPositionUs= */ 0, /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), initialMediaItem); TimelineWindowDefinition secondWindow = new TimelineWindowDefinition( @@ -8435,7 +8866,7 @@ public final class ExoPlayerTest { /* durationUs= */ 10_000_000, /* defaultPositionUs= */ 0, /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), initialMediaItem.buildUpon().setTag(1).build()); FakeTimeline timeline = new FakeTimeline(initialWindow); FakeTimeline newTimeline = new FakeTimeline(secondWindow); @@ -9269,7 +9700,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9319,7 +9750,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9365,7 +9796,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9413,7 +9844,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9431,7 +9862,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs + 50_000), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9576,7 +10007,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 20 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9630,7 +10061,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9675,7 +10106,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9693,7 +10124,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9742,7 +10173,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9760,7 +10191,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder() .setUri(Uri.EMPTY) .setLiveConfiguration( @@ -9850,7 +10281,7 @@ public final class ExoPlayerTest { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), new MediaItem.Builder().setUri(Uri.EMPTY).build())); player.pause(); player.setMediaSource(new FakeMediaSource(liveTimelineWithoutTargetLiveOffset)); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index ef8b0477f0..403d59567f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.robolectric.Shadows.shadowOf; @@ -22,6 +23,7 @@ import static org.robolectric.Shadows.shadowOf; import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.util.Pair; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.C; import androidx.media3.common.MediaItem; @@ -479,8 +481,7 @@ public final class MediaPeriodQueueTest { /* startPositionUs= */ 0, /* requestedContentPositionUs= */ C.TIME_UNSET, /* endPositionUs= */ C.TIME_UNSET, - /* durationUs= */ CONTENT_DURATION_US - + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* durationUs= */ CONTENT_DURATION_US + DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, /* isFollowedByTransitionToSameStream= */ false, /* isLastInPeriod= */ true, /* isLastInWindow= */ false, @@ -740,6 +741,320 @@ public final class MediaPeriodQueueTest { assertThat(getQueueLength()).isEqualTo(3); } + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdPositionInSinglePeriodTimeline_resolvesToAd() { + long adPositionUs = DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + 10_000; + AdPlaybackState adPlaybackState = new AdPlaybackState("adsId", adPositionUs); + adPlaybackState = adPlaybackState.withAdDurationsUs(/* adGroupIndex= */ 0, 5_000); + Object windowUid = new Object(); + FakeTimeline timeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ windowUid, + /* isSeekable= */ true, + /* isDynamic= */ false, + TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US, + adPlaybackState)); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, /* periodUid= */ new Pair<>(windowUid, 0), adPositionUs + 1); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowUid, 0)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toAdPositionInSinglePeriodTimeline_resolvesToAd() { + long adPositionUs = DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + 10_000; + AdPlaybackState adPlaybackState = new AdPlaybackState("adsId", adPositionUs); + adPlaybackState = adPlaybackState.withAdDurationsUs(/* adGroupIndex= */ 0, 5_000); + Object windowUid = new Object(); + FakeTimeline timeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ windowUid, + /* isSeekable= */ true, + /* isDynamic= */ false, + TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US, + adPlaybackState)); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, /* periodUid= */ new Pair<>(windowUid, 0), adPositionUs); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowUid, 0)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_beforeAdPositionInSinglePeriodTimeline_seekNotAdjusted() { + long adPositionUs = DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + 10_000; + AdPlaybackState adPlaybackState = + new AdPlaybackState("adsId", adPositionUs).withAdDurationsUs(/* adGroupIndex= */ 0, 5_000); + Object windowUid = new Object(); + FakeTimeline timeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ windowUid, + /* isSeekable= */ true, + /* isDynamic= */ false, + TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US, + adPlaybackState)); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowUid, 0), adPositionUs - 1); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowUid, 0)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(0); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodTimeline_rollForward() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ true, + false, + true, + true, + true, + false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 1), /* positionUs= */ 1); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 0)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + + mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 5), /* positionUs= */ 0); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 2)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodAllAdsPlayed_seekNotAdjusted() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 4, + /* isAdPeriodFlags...= */ true, + false, + true, + true, + true, + false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 1), /* positionUs= */ 11); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 1)); + + mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 5), /* positionUs= */ 33); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 5)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodFirstTwoAdsPlayed_rollForward() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 2, + /* isAdPeriodFlags...= */ true, + false, + true, + true, + true, + false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 5), /* positionUs= */ 33); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 3)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_beforeAdInMultiPeriodTimeline_seekNotAdjusted() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ false, true); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 0), /* positionUs= */ 33); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 0)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toUnplayedAdInMultiPeriodTimeline_resolvedAsAd() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ false, true, false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 1), /* positionUs= */ 0); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 1)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedAdInMultiPeriodTimeline_skipPlayedAd() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, /* numberOfPlayedAds= */ 1, /* isAdPeriodFlags...= */ false, true, false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 1), /* positionUs= */ 0); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 2)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toStartOfWindowPlayedAdPreroll_skipsPlayedPrerolls() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, /* numberOfPlayedAds= */ 2, /* isAdPeriodFlags...= */ true, true, false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 0), /* positionUs= */ 0); + + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 2)); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedPostrolls_skipsAllButLastPostroll() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 4, + /* isAdPeriodFlags...= */ false, + true, + true, + true, + true); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 1), /* positionUs= */ 0); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 4)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(-1); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_consecutiveContentPeriods_rollForward() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ true, + false, + false, + false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 3), /* positionUs= */ 10_000); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 0)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(0); + assertThat(mediaPeriodId.adIndexInAdGroup).isEqualTo(0); + assertThat(mediaPeriodId.nextAdGroupIndex).isEqualTo(-1); + } + + @Test + public void + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_onlyConsecutiveContentPeriods_seekNotAdjusted() { + Object windowId = new Object(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + false, + false, + false); + + MediaPeriodId mediaPeriodId = + mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( + timeline, new Pair<>(windowId, 3), /* positionUs= */ 10_000); + + assertThat(mediaPeriodId.periodUid).isEqualTo(new Pair<>(windowId, 3)); + assertThat(mediaPeriodId.adGroupIndex).isEqualTo(-1); + } + private void setupAdTimeline(long... adGroupTimesUs) { adPlaybackState = new AdPlaybackState(/* adsId= */ new Object(), adGroupTimesUs) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index bf0f957965..c9c90372c2 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -526,6 +526,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou adPlaybackState, /* fromPositionUs= */ secToUs(cuePoint.getStartTime()), /* contentResumeOffsetUs= */ 0, + // TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed /* adDurationsUs...= */ secToUs(cuePoint.getEndTime() - cuePoint.getStartTime())); } return adPlaybackState; diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java index 10623a725c..5304fa637f 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java @@ -438,12 +438,13 @@ import java.util.Set; new AdPlaybackState(checkNotNull(adsId), /* adGroupTimesUs...= */ 0) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdDurationsUs(/* adGroupIndex= */ 0, periodDurationUs) - .withIsServerSideInserted(/* adGroupIndex= */ 0, true); + .withIsServerSideInserted(/* adGroupIndex= */ 0, true) + .withContentResumeOffsetUs(/* adGroupIndex= */ 0, adGroup.contentResumeOffsetUs); long periodEndUs = periodStartUs + periodDurationUs; long adDurationsUs = 0; for (int i = 0; i < adGroup.count; i++) { adDurationsUs += adGroup.durationsUs[i]; - if (periodEndUs == adGroup.timeUs + adDurationsUs) { + if (periodEndUs <= adGroup.timeUs + adDurationsUs + 10_000) { // Map the state of the global ad state to the period specific ad state. switch (adGroup.states[i]) { case AdPlaybackState.AD_STATE_PLAYED: diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java index 97778742ff..cf47d44d4b 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaAdsLoaderTest.java @@ -1365,7 +1365,9 @@ public final class ImaAdsLoaderTest { } private AdPlaybackState getAdPlaybackState(int periodIndex) { - return timelineWindowDefinitions[periodIndex].adPlaybackState; + int adPlaybackStateCount = timelineWindowDefinitions[periodIndex].adPlaybackStates.size(); + return timelineWindowDefinitions[periodIndex].adPlaybackStates.get( + periodIndex % adPlaybackStateCount); } private static AdEvent getAdEvent(AdEventType adEventType, @Nullable Ad ad) { @@ -1408,7 +1410,11 @@ public final class ImaAdsLoaderTest { adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs); TimelineWindowDefinition timelineWindowDefinition = timelineWindowDefinitions[periodIndex]; - assertThat(adPlaybackState.adsId).isEqualTo(timelineWindowDefinition.adPlaybackState.adsId); + assertThat(adPlaybackState.adsId) + .isEqualTo( + timelineWindowDefinition.adPlaybackStates.get( + periodIndex % timelineWindowDefinition.adPlaybackStates.size()) + .adsId); timelineWindowDefinitions[periodIndex] = new TimelineWindowDefinition( timelineWindowDefinition.periodCount, diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaUtilTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaUtilTest.java index a0435083ce..370e16b77f 100644 --- a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaUtilTest.java +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaUtilTest.java @@ -458,6 +458,52 @@ public class ImaUtilTest { .isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); } + @Test + public void splitAdPlaybackStateForPeriods_singleAdOfAdGroupSpansMultiplePeriods_correctState() { + int periodCount = 8; + long periodDurationUs = DEFAULT_WINDOW_DURATION_US / periodCount; + AdPlaybackState adPlaybackState = + new AdPlaybackState(/* adsId= */ "adsId", 0, periodDurationUs, 2 * periodDurationUs) + .withAdCount(/* adGroupIndex= */ 0, 1) + .withAdCount(/* adGroupIndex= */ 1, 1) + .withAdCount(/* adGroupIndex= */ 2, 1) + .withAdDurationsUs( + /* adGroupIndex= */ 0, /* adDurationsUs...= */ + DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + (2 * periodDurationUs)) + .withAdDurationsUs( + /* adGroupIndex= */ 1, /* adDurationsUs...= */ (2 * periodDurationUs)) + .withAdDurationsUs( + /* adGroupIndex= */ 2, /* adDurationsUs...= */ (2 * periodDurationUs)) + .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) + .withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0) + .withIsServerSideInserted(/* adGroupIndex= */ 0, true) + .withIsServerSideInserted(/* adGroupIndex= */ 1, true) + .withIsServerSideInserted(/* adGroupIndex= */ 2, true); + FakeTimeline timeline = + new FakeTimeline( + new FakeTimeline.TimelineWindowDefinition( + /* periodCount= */ periodCount, /* id= */ 0L)); + + ImmutableMap adPlaybackStates = + ImaUtil.splitAdPlaybackStateForPeriods(adPlaybackState, timeline); + + assertThat(adPlaybackStates).hasSize(periodCount); + assertThat(adPlaybackStates.get(new Pair<>(0L, 0)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_PLAYED); + assertThat(adPlaybackStates.get(new Pair<>(0L, 1)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_PLAYED); + assertThat(adPlaybackStates.get(new Pair<>(0L, 2)).adGroupCount).isEqualTo(0); + assertThat(adPlaybackStates.get(new Pair<>(0L, 3)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_PLAYED); + assertThat(adPlaybackStates.get(new Pair<>(0L, 4)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_PLAYED); + assertThat(adPlaybackStates.get(new Pair<>(0L, 5)).adGroupCount).isEqualTo(0); + assertThat(adPlaybackStates.get(new Pair<>(0L, 6)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + assertThat(adPlaybackStates.get(new Pair<>(0L, 7)).getAdGroup(/* adGroupIndex= */ 0).states[0]) + .isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + } + @Test public void splitAdPlaybackStateForPeriods_lateMidrollAdGroupStartTimeUs_adGroupIgnored() { int periodCount = 4; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSourceFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSourceFactory.java index 0d2bfdca0e..127b9c585d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSourceFactory.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeMediaSourceFactory.java @@ -26,6 +26,7 @@ import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSourceFactory; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition; +import com.google.common.collect.ImmutableList; /** Fake {@link MediaSourceFactory} that creates a {@link FakeMediaSource}. */ @UnstableApi @@ -66,7 +67,7 @@ public final class FakeMediaSourceFactory implements MediaSourceFactory { /* durationUs= */ 1000 * C.MICROS_PER_SECOND, /* defaultPositionUs= */ 2 * C.MICROS_PER_SECOND, /* windowOffsetInFirstPeriodUs= */ Util.msToUs(123456789), - AdPlaybackState.NONE, + ImmutableList.of(AdPlaybackState.NONE), mediaItem); return new FakeMediaSource(new FakeTimeline(timelineWindowDefinition)); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java index 270f0cc327..b6ccdfdf3a 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java @@ -15,6 +15,9 @@ */ package androidx.media3.test.utils; +import static androidx.media3.common.util.Util.sum; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static java.lang.Math.min; import android.net.Uri; @@ -27,7 +30,13 @@ import androidx.media3.common.Timeline; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** Fake {@link Timeline} which can be setup to return custom {@link TimelineWindowDefinition}s. */ @UnstableApi @@ -52,7 +61,7 @@ public final class FakeTimeline extends Timeline { public final long durationUs; public final long defaultPositionUs; public final long windowOffsetInFirstPeriodUs; - public final AdPlaybackState adPlaybackState; + public final List adPlaybackStates; /** * Creates a window definition that corresponds to a placeholder timeline using the given tag. @@ -179,10 +188,41 @@ public final class FakeTimeline extends Timeline { durationUs, defaultPositionUs, windowOffsetInFirstPeriodUs, - adPlaybackState, + ImmutableList.of(adPlaybackState), FAKE_MEDIA_ITEM.buildUpon().setTag(id).build()); } + /** + * @deprecated Use {@link #TimelineWindowDefinition(int, Object, boolean, boolean, boolean, + * boolean, long, long, long, List, MediaItem)} instead. + */ + @Deprecated + public TimelineWindowDefinition( + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + boolean isPlaceholder, + long durationUs, + long defaultPositionUs, + long windowOffsetInFirstPeriodUs, + AdPlaybackState adPlaybackState, + MediaItem mediaItem) { + this( + periodCount, + id, + isSeekable, + isDynamic, + isLive, + isPlaceholder, + durationUs, + defaultPositionUs, + windowOffsetInFirstPeriodUs, + ImmutableList.of(adPlaybackState), + mediaItem); + } + /** * Creates a window definition with ad groups and a custom media item. * @@ -197,7 +237,7 @@ public final class FakeTimeline extends Timeline { * @param defaultPositionUs The default position of the window in microseconds. * @param windowOffsetInFirstPeriodUs The offset of the window in the first period, in * microseconds. - * @param adPlaybackState The ad playback state. + * @param adPlaybackStates The ad playback states for the periods. * @param mediaItem The media item to include in the timeline. */ public TimelineWindowDefinition( @@ -210,7 +250,7 @@ public final class FakeTimeline extends Timeline { long durationUs, long defaultPositionUs, long windowOffsetInFirstPeriodUs, - AdPlaybackState adPlaybackState, + List adPlaybackStates, MediaItem mediaItem) { Assertions.checkArgument(durationUs != C.TIME_UNSET || periodCount == 1); this.periodCount = periodCount; @@ -223,7 +263,7 @@ public final class FakeTimeline extends Timeline { this.durationUs = durationUs; this.defaultPositionUs = defaultPositionUs; this.windowOffsetInFirstPeriodUs = windowOffsetInFirstPeriodUs; - this.adPlaybackState = adPlaybackState; + this.adPlaybackStates = adPlaybackStates; } } @@ -268,6 +308,59 @@ public final class FakeTimeline extends Timeline { return adPlaybackState; } + /** + * Creates a multi-period timeline with ad and content periods specified by the flags passed as + * var-arg arguments. + * + *

    Period uid end up being a {@code new Pair<>(windowId, periodIndex)}. + * + * @param windowId The window ID. + * @param numberOfPlayedAds The number of ads that should be marked as played. + * @param isAdPeriodFlags A value of true indicates an ad period. A value of false indicated a + * content period. + * @return A timeline with a single window with as many periods as var-arg arguments. + */ + public static FakeTimeline createMultiPeriodAdTimeline( + Object windowId, int numberOfPlayedAds, boolean... isAdPeriodFlags) { + long periodDurationUs = DEFAULT_WINDOW_DURATION_US / isAdPeriodFlags.length; + AdPlaybackState firstAdPeriodState = + new AdPlaybackState(/* adsId= */ "adsId", /* adGroupTimesUs... */ 0) + .withAdCount(/* adGroupIndex= */ 0, 1) + .withAdDurationsUs( + /* adGroupIndex= */ 0, DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + periodDurationUs) + .withIsServerSideInserted(/* adGroupIndex= */ 0, true); + AdPlaybackState commonAdPeriodState = firstAdPeriodState.withAdDurationsUs(0, periodDurationUs); + AdPlaybackState contentPeriodState = new AdPlaybackState(/* adsId= */ "adsId"); + + List adPlaybackStates = new ArrayList<>(); + int playedAdsCounter = 0; + for (boolean isAd : isAdPeriodFlags) { + AdPlaybackState periodAdPlaybackState = + isAd + ? (adPlaybackStates.isEmpty() ? firstAdPeriodState : commonAdPeriodState) + : contentPeriodState; + if (isAd && playedAdsCounter < numberOfPlayedAds) { + periodAdPlaybackState = + periodAdPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + playedAdsCounter++; + } + adPlaybackStates.add(periodAdPlaybackState); + } + return new FakeTimeline( + new FakeTimeline.TimelineWindowDefinition( + isAdPeriodFlags.length, + windowId, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* isLive= */ false, + /* isPlaceholder= */ false, + /* durationUs= */ DEFAULT_WINDOW_DURATION_US, + /* defaultPositionUs= */ 0, + /* windowOffsetInFirstPeriodUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* adPlaybackStates= */ adPlaybackStates, + MediaItem.EMPTY)); + } + /** * Create a fake timeline with one seekable, non-dynamic window with one period and a duration of * {@link TimelineWindowDefinition#DEFAULT_WINDOW_DURATION_US}. @@ -363,6 +456,19 @@ public final class FakeTimeline extends Timeline { @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; + long windowDurationUs = 0; + Period period = new Period(); + for (int i = periodOffsets[windowIndex]; i < periodOffsets[windowIndex + 1]; i++) { + long periodDurationUs = getPeriod(/* periodIndex= */ i, period).durationUs; + if (i == periodOffsets[windowIndex] && periodDurationUs != 0) { + windowDurationUs -= windowDefinition.windowOffsetInFirstPeriodUs; + } + if (periodDurationUs == C.TIME_UNSET) { + windowDurationUs = C.TIME_UNSET; + break; + } + windowDurationUs += periodDurationUs; + } window.set( /* uid= */ windowDefinition.id, windowDefinition.mediaItem, @@ -376,7 +482,7 @@ public final class FakeTimeline extends Timeline { windowDefinition.isDynamic, windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null, windowDefinition.defaultPositionUs, - windowDefinition.durationUs, + windowDurationUs, periodOffsets[windowIndex], periodOffsets[windowIndex + 1] - 1, windowDefinition.windowOffsetInFirstPeriodUs); @@ -396,11 +502,15 @@ public final class FakeTimeline extends Timeline { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; Object id = setIds ? windowPeriodIndex : null; Object uid = setIds ? Pair.create(windowDefinition.id, windowPeriodIndex) : null; + AdPlaybackState adPlaybackState = + windowDefinition.adPlaybackStates.get( + periodIndex % windowDefinition.adPlaybackStates.size()); // Arbitrarily set period duration by distributing window duration equally among all periods. long periodDurationUs = - windowDefinition.durationUs == C.TIME_UNSET + periodIndex == windowDefinition.periodCount - 1 + && windowDefinition.durationUs == C.TIME_UNSET ? C.TIME_UNSET - : windowDefinition.durationUs / windowDefinition.periodCount; + : (windowDefinition.durationUs / windowDefinition.periodCount); long positionInWindowUs; if (windowPeriodIndex == 0) { if (windowDefinition.durationUs != C.TIME_UNSET) { @@ -414,9 +524,11 @@ public final class FakeTimeline extends Timeline { id, uid, windowIndex, - periodDurationUs, + periodDurationUs == C.TIME_UNSET + ? C.TIME_UNSET + : periodDurationUs - getServerSideAdInsertionAdDurationUs(adPlaybackState), positionInWindowUs, - windowDefinition.adPlaybackState, + adPlaybackState, windowDefinition.isPlaceholder); return period; } @@ -442,6 +554,22 @@ public final class FakeTimeline extends Timeline { return Pair.create(windowDefinition.id, windowPeriodIndex); } + /** + * Returns a map of ad playback states keyed by the period UID. + * + * @param windowIndex The window index of the window to get the map of ad playback states from. + * @return The map of {@link AdPlaybackState ad playback states}. + */ + public ImmutableMap getAdPlaybackStates(int windowIndex) { + Map adPlaybackStateMap = new HashMap<>(); + TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; + for (int i = 0; i < windowDefinition.adPlaybackStates.size(); i++) { + adPlaybackStateMap.put( + new Pair<>(windowDefinition.id, i), windowDefinition.adPlaybackStates.get(i)); + } + return ImmutableMap.copyOf(adPlaybackStateMap); + } + private static TimelineWindowDefinition[] createDefaultWindowDefinitions(int windowCount) { TimelineWindowDefinition[] windowDefinitions = new TimelineWindowDefinition[windowCount]; for (int i = 0; i < windowCount; i++) { @@ -449,4 +577,15 @@ public final class FakeTimeline extends Timeline { } return windowDefinitions; } + + private static long getServerSideAdInsertionAdDurationUs(AdPlaybackState adPlaybackState) { + long adDurationUs = 0; + for (int i = 0; i < adPlaybackState.adGroupCount; i++) { + AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); + if (adGroup.isServerSideInserted) { + adDurationUs += sum(adGroup.durationsUs); + } + } + return adDurationUs; + } } diff --git a/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java new file mode 100644 index 0000000000..cebf4ba06c --- /dev/null +++ b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.test.utils; + +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.AdPlaybackState; +import androidx.media3.common.Timeline; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link FakeTimeline}. */ +@RunWith(AndroidJUnit4.class) +public class FakeTimelineTest { + + @Test + public void createMultiPeriodAdTimeline_firstPeriodIsAd() { + Timeline.Window window = new Timeline.Window(); + Timeline.Period period = new Timeline.Period(); + Object windowId = new Object(); + int numberOfPlayedAds = 2; + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + windowId, + numberOfPlayedAds, + /* isAdPeriodFlags...= */ true, + false, + true, + true, + true, + false, + true); + + assertThat(timeline.getWindowCount()).isEqualTo(1); + assertThat(timeline.getPeriodCount()).isEqualTo(7); + // Assert content periods and window duration. + Timeline.Period contentPeriod1 = timeline.getPeriod(/* periodIndex= */ 1, period); + Timeline.Period contentPeriod5 = timeline.getPeriod(/* periodIndex= */ 5, period); + assertThat(contentPeriod1.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 7); + assertThat(contentPeriod5.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 7); + assertThat(contentPeriod1.getAdGroupCount()).isEqualTo(0); + assertThat(contentPeriod5.getAdGroupCount()).isEqualTo(0); + timeline.getWindow(/* windowIndex= */ 0, window); + assertThat(window.uid).isEqualTo(windowId); + assertThat(window.durationUs).isEqualTo(contentPeriod1.durationUs + contentPeriod5.durationUs); + assertThat(window.positionInFirstPeriodUs).isEqualTo(DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US); + // Assert ad periods. + int[] adIndices = {0, 2, 3, 4, 6}; + int adCounter = 0; + for (int periodIndex : adIndices) { + Timeline.Period adPeriod = timeline.getPeriod(periodIndex, period); + assertThat(adPeriod.isServerSideInsertedAdGroup(0)).isTrue(); + assertThat(adPeriod.getAdGroupCount()).isEqualTo(1); + assertThat(adPeriod.durationUs).isEqualTo(0); + if (adPeriod.getAdGroupCount() > 0) { + if (adCounter < numberOfPlayedAds) { + assertThat(adPeriod.getAdState(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) + .isEqualTo(AdPlaybackState.AD_STATE_PLAYED); + } else { + assertThat(adPeriod.getAdState(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) + .isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + } + adCounter++; + } + long expectedDurationUs = + (DEFAULT_WINDOW_DURATION_US / 7) + + (periodIndex == 0 ? DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US : 0); + assertThat(adPeriod.getAdDurationUs(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) + .isEqualTo(expectedDurationUs); + } + } + + @Test + public void createMultiPeriodAdTimeline_firstPeriodIsContent_correctWindowDurationUs() { + Timeline.Window window = new Timeline.Window(); + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline( + /* windowId= */ new Object(), + /* numberOfPlayedAds= */ 0, + /* isAdPeriodFlags...= */ false, + true, + true, + false); + + timeline.getWindow(/* windowIndex= */ 0, window); + // Assert content periods and window duration. + assertThat(window.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 2); + assertThat(window.positionInFirstPeriodUs).isEqualTo(DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US); + } +} From 40a5c0127512fd9ff7ddc68f5f265589f22fd336 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Fri, 11 Feb 2022 17:07:26 +0000 Subject: [PATCH 201/251] Add helpers for FBOs in GlUtil. FBOs will be used in follow-up CLs to chain multiple GL shader programs in Transformer. PiperOrigin-RevId: 428016050 --- .../androidx/media3/common/util/GlUtil.java | 116 ++++++++++++++---- .../media3/transformer/FrameEditor.java | 4 +- .../VideoTranscodingSamplePipeline.java | 2 +- 3 files changed, 96 insertions(+), 26 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index b763c6b620..16b040dc47 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -35,7 +35,6 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; -import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGL10; /** OpenGL ES utilities. */ @@ -224,13 +223,29 @@ public final class GlUtil { } /** - * Makes the specified {@code surface} the render target, using a viewport of {@code width} by + * Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by * {@code height} pixels. */ @RequiresApi(17) - public static void focusSurface( - EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) { - Api17.focusSurface(eglDisplay, eglContext, surface, width, height); + public static void focusEglSurface( + EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height) { + Api17.focusRenderTarget( + eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height); + } + + /** + * Makes the specified {@code framebuffer} the render target, using a viewport of {@code width} by + * {@code height} pixels. + */ + @RequiresApi(17) + public static void focusFramebuffer( + EGLDisplay eglDisplay, + EGLContext eglContext, + EGLSurface eglSurface, + int framebuffer, + int width, + int height) { + Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height); } /** @@ -295,21 +310,72 @@ public final class GlUtil { * GL_CLAMP_TO_EDGE wrapping. */ public static int createExternalTexture() { + return generateAndBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + } + + /** + * Returns the texture identifier for a newly-allocated texture with the specified dimensions. + * + * @param width of the new texture in pixels + * @param height of the new texture in pixels + */ + public static int createTexture(int width, int height) { + int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D); + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4); + GLES20.glTexImage2D( + GLES20.GL_TEXTURE_2D, + /* level= */ 0, + GLES20.GL_RGBA, + width, + height, + /* border= */ 0, + GLES20.GL_RGBA, + GLES20.GL_UNSIGNED_BYTE, + byteBuffer); + checkGlError(); + return texId; + } + + /** + * Returns a GL texture identifier of a newly generated and bound texture of the requested type + * with default configuration of GL_LINEAR filtering and GL_CLAMP_TO_EDGE wrapping. + * + * @param textureTarget The target to which the texture is bound, e.g. {@link + * GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link + * GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture. + */ + private static int generateAndBindTexture(int textureTarget) { int[] texId = new int[1]; - GLES20.glGenTextures(/* n= */ 1, IntBuffer.wrap(texId)); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId[0]); - GLES20.glTexParameteri( - GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri( - GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri( - GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri( - GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0); + checkGlError(); + GLES20.glBindTexture(textureTarget, texId[0]); + checkGlError(); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + checkGlError(); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + checkGlError(); + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); checkGlError(); return texId[0]; } + /** + * Returns a new framebuffer for the texture. + * + * @param texId The identifier of the texture to attach to the framebuffer. + */ + public static int createFboForTexture(int texId) { + int[] fboId = new int[1]; + GLES20.glGenFramebuffers(/* n= */ 1, fboId, /* offset= */ 0); + checkGlError(); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId[0]); + checkGlError(); + GLES20.glFramebufferTexture2D( + GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texId, 0); + checkGlError(); + return fboId[0]; + } + /* package */ static void throwGlException(String errorMsg) { Log.e(TAG, errorMsg); if (glAssertionsEnabled) { @@ -380,15 +446,19 @@ public final class GlUtil { } @DoNotInline - public static void focusSurface( - EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) { - int[] boundFrameBuffer = new int[1]; - GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFrameBuffer, /* offset= */ 0); - int defaultFrameBuffer = 0; - if (boundFrameBuffer[0] != defaultFrameBuffer) { - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, defaultFrameBuffer); + public static void focusRenderTarget( + EGLDisplay eglDisplay, + EGLContext eglContext, + EGLSurface eglSurface, + int framebuffer, + int width, + int height) { + int[] boundFramebuffer = new int[1]; + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0); + if (boundFramebuffer[0] != framebuffer) { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer); } - EGL14.eglMakeCurrent(eglDisplay, surface, surface, eglContext); + EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index 57f8be3a1f..9bcc97def0 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -109,7 +109,7 @@ import java.util.concurrent.atomic.AtomicInteger; } } - GlUtil.focusSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight); + GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight); textureId = GlUtil.createExternalTexture(); glProgram = configureGlProgram( @@ -351,7 +351,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** Focuses the specified surface with the specified width and height, then draws a quad. */ private void focusAndDrawQuad(EGLSurface eglSurface, int width, int height) { - GlUtil.focusSurface(eglDisplay, eglContext, eglSurface, width, height); + GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, width, height); // The four-vertex triangle strip forms a quad. GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 80bbeea2e6..6362378472 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -152,7 +152,7 @@ import org.checkerframework.dataflow.qual.Pure; fallbackListener.onTransformationRequestFinalized( createFallbackTransformationRequest( transformationRequest, - !swapEncodingDimensions, + /* resolutionIsHeight= */ !swapEncodingDimensions, requestedEncoderFormat, encoderSupportedFormat)); From 437e178ef8fb1d9e345e0eb41544451b390fff1f Mon Sep 17 00:00:00 2001 From: christosts Date: Fri, 11 Feb 2022 17:48:57 +0000 Subject: [PATCH 202/251] Create MediaNotification.Provider Define MediaNotification.Provider so that apps can customize notification UX. Move MediaNotificationManager's functionality around notifications on DefaultMediaNotificationProvider PiperOrigin-RevId: 428024699 --- .../media3/session/DefaultActionFactory.java | 149 ++++++++++ .../DefaultMediaNotificationProvider.java | 173 ++++++++++++ .../media3/session/MediaNotification.java | 190 +++++++++++++ .../session/MediaNotificationManager.java | 263 +++++------------- .../media3/session/MediaSessionService.java | 164 +++++------ .../session/DefaultActionFactoryTest.java | 64 +++++ 6 files changed, 730 insertions(+), 273 deletions(-) create mode 100644 libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java create mode 100644 libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java create mode 100644 libraries/session/src/main/java/androidx/media3/session/MediaNotification.java create mode 100644 libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java new file mode 100644 index 0000000000..fba9b917a9 --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java @@ -0,0 +1,149 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.media.session.PlaybackStateCompat; +import android.view.KeyEvent; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; +import androidx.core.graphics.drawable.IconCompat; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; + +/** The default {@link MediaNotification.ActionFactory}. */ +@UnstableApi +/* package */ final class DefaultActionFactory implements MediaNotification.ActionFactory { + + private static final String ACTION_CUSTOM = "androidx.media3.session.CUSTOM_NOTIFICATION_ACTION"; + private static final String EXTRAS_KEY_ACTION_CUSTOM = + "androidx.media3.session.EXTRAS_KEY_CUSTOM_NOTIFICATION_ACTION"; + public static final String EXTRAS_KEY_ACTION_CUSTOM_EXTRAS = + "androidx.media3.session.EXTRAS_KEY_CUSTOM_NOTIFICATION_ACTION_EXTRAS"; + + private final Context context; + + public DefaultActionFactory(Context context) { + this.context = context.getApplicationContext(); + } + + @Override + public NotificationCompat.Action createMediaAction( + IconCompat icon, CharSequence title, @Command long command) { + return new NotificationCompat.Action(icon, title, createMediaActionPendingIntent(command)); + } + + @Override + public NotificationCompat.Action createCustomAction( + IconCompat icon, CharSequence title, String customAction, Bundle extras) { + return new NotificationCompat.Action( + icon, title, createCustomActionPendingIntent(customAction, extras)); + } + + @Override + public PendingIntent createMediaActionPendingIntent(@Command long command) { + int keyCode = PlaybackStateCompat.toKeyCode(command); + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.setComponent(new ComponentName(context, context.getClass())); + intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); + if (Util.SDK_INT >= 26 && command != COMMAND_PAUSE && command != COMMAND_STOP) { + return Api26.createPendingIntent(context, /* requestCode= */ keyCode, intent); + } else { + return PendingIntent.getService( + context, + /* requestCode= */ keyCode, + intent, + Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0); + } + } + + private PendingIntent createCustomActionPendingIntent(String action, Bundle extras) { + Intent intent = new Intent(ACTION_CUSTOM); + intent.setComponent(new ComponentName(context, context.getClass())); + intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM, action); + intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM_EXTRAS, extras); + if (Util.SDK_INT >= 26) { + return Api26.createPendingIntent( + context, /* requestCode= */ KeyEvent.KEYCODE_UNKNOWN, intent); + } else { + return PendingIntent.getService( + context, + /* requestCode= */ KeyEvent.KEYCODE_UNKNOWN, + intent, + Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0); + } + } + + /** Returns whether {@code intent} was part of a {@link #createMediaAction media action}. */ + public boolean isMediaAction(Intent intent) { + return Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()); + } + + /** Returns whether {@code intent} was part of a {@link #createCustomAction custom action }. */ + public boolean isCustomAction(Intent intent) { + return ACTION_CUSTOM.equals(intent.getAction()); + } + + /** + * Returns the {@link KeyEvent} that was included in the media action, or {@code null} if no + * {@link KeyEvent} is found in the {@code intent}. + */ + @Nullable + public KeyEvent getKeyEvent(Intent intent) { + @Nullable Bundle extras = intent.getExtras(); + if (extras != null && extras.containsKey(Intent.EXTRA_KEY_EVENT)) { + return extras.getParcelable(Intent.EXTRA_KEY_EVENT); + } + return null; + } + + /** + * Returns the custom action that was included in the {@link #createCustomAction custom action}, + * or {@code null} if no custom action is found in the {@code intent}. + */ + @Nullable + public String getCustomAction(Intent intent) { + @Nullable Bundle extras = intent.getExtras(); + @Nullable Object customAction = extras != null ? extras.get(EXTRAS_KEY_ACTION_CUSTOM) : null; + return customAction instanceof String ? (String) customAction : null; + } + + /** + * Returns extras that were included in the {@link #createCustomAction custom action}, or {@link + * Bundle#EMPTY} is no extras are found. + */ + public Bundle getCustomActionExtras(Intent intent) { + @Nullable Bundle extras = intent.getExtras(); + @Nullable + Object customExtras = extras != null ? extras.get(EXTRAS_KEY_ACTION_CUSTOM_EXTRAS) : null; + return customExtras instanceof Bundle ? (Bundle) customExtras : Bundle.EMPTY; + } + + @RequiresApi(26) + private static final class Api26 { + private Api26() {} + + public static PendingIntent createPendingIntent(Context context, int keyCode, Intent intent) { + return PendingIntent.getForegroundService( + context, /* requestCode= */ keyCode, intent, PendingIntent.FLAG_IMMUTABLE); + } + } +} diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java new file mode 100644 index 0000000000..f253433905 --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -0,0 +1,173 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package androidx.media3.session; + +import static androidx.media3.common.util.Assertions.checkStateNotNull; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import androidx.core.app.NotificationCompat; +import androidx.core.graphics.drawable.IconCompat; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; + +/** + * The default {@link MediaNotification.Provider}. + * + *

    Actions

    + * + * The following actions are included in the provided notifications: + * + *
      + *
    • {@link MediaNotification.ActionFactory#COMMAND_PLAY} to start playback. Included when + * {@link MediaController#getPlayWhenReady()} returns {@code false}. + *
    • {@link MediaNotification.ActionFactory#COMMAND_PAUSE}, to pause playback. Included when + * ({@link MediaController#getPlayWhenReady()} returns {@code true}. + *
    • {@link MediaNotification.ActionFactory#COMMAND_SKIP_TO_PREVIOUS} to skip to the previous + * item. + *
    • {@link MediaNotification.ActionFactory#COMMAND_SKIP_TO_NEXT} to skip to the next item. + *
    + * + *

    Drawables

    + * + * The drawables used can be overridden by drawables with the same names defined the application. + * The drawables are: + * + *
      + *
    • {@code media3_notification_play} - The play icon. + *
    • {@code media3_notification_pause} - The pause icon. + *
    • {@code media3_notification_seek_to_previous} - The previous icon. + *
    • {@code media3_notification_seek_to_next} - The next icon. + *
    + */ +@UnstableApi +/* package */ final class DefaultMediaNotificationProvider implements MediaNotification.Provider { + + private static final int NOTIFICATION_ID = 1001; + private static final String NOTIFICATION_CHANNEL_ID = "default_channel_id"; + private static final String NOTIFICATION_CHANNEL_NAME = "Now playing"; + + private final Context context; + private final NotificationManager notificationManager; + + /** Creates an instance. */ + public DefaultMediaNotificationProvider(Context context) { + this.context = context.getApplicationContext(); + notificationManager = + checkStateNotNull( + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); + } + + @Override + public MediaNotification createNotification( + MediaController mediaController, + MediaNotification.ActionFactory actionFactory, + Callback onNotificationChangedCallback) { + ensureNotificationChannel(); + + NotificationCompat.Builder builder = + new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); + // TODO(b/193193926): Filter actions depending on the player's available commands. + // Skip to previous action. + builder.addAction( + actionFactory.createMediaAction( + IconCompat.createWithResource(context, R.drawable.media3_notification_seek_to_previous), + context.getString(R.string.media3_controls_seek_to_previous_description), + MediaNotification.ActionFactory.COMMAND_SKIP_TO_PREVIOUS)); + if (mediaController.getPlayWhenReady()) { + // Pause action. + builder.addAction( + actionFactory.createMediaAction( + IconCompat.createWithResource(context, R.drawable.media3_notification_pause), + context.getString(R.string.media3_controls_pause_description), + MediaNotification.ActionFactory.COMMAND_PAUSE)); + } else { + // Play action. + builder.addAction( + actionFactory.createMediaAction( + IconCompat.createWithResource(context, R.drawable.media3_notification_play), + context.getString(R.string.media3_controls_play_description), + MediaNotification.ActionFactory.COMMAND_PLAY)); + } + // Skip to next action. + builder.addAction( + actionFactory.createMediaAction( + IconCompat.createWithResource(context, R.drawable.media3_notification_seek_to_next), + context.getString(R.string.media3_controls_seek_to_next_description), + MediaNotification.ActionFactory.COMMAND_SKIP_TO_NEXT)); + + // Set metadata info in the notification. + MediaMetadata metadata = mediaController.getMediaMetadata(); + builder.setContentTitle(metadata.title).setContentText(metadata.artist); + if (metadata.artworkData != null) { + Bitmap artworkBitmap = + BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData.length); + builder.setLargeIcon(artworkBitmap); + } + + androidx.media.app.NotificationCompat.MediaStyle mediaStyle = + new androidx.media.app.NotificationCompat.MediaStyle() + .setCancelButtonIntent( + actionFactory.createMediaActionPendingIntent( + MediaNotification.ActionFactory.COMMAND_STOP)) + .setShowActionsInCompactView(1 /* Show play/pause button only in compact view */); + + Notification notification = + builder + .setContentIntent(mediaController.getSessionActivity()) + .setDeleteIntent( + actionFactory.createMediaActionPendingIntent( + MediaNotification.ActionFactory.COMMAND_STOP)) + .setOnlyAlertOnce(true) + .setSmallIcon(getSmallIconResId(context)) + .setStyle(mediaStyle) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setOngoing(false) + .build(); + return new MediaNotification(NOTIFICATION_ID, notification); + } + + @Override + public void handleCustomAction(MediaController mediaController, String action, Bundle extras) { + // We don't handle custom commands. + } + + private void ensureNotificationChannel() { + if (Util.SDK_INT < 26 + || notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null) { + return; + } + NotificationChannel channel = + new NotificationChannel( + NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + notificationManager.createNotificationChannel(channel); + } + + private static int getSmallIconResId(Context context) { + int appIcon = context.getApplicationInfo().icon; + if (appIcon != 0) { + return appIcon; + } else { + return Util.SDK_INT >= 21 ? R.drawable.media_session_service_notification_ic_music_note : 0; + } + } +} diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java new file mode 100644 index 0000000000..698ab1efb4 --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java @@ -0,0 +1,190 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package androidx.media3.session; + +import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE_USE; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.os.Bundle; +import android.support.v4.media.session.PlaybackStateCompat; +import androidx.annotation.IntRange; +import androidx.annotation.LongDef; +import androidx.core.app.NotificationCompat; +import androidx.core.graphics.drawable.IconCompat; +import androidx.media3.common.util.UnstableApi; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** A notification for media playbacks. */ +public final class MediaNotification { + + /** + * Creates {@link NotificationCompat.Action actions} and {@link PendingIntent pending intents} for + * notifications. + */ + @UnstableApi + public interface ActionFactory { + + /** + * Commands that can be included in a media action. One of {@link #COMMAND_PLAY}, {@link + * #COMMAND_PAUSE}, {@link #COMMAND_STOP}, {@link #COMMAND_REWIND}, {@link + * #COMMAND_FAST_FORWARD}, {@link #COMMAND_SKIP_TO_PREVIOUS}, {@link #COMMAND_SKIP_TO_NEXT} or + * {@link #COMMAND_SET_CAPTIONING_ENABLED}. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target({TYPE_USE}) + @LongDef({ + COMMAND_PLAY, + COMMAND_PAUSE, + COMMAND_STOP, + COMMAND_REWIND, + COMMAND_FAST_FORWARD, + COMMAND_SKIP_TO_PREVIOUS, + COMMAND_SKIP_TO_NEXT, + COMMAND_SET_CAPTIONING_ENABLED + }) + @interface Command {} + + /** The command to start playback. */ + long COMMAND_PLAY = PlaybackStateCompat.ACTION_PLAY; + /** The command to pause playback. */ + long COMMAND_PAUSE = PlaybackStateCompat.ACTION_PAUSE; + /** The command to stop playback. */ + long COMMAND_STOP = PlaybackStateCompat.ACTION_STOP; + /** The command to rewind. */ + long COMMAND_REWIND = PlaybackStateCompat.ACTION_REWIND; + /** The command to fast forward. */ + long COMMAND_FAST_FORWARD = PlaybackStateCompat.ACTION_FAST_FORWARD; + /** The command to skip to the previous item in the queue. */ + long COMMAND_SKIP_TO_PREVIOUS = PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; + /** The command to skip to the next item in the queue. */ + long COMMAND_SKIP_TO_NEXT = PlaybackStateCompat.ACTION_SKIP_TO_NEXT; + /** The command to set captioning enabled. */ + long COMMAND_SET_CAPTIONING_ENABLED = PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED; + + /** + * Creates a {@link NotificationCompat.Action} for a notification. These actions will be handled + * by the library. + * + * @param icon The icon to show for this action. + * @param title The title of the action. + * @param command A command to send when users trigger this action. + */ + NotificationCompat.Action createMediaAction( + IconCompat icon, CharSequence title, @Command long command); + + /** + * Creates a {@link NotificationCompat.Action} for a notification with a custom action. Actions + * created with this method are not expected to be handled by the library and will be forwarded + * to the {@link MediaNotification.Provider#handleCustomAction notification provider} that + * provided them. + * + * @param icon The icon to show for this action. + * @param title The title of the action. + * @param customAction The custom action set. + * @param extras Extras to be included in the action. + * @see MediaNotification.Provider#handleCustomAction + */ + NotificationCompat.Action createCustomAction( + IconCompat icon, CharSequence title, String customAction, Bundle extras); + + /** + * Creates a {@link PendingIntent} for a media action that will be handled by the library. + * + * @param command The intent's command. + */ + PendingIntent createMediaActionPendingIntent(@Command long command); + } + + /** + * Provides {@link MediaNotification media notifications} to be posted as notifications that + * reflect the state of a {@link MediaController} and to send media commands to a {@link + * MediaSession}. + * + *

    The provider is required to create a {@link androidx.core.app.NotificationChannelCompat + * notification channel}, which is required to show notification for {@code SDK_INT >= 26}. + */ + @UnstableApi + public interface Provider { + /** Receives updates for a notification. */ + interface Callback { + /** + * Called when a {@link MediaNotification} is changed. + * + *

    This callback is called when notifications are updated, for example after a bitmap is + * loaded asynchronously and needs to be displayed. + * + * @param notification The updated {@link MediaNotification} + */ + void onNotificationChanged(MediaNotification notification); + } + + /** + * Creates a new {@link MediaNotification}. + * + * @param mediaController The controller of the session. + * @param actionFactory The {@link ActionFactory} for creating notification {@link + * NotificationCompat.Action actions}. + * @param onNotificationChangedCallback A callback that the provider needs to notify when the + * notification has changed and needs to be posted again, for example after a bitmap has + * been loaded asynchronously. + */ + MediaNotification createNotification( + MediaController mediaController, + ActionFactory actionFactory, + Callback onNotificationChangedCallback); + + /** + * Handles a notification's custom action. + * + * @param mediaController The controller of the session. + * @param action The custom action. + * @param extras Extras set in the custom action, otherwise {@link Bundle#EMPTY}. + * @see ActionFactory#createCustomAction + */ + void handleCustomAction(MediaController mediaController, String action, Bundle extras); + } + + /** The notification id. */ + @IntRange(from = 1) + public final int notificationId; + + /** The {@link Notification}. */ + public final Notification notification; + + /** + * Creates an instance. + * + * @param notificationId The notification id to be used for {@link NotificationManager#notify(int, + * Notification)}. + * @param notification A {@link Notification} that reflects the sate of a {@link MediaController} + * and to send media commands to a {@link MediaSession}. The notification may be used to start + * a service in the foreground. + * It's highly recommended to use a {@link androidx.media.app.NotificationCompat.MediaStyle + * media style} {@link Notification notification}. + */ + public MediaNotification(@IntRange(from = 1) int notificationId, Notification notification) { + this.notificationId = notificationId; + this.notification = checkNotNull(notification); + } +} diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java index 50555885d3..c89ebe092d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -15,30 +15,14 @@ */ package androidx.media3.session; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP; - import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.v4.media.session.PlaybackStateCompat; -import android.view.KeyEvent; import androidx.annotation.Nullable; -import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; -import androidx.media.app.NotificationCompat.MediaStyle; -import androidx.media3.common.MediaMetadata; import androidx.media3.common.Player; import androidx.media3.common.util.Util; import com.google.common.util.concurrent.ListenableFuture; @@ -52,63 +36,33 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** - * Provides default media notifications for {@link MediaSessionService} and sets the service as + * Manages media notifications for a {@link MediaSessionService} and sets the service as * foreground/background according to the player state. */ /* package */ final class MediaNotificationManager { - private static final int NOTIFICATION_ID = 1001; - private static final String NOTIFICATION_CHANNEL_ID = "default_channel_id"; - - private final MediaSessionService service; + private final MediaSessionService mediaSessionService; + private final MediaNotification.Provider mediaNotificationProvider; + private final MediaNotification.ActionFactory actionFactory; + private final NotificationManagerCompat notificationManagerCompat; private final Executor mainExecutor; - private final NotificationManager notificationManager; - private final String notificationChannelName; - private final Intent startSelfIntent; - private final NotificationCompat.Action playAction; - private final NotificationCompat.Action pauseAction; - private final NotificationCompat.Action skipToPrevAction; - private final NotificationCompat.Action skipToNextAction; - private final Map> controllerMap; - public MediaNotificationManager(MediaSessionService service) { - this.service = service; + private int totalNotificationCount; + + public MediaNotificationManager( + MediaSessionService mediaSessionService, + MediaNotification.Provider mediaNotificationProvider, + MediaNotification.ActionFactory actionFactory) { + this.mediaSessionService = mediaSessionService; + this.mediaNotificationProvider = mediaNotificationProvider; + this.actionFactory = actionFactory; + notificationManagerCompat = NotificationManagerCompat.from(mediaSessionService); Handler mainHandler = new Handler(Looper.getMainLooper()); mainExecutor = (runnable) -> Util.postOrRun(mainHandler, runnable); - startSelfIntent = new Intent(service, service.getClass()); + startSelfIntent = new Intent(mediaSessionService, mediaSessionService.getClass()); controllerMap = new HashMap<>(); - - notificationManager = - (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); - notificationChannelName = - service.getResources().getString(R.string.default_notification_channel_name); - - playAction = - createNotificationAction( - service, - android.R.drawable.ic_media_play, - R.string.media3_controls_play_description, - ACTION_PLAY); - pauseAction = - createNotificationAction( - service, - android.R.drawable.ic_media_pause, - R.string.media3_controls_pause_description, - ACTION_PAUSE); - skipToPrevAction = - createNotificationAction( - service, - android.R.drawable.ic_media_previous, - R.string.media3_controls_seek_to_previous_description, - ACTION_SKIP_TO_PREVIOUS); - skipToNextAction = - createNotificationAction( - service, - android.R.drawable.ic_media_next, - R.string.media3_controls_seek_to_next_description, - ACTION_SKIP_TO_NEXT); } public void addSession(MediaSession session) { @@ -117,26 +71,22 @@ import java.util.concurrent.TimeoutException; } MediaControllerListener listener = new MediaControllerListener(session); ListenableFuture controllerFuture = - new MediaController.Builder(service, session.getToken()) + new MediaController.Builder(mediaSessionService, session.getToken()) .setListener(listener) .setApplicationLooper(Looper.getMainLooper()) .buildAsync(); controllerFuture.addListener( () -> { - MediaController controller; try { - controller = controllerFuture.get(/* time= */ 0, TimeUnit.MILLISECONDS); + MediaController controller = controllerFuture.get(/* time= */ 0, TimeUnit.MILLISECONDS); + listener.onConnected(); + controller.addListener(listener); } catch (CancellationException | ExecutionException | InterruptedException | TimeoutException e) { - // MediaSession or MediaController is released too early. Stop monitoring session. - service.removeSession(session); - return; - } - if (controller != null) { - listener.onConnected(); - controller.addListener(listener); + // MediaSession or MediaController is released too early. Stop monitoring the session. + mediaSessionService.removeSession(session); } }, mainExecutor); @@ -144,21 +94,59 @@ import java.util.concurrent.TimeoutException; } public void removeSession(MediaSession session) { - ListenableFuture controllerFuture = controllerMap.remove(session); + @Nullable ListenableFuture controllerFuture = controllerMap.remove(session); if (controllerFuture != null) { MediaController.releaseFuture(controllerFuture); } } - private void updateNotificationIfNeeded(MediaSession session) { - @Nullable - MediaSessionService.MediaNotification mediaNotification = service.onUpdateNotification(session); - if (mediaNotification == null) { - // The service implementation doesn't want to use the automatic start/stopForeground - // feature. + public void onCustomAction(MediaSession session, String action, Bundle extras) { + @Nullable ListenableFuture controllerFuture = controllerMap.get(session); + if (controllerFuture == null) { + return; + } + try { + MediaController mediaController = controllerFuture.get(0, TimeUnit.MILLISECONDS); + mediaNotificationProvider.handleCustomAction(mediaController, action, extras); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + // We should never reach this. + throw new IllegalStateException(e); + } + } + + private void updateNotification(MediaSession session) { + @Nullable ListenableFuture controllerFuture = controllerMap.get(session); + if (controllerFuture == null) { return; } + MediaController mediaController; + try { + mediaController = controllerFuture.get(0, TimeUnit.MILLISECONDS); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + // We should never reach this point. + throw new IllegalStateException(e); + } + + int notificationSequence = ++this.totalNotificationCount; + MediaNotification.Provider.Callback callback = + notification -> + mainExecutor.execute( + () -> onNotificationUpdated(notificationSequence, session, notification)); + + MediaNotification mediaNotification = + this.mediaNotificationProvider.createNotification(mediaController, actionFactory, callback); + updateNotification(session, mediaNotification); + } + + private void onNotificationUpdated( + int notificationSequence, MediaSession session, MediaNotification mediaNotification) { + if (notificationSequence == this.totalNotificationCount) { + updateNotification(session, mediaNotification); + } + } + + private void updateNotification(MediaSession session, MediaNotification mediaNotification) { int id = mediaNotification.notificationId; Notification notification = mediaNotification.notification; @@ -172,16 +160,16 @@ import java.util.concurrent.TimeoutException; Player player = session.getPlayer(); if (player.getPlayWhenReady()) { - ContextCompat.startForegroundService(service, startSelfIntent); - service.startForeground(id, notification); + ContextCompat.startForegroundService(mediaSessionService, startSelfIntent); + mediaSessionService.startForeground(id, notification); } else { stopForegroundServiceIfNeeded(); - notificationManager.notify(id, notification); + notificationManagerCompat.notify(id, notification); } } private void stopForegroundServiceIfNeeded() { - List sessions = service.getSessions(); + List sessions = mediaSessionService.getSessions(); for (int i = 0; i < sessions.size(); i++) { Player player = sessions.get(i).getPlayer(); if (player.getPlayWhenReady()) { @@ -191,104 +179,7 @@ import java.util.concurrent.TimeoutException; // Calling stopForeground(true) is a workaround for pre-L devices which prevents // the media notification from being undismissable. boolean shouldRemoveNotification = Util.SDK_INT < 21; - service.stopForeground(shouldRemoveNotification); - } - - /** Creates a default media style notification for {@link MediaSessionService}. */ - public MediaSessionService.MediaNotification onUpdateNotification(MediaSession session) { - Player player = session.getPlayer(); - - ensureNotificationChannel(); - - NotificationCompat.Builder builder = - new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID); - - // TODO(b/193193926): Filter actions depending on the player's available commands. - builder.addAction(skipToPrevAction); - if (player.getPlayWhenReady()) { - builder.addAction(pauseAction); - } else { - builder.addAction(playAction); - } - builder.addAction(skipToNextAction); - - // Set metadata info in the notification. - MediaMetadata metadata = player.getMediaMetadata(); - builder.setContentTitle(metadata.title).setContentText(metadata.artist); - if (metadata.artworkData != null) { - Bitmap artworkBitmap = - BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData.length); - builder.setLargeIcon(artworkBitmap); - } - - MediaStyle mediaStyle = - new MediaStyle() - .setCancelButtonIntent(createPendingIntent(service, ACTION_STOP)) - .setMediaSession(session.getSessionCompat().getSessionToken()) - .setShowActionsInCompactView(1 /* Show play/pause button only in compact view */); - - Notification notification = - builder - .setContentIntent(session.getImpl().getSessionActivity()) - .setDeleteIntent(createPendingIntent(service, ACTION_STOP)) - .setOnlyAlertOnce(true) - .setSmallIcon(getSmallIconResId()) - .setStyle(mediaStyle) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setOngoing(false) - .build(); - - return new MediaSessionService.MediaNotification(NOTIFICATION_ID, notification); - } - - private void ensureNotificationChannel() { - if (Util.SDK_INT < 26 - || notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null) { - return; - } - // Need to create a notification channel. - NotificationChannel channel = - new NotificationChannel( - NOTIFICATION_CHANNEL_ID, notificationChannelName, NotificationManager.IMPORTANCE_LOW); - notificationManager.createNotificationChannel(channel); - } - - private int getSmallIconResId() { - int appIcon = service.getApplicationInfo().icon; - if (appIcon != 0) { - return appIcon; - } else { - // App icon is not set. - return Util.SDK_INT >= 21 ? R.drawable.media_session_service_notification_ic_music_note : 0; - } - } - - private static NotificationCompat.Action createNotificationAction( - MediaSessionService service, - int iconResId, - int titleResId, - @PlaybackStateCompat.Actions long action) { - CharSequence title = service.getResources().getText(titleResId); - return new NotificationCompat.Action(iconResId, title, createPendingIntent(service, action)); - } - - private static PendingIntent createPendingIntent( - MediaSessionService service, @PlaybackStateCompat.Actions long action) { - int keyCode = PlaybackStateCompat.toKeyCode(action); - Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); - intent.setComponent(new ComponentName(service, service.getClass())); - intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); - - if (Util.SDK_INT >= 26 && action != ACTION_PAUSE && action != ACTION_STOP) { - return PendingIntent.getForegroundService( - service, /* requestCode= */ keyCode, intent, PendingIntent.FLAG_IMMUTABLE); - } else { - return PendingIntent.getService( - service, - /* requestCode= */ keyCode, - intent, - Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0); - } + mediaSessionService.stopForeground(shouldRemoveNotification); } private final class MediaControllerListener implements MediaController.Listener, Player.Listener { @@ -299,20 +190,20 @@ import java.util.concurrent.TimeoutException; } public void onConnected() { - updateNotificationIfNeeded(session); + updateNotification(session); } @Override public void onEvents(Player player, Player.Events events) { if (events.containsAny( Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_MEDIA_METADATA_CHANGED)) { - updateNotificationIfNeeded(session); + updateNotification(session); } } @Override public void onDisconnected(MediaController controller) { - service.removeSession(session); + mediaSessionService.removeSession(session); stopForegroundServiceIfNeeded(); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index 3eedcec7e7..cc840c534a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -20,8 +20,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.postOrRun; -import android.app.Notification; -import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -35,13 +33,12 @@ import android.os.RemoteException; import android.view.KeyEvent; import androidx.annotation.CallSuper; import androidx.annotation.GuardedBy; -import androidx.annotation.IntRange; import androidx.annotation.Nullable; import androidx.collection.ArrayMap; import androidx.media.MediaBrowserServiceCompat; import androidx.media.MediaSessionManager; -import androidx.media3.common.Player; import androidx.media3.common.util.Log; +import androidx.media3.common.util.UnstableApi; import androidx.media3.session.MediaSession.ControllerInfo; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -50,16 +47,17 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Superclass to be extended by services hosting {@link MediaSession media sessions}. * *

    It's highly recommended for an app to use this class if they want to keep media playback in * the background. The service allows other apps to know that your app supports {@link MediaSession} - * even when your app isn't running. For example, user's voice command may start your app to play + * even when your app isn't running. For example, a user voice command may start your app to play * media. * - *

    To extend this class, declare the intent filter in your {@code AndroidManifest.xml}. + *

    To extend this class, declare the intent filter in your {@code AndroidManifest.xml}: * *

    {@code
      * 
    @@ -89,20 +87,21 @@ import java.util.concurrent.ConcurrentHashMap;
      *
      * 

    A media session service is a bound service. When a {@link MediaController} is created for the * service, the controller binds to the service. {@link #onGetSession(ControllerInfo)} will be - * called inside of the {@link #onBind(Intent)}. + * called from {@link #onBind(Intent)}. * - *

    After the binding, the session's {@link MediaSession.SessionCallback#onConnect(MediaSession, + *

    After binding, the session's {@link MediaSession.SessionCallback#onConnect(MediaSession, * MediaSession.ControllerInfo)} will be called to accept or reject the connection request from the * controller. If it's accepted, the controller will be available and keep the binding. If it's * rejected, the controller will unbind. * - *

    When a playback is started on the service, {@link #onUpdateNotification(MediaSession)} is - * called and the service will become a foreground service. - * It's required to keep the playback after the controller is destroyed. The service will become a - * background service when all playbacks are stopped. Apps targeting {@code SDK_INT >= 28} must - * request the permission, {@link android.Manifest.permission#FOREGROUND_SERVICE}, in order to make - * the service foreground. + *

    When a playback is started on the service, the service will obtain a {@link MediaNotification} + * from the {@link MediaNotification.Provider} that's set with {@link #setMediaNotificationProvider} + * (or {@link DefaultMediaNotificationProvider}, if no provider is set), and the service will become + * a foreground + * service. It's required to keep the playback after the controller is destroyed. The service + * will become a background service when all playbacks are stopped. Apps targeting {@code SDK_INT >= + * 28} must request the permission, {@link android.Manifest.permission#FOREGROUND_SERVICE}, in order + * to make the service foreground. * *

    The service will be destroyed when all sessions are closed, or no controller is binding to the * service while the service is in the background. @@ -110,18 +109,18 @@ import java.util.concurrent.ConcurrentHashMap; *

    Supporting Multiple Sessions

    * *

    Generally, multiple sessions aren't necessary for most media apps. One exception is if your - * app can play multiple media content at the same time, but only for the playback of video-only - * media or remote playback, since the audio focus policy - * recommends not playing multiple audio content at the same time. Also, keep in mind that multiple - * media sessions would make Android Auto and Bluetooth device with display to show your apps + * recommends not playing multiple audio contents at the same time. Also, keep in mind that multiple + * media sessions would make Android Auto and Bluetooth devices with a display to show your apps * multiple times, because they list up media sessions, not media apps. * *

    However, if you're capable of handling multiple playbacks and want to keep their sessions * while the app is in the background, create multiple sessions and add them to this service with * {@link #addSession(MediaSession)}. * - *

    Note that {@link MediaController} can be created with {@link SessionToken} to connect to a + *

    Note that a {@link MediaController} can be created with {@link SessionToken} to connect to a * session in this service. In that case, {@link #onGetSession(ControllerInfo)} will be called to * decide which session to handle the connection request. Pick the best session among the added * sessions, or create a new session and return it from {@link #onGetSession(ControllerInfo)}. @@ -144,8 +143,13 @@ public abstract class MediaSessionService extends Service { private MediaSessionServiceStub stub; @GuardedBy("lock") - @Nullable - private MediaNotificationManager notificationHandler; + private @MonotonicNonNull MediaNotificationManager mediaNotificationManager; + + @GuardedBy("lock") + private MediaNotification.@MonotonicNonNull Provider mediaNotificationProvider; + + @GuardedBy("lock") + private @MonotonicNonNull DefaultActionFactory actionFactory; /** Creates a service. */ public MediaSessionService() { @@ -165,7 +169,6 @@ public abstract class MediaSessionService extends Service { super.onCreate(); synchronized (lock) { stub = new MediaSessionServiceStub(this); - notificationHandler = new MediaNotificationManager(this); } } @@ -179,7 +182,7 @@ public abstract class MediaSessionService extends Service { * session is closed. You don't need to manually call {@link #addSession(MediaSession)} nor {@link * #removeSession(MediaSession)}. * - *

    There are two special cases where the {@link ControllerInfo#getPackageName()} returns + *

    There are two special cases where the {@link ControllerInfo#getPackageName()} returns a * non-existent package name: * *

      @@ -209,7 +212,7 @@ public abstract class MediaSessionService extends Service { * Adds a {@link MediaSession} to this service. This is not necessary for most media apps. See Supporting Multiple Sessions for details. * - *

      Added session will be removed automatically when it's closed. + *

      The added session will be removed automatically when it's closed. * * @param session A session to be added. * @see #removeSession(MediaSession) @@ -227,11 +230,8 @@ public abstract class MediaSessionService extends Service { if (old == null) { // Session has returned for the first time. Register callbacks. // TODO(b/191644474): Check whether the session is registered to multiple services. - MediaNotificationManager handler; - synchronized (lock) { - handler = checkStateNotNull(notificationHandler); - } - postOrRun(mainHandler, () -> handler.addSession(session)); + MediaNotificationManager notificationManager = getMediaNotificationManager(); + postOrRun(mainHandler, () -> notificationManager.addSession(session)); } } @@ -245,39 +245,12 @@ public abstract class MediaSessionService extends Service { */ public final void removeSession(MediaSession session) { checkNotNull(session, "session must not be null"); - MediaNotificationManager handler; synchronized (lock) { + checkArgument(sessions.containsKey(session.getId()), "session not found"); sessions.remove(session.getId()); - handler = checkStateNotNull(notificationHandler); } - postOrRun(mainHandler, () -> handler.removeSession(session)); - } - - /** - * Called when {@link MediaNotification} needs to be updated. Override this method to show or - * cancel your own notification. - * - *

      This will be called on the application thread of the underlying {@link Player} of {@link - * MediaSession}. - * - *

      With the notification returned by this method, the service becomes a foreground - * service when the playback is started. Apps targeting {@code SDK_INT >= 28} must request the - * permission, {@link android.Manifest.permission#FOREGROUND_SERVICE}. It becomes a background - * service after the playback is stopped. - * - * @param session A session that needs notification update. - * @return A {@link MediaNotification}, or {@code null} if you don't want the automatic - * foreground/background transitions. - */ - @Nullable - public MediaNotification onUpdateNotification(MediaSession session) { - checkNotNull(session, "session must not be null"); - MediaNotificationManager handler; - synchronized (lock) { - handler = checkStateNotNull(notificationHandler, "Service hasn't created"); - } - return handler.onUpdateNotification(session); + MediaNotificationManager notificationManager = getMediaNotificationManager(); + postOrRun(mainHandler, () -> notificationManager.removeSession(session)); } /** @@ -347,9 +320,15 @@ public abstract class MediaSessionService extends Service { if (intent == null) { return START_STICKY; } - if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) { - @Nullable Uri uri = intent.getData(); - @Nullable MediaSession session = uri != null ? MediaSession.getSession(uri) : null; + + DefaultActionFactory actionFactory; + synchronized (lock) { + actionFactory = checkStateNotNull(this.actionFactory); + } + + @Nullable Uri uri = intent.getData(); + @Nullable MediaSession session = uri != null ? MediaSession.getSession(uri) : null; + if (actionFactory.isMediaAction(intent)) { if (session == null) { ControllerInfo controllerInfo = ControllerInfo.createLegacyControllerInfo(); session = onGetSession(controllerInfo); @@ -358,10 +337,16 @@ public abstract class MediaSessionService extends Service { } addSession(session); } - KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + @Nullable KeyEvent keyEvent = actionFactory.getKeyEvent(intent); if (keyEvent != null) { session.getSessionCompat().getController().dispatchMediaButtonEvent(keyEvent); } + } else if (actionFactory.isCustomAction(intent)) { + @Nullable String customAction = actionFactory.getCustomAction(intent); + if (session != null && customAction != null) { + Bundle customExtras = actionFactory.getCustomActionExtras(intent); + getMediaNotificationManager().onCustomAction(session, customAction, customExtras); + } } return START_STICKY; } @@ -383,35 +368,40 @@ public abstract class MediaSessionService extends Service { } } + /** + * Sets the {@link MediaNotification.Provider} to customize notifications. + * + *

      This should be called before any session is attached to this service via {@link + * #onGetSession(ControllerInfo)} or {@link #addSession(MediaSession)}. Otherwise a default UX + * will be shown with {@link DefaultMediaNotificationProvider}. + */ + @UnstableApi + protected final void setMediaNotificationProvider( + MediaNotification.Provider mediaNotificationProvider) { + checkNotNull(mediaNotificationProvider); + synchronized (lock) { + this.mediaNotificationProvider = mediaNotificationProvider; + } + } + /* package */ IBinder getServiceBinder() { synchronized (lock) { return checkStateNotNull(stub).asBinder(); } } - /** A notification for media playback returned by {@link #onUpdateNotification(MediaSession)}. */ - public static final class MediaNotification { - - /** The notification id. */ - @IntRange(from = 1) - public final int notificationId; - - /** The {@link Notification}. */ - public final Notification notification; - - /** - * Creates an instance. - * - * @param notificationId The notification id to be used for {@link - * NotificationManager#notify(int, Notification)}. - * @param notification A {@link Notification} to make the {@link MediaSessionService} foreground. - * It's highly recommended to use a {@link androidx.media.app.NotificationCompat.MediaStyle - * media style} {@link Notification notification}. - */ - public MediaNotification(@IntRange(from = 1) int notificationId, Notification notification) { - this.notificationId = notificationId; - this.notification = checkNotNull(notification); + private MediaNotificationManager getMediaNotificationManager() { + synchronized (lock) { + if (mediaNotificationManager == null) { + if (mediaNotificationProvider == null) { + mediaNotificationProvider = new DefaultMediaNotificationProvider(getApplicationContext()); + } + actionFactory = new DefaultActionFactory(getApplicationContext()); + mediaNotificationManager = + new MediaNotificationManager( + /* mediaSessionService= */ this, mediaNotificationProvider, actionFactory); + } + return mediaNotificationManager; } } diff --git a/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java new file mode 100644 index 0000000000..9301b618b0 --- /dev/null +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.Shadows.shadowOf; + +import android.app.PendingIntent; +import android.content.Intent; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.shadows.ShadowPendingIntent; + +/** Tests for {@link DefaultActionFactory}. */ +@RunWith(AndroidJUnit4.class) +public class DefaultActionFactoryTest { + + @Test + public void createMediaPendingIntent_intentIsMediaAction() { + DefaultActionFactory actionFactory = + new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + + PendingIntent pendingIntent = + actionFactory.createMediaActionPendingIntent(MediaNotification.ActionFactory.COMMAND_PLAY); + + ShadowPendingIntent shadowPendingIntent = shadowOf(pendingIntent); + assertThat(actionFactory.isMediaAction(shadowPendingIntent.getSavedIntent())).isTrue(); + } + + @Test + public void isMediaAction_withNonMediaIntent_returnsFalse() { + DefaultActionFactory actionFactory = + new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + + Intent intent = new Intent("invalid_action"); + + assertThat(actionFactory.isMediaAction(intent)).isFalse(); + } + + @Test + public void isCustomAction_withNonCustomActionIntent_returnsFalse() { + DefaultActionFactory actionFactory = + new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + + Intent intent = new Intent("invalid_action"); + + assertThat(actionFactory.isCustomAction(intent)).isFalse(); + } +} From 743a46e1b2dea0e14506ed8dba3f0a36aa79a93b Mon Sep 17 00:00:00 2001 From: krocard Date: Fri, 11 Feb 2022 18:27:34 +0000 Subject: [PATCH 203/251] Do not reference AnalyticsCollector in common constructor This prevents it to be stripped by R8 if a custom one is passed. #minor-release PiperOrigin-RevId: 428034999 --- .../androidx/media3/exoplayer/ExoPlayer.java | 17 +++++++---------- .../media3/exoplayer/ExoPlayerImpl.java | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index e05289e8c0..23b88faf3d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -16,7 +16,6 @@ package androidx.media3.exoplayer; 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 android.content.Context; @@ -65,6 +64,7 @@ import androidx.media3.exoplayer.video.VideoFrameMetadataListener; import androidx.media3.exoplayer.video.spherical.CameraMotionListener; import androidx.media3.extractor.DefaultExtractorsFactory; import androidx.media3.extractor.ExtractorsFactory; +import com.google.common.base.Function; import com.google.common.base.Supplier; import java.util.List; @@ -383,7 +383,7 @@ public interface ExoPlayer extends Player { /* package */ Supplier trackSelectorSupplier; /* package */ Supplier loadControlSupplier; /* package */ Supplier bandwidthMeterSupplier; - /* package */ Supplier analyticsCollectorSupplier; + /* package */ Function analyticsCollectorFunction; /* package */ Looper looper; @Nullable /* package */ PriorityTaskManager priorityTaskManager; /* package */ AudioAttributes audioAttributes; @@ -547,7 +547,7 @@ public interface ExoPlayer extends Player { () -> trackSelector, () -> loadControl, () -> bandwidthMeter, - () -> analyticsCollector); + (clock) -> analyticsCollector); } private Builder( @@ -561,7 +561,7 @@ public interface ExoPlayer extends Player { () -> new DefaultTrackSelector(context), DefaultLoadControl::new, () -> DefaultBandwidthMeter.getSingletonInstance(context), - /* analyticsCollectorSupplier= */ null); + DefaultAnalyticsCollector::new); } private Builder( @@ -571,17 +571,14 @@ public interface ExoPlayer extends Player { Supplier trackSelectorSupplier, Supplier loadControlSupplier, Supplier bandwidthMeterSupplier, - @Nullable Supplier analyticsCollectorSupplier) { + Function analyticsCollectorFunction) { this.context = context; this.renderersFactorySupplier = renderersFactorySupplier; this.mediaSourceFactorySupplier = mediaSourceFactorySupplier; this.trackSelectorSupplier = trackSelectorSupplier; this.loadControlSupplier = loadControlSupplier; this.bandwidthMeterSupplier = bandwidthMeterSupplier; - this.analyticsCollectorSupplier = - analyticsCollectorSupplier != null - ? analyticsCollectorSupplier - : () -> new DefaultAnalyticsCollector(checkNotNull(clock)); + this.analyticsCollectorFunction = analyticsCollectorFunction; looper = Util.getCurrentOrMainLooper(); audioAttributes = AudioAttributes.DEFAULT; wakeMode = C.WAKE_MODE_NONE; @@ -708,7 +705,7 @@ public interface ExoPlayer extends Player { @UnstableApi public Builder setAnalyticsCollector(AnalyticsCollector analyticsCollector) { checkState(!buildCalled); - this.analyticsCollectorSupplier = () -> analyticsCollector; + this.analyticsCollectorFunction = (clock) -> analyticsCollector; return this; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 1280192eee..930349e302 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -233,7 +233,7 @@ import java.util.concurrent.TimeoutException; + Util.DEVICE_DEBUG_INFO + "]"); applicationContext = builder.context.getApplicationContext(); - analyticsCollector = builder.analyticsCollectorSupplier.get(); + analyticsCollector = builder.analyticsCollectorFunction.apply(builder.clock); priorityTaskManager = builder.priorityTaskManager; audioAttributes = builder.audioAttributes; videoScalingMode = builder.videoScalingMode; From 6adf2f8c915c1b3e2cbc599a98b0c26c2c25b781 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 14 Feb 2022 10:39:05 +0000 Subject: [PATCH 204/251] Improve transformer activity layout On Samsung S21 with light theme the debug labels weren't showing up because the player view was visible (with a black background) while the transformation was in progress, matching the debug label text color. Hide the player view until the transformation is complete to fix this. Also tweak the layout slightly to add space between the card border and the labels. #mse-bug-week PiperOrigin-RevId: 428455824 --- .../demo/transformer/TransformerActivity.java | 2 ++ .../main/res/layout/transformer_activity.xml | 17 +++++------------ .../transformer/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index 3b7ebb4304..ed349ddc55 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -150,6 +150,7 @@ public final class TransformerActivity extends AppCompatActivity { throw new IllegalStateException(e); } informationTextView.setText(R.string.transformation_started); + playerView.setVisibility(View.GONE); Handler mainHandler = new Handler(getMainLooper()); ProgressHolder progressHolder = new ProgressHolder(); mainHandler.post( @@ -287,6 +288,7 @@ public final class TransformerActivity extends AppCompatActivity { getString( R.string.transformation_completed, transformationStopwatch.elapsed(TimeUnit.SECONDS))); progressViewGroup.setVisibility(View.GONE); + playerView.setVisibility(View.VISIBLE); playMediaItem(MediaItem.fromUri("file://" + filePath)); Log.d(TAG, "Output file path: file://" + filePath); } diff --git a/demos/transformer/src/main/res/layout/transformer_activity.xml b/demos/transformer/src/main/res/layout/transformer_activity.xml index 94b4484969..324ed010a2 100644 --- a/demos/transformer/src/main/res/layout/transformer_activity.xml +++ b/demos/transformer/src/main/res/layout/transformer_activity.xml @@ -34,10 +34,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:paddingTop="8dp" - android:paddingBottom="8dp" /> + android:padding="8dp" /> @@ -61,8 +58,6 @@ android:id="@+id/debug_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingLeft="4dp" - android:paddingRight="4dp" android:textSize="10sp" tools:ignore="SmallSp"/> @@ -70,28 +65,26 @@ android:id="@+id/progress_view_group" android:layout_height="match_parent" android:layout_width="match_parent" + android:padding="8dp" android:orientation="vertical"> + android:layout_height="match_parent"> Transformer Demo Configuration - Choose File + Choose file Remove audio Remove video Flatten for slow motion @@ -30,7 +30,7 @@ Transform [Experimental] HDR editing Debug preview: - No debug preview available + No debug preview available. Transformation started Transformation started %d seconds ago. Transformation completed in %d seconds. From f3a9c6f539a9e2ffe39dbe696dea8218ed32b7b3 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 14 Feb 2022 10:39:13 +0000 Subject: [PATCH 205/251] Rewrite AnalyticsCollector to use new track APIs PiperOrigin-RevId: 428455848 --- .../analytics/AnalyticsListener.java | 19 +------------------ .../analytics/DefaultAnalyticsCollector.java | 8 ++------ .../DefaultAnalyticsCollectorTest.java | 2 +- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index f99c2b6e4a..7195661a2a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -45,9 +45,7 @@ import androidx.media3.common.Player.DiscontinuityReason; import androidx.media3.common.Player.PlaybackSuppressionReason; import androidx.media3.common.Player.TimelineChangeReason; import androidx.media3.common.Timeline; -import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelection; -import androidx.media3.common.TrackSelectionArray; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; import androidx.media3.common.VideoSize; @@ -240,10 +238,7 @@ public interface AnalyticsListener { * {@link Player#getCurrentMediaItem()} changed or the player started repeating the current item. */ int EVENT_MEDIA_ITEM_TRANSITION = Player.EVENT_MEDIA_ITEM_TRANSITION; - /** - * {@link Player#getCurrentTracksInfo()}, {@link Player#getCurrentTrackGroups()} or {@link - * Player#getCurrentTrackSelections()} changed. - */ + /** {@link Player#getCurrentTracksInfo()} changed. */ int EVENT_TRACKS_CHANGED = Player.EVENT_TRACKS_CHANGED; /** {@link Player#isLoading()} ()} changed. */ int EVENT_IS_LOADING_CHANGED = Player.EVENT_IS_LOADING_CHANGED; @@ -708,18 +703,6 @@ public interface AnalyticsListener { */ default void onPlayerErrorChanged(EventTime eventTime, @Nullable PlaybackException error) {} - /** - * Called when the available or selected tracks for the renderers changed. - * - * @param eventTime The event time. - * @param trackGroups The available tracks. May be empty. - * @param trackSelections The track selections for each renderer. May contain null elements. - * @deprecated Use {@link #onTracksInfoChanged}. - */ - @Deprecated - default void onTracksChanged( - EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} - /** * Called when the available or selected tracks change. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java index b3d9c3669d..625753d85d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollector.java @@ -484,15 +484,11 @@ public class DefaultAnalyticsCollector implements AnalyticsCollector { listener -> listener.onMediaItemTransition(eventTime, mediaItem, reason)); } + @SuppressWarnings("deprecation") // Implementing deprecated method. @Override - @SuppressWarnings("deprecation") // Implementing and calling deprecate listener method public final void onTracksChanged( TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent( - eventTime, - AnalyticsListener.EVENT_TRACKS_CHANGED, - listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections)); + // Do nothing. Handled by non-deprecated onTracksInfoChanged. } @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java index 7aff4de60f..ab60bd2bc4 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java @@ -1721,7 +1721,7 @@ public final class DefaultAnalyticsCollectorTest { ArgumentCaptor individualTracksChangedEventTimes = ArgumentCaptor.forClass(AnalyticsListener.EventTime.class); verify(listener, atLeastOnce()) - .onTracksChanged(individualTracksChangedEventTimes.capture(), any(), any()); + .onTracksInfoChanged(individualTracksChangedEventTimes.capture(), any()); ArgumentCaptor individualPlayWhenReadyChangedEventTimes = ArgumentCaptor.forClass(AnalyticsListener.EventTime.class); verify(listener, atLeastOnce()) From 6197a712ff87e844d6374a82d43e63ef9723acd1 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 14 Feb 2022 11:47:20 +0000 Subject: [PATCH 206/251] Use content timeline to get ad group index and ad index in ad group The timeline used to map ad groups to periods needs to report the original content period duration without subtracting the serverside inserted ad duration. When marking played ads in onPositionDiscontinuity, the public timeline has been used which crashed the app when the ads media source is playing on a window index different to zero (in a playlist). #minor-release PiperOrigin-RevId: 428465833 --- .../ima/ImaServerSideAdInsertionMediaSource.java | 6 +++++- .../main/java/androidx/media3/exoplayer/ima/ImaUtil.java | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index c9c90372c2..a2421ecbca 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -93,6 +93,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -476,6 +477,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } @MainThread + @EnsuresNonNull("contentTimeline") private void setContentTimeline(Timeline contentTimeline) { if (contentTimeline.equals(this.contentTimeline)) { return; @@ -640,7 +642,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou // Map adGroupIndex and adIndexInAdGroup to multi-period window. Pair adGroupIndexAndAdIndexInAdGroup = getAdGroupAndIndexInMultiPeriodWindow( - oldPosition.periodIndex, adPlaybackState, timeline); + oldPosition.periodIndex - window.firstPeriodIndex, + adPlaybackState, + checkNotNull(contentTimeline)); adGroupIndex = adGroupIndexAndAdIndexInAdGroup.first; adIndexInAdGroup = adGroupIndexAndAdIndexInAdGroup.second; } diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java index 5304fa637f..cede70d9cb 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java @@ -475,11 +475,11 @@ import java.util.Set; * * @param adPeriodIndex The period index of the ad period. * @param adPlaybackState The ad playback state that holds the ad group and ad information. - * @param timeline The timeline that contains the ad period. + * @param contentTimeline The timeline that contains the ad period. * @return A pair with the ad group index (first) and the ad index in that ad group (second). */ public static Pair getAdGroupAndIndexInMultiPeriodWindow( - int adPeriodIndex, AdPlaybackState adPlaybackState, Timeline timeline) { + int adPeriodIndex, AdPlaybackState adPlaybackState, Timeline contentTimeline) { Timeline.Period period = new Timeline.Period(); int periodIndex = 0; long totalElapsedContentDurationUs = 0; @@ -488,8 +488,8 @@ import java.util.Set; AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(/* adGroupIndex= */ i); long adGroupDurationUs = sum(adGroup.durationsUs); long elapsedAdGroupAdDurationUs = 0; - for (int j = periodIndex; j < timeline.getPeriodCount(); j++) { - timeline.getPeriod(j, period, /* setIds= */ true); + for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) { + contentTimeline.getPeriod(j, period, /* setIds= */ true); // TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK. // Subtract one microsecond to work around rounding errors with adGroup.timeUs. if (totalElapsedContentDurationUs < adGroup.timeUs - 1) { From 6048ca2faa7e6fa500661ca4282bef2ddb7596e2 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 14 Feb 2022 12:39:14 +0000 Subject: [PATCH 207/251] Make usage of live minDurationForQualityIncrease more consistent We have two ways to choose the minDurationForQualityIncreaseMs value in AdaptiveTrackSelection: use the configured value for non-live or when enough buffered data is available, or use a fraction of the available duration to allow switching when playing close to the live edge. The decision point when to use which value isn't quite consistent because we compare against availableDurationUs before making the adjustments. This means there is range of values where no up-switching is possible despite perfect buffering. Fix this by choosing the minimum of both values. Issue: google/ExoPlayer#9784 #minor-release PiperOrigin-RevId: 428474332 --- .../trackselection/AdaptiveTrackSelection.java | 11 ++++++----- .../trackselection/AdaptiveTrackSelectionTest.java | 12 +++++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java index 40dfa34a12..0d1c02e80f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.trackselection; import static java.lang.Math.max; +import static java.lang.Math.min; import androidx.annotation.CallSuper; import androidx.annotation.Nullable; @@ -605,10 +606,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { } private long minDurationForQualityIncreaseUs(long availableDurationUs, long chunkDurationUs) { - boolean isAvailableDurationTooShort = - availableDurationUs != C.TIME_UNSET - && availableDurationUs <= minDurationForQualityIncreaseUs; - if (!isAvailableDurationTooShort) { + if (availableDurationUs == C.TIME_UNSET) { + // We are not in a live stream. Use the configured value. return minDurationForQualityIncreaseUs; } if (chunkDurationUs != C.TIME_UNSET) { @@ -619,7 +618,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { // actually achievable. availableDurationUs -= chunkDurationUs; } - return (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease); + long adjustedMinDurationForQualityIncreaseUs = + (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease); + return min(adjustedMinDurationForQualityIncreaseUs, minDurationForQualityIncreaseUs); } /** diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelectionTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelectionTest.java index f2915110f9..b8cc647d9f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelectionTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelectionTest.java @@ -175,7 +175,9 @@ public final class AdaptiveTrackSelectionTest { when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 2000L); AdaptiveTrackSelection adaptiveTrackSelection = prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease( - trackGroup, /* bufferedFractionToLiveEdgeForQualityIncrease= */ 0.75f); + trackGroup, + /* bufferedFractionToLiveEdgeForQualityIncrease= */ 0.75f, + /* minDurationForQualityIncreaseMs= */ 5000); // Not buffered close to live edge yet. adaptiveTrackSelection.updateSelectedTrack( @@ -188,6 +190,8 @@ public final class AdaptiveTrackSelectionTest { assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2); // Buffered all possible chunks (except for newly added chunk of 2 seconds). + // Intentionally choose a situation where availableDurationUs > minDurationForQualityIncreaseMs + // to ensure the live calculation is used regardless. adaptiveTrackSelection.updateSelectedTrack( /* playbackPositionUs= */ 0, /* bufferedDurationUs= */ 3_600_000, @@ -768,14 +772,16 @@ public final class AdaptiveTrackSelectionTest { private AdaptiveTrackSelection prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease( - TrackGroup trackGroup, float bufferedFractionToLiveEdgeForQualityIncrease) { + TrackGroup trackGroup, + float bufferedFractionToLiveEdgeForQualityIncrease, + long minDurationForQualityIncreaseMs) { return prepareTrackSelection( new AdaptiveTrackSelection( trackGroup, selectedAllTracksInGroup(trackGroup), TrackSelection.TYPE_UNSET, mockBandwidthMeter, - AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, + minDurationForQualityIncreaseMs, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, From 28b8ae70767d0c3ff358d1ef4349c788f10d6e9c Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 14 Feb 2022 13:48:56 +0000 Subject: [PATCH 208/251] Seek to start position when reusing the last used shared period The last used shared media period is reused after all media periods have been released. In case the sample streams are already filled up, they need to be reset or they download samples from the current position up to the seek position. This causes long buffering states or load stuck exceptions. A seek when reusing the shared period takes care for reseting the period or internally seeks to the current position in the already available samples. #minor-release PiperOrigin-RevId: 428484187 --- .../source/ads/ServerSideAdInsertionMediaSource.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index a0821d571e..e5e29d4c97 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -264,10 +264,12 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { @Nullable SharedMediaPeriod sharedPeriod = null; Pair sharedMediaPeriodKey = new Pair<>(id.windowSequenceNumber, id.periodUid); + boolean reusedSharedPeriod = false; if (lastUsedMediaPeriod != null) { if (lastUsedMediaPeriod.periodUid.equals(id.periodUid)) { sharedPeriod = lastUsedMediaPeriod; mediaPeriods.put(sharedMediaPeriodKey, sharedPeriod); + reusedSharedPeriod = true; } else { lastUsedMediaPeriod.release(mediaSource); } @@ -298,6 +300,9 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource new MediaPeriodImpl( sharedPeriod, id, createEventDispatcher(id), createDrmEventDispatcher(id)); sharedPeriod.add(mediaPeriod); + if (reusedSharedPeriod && sharedPeriod.trackSelections.length > 0) { + mediaPeriod.seekToUs(startPositionUs); + } return mediaPeriod; } From 015592f534b68b8f281f9d7a2ab9733bf58031a1 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Mon, 14 Feb 2022 14:34:37 +0000 Subject: [PATCH 209/251] Add glClear call before drawing. PiperOrigin-RevId: 428492031 --- .../src/main/java/androidx/media3/transformer/FrameEditor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index 9bcc97def0..1a6f7c619b 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -352,6 +352,8 @@ import java.util.concurrent.atomic.AtomicInteger; /** Focuses the specified surface with the specified width and height, then draws a quad. */ private void focusAndDrawQuad(EGLSurface eglSurface, int width, int height) { GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, width, height); + GLES20.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); // The four-vertex triangle strip forms a quad. GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); } From 76839a02d6c3d062416a4bfb827c7ccb0dda3d3d Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 14 Feb 2022 16:46:44 +0000 Subject: [PATCH 210/251] Add getFormat and length to TrackGroupInfo PiperOrigin-RevId: 428520090 --- .../androidx/media3/common/TracksInfo.java | 19 +++++++++++++++++-- .../analytics/MediaMetricsListener.java | 6 ++---- .../media3/exoplayer/util/EventLogger.java | 5 ++--- .../media3/exoplayer/ExoPlayerTest.java | 2 +- .../androidx/media3/ui/PlayerControlView.java | 6 +++--- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 437f7dda44..696d6f68de 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -38,12 +38,17 @@ import java.util.List; /** Information about groups of tracks. */ public final class TracksInfo implements Bundleable { + /** * Information about a single group of tracks, including the underlying {@link TrackGroup}, the * {@link C.TrackType type} of tracks it contains, and the level to which each track is supported * by the player. */ public static final class TrackGroupInfo implements Bundleable { + + /** The number of tracks in the group. */ + public final int length; + private final TrackGroup trackGroup; private final @C.FormatSupport int[] trackSupport; private final @C.TrackType int trackType; @@ -63,7 +68,7 @@ public final class TracksInfo implements Bundleable { @C.FormatSupport int[] trackSupport, @C.TrackType int trackType, boolean[] tracksSelected) { - int length = trackGroup.length; + length = trackGroup.length; checkArgument(length == trackSupport.length && length == tracksSelected.length); this.trackGroup = trackGroup; this.trackSupport = trackSupport.clone(); @@ -71,11 +76,21 @@ public final class TracksInfo implements Bundleable { this.trackSelected = tracksSelected.clone(); } - /** Returns the {@link TrackGroup} described by this {@code TrackGroupInfo}. */ + /** Returns the underlying {@link TrackGroup}. */ public TrackGroup getTrackGroup() { return trackGroup; } + /** + * Returns the {@link Format} for a specified track. + * + * @param trackIndex The index of the track in the {@link TrackGroup}. + * @return The {@link Format} of the track. + */ + public Format getTrackFormat(int trackIndex) { + return trackGroup.getFormat(trackIndex); + } + /** * Returns the level of support for a specified track. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java index 50d07ce7ca..f3b72a487d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/MediaMetricsListener.java @@ -49,7 +49,6 @@ import androidx.media3.common.ParserException; import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; import androidx.media3.common.Timeline; -import androidx.media3.common.TrackGroup; import androidx.media3.common.TracksInfo; import androidx.media3.common.TracksInfo.TrackGroupInfo; import androidx.media3.common.VideoSize; @@ -825,10 +824,9 @@ public final class MediaMetricsListener @Nullable private static DrmInitData getDrmInitData(ImmutableList trackGroupInfos) { for (TrackGroupInfo trackGroupInfo : trackGroupInfos) { - TrackGroup trackGroup = trackGroupInfo.getTrackGroup(); - for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { + for (int trackIndex = 0; trackIndex < trackGroupInfo.length; trackIndex++) { if (trackGroupInfo.isTrackSelected(trackIndex)) { - @Nullable DrmInitData drmInitData = trackGroup.getFormat(trackIndex).drmInitData; + @Nullable DrmInitData drmInitData = trackGroupInfo.getTrackFormat(trackIndex).drmInitData; if (drmInitData != null) { return drmInitData; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/util/EventLogger.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/util/EventLogger.java index 6999c90005..841a11ed26 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/util/EventLogger.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/util/EventLogger.java @@ -268,9 +268,8 @@ public class EventLogger implements AnalyticsListener { ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); for (int groupIndex = 0; groupIndex < trackGroupInfos.size(); groupIndex++) { TracksInfo.TrackGroupInfo trackGroupInfo = trackGroupInfos.get(groupIndex); - TrackGroup trackGroup = trackGroupInfo.getTrackGroup(); logd(" group ["); - for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { + for (int trackIndex = 0; trackIndex < trackGroupInfo.length; trackIndex++) { String status = getTrackStatusString(trackGroupInfo.isTrackSelected(trackIndex)); String formatSupport = getFormatSupportString(trackGroupInfo.getTrackSupport(trackIndex)); logd( @@ -279,7 +278,7 @@ public class EventLogger implements AnalyticsListener { + " Track:" + trackIndex + ", " - + Format.toLogString(trackGroup.getFormat(trackIndex)) + + Format.toLogString(trackGroupInfo.getTrackFormat(trackIndex)) + ", supported=" + formatSupport); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 0d48e0d7cd..1ae8722199 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -7984,7 +7984,7 @@ public final class ExoPlayerTest { assertThat(timelineAfterError.get().getWindowCount()).isEqualTo(1); assertThat(mediaItemIndexAfterError.get()).isEqualTo(0); assertThat(trackInfosAfterError.get().getTrackGroupInfos()).hasSize(1); - assertThat(trackInfosAfterError.get().getTrackGroupInfos().get(0).getTrackGroup().getFormat(0)) + assertThat(trackInfosAfterError.get().getTrackGroupInfos().get(0).getTrackFormat(0)) .isEqualTo(ExoPlayerTestRunner.AUDIO_FORMAT); assertThat(trackSelectionsAfterError.get().get(0)).isNull(); // Video renderer. assertThat(trackSelectionsAfterError.get().get(1)).isNotNull(); // Audio renderer. diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java index ce3ba265d2..ea16390550 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java @@ -1124,12 +1124,12 @@ public class PlayerControlView extends FrameLayout { if (trackGroupInfo.getTrackType() != trackType) { continue; } - TrackGroup trackGroup = trackGroupInfo.getTrackGroup(); - for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { + for (int trackIndex = 0; trackIndex < trackGroupInfo.length; trackIndex++) { if (!trackGroupInfo.isTrackSupported(trackIndex)) { continue; } - String trackName = trackNameProvider.getTrackName(trackGroup.getFormat(trackIndex)); + String trackName = + trackNameProvider.getTrackName(trackGroupInfo.getTrackFormat(trackIndex)); tracks.add(new TrackInformation(tracksInfo, trackGroupIndex, trackIndex, trackName)); } } From 1afba9676e890f62091c65d64b513e93fc1e263e Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 14 Feb 2022 17:00:44 +0000 Subject: [PATCH 211/251] Fix debug frame showing through player on old devices PiperOrigin-RevId: 428524300 --- .../media3/demo/transformer/TransformerActivity.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index ed349ddc55..dcb2e97171 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -104,6 +104,7 @@ public final class TransformerActivity extends AppCompatActivity { checkNotNull(playerView); checkNotNull(debugTextView); checkNotNull(progressViewGroup); + checkNotNull(debugFrame); startTransformation(); playerView.onResume(); @@ -132,6 +133,7 @@ public final class TransformerActivity extends AppCompatActivity { "progressIndicator", "transformationStopwatch", "progressViewGroup", + "debugFrame", }) private void startTransformation() { requestTransformerPermission(); @@ -189,6 +191,7 @@ public final class TransformerActivity extends AppCompatActivity { "informationTextView", "transformationStopwatch", "progressViewGroup", + "debugFrame", }) private Transformer createTransformer(@Nullable Bundle bundle, String filePath) { Transformer.Builder transformerBuilder = new Transformer.Builder(/* context= */ this); @@ -263,12 +266,14 @@ public final class TransformerActivity extends AppCompatActivity { @RequiresNonNull({ "informationTextView", "progressViewGroup", + "debugFrame", "transformationStopwatch", }) private void onTransformationError(TransformationException exception) { transformationStopwatch.stop(); informationTextView.setText(R.string.transformation_error); progressViewGroup.setVisibility(View.GONE); + debugFrame.removeAllViews(); Toast.makeText( TransformerActivity.this, "Transformation error: " + exception, Toast.LENGTH_LONG) .show(); @@ -280,6 +285,7 @@ public final class TransformerActivity extends AppCompatActivity { "debugTextView", "informationTextView", "progressViewGroup", + "debugFrame", "transformationStopwatch", }) private void onTransformationCompleted(String filePath) { @@ -288,6 +294,7 @@ public final class TransformerActivity extends AppCompatActivity { getString( R.string.transformation_completed, transformationStopwatch.elapsed(TimeUnit.SECONDS))); progressViewGroup.setVisibility(View.GONE); + debugFrame.removeAllViews(); playerView.setVisibility(View.VISIBLE); playMediaItem(MediaItem.fromUri("file://" + filePath)); Log.d(TAG, "Output file path: file://" + filePath); From 741ab34a63d49069f39938f4dd4a0ce81ddae163 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 14 Feb 2022 19:32:34 +0000 Subject: [PATCH 212/251] Make AdsLoader.addMediaSourceResources package private. #minor-release PiperOrigin-RevId: 428565444 --- .../ima/ImaServerSideAdInsertionMediaSource.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index a2421ecbca..17120843e0 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -290,13 +290,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.player = player; } - public void addMediaSourceResources( - ImaServerSideAdInsertionMediaSource mediaSource, - StreamPlayer streamPlayer, - com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { - mediaSourceResources.put(mediaSource, new MediaSourceResourceHolder(streamPlayer, adsLoader)); - } - /** Releases resources when the ads loader is no longer needed. */ public void release() { for (MediaSourceResourceHolder resourceHolder : mediaSourceResources.values()) { @@ -306,6 +299,13 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou mediaSourceResources.clear(); } + /* package */ void addMediaSourceResources( + ImaServerSideAdInsertionMediaSource mediaSource, + StreamPlayer streamPlayer, + com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { + mediaSourceResources.put(mediaSource, new MediaSourceResourceHolder(streamPlayer, adsLoader)); + } + private static final class MediaSourceResourceHolder { public final StreamPlayer streamPlayer; public final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; From 1a31faaeeec27526ca116e1bbac772d2b5a1e29c Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 14 Feb 2022 20:05:19 +0000 Subject: [PATCH 213/251] Reset stream manager in AdsLoader.release() This prevents a stack trace that is shown in the logs when the stream manager is released after the activity was stopped. In this case the call to `streamManager.destroy()` coming from `releaseSourceInternal()` of the media source is too late and produces an error saying `Application attempted to call on a destroyed WebView`. The error has no effect but it's nice to not have this stack trace in the logs. PiperOrigin-RevId: 428574231 --- .../ima/ImaServerSideAdInsertionMediaSource.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 17120843e0..0e04bb4903 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -295,23 +295,31 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou for (MediaSourceResourceHolder resourceHolder : mediaSourceResources.values()) { resourceHolder.streamPlayer.release(); resourceHolder.adsLoader.release(); + resourceHolder.imaServerSideAdInsertionMediaSource.setStreamManager( + /* streamManager= */ null); } mediaSourceResources.clear(); + player = null; } /* package */ void addMediaSourceResources( ImaServerSideAdInsertionMediaSource mediaSource, StreamPlayer streamPlayer, com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { - mediaSourceResources.put(mediaSource, new MediaSourceResourceHolder(streamPlayer, adsLoader)); + mediaSourceResources.put( + mediaSource, new MediaSourceResourceHolder(mediaSource, streamPlayer, adsLoader)); } private static final class MediaSourceResourceHolder { + public final ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource; public final StreamPlayer streamPlayer; public final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; private MediaSourceResourceHolder( - StreamPlayer streamPlayer, com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { + ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource, + StreamPlayer streamPlayer, + com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { + this.imaServerSideAdInsertionMediaSource = imaServerSideAdInsertionMediaSource; this.streamPlayer = streamPlayer; this.adsLoader = adsLoader; } From f3cc5327cdea0e3478e0b8093527785142ea9325 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 10:33:30 +0000 Subject: [PATCH 214/251] Do not manipulate the duration of SSAI periods in FakeTimeline #minor-release PiperOrigin-RevId: 428727560 --- .../exoplayer/MediaPeriodQueueTest.java | 103 ++++++++++++------ .../media3/test/utils/FakeTimeline.java | 21 +--- .../media3/test/utils/FakeTimelineTest.java | 15 +-- 3 files changed, 84 insertions(+), 55 deletions(-) diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index 403d59567f..f0c0b42e9d 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -15,8 +15,12 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.test.utils.ExoPlayerTestRunner.AUDIO_FORMAT; +import static androidx.media3.test.utils.ExoPlayerTestRunner.VIDEO_FORMAT; import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.robolectric.Shadows.shadowOf; @@ -38,6 +42,7 @@ import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSource.MediaSourceCaller; import androidx.media3.exoplayer.source.SinglePeriodTimeline; +import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource; import androidx.media3.exoplayer.source.ads.SinglePeriodAdTimeline; import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.trackselection.TrackSelector; @@ -50,6 +55,8 @@ import androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -824,10 +831,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodTimeline_rollForward() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodTimeline_rollForward() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ true, @@ -858,10 +866,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodAllAdsPlayed_seekNotAdjusted() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodAllAdsPlayed_seekNotAdjusted() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 4, /* isAdPeriodFlags...= */ true, @@ -892,10 +901,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodFirstTwoAdsPlayed_rollForward() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_behindAdInMultiPeriodFirstTwoAdsPlayed_rollForward() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 2, /* isAdPeriodFlags...= */ true, @@ -917,10 +927,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_beforeAdInMultiPeriodTimeline_seekNotAdjusted() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_beforeAdInMultiPeriodTimeline_seekNotAdjusted() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ false, true); MediaPeriodId mediaPeriodId = @@ -935,10 +946,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toUnplayedAdInMultiPeriodTimeline_resolvedAsAd() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toUnplayedAdInMultiPeriodTimeline_resolvedAsAd() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ false, true, false); MediaPeriodId mediaPeriodId = @@ -953,10 +965,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedAdInMultiPeriodTimeline_skipPlayedAd() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedAdInMultiPeriodTimeline_skipPlayedAd() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 1, /* isAdPeriodFlags...= */ false, true, false); MediaPeriodId mediaPeriodId = @@ -971,12 +984,12 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toStartOfWindowPlayedAdPreroll_skipsPlayedPrerolls() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toStartOfWindowPlayedAdPreroll_skipsPlayedPrerolls() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 2, /* isAdPeriodFlags...= */ true, true, false); - MediaPeriodId mediaPeriodId = mediaPeriodQueue.resolveMediaPeriodIdForAdsAfterPeriodPositionChange( timeline, new Pair<>(windowId, 0), /* positionUs= */ 0); @@ -989,10 +1002,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedPostrolls_skipsAllButLastPostroll() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_toPlayedPostrolls_skipsAllButLastPostroll() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 4, /* isAdPeriodFlags...= */ false, @@ -1013,10 +1027,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_consecutiveContentPeriods_rollForward() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_consecutiveContentPeriods_rollForward() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ true, @@ -1036,10 +1051,11 @@ public final class MediaPeriodQueueTest { @Test public void - resolveMediaPeriodIdForAdsAfterPeriodPositionChange_onlyConsecutiveContentPeriods_seekNotAdjusted() { + resolveMediaPeriodIdForAdsAfterPeriodPositionChange_onlyConsecutiveContentPeriods_seekNotAdjusted() + throws InterruptedException { Object windowId = new Object(); - FakeTimeline timeline = - FakeTimeline.createMultiPeriodAdTimeline( + Timeline timeline = + createMultiPeriodServerSideInsertedTimeline( windowId, /* numberOfPlayedAds= */ 0, /* isAdPeriodFlags...= */ false, @@ -1251,4 +1267,29 @@ public final class MediaPeriodQueueTest { } return length; } + + private static Timeline createMultiPeriodServerSideInsertedTimeline( + Object windowId, int numberOfPlayedAds, boolean... isAdPeriodFlags) + throws InterruptedException { + FakeTimeline timeline = + FakeTimeline.createMultiPeriodAdTimeline(windowId, numberOfPlayedAds, isAdPeriodFlags); + ServerSideAdInsertionMediaSource serverSideAdInsertionMediaSource = + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(timeline, VIDEO_FORMAT, AUDIO_FORMAT), contentTimeline -> false); + serverSideAdInsertionMediaSource.setAdPlaybackStates( + timeline.getAdPlaybackStates(/* windowIndex= */ 0)); + AtomicReference serverSideAdInsertionTimelineRef = new AtomicReference<>(); + CountDownLatch countDownLatch = new CountDownLatch(/* count= */ 1); + serverSideAdInsertionMediaSource.prepareSource( + (source, serverSideInsertedAdTimeline) -> { + serverSideAdInsertionTimelineRef.set(serverSideInsertedAdTimeline); + countDownLatch.countDown(); + }, + /* mediaTransferListener= */ null, + new PlayerId()); + if (!countDownLatch.await(/* timeout= */ 2, SECONDS)) { + fail(); + } + return serverSideAdInsertionTimelineRef.get(); + } } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java index b6ccdfdf3a..75e3d26e7b 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTimeline.java @@ -15,7 +15,6 @@ */ package androidx.media3.test.utils; -import static androidx.media3.common.util.Util.sum; import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US; import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static java.lang.Math.min; @@ -323,14 +322,15 @@ public final class FakeTimeline extends Timeline { public static FakeTimeline createMultiPeriodAdTimeline( Object windowId, int numberOfPlayedAds, boolean... isAdPeriodFlags) { long periodDurationUs = DEFAULT_WINDOW_DURATION_US / isAdPeriodFlags.length; + AdPlaybackState contentPeriodState = new AdPlaybackState(/* adsId= */ "adsId"); AdPlaybackState firstAdPeriodState = - new AdPlaybackState(/* adsId= */ "adsId", /* adGroupTimesUs... */ 0) + contentPeriodState + .withNewAdGroup(/* adGroupIndex= */ 0, /* adGroupTimesUs */ 0) .withAdCount(/* adGroupIndex= */ 0, 1) .withAdDurationsUs( /* adGroupIndex= */ 0, DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + periodDurationUs) .withIsServerSideInserted(/* adGroupIndex= */ 0, true); AdPlaybackState commonAdPeriodState = firstAdPeriodState.withAdDurationsUs(0, periodDurationUs); - AdPlaybackState contentPeriodState = new AdPlaybackState(/* adsId= */ "adsId"); List adPlaybackStates = new ArrayList<>(); int playedAdsCounter = 0; @@ -524,9 +524,7 @@ public final class FakeTimeline extends Timeline { id, uid, windowIndex, - periodDurationUs == C.TIME_UNSET - ? C.TIME_UNSET - : periodDurationUs - getServerSideAdInsertionAdDurationUs(adPlaybackState), + periodDurationUs, positionInWindowUs, adPlaybackState, windowDefinition.isPlaceholder); @@ -577,15 +575,4 @@ public final class FakeTimeline extends Timeline { } return windowDefinitions; } - - private static long getServerSideAdInsertionAdDurationUs(AdPlaybackState adPlaybackState) { - long adDurationUs = 0; - for (int i = 0; i < adPlaybackState.adGroupCount; i++) { - AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); - if (adGroup.isServerSideInserted) { - adDurationUs += sum(adGroup.durationsUs); - } - } - return adDurationUs; - } } diff --git a/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java index cebf4ba06c..9d03c10dd1 100644 --- a/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java +++ b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeTimelineTest.java @@ -45,20 +45,21 @@ public class FakeTimelineTest { true, true, false, + true, true); assertThat(timeline.getWindowCount()).isEqualTo(1); - assertThat(timeline.getPeriodCount()).isEqualTo(7); + assertThat(timeline.getPeriodCount()).isEqualTo(8); // Assert content periods and window duration. Timeline.Period contentPeriod1 = timeline.getPeriod(/* periodIndex= */ 1, period); Timeline.Period contentPeriod5 = timeline.getPeriod(/* periodIndex= */ 5, period); - assertThat(contentPeriod1.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 7); - assertThat(contentPeriod5.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 7); + assertThat(contentPeriod1.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 8); + assertThat(contentPeriod5.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 8); assertThat(contentPeriod1.getAdGroupCount()).isEqualTo(0); assertThat(contentPeriod5.getAdGroupCount()).isEqualTo(0); timeline.getWindow(/* windowIndex= */ 0, window); assertThat(window.uid).isEqualTo(windowId); - assertThat(window.durationUs).isEqualTo(contentPeriod1.durationUs + contentPeriod5.durationUs); + assertThat(window.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US); assertThat(window.positionInFirstPeriodUs).isEqualTo(DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US); // Assert ad periods. int[] adIndices = {0, 2, 3, 4, 6}; @@ -67,7 +68,6 @@ public class FakeTimelineTest { Timeline.Period adPeriod = timeline.getPeriod(periodIndex, period); assertThat(adPeriod.isServerSideInsertedAdGroup(0)).isTrue(); assertThat(adPeriod.getAdGroupCount()).isEqualTo(1); - assertThat(adPeriod.durationUs).isEqualTo(0); if (adPeriod.getAdGroupCount() > 0) { if (adCounter < numberOfPlayedAds) { assertThat(adPeriod.getAdState(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) @@ -79,8 +79,9 @@ public class FakeTimelineTest { adCounter++; } long expectedDurationUs = - (DEFAULT_WINDOW_DURATION_US / 7) + (DEFAULT_WINDOW_DURATION_US / 8) + (periodIndex == 0 ? DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US : 0); + assertThat(adPeriod.durationUs).isEqualTo(expectedDurationUs); assertThat(adPeriod.getAdDurationUs(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) .isEqualTo(expectedDurationUs); } @@ -100,7 +101,7 @@ public class FakeTimelineTest { timeline.getWindow(/* windowIndex= */ 0, window); // Assert content periods and window duration. - assertThat(window.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US / 2); + assertThat(window.durationUs).isEqualTo(DEFAULT_WINDOW_DURATION_US); assertThat(window.positionInFirstPeriodUs).isEqualTo(DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US); } } From 4dae8c75a8fad75245d3ee2e36f0e676c2106e37 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 15 Feb 2022 11:07:42 +0000 Subject: [PATCH 215/251] Remove old TODO. PiperOrigin-RevId: 428732950 --- .../media3/transformer/FrameEditorDataProcessingTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java index 297c86e189..2c3256ba83 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java @@ -48,7 +48,6 @@ import org.junit.runner.RunWith; * from emulators, so tests on physical devices may fail. To test on physical devices, please modify * the MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE. */ -// TODO(b/214510265): Fix these tests on Pixel 4 emulators. @RunWith(AndroidJUnit4.class) public final class FrameEditorDataProcessingTest { From d681e264aaebea54b21cbd68c17da357144998df Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 11:18:22 +0000 Subject: [PATCH 216/251] Skip played server side inserted ads in a single period window This change makes sure played server side ads are skipped in a single period timeline. It avoids creating an ad-MediaPeriodInfo for played postrolls and creates a content info instead. It also sets the end position for content infos that terminate the stream before the stream is actually finished. This prevents the player from continue playing the remaining media delivered by the MediaPeriod. We also make sure that the discontinuity of played ads are not reported because there is actually no discontinuity. #minor-release PiperOrigin-RevId: 428734387 --- .../exoplayer/ExoPlayerImplInternal.java | 11 +- .../media3/exoplayer/MediaPeriodInfo.java | 7 +- .../media3/exoplayer/MediaPeriodQueue.java | 27 +++- .../media3/exoplayer/ExoPlayerTest.java | 149 ++++++++++++------ .../exoplayer/MediaPeriodQueueTest.java | 2 +- .../ServerSideAdInsertionMediaSourceTest.java | 2 +- .../playbackdumps/mp4/ssai-sample.mp4.dump | 79 ++++++++++ 7 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 14d3f9af17..99896816ae 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.max; import static java.lang.Math.min; @@ -2153,14 +2154,20 @@ import java.util.concurrent.atomic.AtomicBoolean; // If we advance more than one period at a time, notify listeners after each update. maybeNotifyPlaybackInfoChanged(); } - MediaPeriodHolder newPlayingPeriodHolder = queue.advancePlayingPeriod(); + MediaPeriodHolder newPlayingPeriodHolder = checkNotNull(queue.advancePlayingPeriod()); + boolean isCancelledSSAIAdTransition = + playbackInfo.periodId.periodUid.equals(newPlayingPeriodHolder.info.id.periodUid) + && playbackInfo.periodId.adGroupIndex == C.INDEX_UNSET + && newPlayingPeriodHolder.info.id.adGroupIndex == C.INDEX_UNSET + && playbackInfo.periodId.nextAdGroupIndex + != newPlayingPeriodHolder.info.id.nextAdGroupIndex; playbackInfo = handlePositionDiscontinuity( newPlayingPeriodHolder.info.id, newPlayingPeriodHolder.info.startPositionUs, newPlayingPeriodHolder.info.requestedContentPositionUs, /* discontinuityStartPositionUs= */ newPlayingPeriodHolder.info.startPositionUs, - /* reportDiscontinuity= */ true, + /* reportDiscontinuity= */ !isCancelledSSAIAdTransition, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); resetPendingPauseAtEndOfPeriod(); updatePlaybackPositions(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java index 520fd63606..cd50c7e739 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodInfo.java @@ -39,9 +39,10 @@ import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; public final long requestedContentPositionUs; /** * The end position to which the media period's content is clipped in order to play a following ad - * group, in microseconds, or {@link C#TIME_UNSET} if there is no following ad group or if this - * media period is an ad. The value {@link C#TIME_END_OF_SOURCE} indicates that a postroll ad - * follows at the end of this content media period. + * group or to terminate a server side ad inserted stream before a played postroll, in + * microseconds, or {@link C#TIME_UNSET} if the content is not clipped or if this media period is + * an ad. The value {@link C#TIME_END_OF_SOURCE} indicates that a postroll ad follows at the end + * of this content media period. */ public final long endPositionUs; /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java index 12edf0605c..a0731462bb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java @@ -21,6 +21,7 @@ import static java.lang.Math.max; import android.os.Handler; import android.util.Pair; import androidx.annotation.Nullable; +import androidx.media3.common.AdPlaybackState; import androidx.media3.common.C; import androidx.media3.common.Player.RepeatMode; import androidx.media3.common.Timeline; @@ -801,8 +802,14 @@ import com.google.common.collect.ImmutableList; } else { // Play the next ad group if it's still available. int adIndexInAdGroup = period.getFirstAdIndexToPlay(currentPeriodId.nextAdGroupIndex); - if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex)) { - // The next ad group has no ads left to play. Play content from the end position instead. + boolean isPlayedServerSideInsertedAd = + period.isServerSideInsertedAdGroup(currentPeriodId.nextAdGroupIndex) + && period.getAdState(currentPeriodId.nextAdGroupIndex, adIndexInAdGroup) + == AdPlaybackState.AD_STATE_PLAYED; + if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex) + || isPlayedServerSideInsertedAd) { + // The next ad group has no ads left to play or is a played SSAI ad group. Play content from + // the end position instead. long startPositionUs = getMinStartPositionAfterAdGroupUs( timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex); @@ -888,6 +895,20 @@ import com.google.common.collect.ImmutableList; long windowSequenceNumber) { timeline.getPeriodByUid(periodUid, period); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs); + boolean clipPeriodAtContentDuration = false; + if (nextAdGroupIndex == C.INDEX_UNSET) { + // Clip SSAI streams when at the end of the period. + clipPeriodAtContentDuration = + period.getAdGroupCount() > 0 + && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount()); + } else if (period.isServerSideInsertedAdGroup(nextAdGroupIndex) + && period.getAdGroupTimeUs(nextAdGroupIndex) == period.durationUs) { + if (period.hasPlayedAdGroup(nextAdGroupIndex)) { + // Clip period before played SSAI post-rolls. + nextAdGroupIndex = C.INDEX_UNSET; + clipPeriodAtContentDuration = true; + } + } MediaPeriodId id = new MediaPeriodId(periodUid, windowSequenceNumber, nextAdGroupIndex); boolean isLastInPeriod = isLastInPeriod(id); boolean isLastInWindow = isLastInWindow(timeline, id); @@ -897,7 +918,7 @@ import com.google.common.collect.ImmutableList; long endPositionUs = nextAdGroupIndex != C.INDEX_UNSET ? period.getAdGroupTimeUs(nextAdGroupIndex) - : C.TIME_UNSET; + : clipPeriodAtContentDuration ? period.durationUs : C.TIME_UNSET; long durationUs = endPositionUs == C.TIME_UNSET || endPositionUs == C.TIME_END_OF_SOURCE ? period.durationUs diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 1ae8722199..67300997f5 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -46,8 +46,11 @@ import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE; import static androidx.media3.common.Player.COMMAND_SET_VOLUME; import static androidx.media3.common.Player.COMMAND_STOP; import static androidx.media3.common.Player.STATE_ENDED; +import static androidx.media3.exoplayer.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState; 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.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US; +import static androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US; import static androidx.media3.test.utils.TestUtil.assertTimelinesSame; import static androidx.media3.test.utils.robolectric.RobolectricUtil.runMainLooperUntil; import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.playUntilPosition; @@ -789,21 +792,8 @@ public final class ExoPlayerTest { .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); player.release(); - // There is still one discontinuity from content to content for the failed ad insertion. - PositionInfo positionInfo = - new PositionInfo( - window.uid, - /* mediaItemIndex= */ 0, - window.mediaItem, - period.uid, - /* periodIndex= */ 0, - /* positionMs= */ 5_000, - /* contentPositionMs= */ 5_000, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - verify(mockListener) - .onPositionDiscontinuity( - positionInfo, positionInfo, Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + // Content to content transition is ignored. + verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt()); } @Test @@ -863,24 +853,7 @@ public final class ExoPlayerTest { .getPeriod(/* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true); player.release(); - // There is still one discontinuity from content to content for the failed ad insertion and the - // normal ad transition for the successful ad insertion. - PositionInfo positionInfoFailedAd = - new PositionInfo( - window.uid, - /* mediaItemIndex= */ 0, - window.mediaItem, - period.uid, - /* periodIndex= */ 0, - /* positionMs= */ 5_000, - /* contentPositionMs= */ 5_000, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - verify(mockListener) - .onPositionDiscontinuity( - positionInfoFailedAd, - positionInfoFailedAd, - Player.DISCONTINUITY_REASON_AUTO_TRANSITION); + // There content to content discontinuity after the failed ad is suppressed. PositionInfo positionInfoContentAtSuccessfulAd = new PositionInfo( window.uid, @@ -5029,10 +5002,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); // seek discontinuities assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5121,10 +5093,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0).inOrder(); // seek assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5184,10 +5155,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 0, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 0, 0, 0, 0, 0).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(newPositions.get(0).periodIndex).isEqualTo(0); @@ -5252,10 +5222,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1, 2, 0, 0, 0, 0).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5334,10 +5303,9 @@ public final class ExoPlayerTest { oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(1).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(1).inOrder(); // seek discontinuity assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); @@ -5347,7 +5315,8 @@ public final class ExoPlayerTest { } @Test - public void play_playedSSAIPreMidPostRolls_skipsAllAds() throws Exception { + public void play_playedSSAIPreMidPostRollsMultiPeriodWindow_contentPeriodTransitionsOnly() + throws Exception { ArgumentCaptor oldPositionArgumentCaptor = ArgumentCaptor.forClass(PositionInfo.class); ArgumentCaptor newPositionArgumentCaptor = @@ -5371,7 +5340,7 @@ public final class ExoPlayerTest { AtomicReference sourceReference = new AtomicReference<>(); sourceReference.set( new ServerSideAdInsertionMediaSource( - new FakeMediaSource(adTimeline), + new FakeMediaSource(adTimeline, ExoPlayerTestRunner.AUDIO_FORMAT), contentTimeline -> { sourceReference .get() @@ -5385,16 +5354,18 @@ public final class ExoPlayerTest { runUntilPlaybackState(player, Player.STATE_ENDED); player.release(); + ArgumentCaptor playbackStateCaptor = ArgumentCaptor.forClass(Integer.class); + verify(listener, times(3)).onPlaybackStateChanged(playbackStateCaptor.capture()); + assertThat(playbackStateCaptor.getAllValues()).containsExactly(2, 3, 4).inOrder(); verify(listener, times(3)) .onPositionDiscontinuity( oldPositionArgumentCaptor.capture(), newPositionArgumentCaptor.capture(), reasonArgumentCaptor.capture()); + assertThat(reasonArgumentCaptor.getAllValues()).containsExactly(0, 0, 0).inOrder(); List oldPositions = oldPositionArgumentCaptor.getAllValues(); List newPositions = newPositionArgumentCaptor.getAllValues(); - List reasons = reasonArgumentCaptor.getAllValues(); - assertThat(reasons).containsExactly(0, 0, 0).inOrder(); - // Auto discontinuity from the empty ad period to the first content period. + // Auto discontinuity from the empty pre-roll period to the first content period. assertThat(oldPositions.get(0).periodIndex).isEqualTo(0); assertThat(oldPositions.get(0).adGroupIndex).isEqualTo(-1); assertThat(oldPositions.get(0).positionMs).isEqualTo(0); @@ -5407,7 +5378,7 @@ public final class ExoPlayerTest { assertThat(newPositions.get(1).periodIndex).isEqualTo(4); assertThat(newPositions.get(1).adGroupIndex).isEqualTo(-1); assertThat(newPositions.get(1).positionMs).isEqualTo(1250); - // Auto discontinuity from the second content period to the last frame of the last postroll. + // Auto discontinuity from the second content period to the last frame of the last ad period. assertThat(oldPositions.get(2).periodIndex).isEqualTo(4); assertThat(oldPositions.get(2).adGroupIndex).isEqualTo(-1); assertThat(newPositions.get(2).periodIndex).isEqualTo(7); @@ -5415,6 +5386,86 @@ public final class ExoPlayerTest { assertThat(newPositions.get(2).positionMs).isEqualTo(2500); } + @Test + public void play_playedSSAIPreMidPostRollsSinglePeriodWindow_noDiscontinuities() + throws Exception { + AdPlaybackState adPlaybackState = + addAdGroupToAdPlaybackState( + new AdPlaybackState("adsId"), + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (3 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (5 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + addAdGroupToAdPlaybackState( + adPlaybackState, + /* fromPositionUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + (9 * C.MICROS_PER_SECOND), + /* contentResumeOffsetUs= */ 0, + /* adDurationsUs...= */ C.MICROS_PER_SECOND); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup+ */ 0); + adPlaybackState = + adPlaybackState.withPlayedAd(/* adGroupIndex= */ 3, /* adIndexInAdGroup+ */ 0); + FakeTimeline adTimeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + "windowId", + /* isSeekable= */ true, + /* isDynamic= */ false, + /* isLive= */ false, + /* isPlaceholder= */ false, + /* durationUs= */ DEFAULT_WINDOW_DURATION_US, + /* defaultPositionUs= */ 0, + /* windowOffsetInFirstPeriodUs= */ DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + /* adPlaybackStates= */ ImmutableList.of(adPlaybackState), + MediaItem.EMPTY)); + + Listener listener = mock(Listener.class); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.addListener(listener); + AtomicReference sourceReference = new AtomicReference<>(); + sourceReference.set( + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(adTimeline, ExoPlayerTestRunner.AUDIO_FORMAT), + contentTimeline -> { + sourceReference + .get() + .setAdPlaybackStates(adTimeline.getAdPlaybackStates(/* windowIndex= */ 0)); + return true; + })); + player.setMediaSource(sourceReference.get()); + player.prepare(); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + long finalPositionMs = player.getCurrentPosition(); + player.release(); + + assertThat(finalPositionMs).isEqualTo(6000); + verify(listener, never()).onPositionDiscontinuity(any(), any(), anyInt()); + ArgumentCaptor playbackStateCaptor = ArgumentCaptor.forClass(Integer.class); + verify(listener, times(3)).onPlaybackStateChanged(playbackStateCaptor.capture()); + assertThat(playbackStateCaptor.getAllValues()).containsExactly(2, 3, 4).inOrder(); + } + @Test public void becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled() throws Exception { ExoPlayer player = new TestExoPlayerBuilder(context).build(); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index f0c0b42e9d..c649b6a680 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -388,7 +388,7 @@ public final class MediaPeriodQueueTest { /* periodUid= */ firstPeriodUid, /* startPositionUs= */ SECOND_AD_START_TIME_US, /* requestedContentPositionUs= */ SECOND_AD_START_TIME_US, - /* endPositionUs= */ C.TIME_UNSET, + /* endPositionUs= */ CONTENT_DURATION_US, /* durationUs= */ CONTENT_DURATION_US, /* isFollowedByTransitionToSameStream= */ false, /* isLastInPeriod= */ true, diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java index ef8d30a56c..82a5941365 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java @@ -68,7 +68,7 @@ public final class ServerSideAdInsertionMediaSourceTest { ShadowMediaCodecConfig.forAllSupportedMimeTypes(); private static final String TEST_ASSET = "asset:///media/mp4/sample.mp4"; - private static final String TEST_ASSET_DUMP = "playbackdumps/mp4/sample.mp4.dump"; + private static final String TEST_ASSET_DUMP = "playbackdumps/mp4/ssai-sample.mp4.dump"; @Test public void timeline_containsAdsDefinedInAdPlaybackState() throws Exception { diff --git a/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump b/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump new file mode 100644 index 0000000000..5417858cbd --- /dev/null +++ b/libraries/test_data/src/test/assets/playbackdumps/mp4/ssai-sample.mp4.dump @@ -0,0 +1,79 @@ +MediaCodecAdapter (exotest.audio.aac): + buffers.length = 44 + buffers[0] = length 23, hash 47DE9131 + buffers[1] = length 6, hash 31EC5206 + buffers[2] = length 148, hash 894A176B + buffers[3] = length 189, hash CEF235A1 + buffers[4] = length 205, hash BBF5F7B0 + buffers[5] = length 210, hash F278B193 + buffers[6] = length 210, hash 82DA1589 + buffers[7] = length 207, hash 5BE231DF + buffers[8] = length 225, hash 18819EE1 + buffers[9] = length 215, hash CA7FA67B + buffers[10] = length 211, hash 581A1C18 + buffers[11] = length 216, hash ADB88187 + buffers[12] = length 229, hash 2E8BA4DC + buffers[13] = length 232, hash 22F0C510 + buffers[14] = length 235, hash 867AD0DC + buffers[15] = length 231, hash 84E823A8 + buffers[16] = length 226, hash 1BEF3A95 + buffers[17] = length 216, hash EAA345AE + buffers[18] = length 229, hash 6957411F + buffers[19] = length 219, hash 41275022 + buffers[20] = length 241, hash 6495DF96 + buffers[21] = length 228, hash 63D95906 + buffers[22] = length 238, hash 34F676F9 + buffers[23] = length 234, hash E5CBC045 + buffers[24] = length 231, hash 5FC43661 + buffers[25] = length 217, hash 682708ED + buffers[26] = length 239, hash D43780FC + buffers[27] = length 243, hash C5E17980 + buffers[28] = length 231, hash AC5837BA + buffers[29] = length 230, hash 169EE895 + buffers[30] = length 238, hash C48FF3F1 + buffers[31] = length 225, hash 531E4599 + buffers[32] = length 232, hash CB3C6B8D + buffers[33] = length 243, hash F8C94C7 + buffers[34] = length 232, hash A646A7D0 + buffers[35] = length 237, hash E8B787A5 + buffers[36] = length 228, hash 3FA7A29F + buffers[37] = length 235, hash B9B33B0A + buffers[38] = length 264, hash 71A4869E + buffers[39] = length 257, hash D049B54C + buffers[40] = length 227, hash 66757231 + buffers[41] = length 227, hash BD374F1B + buffers[42] = length 235, hash 999477F6 + buffers[43] = length 0, hash 1 +MediaCodecAdapter (exotest.video.avc): + buffers.length = 31 + buffers[0] = length 36692, hash D216076E + buffers[1] = length 5312, hash D45D3CA0 + buffers[2] = length 599, hash 1BE7812D + buffers[3] = length 7735, hash 4490F110 + buffers[4] = length 987, hash 560B5036 + buffers[5] = length 673, hash ED7CD8C7 + buffers[6] = length 523, hash 3020DF50 + buffers[7] = length 6061, hash 736C72B2 + buffers[8] = length 992, hash FE132F23 + buffers[9] = length 623, hash 5B2C1816 + buffers[10] = length 421, hash 742E69C1 + buffers[11] = length 4899, hash F72F86A1 + buffers[12] = length 568, hash 519A8E50 + buffers[13] = length 620, hash 3990AA39 + buffers[14] = length 5450, hash F06EC4AA + buffers[15] = length 1051, hash 92DFA63A + buffers[16] = length 874, hash 69587FB4 + buffers[17] = length 781, hash 36BE495B + buffers[18] = length 4725, hash AC0C8CD3 + buffers[19] = length 1022, hash 5D8BFF34 + buffers[20] = length 790, hash 99413A99 + buffers[21] = length 610, hash 5E129290 + buffers[22] = length 2751, hash 769974CB + buffers[23] = length 745, hash B78A477A + buffers[24] = length 621, hash CF741E7A + buffers[25] = length 505, hash 1DB4894E + buffers[26] = length 1268, hash C15348DC + buffers[27] = length 880, hash C2DE85D0 + buffers[28] = length 530, hash C98BC6A8 + buffers[29] = length 568, hash 4FE5C8EA + buffers[30] = length 0, hash 1 From 91d4add08295e215bd7aa347e241c18b5537598f Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 12:12:34 +0000 Subject: [PATCH 217/251] Prevent adding multiple IMA SSAI media source instances to the playlist Currently only a single instance of ImaServerSideAdInsertionMediaSource is supported at the same time in a playlist. This change makes sure that an attempt to add multiple instances is prevented by throwing a an exception. #minor-release PiperOrigin-RevId: 428743140 --- .../ImaServerSideAdInsertionMediaSource.java | 17 +++++++++++++++++ .../ima/ImaServerSideAdInsertionUriBuilder.java | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 0e04bb4903..d77b50fc0c 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -379,6 +379,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) { + mainHandler.post(() -> assertSingleInstanceInPlaylist(checkNotNull(player))); super.prepareSourceInternal(mediaTransferListener); if (loader == null) { Loader loader = new Loader("ImaServerSideAdInsertionMediaSource"); @@ -1152,4 +1153,20 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou overlayInfo.reasonDetail != null ? overlayInfo.reasonDetail : "Unknown reason")); } } + + private static void assertSingleInstanceInPlaylist(Player player) { + int counter = 0; + for (int i = 0; i < player.getMediaItemCount(); i++) { + MediaItem mediaItem = player.getMediaItemAt(i); + if (mediaItem.localConfiguration != null + && C.SSAI_SCHEME.equals(mediaItem.localConfiguration.uri.getScheme()) + && ImaServerSideAdInsertionUriBuilder.IMA_AUTHORITY.equals( + mediaItem.localConfiguration.uri.getAuthority())) { + if (++counter > 1) { + throw new IllegalStateException( + "Multiple IMA server side ad insertion sources not supported."); + } + } + } + } } diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java index c9cf63973e..6e00303d58 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java @@ -43,7 +43,7 @@ public final class ImaServerSideAdInsertionUriBuilder { /** The default timeout for loading the video URI, in milliseconds. */ public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000; - private static final String IMA_AUTHORITY = "dai.google.com"; + /* package */ static final String IMA_AUTHORITY = "dai.google.com"; private static final String ADS_ID = "adsId"; private static final String ASSET_KEY = "assetKey"; private static final String API_KEY = "apiKey"; From 8b01d0751274881f215eca8ca470df956d8f69dc Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 15 Feb 2022 12:39:30 +0000 Subject: [PATCH 218/251] Flatten TrackSelectionParameters PiperOrigin-RevId: 428747243 --- .../media3/common/TrackSelectionOverride.java | 139 ++++++++ .../common/TrackSelectionOverrides.java | 313 ------------------ .../common/TrackSelectionParameters.java | 98 ++++-- .../common/TrackSelectionOverrideTest.java | 71 ++++ .../common/TrackSelectionOverridesTest.java | 172 ---------- .../common/TrackSelectionParametersTest.java | 131 +++++++- .../trackselection/DefaultTrackSelector.java | 50 ++- .../DefaultTrackSelectorTest.java | 70 ++-- .../androidx/media3/ui/PlayerControlView.java | 34 +- 9 files changed, 465 insertions(+), 613 deletions(-) create mode 100644 libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java delete mode 100644 libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java create mode 100644 libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverrideTest.java delete mode 100644 libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverridesTest.java diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java new file mode 100644 index 0000000000..77b9c2bbf1 --- /dev/null +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common; + +import static androidx.media3.common.util.Assertions.checkNotNull; +import static java.util.Collections.max; +import static java.util.Collections.min; + +import android.os.Bundle; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import androidx.media3.common.util.UnstableApi; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Forces the selection of {@link #trackIndices} for a {@link TrackGroup}. + * + *

      If multiple tracks in {@link #trackGroup} are overridden, as many as possible will be selected + * depending on the player capabilities. + * + *

      If {@link #trackIndices} is empty, no tracks from {@link #trackGroup} will be played. This is + * similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect the + * playback of the associated {@link TrackGroup}. For example, if the only {@link + * C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until the + * next video starts. + */ +public final class TrackSelectionOverride implements Bundleable { + + /** The {@link TrackGroup} whose {@link #trackIndices} are forced to be selected. */ + public final TrackGroup trackGroup; + /** The indices of tracks in a {@link TrackGroup} to be selected. */ + public final ImmutableList trackIndices; + + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + FIELD_TRACK_GROUP, + FIELD_TRACKS, + }) + private @interface FieldNumber {} + + private static final int FIELD_TRACK_GROUP = 0; + private static final int FIELD_TRACKS = 1; + + /** Constructs an instance to force all tracks in {@code trackGroup} to be selected. */ + public TrackSelectionOverride(TrackGroup trackGroup) { + this.trackGroup = trackGroup; + ImmutableList.Builder builder = new ImmutableList.Builder<>(); + for (int i = 0; i < trackGroup.length; i++) { + builder.add(i); + } + this.trackIndices = builder.build(); + } + + /** + * Constructs an instance to force {@code trackIndices} in {@code trackGroup} to be selected. + * + * @param trackGroup The {@link TrackGroup} for which to override the track selection. + * @param trackIndices The indices of the tracks in the {@link TrackGroup} to select. + */ + public TrackSelectionOverride(TrackGroup trackGroup, List trackIndices) { + if (!trackIndices.isEmpty()) { + if (min(trackIndices) < 0 || max(trackIndices) >= trackGroup.length) { + throw new IndexOutOfBoundsException(); + } + } + this.trackGroup = trackGroup; + this.trackIndices = ImmutableList.copyOf(trackIndices); + } + + /** Returns the {@link C.TrackType} of the overridden track group. */ + public @C.TrackType int getTrackType() { + return MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TrackSelectionOverride that = (TrackSelectionOverride) obj; + return trackGroup.equals(that.trackGroup) && trackIndices.equals(that.trackIndices); + } + + @Override + public int hashCode() { + return trackGroup.hashCode() + 31 * trackIndices.hashCode(); + } + + // Bundleable implementation + + @UnstableApi + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle()); + bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(trackIndices)); + return bundle; + } + + /** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */ + @UnstableApi + public static final Creator CREATOR = + bundle -> { + @Nullable Bundle trackGroupBundle = bundle.getBundle(keyForField(FIELD_TRACK_GROUP)); + checkNotNull(trackGroupBundle); // Mandatory as there are no reasonable defaults. + TrackGroup trackGroup = TrackGroup.CREATOR.fromBundle(trackGroupBundle); + @Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS)); + if (tracks == null) { + return new TrackSelectionOverride(trackGroup); + } + return new TrackSelectionOverride(trackGroup, Ints.asList(tracks)); + }; + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java deleted file mode 100644 index 16fffadb49..0000000000 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverrides.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.common; - -import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList; -import static androidx.media3.common.util.BundleableUtil.toBundleArrayList; -import static java.lang.annotation.ElementType.TYPE_USE; -import static java.util.Collections.max; -import static java.util.Collections.min; - -import android.os.Bundle; -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; -import androidx.media3.common.util.UnstableApi; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.primitives.Ints; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * Forces the selection of the specified tracks in {@link TrackGroup TrackGroups}. - * - *

      Each {@link TrackSelectionOverride override} only affects the selection of tracks of that - * {@link C.TrackType type}. For example overriding the selection of an {@link C#TRACK_TYPE_AUDIO - * audio} {@link TrackGroup} will not affect the selection of {@link C#TRACK_TYPE_VIDEO video} or - * {@link C#TRACK_TYPE_TEXT text} tracks. - * - *

      If multiple {@link TrackGroup TrackGroups} of the same {@link C.TrackType} are overridden, - * which tracks will be selected depend on the player capabilities. For example, by default {@code - * ExoPlayer} doesn't support selecting more than one {@link TrackGroup} per {@link C.TrackType}. - * - *

      Overrides of {@link TrackGroup} that are not currently available are ignored. For example, - * when the player transitions to the next {@link MediaItem} in a playlist, any overrides of the - * previous {@link MediaItem} are ignored. - * - * @see TrackSelectionParameters#trackSelectionOverrides - */ -public final class TrackSelectionOverrides implements Bundleable { - - /** Builder for {@link TrackSelectionOverrides}. */ - public static final class Builder { - // Cannot use ImmutableMap.Builder as it doesn't support removing entries. - private final HashMap overrides; - - /** Creates an builder with no {@link TrackSelectionOverride}. */ - public Builder() { - overrides = new HashMap<>(); - } - - private Builder(Map overrides) { - this.overrides = new HashMap<>(overrides); - } - - /** Adds an override for the provided {@link TrackGroup}. */ - @UnstableApi - public Builder addOverride(TrackSelectionOverride override) { - overrides.put(override.trackGroup, override); - return this; - } - - /** Removes the override associated with the provided {@link TrackGroup} if present. */ - @UnstableApi - public Builder clearOverride(TrackGroup trackGroup) { - overrides.remove(trackGroup); - return this; - } - - /** Set the override for the type of the provided {@link TrackGroup}. */ - public Builder setOverrideForType(TrackSelectionOverride override) { - clearOverridesOfType(override.getTrackType()); - overrides.put(override.trackGroup, override); - return this; - } - - /** - * Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}. - */ - public Builder clearOverridesOfType(@C.TrackType int trackType) { - for (Iterator it = overrides.values().iterator(); it.hasNext(); ) { - TrackSelectionOverride trackSelectionOverride = it.next(); - if (trackSelectionOverride.getTrackType() == trackType) { - it.remove(); - } - } - return this; - } - - /** Returns a new {@link TrackSelectionOverrides} instance with the current builder values. */ - public TrackSelectionOverrides build() { - return new TrackSelectionOverrides(overrides); - } - } - - /** - * Forces the selection of {@link #trackIndices} for a {@link TrackGroup}. - * - *

      If multiple tracks in {@link #trackGroup} are overridden, as many as possible will be - * selected depending on the player capabilities. - * - *

      If {@link #trackIndices} is empty, no tracks from {@link #trackGroup} will be played. This - * is similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect - * the playback of the associated {@link TrackGroup}. For example, if the only {@link - * C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until - * the next video starts. - */ - public static final class TrackSelectionOverride implements Bundleable { - - /** The {@link TrackGroup} whose {@link #trackIndices} are forced to be selected. */ - public final TrackGroup trackGroup; - /** The indices of tracks in a {@link TrackGroup} to be selected. */ - public final ImmutableList trackIndices; - - /** Constructs an instance to force all tracks in {@code trackGroup} to be selected. */ - public TrackSelectionOverride(TrackGroup trackGroup) { - this.trackGroup = trackGroup; - ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (int i = 0; i < trackGroup.length; i++) { - builder.add(i); - } - this.trackIndices = builder.build(); - } - - /** - * Constructs an instance to force {@code trackIndices} in {@code trackGroup} to be selected. - * - * @param trackGroup The {@link TrackGroup} for which to override the track selection. - * @param trackIndices The indices of the tracks in the {@link TrackGroup} to select. - */ - public TrackSelectionOverride(TrackGroup trackGroup, List trackIndices) { - if (!trackIndices.isEmpty()) { - if (min(trackIndices) < 0 || max(trackIndices) >= trackGroup.length) { - throw new IndexOutOfBoundsException(); - } - } - this.trackGroup = trackGroup; - this.trackIndices = ImmutableList.copyOf(trackIndices); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - TrackSelectionOverride that = (TrackSelectionOverride) obj; - return trackGroup.equals(that.trackGroup) && trackIndices.equals(that.trackIndices); - } - - @Override - public int hashCode() { - return trackGroup.hashCode() + 31 * trackIndices.hashCode(); - } - - /** Returns the {@link C.TrackType} of the overriden track group. */ - public @C.TrackType int getTrackType() { - return MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); - } - - // Bundleable implementation - - @Documented - @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) - @IntDef({ - FIELD_TRACK_GROUP, - FIELD_TRACKS, - }) - private @interface FieldNumber {} - - private static final int FIELD_TRACK_GROUP = 0; - private static final int FIELD_TRACKS = 1; - - @UnstableApi - @Override - public Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle()); - bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(trackIndices)); - return bundle; - } - - /** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */ - @UnstableApi - public static final Creator CREATOR = - bundle -> { - @Nullable Bundle trackGroupBundle = bundle.getBundle(keyForField(FIELD_TRACK_GROUP)); - checkNotNull(trackGroupBundle); // Mandatory as there are no reasonable defaults. - TrackGroup trackGroup = TrackGroup.CREATOR.fromBundle(trackGroupBundle); - @Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS)); - if (tracks == null) { - return new TrackSelectionOverride(trackGroup); - } - return new TrackSelectionOverride(trackGroup, Ints.asList(tracks)); - }; - - private static String keyForField(@FieldNumber int field) { - return Integer.toString(field, Character.MAX_RADIX); - } - } - - /** Empty {@code TrackSelectionOverrides}, where no track selection is overridden. */ - public static final TrackSelectionOverrides EMPTY = - new TrackSelectionOverrides(ImmutableMap.of()); - - private final ImmutableMap overrides; - - private TrackSelectionOverrides(Map overrides) { - this.overrides = ImmutableMap.copyOf(overrides); - } - - /** Returns a {@link Builder} initialized with the values of this instance. */ - public Builder buildUpon() { - return new Builder(overrides); - } - - /** Returns a list of the {@link TrackSelectionOverride overrides}. */ - @UnstableApi - public ImmutableList asList() { - return ImmutableList.copyOf(overrides.values()); - } - - /** - * Returns the {@link TrackSelectionOverride} of the provided {@link TrackGroup} or {@code null} - * if there is none. - */ - @Nullable - public TrackSelectionOverride getOverride(TrackGroup trackGroup) { - return overrides.get(trackGroup); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - TrackSelectionOverrides that = (TrackSelectionOverrides) obj; - return overrides.equals(that.overrides); - } - - @Override - public int hashCode() { - return overrides.hashCode(); - } - - // Bundleable implementation - - @Documented - @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) - @IntDef({ - FIELD_OVERRIDES, - }) - private @interface FieldNumber {} - - private static final int FIELD_OVERRIDES = 0; - - @UnstableApi - @Override - public Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putParcelableArrayList( - keyForField(FIELD_OVERRIDES), toBundleArrayList(overrides.values())); - return bundle; - } - - /** Object that can restore {@code TrackSelectionOverrides} from a {@link Bundle}. */ - @UnstableApi - public static final Creator CREATOR = - bundle -> { - List trackSelectionOverrides = - fromBundleNullableList( - TrackSelectionOverride.CREATOR, - bundle.getParcelableArrayList(keyForField(FIELD_OVERRIDES)), - ImmutableList.of()); - ImmutableMap.Builder builder = - new ImmutableMap.Builder<>(); - for (int i = 0; i < trackSelectionOverrides.size(); i++) { - TrackSelectionOverride trackSelectionOverride = trackSelectionOverrides.get(i); - builder.put(trackSelectionOverride.trackGroup, trackSelectionOverride); - } - return new TrackSelectionOverrides(builder.buildOrThrow()); - }; - - private static String keyForField(@FieldNumber int field) { - return Integer.toString(field, Character.MAX_RADIX); - } -} diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java index 91ac8c3a9d..c70376d4fd 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionParameters.java @@ -16,9 +16,9 @@ package androidx.media3.common; import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.BundleableUtil.fromNullableBundle; +import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList; +import static androidx.media3.common.util.BundleableUtil.toBundleArrayList; import static com.google.common.base.MoreObjects.firstNonNull; -import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.graphics.Point; @@ -31,12 +31,15 @@ import androidx.annotation.RequiresApi; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Ints; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Set; import org.checkerframework.checker.initialization.qual.UnknownInitialization; @@ -95,7 +98,7 @@ public class TrackSelectionParameters implements Bundleable { // General private boolean forceLowestBitrate; private boolean forceHighestSupportedBitrate; - private TrackSelectionOverrides trackSelectionOverrides; + private HashMap overrides; private ImmutableSet<@C.TrackType Integer> disabledTrackTypes; /** @@ -128,7 +131,7 @@ public class TrackSelectionParameters implements Bundleable { // General forceLowestBitrate = false; forceHighestSupportedBitrate = false; - trackSelectionOverrides = TrackSelectionOverrides.EMPTY; + overrides = new HashMap<>(); disabledTrackTypes = ImmutableSet.of(); } @@ -236,11 +239,16 @@ public class TrackSelectionParameters implements Bundleable { bundle.getBoolean( keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), DEFAULT_WITHOUT_CONTEXT.forceHighestSupportedBitrate); - trackSelectionOverrides = - fromNullableBundle( - TrackSelectionOverrides.CREATOR, - bundle.getBundle(keyForField(FIELD_SELECTION_OVERRIDE_KEYS)), - TrackSelectionOverrides.EMPTY); + overrides = new HashMap<>(); + List overrideList = + fromBundleNullableList( + TrackSelectionOverride.CREATOR, + bundle.getParcelableArrayList(keyForField(FIELD_SELECTION_OVERRIDES)), + ImmutableList.of()); + for (int i = 0; i < overrideList.size(); i++) { + TrackSelectionOverride override = overrideList.get(i); + overrides.put(override.trackGroup, override); + } disabledTrackTypes = ImmutableSet.copyOf( Ints.asList( @@ -254,7 +262,7 @@ public class TrackSelectionParameters implements Bundleable { "preferredAudioLanguages", "preferredAudioMimeTypes", "preferredTextLanguages", - "trackSelectionOverrides", + "overrides", "disabledTrackTypes", }) private void init(@UnknownInitialization Builder this, TrackSelectionParameters parameters) { @@ -285,8 +293,8 @@ public class TrackSelectionParameters implements Bundleable { // General forceLowestBitrate = parameters.forceLowestBitrate; forceHighestSupportedBitrate = parameters.forceHighestSupportedBitrate; - trackSelectionOverrides = parameters.trackSelectionOverrides; disabledTrackTypes = parameters.disabledTrackTypes; + overrides = new HashMap<>(parameters.overrides); } /** Overrides the value of the builder with the value of {@link TrackSelectionParameters}. */ @@ -646,14 +654,42 @@ public class TrackSelectionParameters implements Bundleable { return this; } + /** Adds an override for the provided {@link TrackGroup}. */ + public Builder addOverride(TrackSelectionOverride override) { + overrides.put(override.trackGroup, override); + return this; + } + + /** Removes the override associated with the provided {@link TrackGroup} if present. */ + public Builder clearOverride(TrackGroup trackGroup) { + overrides.remove(trackGroup); + return this; + } + + /** Set the override for the type of the provided {@link TrackGroup}. */ + public Builder setOverrideForType(TrackSelectionOverride override) { + clearOverridesOfType(override.getTrackType()); + overrides.put(override.trackGroup, override); + return this; + } + /** - * Sets the selection overrides. - * - * @param trackSelectionOverrides The track selection overrides. - * @return This builder. + * Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}. */ - public Builder setTrackSelectionOverrides(TrackSelectionOverrides trackSelectionOverrides) { - this.trackSelectionOverrides = trackSelectionOverrides; + public Builder clearOverridesOfType(@C.TrackType int trackType) { + Iterator it = overrides.values().iterator(); + while (it.hasNext()) { + TrackSelectionOverride override = it.next(); + if (override.getTrackType() == trackType) { + it.remove(); + } + } + return this; + } + + /** Removes all track overrides. */ + public Builder clearOverrides() { + overrides.clear(); return this; } @@ -860,8 +896,9 @@ public class TrackSelectionParameters implements Bundleable { */ public final boolean forceHighestSupportedBitrate; - /** Overrides to force tracks to be selected. */ - public final TrackSelectionOverrides trackSelectionOverrides; + /** Overrides to force selection of specific tracks. */ + public final ImmutableMap overrides; + /** * The track types that are disabled. No track of a disabled type will be selected, thus no track * type contained in the set will be played. The default value is that no track type is disabled @@ -898,7 +935,7 @@ public class TrackSelectionParameters implements Bundleable { // General this.forceLowestBitrate = builder.forceLowestBitrate; this.forceHighestSupportedBitrate = builder.forceHighestSupportedBitrate; - this.trackSelectionOverrides = builder.trackSelectionOverrides; + this.overrides = ImmutableMap.copyOf(builder.overrides); this.disabledTrackTypes = builder.disabledTrackTypes; } @@ -943,7 +980,7 @@ public class TrackSelectionParameters implements Bundleable { // General && forceLowestBitrate == other.forceLowestBitrate && forceHighestSupportedBitrate == other.forceHighestSupportedBitrate - && trackSelectionOverrides.equals(other.trackSelectionOverrides) + && overrides.equals(other.overrides) && disabledTrackTypes.equals(other.disabledTrackTypes); } @@ -977,7 +1014,7 @@ public class TrackSelectionParameters implements Bundleable { // General result = 31 * result + (forceLowestBitrate ? 1 : 0); result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0); - result = 31 * result + trackSelectionOverrides.hashCode(); + result = 31 * result + overrides.hashCode(); result = 31 * result + disabledTrackTypes.hashCode(); return result; } @@ -986,7 +1023,6 @@ public class TrackSelectionParameters implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) @IntDef({ FIELD_PREFERRED_AUDIO_LANGUAGES, FIELD_PREFERRED_AUDIO_ROLE_FLAGS, @@ -1010,8 +1046,7 @@ public class TrackSelectionParameters implements Bundleable { FIELD_PREFERRED_AUDIO_MIME_TYPES, FIELD_FORCE_LOWEST_BITRATE, FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE, - FIELD_SELECTION_OVERRIDE_KEYS, - FIELD_SELECTION_OVERRIDE_VALUES, + FIELD_SELECTION_OVERRIDES, FIELD_DISABLED_TRACK_TYPE, FIELD_PREFERRED_VIDEO_ROLE_FLAGS }) @@ -1039,10 +1074,9 @@ public class TrackSelectionParameters implements Bundleable { private static final int FIELD_PREFERRED_AUDIO_MIME_TYPES = 20; private static final int FIELD_FORCE_LOWEST_BITRATE = 21; private static final int FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE = 22; - private static final int FIELD_SELECTION_OVERRIDE_KEYS = 23; - private static final int FIELD_SELECTION_OVERRIDE_VALUES = 24; - private static final int FIELD_DISABLED_TRACK_TYPE = 25; - private static final int FIELD_PREFERRED_VIDEO_ROLE_FLAGS = 26; + private static final int FIELD_SELECTION_OVERRIDES = 23; + private static final int FIELD_DISABLED_TRACK_TYPE = 24; + private static final int FIELD_PREFERRED_VIDEO_ROLE_FLAGS = 25; @UnstableApi @Override @@ -1086,8 +1120,8 @@ public class TrackSelectionParameters implements Bundleable { bundle.putBoolean(keyForField(FIELD_FORCE_LOWEST_BITRATE), forceLowestBitrate); bundle.putBoolean( keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), forceHighestSupportedBitrate); - bundle.putBundle( - keyForField(FIELD_SELECTION_OVERRIDE_KEYS), trackSelectionOverrides.toBundle()); + bundle.putParcelableArrayList( + keyForField(FIELD_SELECTION_OVERRIDES), toBundleArrayList(overrides.values())); bundle.putIntArray(keyForField(FIELD_DISABLED_TRACK_TYPE), Ints.toArray(disabledTrackTypes)); return bundle; diff --git a/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverrideTest.java b/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverrideTest.java new file mode 100644 index 0000000000..1de9157adb --- /dev/null +++ b/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverrideTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link TrackSelectionOverride}. */ +@RunWith(AndroidJUnit4.class) +public final class TrackSelectionOverrideTest { + + @Test + public void newTrackSelectionOverride_withJustTrackGroup_selectsAllTracks() { + TrackSelectionOverride trackSelectionOverride = + new TrackSelectionOverride(newTrackGroupWithIds(1, 2)); + + assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); + assertThat(trackSelectionOverride.trackIndices).containsExactly(0, 1).inOrder(); + } + + @Test + public void newTrackSelectionOverride_withTracks_selectsOnlySpecifiedTracks() { + TrackSelectionOverride trackSelectionOverride = + new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(1)); + + assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); + assertThat(trackSelectionOverride.trackIndices).containsExactly(1); + } + + @Test + public void newTrackSelectionOverride_with0Tracks_selectsAllSpecifiedTracks() { + TrackSelectionOverride trackSelectionOverride = + new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of()); + + assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); + assertThat(trackSelectionOverride.trackIndices).isEmpty(); + } + + @Test + public void newTrackSelectionOverride_withInvalidIndex_throws() { + assertThrows( + IndexOutOfBoundsException.class, + () -> new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(2))); + } + + private static TrackGroup newTrackGroupWithIds(int... ids) { + return new TrackGroup( + Arrays.stream(ids) + .mapToObj(id -> new Format.Builder().setId(id).build()) + .toArray(Format[]::new)); + } +} diff --git a/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverridesTest.java b/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverridesTest.java deleted file mode 100644 index 19c53614a7..0000000000 --- a/libraries/common/src/test/java/androidx/media3/common/TrackSelectionOverridesTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.common; - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; - -import androidx.media3.common.TrackSelectionOverrides.TrackSelectionOverride; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link TrackSelectionOverrides}. */ -@RunWith(AndroidJUnit4.class) -public final class TrackSelectionOverridesTest { - - public static final TrackGroup AAC_TRACK_GROUP = - new TrackGroup(new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build()); - - private static TrackGroup newTrackGroupWithIds(int... ids) { - return new TrackGroup( - Arrays.stream(ids) - .mapToObj(id -> new Format.Builder().setId(id).build()) - .toArray(Format[]::new)); - } - - @Test - public void newTrackSelectionOverride_withJustTrackGroup_selectsAllTracks() { - TrackSelectionOverride trackSelectionOverride = - new TrackSelectionOverride(newTrackGroupWithIds(1, 2)); - - assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); - assertThat(trackSelectionOverride.trackIndices).containsExactly(0, 1).inOrder(); - } - - @Test - public void newTrackSelectionOverride_withTracks_selectsOnlySpecifiedTracks() { - TrackSelectionOverride trackSelectionOverride = - new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(1)); - - assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); - assertThat(trackSelectionOverride.trackIndices).containsExactly(1); - } - - @Test - public void newTrackSelectionOverride_with0Tracks_selectsAllSpecifiedTracks() { - TrackSelectionOverride trackSelectionOverride = - new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of()); - - assertThat(trackSelectionOverride.trackGroup).isEqualTo(newTrackGroupWithIds(1, 2)); - assertThat(trackSelectionOverride.trackIndices).isEmpty(); - } - - @Test - public void newTrackSelectionOverride_withInvalidIndex_throws() { - assertThrows( - IndexOutOfBoundsException.class, - () -> new TrackSelectionOverride(newTrackGroupWithIds(1, 2), ImmutableList.of(2))); - } - - @Test - public void roundTripViaBundle_withOverrides_yieldsEqualInstance() { - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder() - .setOverrideForType( - new TrackSelectionOverride(newTrackGroupWithIds(3, 4), ImmutableList.of(1))) - .addOverride(new TrackSelectionOverride(newTrackGroupWithIds(5, 6))) - .build(); - - TrackSelectionOverrides fromBundle = - TrackSelectionOverrides.CREATOR.fromBundle(trackSelectionOverrides.toBundle()); - - assertThat(fromBundle).isEqualTo(trackSelectionOverrides); - assertThat(fromBundle.asList()).isEqualTo(trackSelectionOverrides.asList()); - } - - @Test - public void builder_byDefault_isEmpty() { - TrackSelectionOverrides trackSelectionOverrides = new TrackSelectionOverrides.Builder().build(); - - assertThat(trackSelectionOverrides.asList()).isEmpty(); - assertThat(trackSelectionOverrides).isEqualTo(TrackSelectionOverrides.EMPTY); - } - - @Test - public void addOverride_onDifferentGroups_addsOverride() { - TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1)); - TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2)); - - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder().addOverride(override1).addOverride(override2).build(); - - assertThat(trackSelectionOverrides.asList()).containsExactly(override1, override2); - assertThat(trackSelectionOverrides.getOverride(override1.trackGroup)).isEqualTo(override1); - assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2); - } - - @Test - public void addOverride_onSameGroup_replacesOverride() { - TrackGroup trackGroup = newTrackGroupWithIds(1, 2, 3); - TrackSelectionOverride override1 = - new TrackSelectionOverride(trackGroup, /* trackIndices= */ ImmutableList.of(0)); - TrackSelectionOverride override2 = - new TrackSelectionOverride(trackGroup, /* trackIndices= */ ImmutableList.of(1)); - - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder().addOverride(override1).addOverride(override2).build(); - - assertThat(trackSelectionOverrides.asList()).containsExactly(override2); - assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2); - } - - @Test - public void setOverrideForType_onSameType_replacesOverride() { - TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1)); - TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2)); - - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder() - .setOverrideForType(override1) - .setOverrideForType(override2) - .build(); - - assertThat(trackSelectionOverrides.asList()).containsExactly(override2); - assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2); - } - - @Test - public void clearOverridesOfType_ofTypeAudio_removesAudioOverride() { - TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP); - TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1)); - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder() - .addOverride(override1) - .addOverride(override2) - .clearOverridesOfType(C.TRACK_TYPE_AUDIO) - .build(); - - assertThat(trackSelectionOverrides.asList()).containsExactly(override2); - assertThat(trackSelectionOverrides.getOverride(override2.trackGroup)).isEqualTo(override2); - } - - @Test - public void clearOverride_ofTypeGroup_removesOverride() { - TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP); - TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1)); - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder() - .addOverride(override1) - .addOverride(override2) - .clearOverride(override2.trackGroup) - .build(); - - assertThat(trackSelectionOverrides.asList()).containsExactly(override1); - assertThat(trackSelectionOverrides.getOverride(override1.trackGroup)).isEqualTo(override1); - } -} diff --git a/libraries/common/src/test/java/androidx/media3/common/TrackSelectionParametersTest.java b/libraries/common/src/test/java/androidx/media3/common/TrackSelectionParametersTest.java index 175644edfc..d11efe782d 100644 --- a/libraries/common/src/test/java/androidx/media3/common/TrackSelectionParametersTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/TrackSelectionParametersTest.java @@ -18,7 +18,6 @@ package androidx.media3.common; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.google.common.truth.Truth.assertThat; -import androidx.media3.common.TrackSelectionOverrides.TrackSelectionOverride; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -29,6 +28,9 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class TrackSelectionParametersTest { + private static final TrackGroup AAC_TRACK_GROUP = + new TrackGroup(new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build()); + @Test public void defaultValue_withoutChange_isAsExpected() { TrackSelectionParameters parameters = TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT; @@ -59,22 +61,19 @@ public final class TrackSelectionParametersTest { // General assertThat(parameters.forceLowestBitrate).isFalse(); assertThat(parameters.forceHighestSupportedBitrate).isFalse(); - assertThat(parameters.trackSelectionOverrides.asList()).isEmpty(); + assertThat(parameters.overrides).isEmpty(); assertThat(parameters.disabledTrackTypes).isEmpty(); } @Test public void parametersSet_fromDefault_isAsExpected() { - TrackSelectionOverrides trackSelectionOverrides = - new TrackSelectionOverrides.Builder() - .addOverride(new TrackSelectionOverride(new TrackGroup(new Format.Builder().build()))) - .addOverride( - new TrackSelectionOverride( - new TrackGroup( - new Format.Builder().setId(4).build(), - new Format.Builder().setId(5).build()), - /* trackIndices= */ ImmutableList.of(1))) - .build(); + TrackSelectionOverride override1 = + new TrackSelectionOverride(new TrackGroup(new Format.Builder().build())); + TrackSelectionOverride override2 = + new TrackSelectionOverride( + new TrackGroup( + new Format.Builder().setId(4).build(), new Format.Builder().setId(5).build()), + /* trackIndices= */ ImmutableList.of(1)); TrackSelectionParameters parameters = TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT .buildUpon() @@ -103,7 +102,13 @@ public final class TrackSelectionParametersTest { // General .setForceLowestBitrate(false) .setForceHighestSupportedBitrate(true) - .setTrackSelectionOverrides(trackSelectionOverrides) + .addOverride(new TrackSelectionOverride(new TrackGroup(new Format.Builder().build()))) + .addOverride( + new TrackSelectionOverride( + new TrackGroup( + new Format.Builder().setId(4).build(), + new Format.Builder().setId(5).build()), + /* trackIndices= */ ImmutableList.of(1))) .setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT)) .build(); @@ -137,7 +142,8 @@ public final class TrackSelectionParametersTest { // General assertThat(parameters.forceLowestBitrate).isFalse(); assertThat(parameters.forceHighestSupportedBitrate).isTrue(); - assertThat(parameters.trackSelectionOverrides).isEqualTo(trackSelectionOverrides); + assertThat(parameters.overrides) + .containsExactly(override1.trackGroup, override1, override2.trackGroup, override2); assertThat(parameters.disabledTrackTypes) .containsExactly(C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT); } @@ -178,4 +184,101 @@ public final class TrackSelectionParametersTest { assertThat(parameters.viewportHeight).isEqualTo(Integer.MAX_VALUE); assertThat(parameters.viewportOrientationMayChange).isTrue(); } + + @Test + public void roundTripViaBundle_withOverride_yieldsEqualInstance() { + TrackSelectionOverride override = + new TrackSelectionOverride( + newTrackGroupWithIds(3, 4), /* trackIndices= */ ImmutableList.of(1)); + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()).addOverride(override).build(); + + TrackSelectionParameters fromBundle = + TrackSelectionParameters.CREATOR.fromBundle(trackSelectionParameters.toBundle()); + + assertThat(fromBundle).isEqualTo(trackSelectionParameters); + assertThat(trackSelectionParameters.overrides).containsExactly(override.trackGroup, override); + } + + @Test + public void addOverride_onDifferentGroups_addsOverride() { + TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1)); + TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2)); + + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()) + .addOverride(override1) + .addOverride(override2) + .build(); + + assertThat(trackSelectionParameters.overrides) + .containsExactly(override1.trackGroup, override1, override2.trackGroup, override2); + } + + @Test + public void addOverride_onSameGroup_replacesOverride() { + TrackGroup trackGroup = newTrackGroupWithIds(1, 2, 3); + TrackSelectionOverride override1 = + new TrackSelectionOverride(trackGroup, /* trackIndices= */ ImmutableList.of(0)); + TrackSelectionOverride override2 = + new TrackSelectionOverride(trackGroup, /* trackIndices= */ ImmutableList.of(1)); + + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()) + .addOverride(override1) + .addOverride(override2) + .build(); + + assertThat(trackSelectionParameters.overrides).containsExactly(override2.trackGroup, override2); + } + + @Test + public void setOverrideForType_onSameType_replacesOverride() { + TrackSelectionOverride override1 = new TrackSelectionOverride(newTrackGroupWithIds(1)); + TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(2)); + + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()) + .setOverrideForType(override1) + .setOverrideForType(override2) + .build(); + + assertThat(trackSelectionParameters.overrides).containsExactly(override2.trackGroup, override2); + } + + @Test + public void clearOverridesOfType_ofTypeAudio_removesAudioOverride() { + TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP); + TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1)); + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()) + .addOverride(override1) + .addOverride(override2) + .clearOverridesOfType(C.TRACK_TYPE_AUDIO) + .build(); + + assertThat(trackSelectionParameters.overrides).containsExactly(override2.trackGroup, override2); + } + + @Test + public void clearOverride_ofTypeGroup_removesOverride() { + TrackSelectionOverride override1 = new TrackSelectionOverride(AAC_TRACK_GROUP); + TrackSelectionOverride override2 = new TrackSelectionOverride(newTrackGroupWithIds(1)); + TrackSelectionParameters trackSelectionParameters = + new TrackSelectionParameters.Builder(getApplicationContext()) + .addOverride(override1) + .addOverride(override2) + .clearOverride(override2.trackGroup) + .build(); + + assertThat(trackSelectionParameters.overrides).containsExactly(override1.trackGroup, override1); + } + + private static TrackGroup newTrackGroupWithIds(int... ids) { + Format[] formats = new Format[ids.length]; + for (int i = 0; i < ids.length; i++) { + formats[i] = new Format.Builder().setId(ids[i]).build(); + } + return new TrackGroup(formats); + } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 037304cfe2..d59db6a91e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -38,8 +38,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelection; -import androidx.media3.common.TrackSelectionOverrides; -import androidx.media3.common.TrackSelectionOverrides.TrackSelectionOverride; +import androidx.media3.common.TrackSelectionOverride; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.BundleableUtil; @@ -598,9 +597,32 @@ public class DefaultTrackSelector extends MappingTrackSelector { } @Override - public ParametersBuilder setTrackSelectionOverrides( - TrackSelectionOverrides trackSelectionOverrides) { - super.setTrackSelectionOverrides(trackSelectionOverrides); + public ParametersBuilder addOverride(TrackSelectionOverride override) { + super.addOverride(override); + return this; + } + + @Override + public ParametersBuilder clearOverride(TrackGroup trackGroup) { + super.clearOverride(trackGroup); + return this; + } + + @Override + public ParametersBuilder setOverrideForType(TrackSelectionOverride override) { + super.setOverrideForType(override); + return this; + } + + @Override + public ParametersBuilder clearOverridesOfType(@C.TrackType int trackType) { + super.clearOverridesOfType(trackType); + return this; + } + + @Override + public ParametersBuilder clearOverrides() { + super.clearOverrides(); return this; } @@ -705,7 +727,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param groups The {@link TrackGroupArray} for which the override should be applied. * @param override The override. * @return This builder. - * @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}. + * @deprecated Use {@link TrackSelectionParameters.Builder#addOverride(TrackSelectionOverride)}. */ @Deprecated public final ParametersBuilder setSelectionOverride( @@ -730,7 +752,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param rendererIndex The renderer index. * @param groups The {@link TrackGroupArray} for which the override should be cleared. * @return This builder. - * @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}. + * @deprecated Use {@link TrackSelectionParameters.Builder#clearOverride(TrackGroup)}. */ @Deprecated public final ParametersBuilder clearSelectionOverride( @@ -753,7 +775,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * * @param rendererIndex The renderer index. * @return This builder. - * @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}. + * @deprecated Use {@link TrackSelectionParameters.Builder#clearOverridesOfType(int)}. */ @Deprecated public final ParametersBuilder clearSelectionOverrides(int rendererIndex) { @@ -771,7 +793,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * Clears all track selection overrides for all renderers. * * @return This builder. - * @deprecated Use {@link TrackSelectionParameters.Builder#setTrackSelectionOverrides}. + * @deprecated Use {@link TrackSelectionParameters.Builder#clearOverrides()}. */ @Deprecated public final ParametersBuilder clearSelectionOverrides() { @@ -1022,7 +1044,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @return Whether there is an override. * @deprecated Only works to retrieve the overrides set with the deprecated {@link * ParametersBuilder#setSelectionOverride(int, TrackGroupArray, SelectionOverride)}. Use - * {@link TrackSelectionParameters#trackSelectionOverrides} instead. + * {@link TrackSelectionParameters#overrides} instead. */ @Deprecated public final boolean hasSelectionOverride(int rendererIndex, TrackGroupArray groups) { @@ -1039,7 +1061,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @return The override, or null if no override exists. * @deprecated Only works to retrieve the overrides set with the deprecated {@link * ParametersBuilder#setSelectionOverride(int, TrackGroupArray, SelectionOverride)}. Use - * {@link TrackSelectionParameters#trackSelectionOverrides} instead. + * {@link TrackSelectionParameters#overrides} instead. */ @Deprecated @Nullable @@ -1661,9 +1683,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); for (int j = 0; j < rendererTrackGroups.length; j++) { maybeUpdateApplicableOverrides( - applicableOverrides, - params.trackSelectionOverrides.getOverride(rendererTrackGroups.get(j)), - rendererIndex); + applicableOverrides, params.overrides.get(rendererTrackGroups.get(j)), rendererIndex); } } // Also iterate unmapped groups to see if they have overrides. @@ -1671,7 +1691,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { for (int i = 0; i < unmappedGroups.length; i++) { maybeUpdateApplicableOverrides( applicableOverrides, - params.trackSelectionOverrides.getOverride(unmappedGroups.get(i)), + params.overrides.get(unmappedGroups.get(i)), /* rendererIndex= */ C.INDEX_UNSET); } return applicableOverrides; diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java index a5514c7068..442725a2f5 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelectorTest.java @@ -42,8 +42,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; import androidx.media3.common.TrackSelection; -import androidx.media3.common.TrackSelectionOverrides; -import androidx.media3.common.TrackSelectionOverrides.TrackSelectionOverride; +import androidx.media3.common.TrackSelectionOverride; import androidx.media3.common.TracksInfo; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.ExoPlaybackException; @@ -171,10 +170,8 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .addOverride(new TrackSelectionOverride(VIDEO_TRACK_GROUP, ImmutableList.of())) - .build())); + .addOverride(new TrackSelectionOverride(VIDEO_TRACK_GROUP, ImmutableList.of())) + .build()); TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS, periodId, TIMELINE); @@ -225,18 +222,16 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .addOverride( - new TrackSelectionOverride( - videoGroupHighBitrate, /* trackIndices= */ ImmutableList.of())) - .addOverride( - new TrackSelectionOverride( - videoGroupMidBitrate, /* trackIndices= */ ImmutableList.of(0))) - .addOverride( - new TrackSelectionOverride( - videoGroupLowBitrate, /* trackIndices= */ ImmutableList.of())) - .build())); + .addOverride( + new TrackSelectionOverride( + videoGroupHighBitrate, /* trackIndices= */ ImmutableList.of())) + .addOverride( + new TrackSelectionOverride( + videoGroupMidBitrate, /* trackIndices= */ ImmutableList.of(0))) + .addOverride( + new TrackSelectionOverride( + videoGroupLowBitrate, /* trackIndices= */ ImmutableList.of())) + .build()); TrackSelectorResult result = trackSelector.selectTracks( @@ -260,12 +255,10 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .setOverrideForType( - new TrackSelectionOverride( - new TrackGroup(VIDEO_FORMAT, VIDEO_FORMAT), ImmutableList.of())) - .build())); + .setOverrideForType( + new TrackSelectionOverride( + new TrackGroup(VIDEO_FORMAT, VIDEO_FORMAT), ImmutableList.of())) + .build()); TrackSelectorResult result = trackSelector.selectTracks( @@ -309,10 +302,8 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .setOverrideForType(new TrackSelectionOverride(videoGroupH264)) - .build())); + .setOverrideForType(new TrackSelectionOverride(videoGroupH264)) + .build()); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {rendererCapabilitiesH264, rendererCapabilitiesAv1}, @@ -328,10 +319,8 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .setOverrideForType(new TrackSelectionOverride(videoGroupAv1)) - .build())); + .setOverrideForType(new TrackSelectionOverride(videoGroupAv1)) + .build()); result = trackSelector.selectTracks( new RendererCapabilities[] {rendererCapabilitiesH264, rendererCapabilitiesAv1}, @@ -361,10 +350,8 @@ public final class DefaultTrackSelectorTest { trackSelector.setParameters( trackSelector .buildUponParameters() - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .setOverrideForType(new TrackSelectionOverride(audioGroupUnsupported)) - .build())); + .setOverrideForType(new TrackSelectionOverride(audioGroupUnsupported)) + .build()); TrackSelectorResult result = trackSelector.selectTracks( new RendererCapabilities[] {VIDEO_CAPABILITIES, audioRendererCapabilties}, @@ -2397,13 +2384,10 @@ public final class DefaultTrackSelectorTest { .setRendererDisabled(1, true) .setRendererDisabled(3, true) .setRendererDisabled(5, false) - .setTrackSelectionOverrides( - new TrackSelectionOverrides.Builder() - .setOverrideForType( - new TrackSelectionOverride( - new TrackGroup(AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT), - /* trackIndices= */ ImmutableList.of(0, 2, 3))) - .build()) + .setOverrideForType( + new TrackSelectionOverride( + new TrackGroup(AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT), + /* trackIndices= */ ImmutableList.of(0, 2, 3))) .setDisabledTrackTypes(ImmutableSet.of(C.TRACK_TYPE_AUDIO)) .build(); } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java index ea16390550..35f5cabd5d 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerControlView.java @@ -63,8 +63,7 @@ import androidx.media3.common.Player.Events; import androidx.media3.common.Player.State; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; -import androidx.media3.common.TrackSelectionOverrides; -import androidx.media3.common.TrackSelectionOverrides.TrackSelectionOverride; +import androidx.media3.common.TrackSelectionOverride; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; import androidx.media3.common.TracksInfo.TrackGroupInfo; @@ -1902,7 +1901,7 @@ public class PlayerControlView extends FrameLayout { holder.textView.setText(R.string.exo_track_selection_auto); // hasSelectionOverride is true means there is an explicit track selection, not "Auto". TrackSelectionParameters parameters = checkNotNull(player).getTrackSelectionParameters(); - boolean hasSelectionOverride = hasSelectionOverride(parameters.trackSelectionOverrides); + boolean hasSelectionOverride = hasSelectionOverride(parameters); holder.checkView.setVisibility(hasSelectionOverride ? INVISIBLE : VISIBLE); holder.itemView.setOnClickListener( v -> { @@ -1911,12 +1910,6 @@ public class PlayerControlView extends FrameLayout { } TrackSelectionParameters trackSelectionParameters = player.getTrackSelectionParameters(); - TrackSelectionOverrides trackSelectionOverrides = - trackSelectionParameters - .trackSelectionOverrides - .buildUpon() - .clearOverridesOfType(C.TRACK_TYPE_AUDIO) - .build(); Set<@C.TrackType Integer> disabledTrackTypes = new HashSet<>(trackSelectionParameters.disabledTrackTypes); disabledTrackTypes.remove(C.TRACK_TYPE_AUDIO); @@ -1924,7 +1917,7 @@ public class PlayerControlView extends FrameLayout { .setTrackSelectionParameters( trackSelectionParameters .buildUpon() - .setTrackSelectionOverrides(trackSelectionOverrides) + .clearOverridesOfType(C.TRACK_TYPE_AUDIO) .setDisabledTrackTypes(disabledTrackTypes) .build()); settingsAdapter.setSubTextAtPosition( @@ -1934,10 +1927,10 @@ public class PlayerControlView extends FrameLayout { }); } - private boolean hasSelectionOverride(TrackSelectionOverrides trackSelectionOverrides) { + private boolean hasSelectionOverride(TrackSelectionParameters trackSelectionParameters) { for (int i = 0; i < tracks.size(); i++) { TrackGroup trackGroup = tracks.get(i).trackGroupInfo.getTrackGroup(); - if (trackSelectionOverrides.getOverride(trackGroup) != null) { + if (trackSelectionParameters.overrides.containsKey(trackGroup)) { return true; } } @@ -1960,7 +1953,7 @@ public class PlayerControlView extends FrameLayout { getResources().getString(R.string.exo_track_selection_none)); // TODO(insun) : Make the audio item in main settings (settingsAdapater) // to be non-clickable. - } else if (!hasSelectionOverride(params.trackSelectionOverrides)) { + } else if (!hasSelectionOverride(params)) { settingsAdapter.setSubTextAtPosition( SETTINGS_AUDIO_TRACK_SELECTION_POSITION, getResources().getString(R.string.exo_track_selection_auto)); @@ -2011,8 +2004,7 @@ public class PlayerControlView extends FrameLayout { TrackInformation track = tracks.get(position - 1); TrackGroup trackGroup = track.trackGroupInfo.getTrackGroup(); TrackSelectionParameters params = checkNotNull(player).getTrackSelectionParameters(); - boolean explicitlySelected = - params.trackSelectionOverrides.getOverride(trackGroup) != null && track.isSelected(); + boolean explicitlySelected = params.overrides.get(trackGroup) != null && track.isSelected(); holder.textView.setText(track.trackName); holder.checkView.setVisibility(explicitlySelected ? VISIBLE : INVISIBLE); holder.itemView.setOnClickListener( @@ -2022,14 +2014,6 @@ public class PlayerControlView extends FrameLayout { } TrackSelectionParameters trackSelectionParameters = player.getTrackSelectionParameters(); - TrackSelectionOverrides overrides = - trackSelectionParameters - .trackSelectionOverrides - .buildUpon() - .setOverrideForType( - new TrackSelectionOverride( - trackGroup, ImmutableList.of(track.trackIndex))) - .build(); Set<@C.TrackType Integer> disabledTrackTypes = new HashSet<>(trackSelectionParameters.disabledTrackTypes); disabledTrackTypes.remove(track.trackGroupInfo.getTrackType()); @@ -2037,7 +2021,9 @@ public class PlayerControlView extends FrameLayout { .setTrackSelectionParameters( trackSelectionParameters .buildUpon() - .setTrackSelectionOverrides(overrides) + .setOverrideForType( + new TrackSelectionOverride( + trackGroup, ImmutableList.of(track.trackIndex))) .setDisabledTrackTypes(disabledTrackTypes) .build()); onTrackSelection(track.trackName); From 78b461f94c4e4bf232234c011bb6cc8a120b8be0 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Tue, 15 Feb 2022 12:54:20 +0000 Subject: [PATCH 219/251] Transformer GL: Update TODOs. Document some suboptimal things, and remove TODOs that were fixed already or no longer relevant. Comment-only change. PiperOrigin-RevId: 428749344 --- .../media3/demo/transformer/TransformerActivity.java | 5 +++-- .../media3/transformer/TransformationRequest.java | 11 +++-------- .../media3/transformer/TransformerVideoRenderer.java | 3 +++ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index dcb2e97171..e2829defcd 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -248,8 +248,9 @@ public final class TransformerActivity extends AppCompatActivity { float translateX = bundle.getFloat(ConfigurationActivity.TRANSLATE_X, /* defaultValue= */ 0); float translateY = bundle.getFloat(ConfigurationActivity.TRANSLATE_Y, /* defaultValue= */ 0); - // TODO(b/213198690): Get resolution for aspect ratio and scale all translations' translateX - // by this aspect ratio. + // TODO(b/201293185): Implement an AdvancedFrameEditor to handle translation, as the current + // transformationMatrix is automatically adjusted to focus on the original pixels and + // effectively undo translations. transformationMatrix.postTranslate(translateX, translateY); float scaleX = bundle.getFloat(ConfigurationActivity.SCALE_X, /* defaultValue= */ 1); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java index a74c1ca0fc..cada9eda7c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java @@ -76,14 +76,9 @@ public final class TransformationRequest { * @return This builder. */ public Builder setTransformationMatrix(Matrix transformationMatrix) { - // TODO(b/201293185): After {@link #setResolution} supports arbitrary resolutions, - // allow transformations to change the resolution, by scaling to the appropriate min/max - // values. This will also be required to create the VertexTransformation class, in order to - // have aspect ratio helper methods (which require resolution to change). - - // TODO(b/213198690): Consider changing how transformationMatrix is applied, so that - // dimensions will be from -1 to 1 on both x and y axes, but transformations will be applied - // in a predictable manner. + // TODO(b/201293185): Implement an AdvancedFrameEditor to handle translation, as the current + // transformationMatrix is automatically adjusted to focus on the original pixels and + // effectively undo translations. this.transformationMatrix = new Matrix(transformationMatrix); return this; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java index 21cbf93204..36b2bd41fa 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerVideoRenderer.java @@ -115,6 +115,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return false; } if (!transformationRequest.transformationMatrix.isIdentity()) { + // TODO(b/201293185, b/214010296): Move FrameProcessor transformationMatrix calculation / + // adjustments out of the VideoTranscodingSamplePipeline, so that we can skip transcoding when + // adjustments result in identity matrices. return false; } return true; From b22273de0184d0a650902f7f5e144977e00c1bf4 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 14:09:18 +0000 Subject: [PATCH 220/251] Disable IMA SSAI DASH live streams for now #minor-release PiperOrigin-RevId: 428761508 --- .../exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java index 6e00303d58..accb4368a6 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java @@ -368,6 +368,10 @@ public final class ImaServerSideAdInsertionUriBuilder { if (streamActivityMonitorId != null) { streamRequest.setStreamActivityMonitorId(streamActivityMonitorId); } + checkState( + streamRequest.getFormat() != StreamFormat.DASH + || TextUtils.isEmpty(streamRequest.getAssetKey()), + "DASH live streams are not supported yet."); return streamRequest; } } From 9370665b79f5a65e7375b86c75f998c902ed5a0a Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 15 Feb 2022 14:19:21 +0000 Subject: [PATCH 221/251] Track presentation timestamps in FrameEditor. PiperOrigin-RevId: 428763554 --- .../java/androidx/media3/transformer/DefaultCodec.java | 8 +++++++- .../java/androidx/media3/transformer/FrameEditor.java | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java index 9482014983..da53c2ba19 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java @@ -209,7 +209,13 @@ public final class DefaultCodec implements Codec { public void releaseOutputBuffer(boolean render) throws TransformationException { outputBuffer = null; try { - mediaCodec.releaseOutputBuffer(outputBufferIndex, render); + if (render) { + mediaCodec.releaseOutputBuffer( + outputBufferIndex, + /* renderTimestampNs= */ checkStateNotNull(outputBufferInfo).presentationTimeUs * 1000); + } else { + mediaCodec.releaseOutputBuffer(outputBufferIndex, /* render= */ false); + } } catch (RuntimeException e) { throw createTransformationException(e); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index 1a6f7c619b..40e63b320d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -325,8 +325,8 @@ import java.util.concurrent.atomic.AtomicInteger; glProgram.bindAttributesAndUniforms(); focusAndDrawQuad(eglSurface, outputWidth, outputHeight); - long surfaceTextureTimestampNs = inputSurfaceTexture.getTimestamp(); - EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, surfaceTextureTimestampNs); + long presentationTimeNs = inputSurfaceTexture.getTimestamp(); + EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, presentationTimeNs); EGL14.eglSwapBuffers(eglDisplay, eglSurface); if (debugPreviewEglSurface != null) { From 0db8047756fe2bde3c37fe0b06b0f05eafa3acb9 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 14:19:54 +0000 Subject: [PATCH 222/251] Store ad playback state in AdsLoader for resume after backgrounding #minor-release PiperOrigin-RevId: 428763656 --- .../ImaServerSideAdInsertionMediaSource.java | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index d77b50fc0c..5ec5b202f6 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -151,6 +151,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public MediaSource createMediaSource(MediaItem mediaItem) { + checkNotNull(mediaItem.localConfiguration); Player player = checkNotNull(adsLoader.player); StreamPlayer streamPlayer = new StreamPlayer(player, mediaItem); ImaSdkFactory imaSdkFactory = ImaSdkFactory.getInstance(); @@ -163,6 +164,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou new ImaServerSideAdInsertionMediaSource( mediaItem, player, + adsLoader, imaAdsLoader, streamPlayer, contentMediaSourceFactory, @@ -185,6 +187,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Nullable private ImaSdkSettings imaSdkSettings; @Nullable private AdEventListener adEventListener; @Nullable private AdErrorEvent.AdErrorListener adErrorListener; + private State state; private ImmutableList companionAdSlots; /** @@ -197,6 +200,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.context = context; this.adViewProvider = adViewProvider; companionAdSlots = ImmutableList.of(); + state = new State(ImmutableMap.of()); } /** @@ -248,6 +252,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou return this; } + /** + * Sets the optional state to resume with. + * + *

      The state can be received when {@link #release() releasing} the {@link AdsLoader}. + * + * @param state The state to resume with. + * @return This builder, for convenience. + */ + public AdsLoader.Builder setAdsLoaderState(State state) { + this.state = state; + return this; + } + /** Returns a new {@link AdsLoader}. */ public AdsLoader build() { @Nullable ImaSdkSettings imaSdkSettings = this.imaSdkSettings; @@ -263,7 +280,17 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou adErrorListener, companionAdSlots, imaSdkSettings.isDebugMode()); - return new AdsLoader(context, configuration); + return new AdsLoader(context, configuration, state); + } + } + + /** The state of the {@link AdsLoader}. */ + public static class State { + + private final ImmutableMap adPlaybackStates; + + private State(ImmutableMap adPlaybackStates) { + this.adPlaybackStates = adPlaybackStates; } } @@ -271,13 +298,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private final Context context; private final Map mediaSourceResources; + private final Map adPlaybackStateMap; @Nullable private Player player; - private AdsLoader(Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration) { + private AdsLoader( + Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration, State state) { this.context = context.getApplicationContext(); this.configuration = configuration; mediaSourceResources = new HashMap<>(); + adPlaybackStateMap = new HashMap<>(); + for (Map.Entry entry : state.adPlaybackStates.entrySet()) { + adPlaybackStateMap.put(entry.getKey(), entry.getValue()); + } } /** @@ -290,8 +323,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.player = player; } - /** Releases resources when the ads loader is no longer needed. */ - public void release() { + /** + * Releases resources. + * + * @return The {@link State} that can be used to resume with. + */ + public State release() { for (MediaSourceResourceHolder resourceHolder : mediaSourceResources.values()) { resourceHolder.streamPlayer.release(); resourceHolder.adsLoader.release(); @@ -300,9 +337,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } mediaSourceResources.clear(); player = null; + return new State(ImmutableMap.copyOf(adPlaybackStateMap)); } - /* package */ void addMediaSourceResources( + // Internal methods. + + private void addMediaSourceResources( ImaServerSideAdInsertionMediaSource mediaSource, StreamPlayer streamPlayer, com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { @@ -310,6 +350,15 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou mediaSource, new MediaSourceResourceHolder(mediaSource, streamPlayer, adsLoader)); } + private AdPlaybackState getAdPlaybackState(String adsId) { + @Nullable AdPlaybackState adPlaybackState = adPlaybackStateMap.get(adsId); + return adPlaybackState != null ? adPlaybackState : AdPlaybackState.NONE; + } + + private void setAdPlaybackState(String adsId, AdPlaybackState adPlaybackState) { + this.adPlaybackStateMap.put(adsId, adPlaybackState); + } + private static final class MediaSourceResourceHolder { public final ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource; public final StreamPlayer streamPlayer; @@ -329,7 +378,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private final MediaItem mediaItem; private final Player player; private final MediaSource.Factory contentMediaSourceFactory; - private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; + private final AdsLoader adsLoader; + private final com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader; @Nullable private final AdEventListener applicationAdEventListener; @Nullable private final AdErrorListener applicationAdErrorListener; private final boolean isLiveStream; @@ -350,7 +400,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private ImaServerSideAdInsertionMediaSource( MediaItem mediaItem, Player player, - com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader, + AdsLoader adsLoader, + com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader, StreamPlayer streamPlayer, MediaSource.Factory contentMediaSourceFactory, @Nullable AdEventListener applicationAdEventListener, @@ -358,18 +409,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.mediaItem = mediaItem; this.player = player; this.adsLoader = adsLoader; + this.sdkAdsLoader = sdkAdsLoader; this.streamPlayer = streamPlayer; this.contentMediaSourceFactory = contentMediaSourceFactory; this.applicationAdEventListener = applicationAdEventListener; this.applicationAdErrorListener = applicationAdErrorListener; componentListener = new ComponentListener(); - adPlaybackState = AdPlaybackState.NONE; mainHandler = Util.createHandlerForCurrentLooper(); Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri; isLiveStream = ImaServerSideAdInsertionUriBuilder.isLiveStream(streamRequestUri); adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri); loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri); streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri); + adPlaybackState = adsLoader.getAdPlaybackState(adsId); } @Override @@ -386,7 +438,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou player.addListener(componentListener); StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( - adsLoader, + sdkAdsLoader, streamRequest, streamPlayer, applicationAdErrorListener, @@ -502,6 +554,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline); streamPlayer.setAdPlaybackStates(adsId, splitAdPlaybackStates, contentTimeline); checkNotNull(serverSideAdInsertionMediaSource).setAdPlaybackStates(splitAdPlaybackStates); + if (!ImaServerSideAdInsertionUriBuilder.isLiveStream( + checkNotNull(mediaItem.localConfiguration).uri)) { + adsLoader.setAdPlaybackState(adsId, adPlaybackState); + } } } From 3cd23de1cbc0c2123ae02c29cc709cda28d4a60e Mon Sep 17 00:00:00 2001 From: hschlueter Date: Tue, 15 Feb 2022 15:44:06 +0000 Subject: [PATCH 223/251] Split GlFrameProcessor out of FrameEditor. The GlFrameProcessor handles everything related to the GLSL program, the FrameEditor manages the GL context and the data flow including the input SurfaceTexture and output EGLSurface. This will be split up further in follow-up CLs so that GlFrameProcessors can be chained. At this CL, there are no functional changes intended. PiperOrigin-RevId: 428779179 --- .../media3/transformer/FrameEditor.java | 145 +++----------- .../media3/transformer/GlFrameProcessor.java | 181 ++++++++++++++++++ 2 files changed, 208 insertions(+), 118 deletions(-) create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java index 40e63b320d..f3dc89c94d 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java @@ -31,12 +31,18 @@ import android.view.Surface; import android.view.SurfaceView; import androidx.annotation.Nullable; import androidx.media3.common.C; -import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; -/** FrameEditor applies changes to individual video frames. */ +/** + * {@code FrameEditor} applies changes to individual video frames. + * + *

      Input becomes available on its {@link #getInputSurface() input surface} asynchronously so + * {@link #canProcessData()} needs to be checked before calling {@link #processData()}. Output is + * written to its {@link #create(Context, int, int, float, Matrix, Surface, boolean, + * Transformer.DebugViewProvider) output surface}. + */ /* package */ final class FrameEditor { static { @@ -79,6 +85,8 @@ import java.util.concurrent.atomic.AtomicInteger; TransformationException.ERROR_CODE_GL_INIT_FAILED); } + GlFrameProcessor frameProcessor = + new GlFrameProcessor(context, transformationMatrix, enableExperimentalHdrEditing); @Nullable SurfaceView debugSurfaceView = debugViewProvider.getDebugPreviewSurfaceView(outputWidth, outputHeight); @@ -87,7 +95,6 @@ import java.util.concurrent.atomic.AtomicInteger; EGLContext eglContext; EGLSurface eglSurface; int textureId; - GlProgram glProgram; @Nullable EGLSurface debugPreviewEglSurface = null; try { eglDisplay = GlUtil.createEglDisplay(); @@ -111,9 +118,7 @@ import java.util.concurrent.atomic.AtomicInteger; GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight); textureId = GlUtil.createExternalTexture(); - glProgram = - configureGlProgram( - context, transformationMatrix, textureId, enableExperimentalHdrEditing); + frameProcessor.initialize(); } catch (IOException | GlUtil.GlException e) { throw TransformationException.createForFrameEditor( e, TransformationException.ERROR_CODE_GL_INIT_FAILED); @@ -134,7 +139,7 @@ import java.util.concurrent.atomic.AtomicInteger; eglContext, eglSurface, textureId, - glProgram, + frameProcessor, outputWidth, outputHeight, debugPreviewEglSurface, @@ -142,97 +147,7 @@ import java.util.concurrent.atomic.AtomicInteger; debugPreviewHeight); } - private static GlProgram configureGlProgram( - Context context, - Matrix transformationMatrix, - int textureId, - boolean enableExperimentalHdrEditing) - throws IOException { - // TODO(b/205002913): check the loaded program is consistent with the attributes - // and uniforms expected in the code. - String vertexShaderFilePath = - enableExperimentalHdrEditing - ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH - : VERTEX_SHADER_TRANSFORMATION_PATH; - String fragmentShaderFilePath = - enableExperimentalHdrEditing - ? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH - : FRAGMENT_SHADER_COPY_EXTERNAL_PATH; - GlProgram glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); - - // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. - glProgram.setBufferAttribute( - "aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); - glProgram.setBufferAttribute( - "aTexCoords", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); - glProgram.setSamplerTexIdUniform("uTexSampler", textureId, /* unit= */ 0); - - if (enableExperimentalHdrEditing) { - // In HDR editing mode the decoder output is sampled in YUV. - glProgram.setFloatsUniform("uColorTransform", MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM); - } - - float[] transformationMatrixArray = getGlMatrixArray(transformationMatrix); - glProgram.setFloatsUniform("uTransformationMatrix", transformationMatrixArray); - return glProgram; - } - - /** - * Returns a 4x4, column-major Matrix float array, from an input {@link Matrix}. This is useful - * for converting to the 4x4 column-major format commonly used in OpenGL. - */ - private static float[] getGlMatrixArray(Matrix matrix) { - float[] matrix3x3Array = new float[9]; - matrix.getValues(matrix3x3Array); - float[] matrix4x4Array = getMatrix4x4Array(matrix3x3Array); - - // Transpose from row-major to column-major representations. - float[] transposedMatrix4x4Array = new float[16]; - android.opengl.Matrix.transposeM( - transposedMatrix4x4Array, /* mTransOffset= */ 0, matrix4x4Array, /* mOffset= */ 0); - - return transposedMatrix4x4Array; - } - - /** - * Returns a 4x4 matrix array containing the 3x3 matrix array's contents. - * - *

      The 3x3 matrix array is expected to be in 2 dimensions, and the 4x4 matrix array is expected - * to be in 3 dimensions. The output will have the third row/column's values be an identity - * matrix's values, so that vertex transformations using this matrix will not affect the z axis. - *
      - * Input format: [a, b, c, d, e, f, g, h, i]
      - * Output format: [a, b, 0, c, d, e, 0, f, 0, 0, 1, 0, g, h, 0, i] - */ - private static float[] getMatrix4x4Array(float[] matrix3x3Array) { - float[] matrix4x4Array = new float[16]; - matrix4x4Array[10] = 1; - for (int inputRow = 0; inputRow < 3; inputRow++) { - for (int inputColumn = 0; inputColumn < 3; inputColumn++) { - int outputRow = (inputRow == 2) ? 3 : inputRow; - int outputColumn = (inputColumn == 2) ? 3 : inputColumn; - matrix4x4Array[outputRow * 4 + outputColumn] = matrix3x3Array[inputRow * 3 + inputColumn]; - } - } - return matrix4x4Array; - } - - private static final String VERTEX_SHADER_TRANSFORMATION_PATH = - "shaders/vertex_shader_transformation.glsl"; - private static final String FRAGMENT_SHADER_COPY_EXTERNAL_PATH = - "shaders/fragment_shader_copy_external.glsl"; - private static final String VERTEX_SHADER_TRANSFORMATION_ES3_PATH = - "shaders/vertex_shader_transformation_es3.glsl"; - private static final String FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH = - "shaders/fragment_shader_copy_external_yuv_es3.glsl"; - // Color transform coefficients from - // https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp;l=668-670;drc=487adf977a50cac3929eba15fad0d0f461c7ff0f. - private static final float[] MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM = { - 1.168f, 1.168f, 1.168f, - 0.0f, -0.188f, 2.148f, - 1.683f, -0.652f, 0.0f, - }; - + private final GlFrameProcessor frameProcessor; private final float[] textureTransformMatrix; private final EGLDisplay eglDisplay; private final EGLContext eglContext; @@ -242,7 +157,6 @@ import java.util.concurrent.atomic.AtomicInteger; private final AtomicInteger availableInputFrameCount; private final SurfaceTexture inputSurfaceTexture; private final Surface inputSurface; - private final GlProgram glProgram; private final int outputWidth; private final int outputHeight; @Nullable private final EGLSurface debugPreviewEglSurface; @@ -256,7 +170,7 @@ import java.util.concurrent.atomic.AtomicInteger; EGLContext eglContext, EGLSurface eglSurface, int textureId, - GlProgram glProgram, + GlFrameProcessor frameProcessor, int outputWidth, int outputHeight, @Nullable EGLSurface debugPreviewEglSurface, @@ -266,14 +180,14 @@ import java.util.concurrent.atomic.AtomicInteger; this.eglContext = eglContext; this.eglSurface = eglSurface; this.textureId = textureId; - this.glProgram = glProgram; - this.pendingInputFrameCount = new AtomicInteger(); - this.availableInputFrameCount = new AtomicInteger(); + this.frameProcessor = frameProcessor; this.outputWidth = outputWidth; this.outputHeight = outputHeight; this.debugPreviewEglSurface = debugPreviewEglSurface; this.debugPreviewWidth = debugPreviewWidth; this.debugPreviewHeight = debugPreviewHeight; + pendingInputFrameCount = new AtomicInteger(); + availableInputFrameCount = new AtomicInteger(); textureTransformMatrix = new float[16]; inputSurfaceTexture = new SurfaceTexture(textureId); inputSurfaceTexture.setOnFrameAvailableListener( @@ -321,16 +235,20 @@ import java.util.concurrent.atomic.AtomicInteger; try { inputSurfaceTexture.updateTexImage(); inputSurfaceTexture.getTransformMatrix(textureTransformMatrix); - glProgram.setFloatsUniform("uTexTransform", textureTransformMatrix); - glProgram.bindAttributesAndUniforms(); - - focusAndDrawQuad(eglSurface, outputWidth, outputHeight); + GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight); + frameProcessor.setTextureTransformMatrix(textureTransformMatrix); + frameProcessor.updateProgramAndDraw(textureId); long presentationTimeNs = inputSurfaceTexture.getTimestamp(); EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, presentationTimeNs); EGL14.eglSwapBuffers(eglDisplay, eglSurface); if (debugPreviewEglSurface != null) { - focusAndDrawQuad(debugPreviewEglSurface, debugPreviewWidth, debugPreviewHeight); + GlUtil.focusEglSurface( + eglDisplay, eglContext, debugPreviewEglSurface, debugPreviewWidth, debugPreviewHeight); + GLES20.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + // The four-vertex triangle strip forms a quad. + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); EGL14.eglSwapBuffers(eglDisplay, debugPreviewEglSurface); } } catch (GlUtil.GlException e) { @@ -342,22 +260,13 @@ import java.util.concurrent.atomic.AtomicInteger; /** Releases all resources. */ public void release() { - glProgram.delete(); + frameProcessor.release(); GlUtil.deleteTexture(textureId); GlUtil.destroyEglContext(eglDisplay, eglContext); inputSurfaceTexture.release(); inputSurface.release(); } - /** Focuses the specified surface with the specified width and height, then draws a quad. */ - private void focusAndDrawQuad(EGLSurface eglSurface, int width, int height) { - GlUtil.focusEglSurface(eglDisplay, eglContext, eglSurface, width, height); - GLES20.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - // The four-vertex triangle strip forms a quad. - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); - } - /** Returns whether all data has been processed. */ public boolean isEnded() { return inputStreamEnded diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java new file mode 100644 index 0000000000..55bd315111 --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/GlFrameProcessor.java @@ -0,0 +1,181 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static androidx.media3.common.util.Assertions.checkStateNotNull; + +import android.content.Context; +import android.graphics.Matrix; +import android.opengl.GLES20; +import androidx.media3.common.util.GlProgram; +import androidx.media3.common.util.GlUtil; +import java.io.IOException; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** Manages a GLSL shader program for applying a transformation matrix to a frame. */ +/* package */ class GlFrameProcessor { + + static { + GlUtil.glAssertionsEnabled = true; + } + + /** + * Returns a 4x4, column-major Matrix float array, from an input {@link Matrix}. This is useful + * for converting to the 4x4 column-major format commonly used in OpenGL. + */ + private static float[] getGlMatrixArray(Matrix matrix) { + float[] matrix3x3Array = new float[9]; + matrix.getValues(matrix3x3Array); + float[] matrix4x4Array = getMatrix4x4Array(matrix3x3Array); + + // Transpose from row-major to column-major representations. + float[] transposedMatrix4x4Array = new float[16]; + android.opengl.Matrix.transposeM( + transposedMatrix4x4Array, /* mTransOffset= */ 0, matrix4x4Array, /* mOffset= */ 0); + + return transposedMatrix4x4Array; + } + + /** + * Returns a 4x4 matrix array containing the 3x3 matrix array's contents. + * + *

      The 3x3 matrix array is expected to be in 2 dimensions, and the 4x4 matrix array is expected + * to be in 3 dimensions. The output will have the third row/column's values be an identity + * matrix's values, so that vertex transformations using this matrix will not affect the z axis. + *
      + * Input format: [a, b, c, d, e, f, g, h, i]
      + * Output format: [a, b, 0, c, d, e, 0, f, 0, 0, 1, 0, g, h, 0, i] + */ + private static float[] getMatrix4x4Array(float[] matrix3x3Array) { + float[] matrix4x4Array = new float[16]; + matrix4x4Array[10] = 1; + for (int inputRow = 0; inputRow < 3; inputRow++) { + for (int inputColumn = 0; inputColumn < 3; inputColumn++) { + int outputRow = (inputRow == 2) ? 3 : inputRow; + int outputColumn = (inputColumn == 2) ? 3 : inputColumn; + matrix4x4Array[outputRow * 4 + outputColumn] = matrix3x3Array[inputRow * 3 + inputColumn]; + } + } + return matrix4x4Array; + } + + private static final String VERTEX_SHADER_TRANSFORMATION_PATH = + "shaders/vertex_shader_transformation.glsl"; + private static final String FRAGMENT_SHADER_COPY_EXTERNAL_PATH = + "shaders/fragment_shader_copy_external.glsl"; + private static final String VERTEX_SHADER_TRANSFORMATION_ES3_PATH = + "shaders/vertex_shader_transformation_es3.glsl"; + private static final String FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH = + "shaders/fragment_shader_copy_external_yuv_es3.glsl"; + // Color transform coefficients from + // https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libstagefright/colorconversion/ColorConverter.cpp;l=668-670;drc=487adf977a50cac3929eba15fad0d0f461c7ff0f. + private static final float[] MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM = { + 1.168f, 1.168f, 1.168f, + 0.0f, -0.188f, 2.148f, + 1.683f, -0.652f, 0.0f, + }; + + private final Context context; + private final Matrix transformationMatrix; + private final boolean enableExperimentalHdrEditing; + + private @MonotonicNonNull GlProgram glProgram; + + /** + * Creates a new instance. + * + * @param context A {@link Context}. + * @param transformationMatrix The transformation matrix to apply to each frame. + * @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal. + */ + public GlFrameProcessor( + Context context, Matrix transformationMatrix, boolean enableExperimentalHdrEditing) { + this.context = context; + this.transformationMatrix = transformationMatrix; + this.enableExperimentalHdrEditing = enableExperimentalHdrEditing; + } + + /** + * Does any initialization necessary such as loading and compiling a GLSL shader programs. + * + *

      This method may only be called after creating the OpenGL context and focusing a render + * target. + */ + public void initialize() throws IOException { + // TODO(b/205002913): check the loaded program is consistent with the attributes + // and uniforms expected in the code. + String vertexShaderFilePath = + enableExperimentalHdrEditing + ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH + : VERTEX_SHADER_TRANSFORMATION_PATH; + String fragmentShaderFilePath = + enableExperimentalHdrEditing + ? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH + : FRAGMENT_SHADER_COPY_EXTERNAL_PATH; + + glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); + // Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y. + glProgram.setBufferAttribute( + "aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); + glProgram.setBufferAttribute( + "aTexCoords", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT); + if (enableExperimentalHdrEditing) { + // In HDR editing mode the decoder output is sampled in YUV. + glProgram.setFloatsUniform("uColorTransform", MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM); + } + float[] transformationMatrixArray = getGlMatrixArray(transformationMatrix); + glProgram.setFloatsUniform("uTransformationMatrix", transformationMatrixArray); + } + + /** + * Sets the texture transform matrix for converting an external surface texture's coordinates to + * sampling locations. + * + * @param textureTransformMatrix The external surface texture's {@link + * android.graphics.SurfaceTexture#getTransformMatrix(float[]) transform matrix}. + */ + public void setTextureTransformMatrix(float[] textureTransformMatrix) { + checkStateNotNull(glProgram); + glProgram.setFloatsUniform("uTexTransform", textureTransformMatrix); + } + + /** + * Updates the shader program's vertex attributes and uniforms, binds them, and draws. + * + *

      The frame processor must be {@link #initialize() initialized}. The caller is responsible for + * focussing the correct render target before calling this method. + * + * @param inputTexId The identifier of an OpenGL texture that the fragment shader can sample from. + */ + // TODO(b/214975934): Also pass presentationTimeNs. + public void updateProgramAndDraw(int inputTexId) { + checkStateNotNull(glProgram); + glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* unit= */ 0); + glProgram.use(); + glProgram.bindAttributesAndUniforms(); + GLES20.glClearColor(/* red= */ 0, /* green= */ 0, /* blue= */ 0, /* alpha= */ 0); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + // The four-vertex triangle strip forms a quad. + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); + } + + /** Releases all resources. */ + public void release() { + if (glProgram != null) { + glProgram.delete(); + } + } +} From 9137947797b029daf8d54d4b8d76cce66e1abb6c Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 15 Feb 2022 16:53:12 +0000 Subject: [PATCH 224/251] Add TracksInfo methods for track selection UI We need TracksInfo.hasTracksOfType to determine which tabs to display in TrackSelectionDialog. We need TrackGroupInfo.isAdaptiveSupported to determine whether to allow multiple selection (check boxes) or not (radio buttons). PiperOrigin-RevId: 428793739 --- .../java/androidx/media3/cast/CastPlayer.java | 7 ++- .../androidx/media3/common/TracksInfo.java | 44 +++++++++++--- .../media3/common/TracksInfoTest.java | 60 +++++++++++++------ .../trackselection/MappingTrackSelector.java | 10 +++- 4 files changed, 91 insertions(+), 30 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 07c86e68b8..071f38c59a 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -1037,7 +1037,12 @@ public final class CastPlayer extends BasePlayer { int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE}; final boolean[] trackSelected = new boolean[] {selected}; trackGroupInfos[i] = - new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected); + new TracksInfo.TrackGroupInfo( + trackType, + trackGroups[i], + /* adaptiveSupported= */ false, + trackSupport, + trackSelected); } TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 696d6f68de..bf98c0f5ef 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -49,30 +49,35 @@ public final class TracksInfo implements Bundleable { /** The number of tracks in the group. */ public final int length; - private final TrackGroup trackGroup; - private final @C.FormatSupport int[] trackSupport; private final @C.TrackType int trackType; + private final TrackGroup trackGroup; + private final boolean adaptiveSupported; + private final @C.FormatSupport int[] trackSupport; private final boolean[] trackSelected; /** * Constructs a TrackGroupInfo. * - * @param trackGroup The {@link TrackGroup} described. - * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. * @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}. + * @param trackGroup The {@link TrackGroup} described. + * @param adaptiveSupported Whether adaptive selections containing more than one track are + * supported. + * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. * @param tracksSelected Whether a track is selected for each track in {@code trackGroup}. */ @UnstableApi public TrackGroupInfo( - TrackGroup trackGroup, - @C.FormatSupport int[] trackSupport, @C.TrackType int trackType, + TrackGroup trackGroup, + boolean adaptiveSupported, + @C.FormatSupport int[] trackSupport, boolean[] tracksSelected) { length = trackGroup.length; checkArgument(length == trackSupport.length && length == tracksSelected.length); - this.trackGroup = trackGroup; - this.trackSupport = trackSupport.clone(); this.trackType = trackType; + this.trackGroup = trackGroup; + this.adaptiveSupported = adaptiveSupported && length > 1; + this.trackSupport = trackSupport.clone(); this.trackSelected = tracksSelected.clone(); } @@ -135,6 +140,11 @@ public final class TracksInfo implements Bundleable { return Booleans.contains(trackSelected, true); } + /** Returns whether adaptive selections containing more than one track are supported. */ + public boolean isAdaptiveSupported() { + return adaptiveSupported; + } + /** * Returns whether at least one track in the group is supported for playback, without exceeding * the advertised capabilities of the device. Equivalent to {@code isSupported(false)}. @@ -217,6 +227,7 @@ public final class TracksInfo implements Bundleable { FIELD_TRACK_SUPPORT, FIELD_TRACK_TYPE, FIELD_TRACK_SELECTED, + FIELD_ADAPTIVE_SUPPORTED, }) private @interface FieldNumber {} @@ -224,6 +235,7 @@ public final class TracksInfo implements Bundleable { private static final int FIELD_TRACK_SUPPORT = 1; private static final int FIELD_TRACK_TYPE = 2; private static final int FIELD_TRACK_SELECTED = 3; + private static final int FIELD_ADAPTIVE_SUPPORTED = 4; @Override public Bundle toBundle() { @@ -232,6 +244,7 @@ public final class TracksInfo implements Bundleable { bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport); bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType); bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected); + bundle.putBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), adaptiveSupported); return bundle; } @@ -252,7 +265,10 @@ public final class TracksInfo implements Bundleable { MoreObjects.firstNonNull( bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)), new boolean[trackGroup.length]); - return new TrackGroupInfo(trackGroup, trackSupport, trackType, selected); + boolean adaptiveSupported = + bundle.getBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), false); + return new TrackGroupInfo( + trackType, trackGroup, adaptiveSupported, trackSupport, selected); }; private static String keyForField(@FieldNumber int field) { @@ -281,6 +297,16 @@ public final class TracksInfo implements Bundleable { return trackGroupInfos; } + /** Returns true if there are tracks of type {@code trackType}, and false otherwise. */ + public boolean hasTracksOfType(@C.TrackType int trackType) { + for (int i = 0; i < trackGroupInfos.size(); i++) { + if (trackGroupInfos.get(i).trackType == trackType) { + return true; + } + } + return false; + } + /** * Returns true if at least one track of type {@code trackType} is {@link * TrackGroupInfo#isTrackSupported(int) supported} or if there are no tracks of this type. diff --git a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java index 420c3def53..b2eb0c80fe 100644 --- a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java @@ -17,6 +17,7 @@ package androidx.media3.common; import static com.google.common.truth.Truth.assertThat; +import androidx.media3.common.TracksInfo.TrackGroupInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import org.junit.Test; @@ -38,16 +39,18 @@ public class TracksInfoTest { TracksInfo before = new TracksInfo( ImmutableList.of( - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build()), - new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + new TrackGroupInfo( C.TRACK_TYPE_AUDIO, - new boolean[] {true}), - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), - new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + /* tracksSelected= */ new boolean[] {true}), + new TrackGroupInfo( C.TRACK_TYPE_VIDEO, - new boolean[] {false, true}))); + new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, + /* tracksSelected= */ new boolean[] {false, true}))); TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle()); assertThat(after).isEqualTo(before); } @@ -58,7 +61,7 @@ public class TracksInfoTest { assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); - ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); + ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).isEmpty(); } @@ -72,26 +75,31 @@ public class TracksInfoTest { @Test public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() { - TracksInfo.TrackGroupInfo trackGroupInfo0 = - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build()), - new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + TrackGroupInfo trackGroupInfo0 = + new TrackGroupInfo( C.TRACK_TYPE_AUDIO, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, /* tracksSelected= */ new boolean[] {false}); - TracksInfo.TrackGroupInfo trackGroupInfo1 = - new TracksInfo.TrackGroupInfo( - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), - new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED}, + TrackGroupInfo trackGroupInfo1 = + new TrackGroupInfo( C.TRACK_TYPE_VIDEO, + new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED}, /* tracksSelected= */ new boolean[] {false, true}); TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1)); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_AUDIO)).isTrue(); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_VIDEO)).isTrue(); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_TEXT)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue(); - ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); + ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).hasSize(2); assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0); assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1); @@ -107,4 +115,20 @@ public class TracksInfoTest { assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO); assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO); } + + /** + * Tests that {@link TrackGroupInfo#isAdaptiveSupported} returns false if the group only contains + * a single track, even if true is passed to the constructor. + */ + @Test + public void trackGroupInfo_withSingleTrack_isNotAdaptive() { + TrackGroupInfo trackGroupInfo0 = + new TrackGroupInfo( + C.TRACK_TYPE_AUDIO, + new TrackGroup(new Format.Builder().build()), + /* adaptiveSupported= */ true, + new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, + /* tracksSelected= */ new boolean[] {false}); + assertThat(trackGroupInfo0.isAdaptiveSupported()).isFalse(); + } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index d1836b7094..11f381e734 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -573,6 +573,10 @@ public abstract class MappingTrackSelector extends TrackSelector { @Nullable TrackSelection trackSelection = selections[rendererIndex]; for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) { TrackGroup trackGroup = trackGroupArray.get(groupIndex); + boolean adaptiveSupported = + mappedTrackInfo.getAdaptiveSupport( + rendererIndex, groupIndex, /* includeCapabilitiesExceededTracks= */ false) + != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED; @C.FormatSupport int[] trackSupport = new int[trackGroup.length]; boolean[] selected = new boolean[trackGroup.length]; for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { @@ -586,7 +590,8 @@ public abstract class MappingTrackSelector extends TrackSelector { } @C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex); builder.add( - new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); + new TracksInfo.TrackGroupInfo( + trackGroupType, trackGroup, adaptiveSupported, trackSupport, selected)); } } TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups(); @@ -599,7 +604,8 @@ public abstract class MappingTrackSelector extends TrackSelector { int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); boolean[] selected = new boolean[trackGroup.length]; // Initialized to false. builder.add( - new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected)); + new TracksInfo.TrackGroupInfo( + trackGroupType, trackGroup, /* adaptiveSupported= */ false, trackSupport, selected)); } return new TracksInfo(builder.build()); } From f8d8bfb6642ca9a8d11b3a4e40a3bfcf30374644 Mon Sep 17 00:00:00 2001 From: kimvde Date: Tue, 15 Feb 2022 16:56:49 +0000 Subject: [PATCH 225/251] Handle player release timing out in transformer - The resources were released twice before, which is not necessary since the MSG_RELEASE message is already in the internal player queue. - The demo app was failing because the stop watch was stopped in onTransformationError after being reset. #minor-release #mse-bug-week PiperOrigin-RevId: 428794426 --- .../demo/transformer/TransformerActivity.java | 6 ++++-- .../androidx/media3/transformer/Transformer.java | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index e2829defcd..8810495706 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -114,11 +114,13 @@ public final class TransformerActivity extends AppCompatActivity { protected void onStop() { super.onStop(); - checkNotNull(transformationStopwatch).reset(); - checkNotNull(transformer).cancel(); transformer = null; + // The stop watch is reset after cancelling the transformation, in case cancelling causes the + // stop watch to be stopped in a transformer callback. + checkNotNull(transformationStopwatch).reset(); + checkNotNull(playerView).onPause(); releasePlayer(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 0e1572a8c7..8b26a65dcb 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -518,6 +518,7 @@ public final class Transformer { @Nullable private MuxerWrapper muxerWrapper; @Nullable private ExoPlayer player; private @ProgressState int progressState; + private boolean isCancelling; private Transformer( Context context, @@ -736,11 +737,13 @@ public final class Transformer { * @throws IllegalStateException If this method is called from the wrong thread. */ public void cancel() { + isCancelling = true; try { releaseResources(/* forCancellation= */ true); } catch (TransformationException impossible) { throw new IllegalStateException(impossible); } + isCancelling = false; } /** @@ -898,10 +901,19 @@ public final class Transformer { @Override public void onPlayerError(PlaybackException error) { @Nullable Throwable cause = error.getCause(); - handleTransformationEnded( + TransformationException transformationException = cause instanceof TransformationException ? (TransformationException) cause - : TransformationException.createForPlaybackException(error)); + : TransformationException.createForPlaybackException(error); + if (isCancelling) { + // Resources are already being released. + listeners.queueEvent( + /* eventFlag= */ C.INDEX_UNSET, + listener -> listener.onTransformationError(mediaItem, transformationException)); + listeners.flushEvents(); + } else { + handleTransformationEnded(transformationException); + } } private void handleTransformationEnded(@Nullable TransformationException exception) { From b8687a3111ce4fc0705a1ad3598dd94aae8dc6a2 Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 16 Feb 2022 11:53:27 +0000 Subject: [PATCH 226/251] Copy playback configuration when creating the content media item #minor-release PiperOrigin-RevId: 429006934 --- .../ima/ImaServerSideAdInsertionMediaSource.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 5ec5b202f6..faa4dc7680 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -567,10 +567,17 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou if (serverSideAdInsertionMediaSource != null) { return; } + MediaItem contentMediaItem = + new MediaItem.Builder() + .setUri(contentUri) + .setDrmConfiguration(checkNotNull(mediaItem.localConfiguration).drmConfiguration) + .setLiveConfiguration(mediaItem.liveConfiguration) + .setCustomCacheKey(mediaItem.localConfiguration.customCacheKey) + .setStreamKeys(mediaItem.localConfiguration.streamKeys) + .build(); ServerSideAdInsertionMediaSource serverSideAdInsertionMediaSource = new ServerSideAdInsertionMediaSource( - contentMediaSourceFactory.createMediaSource(MediaItem.fromUri(contentUri)), - componentListener); + contentMediaSourceFactory.createMediaSource(contentMediaItem), componentListener); this.serverSideAdInsertionMediaSource = serverSideAdInsertionMediaSource; if (isLiveStream) { AdPlaybackState liveAdPlaybackState = From b59869ede3b2a7f403963ab397081476a53cdbb9 Mon Sep 17 00:00:00 2001 From: christosts Date: Wed, 16 Feb 2022 12:10:37 +0000 Subject: [PATCH 227/251] Create BitmapLoader Create BitmapLoader component for loading artwork images. Add the SimpleBitmapLoader which fetches images from HTTP/HTTPS endpoints. Integrate BitmapLoader in DefaultMediaNotificationProvider. PiperOrigin-RevId: 429010249 --- libraries/session/build.gradle | 17 +- .../src/androidTest/AndroidManifest.xml | 34 ++++ .../session/SimpleBitmapLoaderTest.java | 141 ++++++++++++++++ .../androidx/media3/session/BitmapLoader.java | 30 ++++ .../DefaultMediaNotificationProvider.java | 159 +++++++++++++++++- .../media3/session/SimpleBitmapLoader.java | 106 ++++++++++++ 6 files changed, 477 insertions(+), 10 deletions(-) create mode 100644 libraries/session/src/androidTest/AndroidManifest.xml create mode 100644 libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java create mode 100644 libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java create mode 100644 libraries/session/src/main/java/androidx/media3/session/SimpleBitmapLoader.java diff --git a/libraries/session/build.gradle b/libraries/session/build.gradle index 1cfc78bcf6..dcfd31f616 100644 --- a/libraries/session/build.gradle +++ b/libraries/session/build.gradle @@ -16,14 +16,27 @@ apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle" // TODO(b/178560255): Remove the "group" override after the "group" in build.gradle changed group 'androidx.media3' +android { + defaultConfig { + multiDexEnabled true + } + buildTypes { + debug { + testCoverageEnabled = true + } + } + sourceSets.androidTest.assets.srcDir '../test_data/src/test/assets/' +} dependencies { api project(modulePrefix + 'lib-common') compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion implementation 'androidx.collection:collection:' + androidxCollectionVersion implementation 'androidx.media:media:' + androidxMediaVersion - - testImplementation project(modulePrefix + 'test-utils') + androidTestImplementation 'com.squareup.okhttp3:mockwebserver:' + okhttpVersion + androidTestImplementation project(modulePrefix + 'test-utils') + androidTestImplementation project(modulePrefix + 'test-data') + androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion testImplementation 'org.robolectric:robolectric:' + robolectricVersion } diff --git a/libraries/session/src/androidTest/AndroidManifest.xml b/libraries/session/src/androidTest/AndroidManifest.xml new file mode 100644 index 0000000000..f96881ea89 --- /dev/null +++ b/libraries/session/src/androidTest/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java b/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java new file mode 100644 index 0000000000..d7ba1d979f --- /dev/null +++ b/libraries/session/src/androidTest/java/androidx/media3/session/SimpleBitmapLoaderTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import androidx.media3.test.utils.TestUtil; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.concurrent.ExecutionException; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okio.Buffer; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.junit.runner.RunWith; + +/** + * Tests for {@link SimpleBitmapLoader}. + * + *

      This test needs to run as an androidTest because robolectric's BitmapFactory is not fully + * functional. + */ +@RunWith(AndroidJUnit4.class) +public class SimpleBitmapLoaderTest { + + private static final String TEST_IMAGE_PATH = "media/jpeg/non-motion-photo-shortened.jpg"; + + @Test + public void loadData() throws Exception { + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + byte[] imageData = + TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); + + Bitmap bitmap = bitmapLoader.decodeBitmap(imageData).get(); + + assertThat( + bitmap.sameAs( + BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length))) + .isTrue(); + } + + @Test + public void loadData_withInvalidData_throwsException() { + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + + ListenableFuture future = bitmapLoader.decodeBitmap(new byte[0]); + + assertException( + future::get, IllegalArgumentException.class, /* messagePart= */ "Could not decode bitmap"); + } + + @Test + public void loadUri_loadsImage() throws Exception { + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + MockWebServer mockWebServer = new MockWebServer(); + byte[] imageData = + TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); + Buffer responseBody = new Buffer().write(imageData); + mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody)); + + Bitmap bitmap = + bitmapLoader.loadBitmap(Uri.parse(mockWebServer.url("test_path").toString())).get(); + + assertThat( + bitmap.sameAs( + BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length))) + .isTrue(); + } + + @Test + public void loadUri_serverError_throwsException() { + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.enqueue(new MockResponse().setResponseCode(404)); + + ListenableFuture future = + bitmapLoader.loadBitmap(Uri.parse(mockWebServer.url("test_path").toString())); + + assertException(future::get, IOException.class, /* messagePart= */ "Invalid response status"); + } + + @Test + public void loadUri_nonHttpUri_throwsException() { + SimpleBitmapLoader bitmapLoader = + new SimpleBitmapLoader(MoreExecutors.newDirectExecutorService()); + + assertException( + () -> bitmapLoader.loadBitmap(Uri.parse("/local/path")).get(), + MalformedURLException.class, + /* messagePart= */ "no protocol"); + assertException( + () -> bitmapLoader.loadBitmap(Uri.parse("file://local/path")).get(), + UnsupportedOperationException.class, + /* messagePart= */ "Unsupported scheme"); + assertException( + () -> bitmapLoader.loadBitmap(Uri.parse("asset://asset/path")).get(), + MalformedURLException.class, + /* messagePart= */ "unknown protocol"); + assertException( + () -> bitmapLoader.loadBitmap(Uri.parse("raw://raw/path")).get(), + MalformedURLException.class, + /* messagePart= */ "unknown protocol"); + assertException( + () -> bitmapLoader.loadBitmap(Uri.parse("data://data")).get(), + MalformedURLException.class, + /* messagePart= */ "unknown protocol"); + } + + private static void assertException( + ThrowingRunnable runnable, Class clazz, String messagePart) { + ExecutionException executionException = assertThrows(ExecutionException.class, runnable); + assertThat(executionException).hasCauseThat().isInstanceOf(clazz); + assertThat(executionException).hasMessageThat().contains(messagePart); + } +} diff --git a/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java b/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java new file mode 100644 index 0000000000..2b88806943 --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/BitmapLoader.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package androidx.media3.session; + +import android.graphics.Bitmap; +import android.net.Uri; +import androidx.media3.common.util.UnstableApi; +import com.google.common.util.concurrent.ListenableFuture; + +/** Loads images. */ +@UnstableApi +public interface BitmapLoader { + /** Decodes an image from compressed binary data. */ + ListenableFuture decodeBitmap(byte[] data); + /** Loads an image from {@code uri}. */ + ListenableFuture loadBitmap(Uri uri); +} diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java index f253433905..1b308d425a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -16,19 +16,30 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkStateNotNull; +import static androidx.media3.common.util.Util.castNonNull; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; +import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.graphics.drawable.IconCompat; import androidx.media3.common.MediaMetadata; +import androidx.media3.common.util.Consumer; +import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; /** * The default {@link MediaNotification.Provider}. @@ -60,21 +71,37 @@ import androidx.media3.common.util.Util; *

    */ @UnstableApi -/* package */ final class DefaultMediaNotificationProvider implements MediaNotification.Provider { - +public final class DefaultMediaNotificationProvider implements MediaNotification.Provider { + private static final String TAG = "NotificationProvider"; private static final int NOTIFICATION_ID = 1001; private static final String NOTIFICATION_CHANNEL_ID = "default_channel_id"; private static final String NOTIFICATION_CHANNEL_NAME = "Now playing"; private final Context context; private final NotificationManager notificationManager; + private final BitmapLoader bitmapLoader; + // Cache the last loaded bitmap to avoid reloading the bitmap again, particularly useful when + // showing a notification for the same item (e.g. when switching from playing to paused). + private final LoadedBitmapInfo lastLoadedBitmapInfo; + private final Handler mainHandler; - /** Creates an instance. */ + private OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback; + + /** Creates an instance that uses a {@link SimpleBitmapLoader} for loading artwork images. */ public DefaultMediaNotificationProvider(Context context) { + this(context, new SimpleBitmapLoader()); + } + + /** Creates an instance that uses the {@code bitmapLoader} for loading artwork images. */ + public DefaultMediaNotificationProvider(Context context, BitmapLoader bitmapLoader) { this.context = context.getApplicationContext(); + this.bitmapLoader = bitmapLoader; + lastLoadedBitmapInfo = new LoadedBitmapInfo(); + mainHandler = new Handler(Looper.getMainLooper()); notificationManager = checkStateNotNull( (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); + pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback(bitmap -> {}); } @Override @@ -118,10 +145,28 @@ import androidx.media3.common.util.Util; // Set metadata info in the notification. MediaMetadata metadata = mediaController.getMediaMetadata(); builder.setContentTitle(metadata.title).setContentText(metadata.artist); - if (metadata.artworkData != null) { - Bitmap artworkBitmap = - BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData.length); - builder.setLargeIcon(artworkBitmap); + + @Nullable ListenableFuture bitmapFuture = loadArtworkBitmap(metadata); + if (bitmapFuture != null) { + if (bitmapFuture.isDone()) { + try { + builder.setLargeIcon(Futures.getDone(bitmapFuture)); + } catch (ExecutionException e) { + Log.w(TAG, "Failed to load bitmap", e); + } + } else { + Futures.addCallback( + bitmapFuture, + new OnBitmapLoadedFutureCallback( + bitmap -> { + builder.setLargeIcon(bitmap); + onNotificationChangedCallback.onNotificationChanged( + new MediaNotification(NOTIFICATION_ID, builder.build())); + }), + // This callback must be executed on the next looper iteration, after this method has + // returned a media notification. + mainHandler::post); + } } androidx.media.app.NotificationCompat.MediaStyle mediaStyle = @@ -162,6 +207,41 @@ import androidx.media3.common.util.Util; notificationManager.createNotificationChannel(channel); } + /** + * Requests from the bitmapLoader to load artwork or returns null if the metadata don't include + * artwork. + */ + @Nullable + private ListenableFuture loadArtworkBitmap(MediaMetadata metadata) { + if (lastLoadedBitmapInfo.matches(metadata.artworkData) + || lastLoadedBitmapInfo.matches(metadata.artworkUri)) { + return Futures.immediateFuture(lastLoadedBitmapInfo.getBitmap()); + } + + ListenableFuture future; + Consumer onBitmapLoaded; + if (metadata.artworkData != null) { + future = bitmapLoader.decodeBitmap(metadata.artworkData); + onBitmapLoaded = + bitmap -> lastLoadedBitmapInfo.setBitmapInfo(castNonNull(metadata.artworkData), bitmap); + } else if (metadata.artworkUri != null) { + future = bitmapLoader.loadBitmap(metadata.artworkUri); + onBitmapLoaded = + bitmap -> lastLoadedBitmapInfo.setBitmapInfo(castNonNull(metadata.artworkUri), bitmap); + } else { + return null; + } + + pendingOnBitmapLoadedFutureCallback.discardIfPending(); + pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback(onBitmapLoaded); + Futures.addCallback( + future, + pendingOnBitmapLoadedFutureCallback, + // It's ok to run this immediately to update the last loaded bitmap. + runnable -> Util.postOrRun(mainHandler, runnable)); + return future; + } + private static int getSmallIconResId(Context context) { int appIcon = context.getApplicationInfo().icon; if (appIcon != 0) { @@ -170,4 +250,67 @@ import androidx.media3.common.util.Util; return Util.SDK_INT >= 21 ? R.drawable.media_session_service_notification_ic_music_note : 0; } } + + private static class OnBitmapLoadedFutureCallback implements FutureCallback { + + private final Consumer consumer; + + private boolean discarded; + + private OnBitmapLoadedFutureCallback(Consumer consumer) { + this.consumer = consumer; + } + + public void discardIfPending() { + discarded = true; + } + + @Override + public void onSuccess(Bitmap result) { + if (!discarded) { + consumer.accept(result); + } + } + + @Override + public void onFailure(Throwable t) { + if (!discarded) { + Log.d(TAG, "Failed to load bitmap", t); + } + } + } + + /** + * Caches the last loaded bitmap. The key to identify a bitmap is either a byte array, if the + * bitmap is loaded from compressed data, or a URI, if the bitmap was loaded from a URI. + */ + private static class LoadedBitmapInfo { + @Nullable private byte[] data; + @Nullable private Uri uri; + @Nullable private Bitmap bitmap; + + public boolean matches(@Nullable byte[] data) { + return this.data != null && data != null && Arrays.equals(this.data, data); + } + + public boolean matches(@Nullable Uri uri) { + return this.uri != null && this.uri.equals(uri); + } + + public Bitmap getBitmap() { + return checkStateNotNull(bitmap); + } + + public void setBitmapInfo(byte[] data, Bitmap bitmap) { + this.data = data; + this.bitmap = bitmap; + this.uri = null; + } + + public void setBitmapInfo(Uri uri, Bitmap bitmap) { + this.uri = uri; + this.bitmap = bitmap; + this.data = null; + } + } } diff --git a/libraries/session/src/main/java/androidx/media3/session/SimpleBitmapLoader.java b/libraries/session/src/main/java/androidx/media3/session/SimpleBitmapLoader.java new file mode 100644 index 0000000000..e3ddd87e76 --- /dev/null +++ b/libraries/session/src/main/java/androidx/media3/session/SimpleBitmapLoader.java @@ -0,0 +1,106 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package androidx.media3.session; + +import static androidx.media3.common.util.Assertions.checkStateNotNull; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import androidx.annotation.Nullable; +import androidx.media3.common.util.UnstableApi; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.io.ByteStreams; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * A simple bitmap loader that delegates all tasks to an executor and supports fetching images from + * HTTP/HTTPS endpoints. + * + *

    Loading tasks are delegated to an {@link ExecutorService} (or {@link + * ListeningExecutorService}) defined during construction. If no executor service is defined, all + * tasks are delegated to a single-thread executor service that is shared between instances of this + * class. + * + *

    The supported URI scheme is only HTTP/HTTPS and this class reads a resource only when the + * endpoint responds with an {@code HTTP 200} after sending the HTTP request. + */ +@UnstableApi +public final class SimpleBitmapLoader implements BitmapLoader { + + private static final Supplier DEFAULT_EXECUTOR_SERVICE = + Suppliers.memoize( + () -> MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); + + private final ListeningExecutorService executorService; + + /** + * Creates an instance that delegates all load tasks to a single-thread executor service shared + * between instances. + */ + public SimpleBitmapLoader() { + this(checkStateNotNull(DEFAULT_EXECUTOR_SERVICE.get())); + } + + /** Creates an instance that delegates loading tasks to the {@code executorService}. */ + public SimpleBitmapLoader(ExecutorService executorService) { + this.executorService = MoreExecutors.listeningDecorator(executorService); + } + + @Override + public ListenableFuture decodeBitmap(byte[] data) { + return executorService.submit(() -> decode(data)); + } + + @Override + public ListenableFuture loadBitmap(Uri uri) { + return executorService.submit(() -> load(uri)); + } + + private static Bitmap decode(byte[] data) { + @Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, data.length); + if (bitmap == null) { + throw new IllegalArgumentException("Could not decode bitmap"); + } + return bitmap; + } + + private static Bitmap load(Uri uri) throws IOException { + URLConnection connection = new URL(uri.toString()).openConnection(); + if (!(connection instanceof HttpURLConnection)) { + throw new UnsupportedOperationException("Unsupported scheme: " + uri.getScheme()); + } + HttpURLConnection httpConnection = (HttpURLConnection) connection; + httpConnection.connect(); + int responseCode = httpConnection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new IOException("Invalid response status code: " + responseCode); + } + try (InputStream inputStream = httpConnection.getInputStream()) { + return decode(ByteStreams.toByteArray(inputStream)); + } + } +} From 677c5dc6d44d23be1647033225834ba1df799412 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 16 Feb 2022 14:18:31 +0000 Subject: [PATCH 228/251] Skip tests requiring out of order muxing pre API 25 PiperOrigin-RevId: 429029496 --- .../transformer/mh/RemoveAudioTransformationTest.java | 11 +++++++++++ .../media3/transformer/mh/SefTransformationTest.java | 11 +++++++++++ .../media3/transformer/mh/TransformationTest.java | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java index fa3e4a8a26..2e6dbab20a 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java @@ -19,6 +19,8 @@ import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -28,8 +30,17 @@ import org.junit.runner.RunWith; /** {@link Transformer} instrumentation test for removing audio. */ @RunWith(AndroidJUnit4.class) public class RemoveAudioTransformationTest { + + private static final String TAG = "RemoveAudioTransformationTest"; + @Test public void removeAudioTransform() throws Exception { + if (Util.SDK_INT < 25) { + // TODO(b/210593256): Remove test skipping after removing the MediaMuxer dependency. + Log.i(TAG, "Skipping on this API version due to lack of muxing support"); + return; + } + Context context = ApplicationProvider.getApplicationContext(); Transformer transformer = new Transformer.Builder(context).setRemoveAudio(true).build(); runTransformer( diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java index 4212ddc391..165e48e30d 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java @@ -19,6 +19,8 @@ import static androidx.media3.transformer.AndroidTestUtil.SEF_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; @@ -29,8 +31,17 @@ import org.junit.runner.RunWith; /** {@link Transformer} instrumentation test for SEF. */ @RunWith(AndroidJUnit4.class) public class SefTransformationTest { + + private static final String TAG = "SefTransformationTest"; + @Test public void sefTransform() throws Exception { + if (Util.SDK_INT < 25) { + // TODO(b/210593256): Remove test skipping after removing the MediaMuxer dependency. + Log.i(TAG, "Skipping on this API version due to lack of muxing support"); + return; + } + Context context = ApplicationProvider.getApplicationContext(); Transformer transformer = new Transformer.Builder(context) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java index 532cb1dc96..adeb10aec7 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java @@ -19,6 +19,8 @@ import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.Util; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -28,8 +30,17 @@ import org.junit.runner.RunWith; /** {@link Transformer} instrumentation test. */ @RunWith(AndroidJUnit4.class) public class TransformationTest { + + private static final String TAG = "TransformationTest"; + @Test public void transform() throws Exception { + if (Util.SDK_INT < 25) { + // TODO(b/210593256): Remove test skipping after removing the MediaMuxer dependency. + Log.i(TAG, "Skipping on this API version due to lack of muxing support"); + return; + } + Context context = ApplicationProvider.getApplicationContext(); Transformer transformer = new Transformer.Builder(context).build(); runTransformer( From 1af841698fe0230ae5b482eb284cc3eed5baf34b Mon Sep 17 00:00:00 2001 From: samrobinson Date: Wed, 16 Feb 2022 15:10:32 +0000 Subject: [PATCH 229/251] Add exception information to AndroidTest analysis file. Tested: Verified that the additional information is available through instrumentation tests, as well as via manual testing. #mse-bug-week PiperOrigin-RevId: 429038695 --- .../media3/transformer/AndroidTestUtil.java | 101 +++++++++++++----- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 6fbff6917f..2db87e4187 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; -import static com.google.common.truth.Truth.assertWithMessage; import static java.util.concurrent.TimeUnit.SECONDS; import android.content.Context; @@ -25,11 +25,13 @@ import android.os.Build; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.MediaItem; +import androidx.media3.common.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.compatqual.NullableType; import org.json.JSONException; @@ -43,21 +45,42 @@ public final class AndroidTestUtil { "https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4"; /** - * Transforms the {@code uriString} with the {@link Transformer}. + * Transforms the {@code uriString} with the {@link Transformer}, saving a summary of the + * transformation to the application cache. * * @param context The {@link Context}. * @param testId An identifier for the test. * @param transformer The {@link Transformer} that performs the transformation. * @param uriString The uri (as a {@link String}) that will be transformed. - * @param timeoutSeconds The transformer timeout. An assertion confirms this is not exceeded. + * @param timeoutSeconds The transformer timeout. An exception is thrown if this is exceeded. * @return The {@link TransformationResult}. * @throws Exception The cause of the transformation not completing. */ public static TransformationResult runTransformer( Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) throws Exception { - AtomicReference<@NullableType Exception> exceptionReference = new AtomicReference<>(); - AtomicReference resultReference = new AtomicReference<>(); + JSONObject resultJson = new JSONObject(); + try { + TransformationResult transformationResult = + runTransformerInternal(context, testId, transformer, uriString, timeoutSeconds); + resultJson.put("transformationResult", getTransformationResultJson(transformationResult)); + return transformationResult; + } catch (Exception e) { + resultJson.put("exception", getExceptionJson(e)); + throw e; + } finally { + writeTestSummaryToFile(context, testId, resultJson); + } + } + + private static TransformationResult runTransformerInternal( + Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) + throws Exception { + AtomicReference<@NullableType TransformationException> transformationExceptionReference = + new AtomicReference<>(); + AtomicReference<@NullableType Exception> unexpectedExceptionReference = new AtomicReference<>(); + AtomicReference<@NullableType TransformationResult> transformationResultReference = + new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); Transformer testTransformer = @@ -68,14 +91,14 @@ public final class AndroidTestUtil { @Override public void onTransformationCompleted( MediaItem inputMediaItem, TransformationResult result) { - resultReference.set(result); + transformationResultReference.set(result); countDownLatch.countDown(); } @Override public void onTransformationError( MediaItem inputMediaItem, TransformationException exception) { - exceptionReference.set(exception); + transformationExceptionReference.set(exception); countDownLatch.countDown(); } }) @@ -89,38 +112,49 @@ public final class AndroidTestUtil { try { testTransformer.startTransformation( MediaItem.fromUri(uri), outputVideoFile.getAbsolutePath()); - } catch (IOException e) { - exceptionReference.set(e); + // Catch all exceptions to report. Exceptions thrown here and not caught will NOT + // propagate. + } catch (Exception e) { + unexpectedExceptionReference.set(e); + countDownLatch.countDown(); } }); - assertWithMessage("Transformer timed out after " + timeoutSeconds + " seconds.") - .that(countDownLatch.await(timeoutSeconds, SECONDS)) - .isTrue(); - @Nullable Exception exception = exceptionReference.get(); - if (exception != null) { - throw exception; + if (!countDownLatch.await(timeoutSeconds, SECONDS)) { + throw new TimeoutException("Transformer timed out after " + timeoutSeconds + " seconds."); } - TransformationResult result = - resultReference.get().buildUpon().setFileSizeBytes(outputVideoFile.length()).build(); + @Nullable Exception unexpectedException = unexpectedExceptionReference.get(); + if (unexpectedException != null) { + throw unexpectedException; + } - writeResultToFile(context, testId, result); - return result; + @Nullable + TransformationException transformationException = transformationExceptionReference.get(); + if (transformationException != null) { + throw transformationException; + } + + // If both exceptions are null, the Transformation must have succeeded, and a + // transformationResult will be available. + return checkNotNull(transformationResultReference.get()) + .buildUpon() + .setFileSizeBytes(outputVideoFile.length()) + .build(); } - private static void writeResultToFile( - Context context, String testId, TransformationResult transformationResult) + private static void writeTestSummaryToFile(Context context, String testId, JSONObject resultJson) throws IOException, JSONException { + resultJson.put("testId", testId).put("device", getDeviceJson()); + + String analysisContents = resultJson.toString(/* indentSpaces= */ 2); + + // Log contents as well as writing to file, for easier visibility on individual device testing. + Log.i("TransformerAndroidTest_" + testId, analysisContents); + File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt"); - String analysisContent = - new JSONObject() - .put("testId", testId) - .put("device", getDeviceJson()) - .put("transformationResult", getTransformationResultJson(transformationResult)) - .toString(/* indentSpaces= */ 2); try (FileWriter fileWriter = new FileWriter(analysisFile)) { - fileWriter.write(analysisContent); + fileWriter.write(analysisContents); } } @@ -154,5 +188,16 @@ public final class AndroidTestUtil { return transformationResultJson; } + private static JSONObject getExceptionJson(Exception exception) throws JSONException { + JSONObject exceptionJson = new JSONObject(); + exceptionJson.put("message", exception.getMessage()); + exceptionJson.put("type", exception.getClass()); + if (exception instanceof TransformationException) { + exceptionJson.put("errorCode", ((TransformationException) exception).errorCode); + } + exceptionJson.put("stackTrace", Log.getThrowableString(exception)); + return exceptionJson; + } + private AndroidTestUtil() {} } From 0842295a88f9a5440b5de0698bc99c2d84a33777 Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 16 Feb 2022 16:45:46 +0000 Subject: [PATCH 230/251] Make AdsLoader.State implement Bundleable This allows the AdsLoader.State to be stored in savedInstanceState #minor-release PiperOrigin-RevId: 429057697 --- .../ImaServerSideAdInsertionMediaSource.java | 67 ++++++++++++++++- ...aServerSideAdInsertionMediaSourceTest.java | 73 +++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSourceTest.java diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index faa4dc7680..6baf54e974 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -28,17 +28,22 @@ import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationAndPropagate import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationInAdGroup; import static androidx.media3.exoplayer.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState; import static java.lang.Math.min; +import static java.lang.annotation.ElementType.TYPE_USE; import android.content.Context; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.util.Pair; import android.view.ViewGroup; +import androidx.annotation.IntDef; import androidx.annotation.MainThread; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.media3.common.AdOverlayInfo; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AdViewProvider; +import androidx.media3.common.Bundleable; import androidx.media3.common.C; import androidx.media3.common.MediaItem; import androidx.media3.common.Metadata; @@ -88,6 +93,10 @@ import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -285,13 +294,67 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } /** The state of the {@link AdsLoader}. */ - public static class State { + public static class State implements Bundleable { private final ImmutableMap adPlaybackStates; - private State(ImmutableMap adPlaybackStates) { + @VisibleForTesting + /* package */ State(ImmutableMap adPlaybackStates) { this.adPlaybackStates = adPlaybackStates; } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof State)) { + return false; + } + State state = (State) o; + return adPlaybackStates.equals(state.adPlaybackStates); + } + + @Override + public int hashCode() { + return adPlaybackStates.hashCode(); + } + + // Bundleable implementation. + + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({FIELD_AD_PLAYBACK_STATES}) + private @interface FieldNumber {} + + private static final int FIELD_AD_PLAYBACK_STATES = 1; + + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putSerializable(keyForField(FIELD_AD_PLAYBACK_STATES), adPlaybackStates); + return bundle; + } + + /** Object that can restore {@link AdsLoader.State} from a {@link Bundle}. */ + public static final Bundleable.Creator CREATOR = State::fromBundle; + + @SuppressWarnings("unchecked") + private static State fromBundle(Bundle bundle) { + @Nullable + Map adPlaybackStateMap = + (Map) + bundle.getSerializable(keyForField(FIELD_AD_PLAYBACK_STATES)); + return new State( + adPlaybackStateMap != null + ? ImmutableMap.copyOf(adPlaybackStateMap) + : ImmutableMap.of()); + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } } private final ImaUtil.ServerSideAdInsertionConfiguration configuration; diff --git a/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSourceTest.java b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSourceTest.java new file mode 100644 index 0000000000..981ac9adb3 --- /dev/null +++ b/libraries/exoplayer_ima/src/test/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSourceTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.ima; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.AdPlaybackState; +import androidx.media3.common.C; +import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource.AdsLoader.State; +import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionUtil; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableMap; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link ImaServerSideAdInsertionMediaSource}. */ +@RunWith(AndroidJUnit4.class) +public class ImaServerSideAdInsertionMediaSourceTest { + + @Test + public void adsLoaderStateToBundle_marshallAndUnmarshalling_resultIsEqual() { + AdPlaybackState firstAdPlaybackState = + ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState( + new AdPlaybackState("adsId1"), + /* fromPositionUs= */ 0, + /* contentResumeOffsetUs= */ 10, + /* adDurationsUs...= */ 5_000_000, + 10_000_000, + 20_000_000); + AdPlaybackState secondAdPlaybackState = + ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState( + new AdPlaybackState("adsId2"), + /* fromPositionUs= */ 0, + /* contentResumeOffsetUs= */ 10, + /* adDurationsUs...= */ 10_000_000) + .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + AdPlaybackState thirdAdPlaybackState = + ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState( + new AdPlaybackState("adsId3"), + /* fromPositionUs= */ C.TIME_END_OF_SOURCE, + /* contentResumeOffsetUs= */ 10, + /* adDurationsUs...= */ 10_000_000); + thirdAdPlaybackState = + ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState( + thirdAdPlaybackState, + /* fromPositionUs= */ 0, + /* contentResumeOffsetUs= */ 10, + /* adDurationsUs...= */ 10_000_000) + .withRemovedAdGroupCount(1); + State state = + new State( + ImmutableMap.builder() + .put("adsId1", firstAdPlaybackState) + .put("adsId2", secondAdPlaybackState) + .put("adsId3", thirdAdPlaybackState) + .buildOrThrow()); + + assertThat(State.CREATOR.fromBundle(state.toBundle())).isEqualTo(state); + } +} From 5ee9c482443ed3982bd0bd3ab0980420eaf7145d Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 16 Feb 2022 16:55:33 +0000 Subject: [PATCH 231/251] Integrate IMA SSAI samples in main demo app PiperOrigin-RevId: 429059793 --- demos/main/src/main/AndroidManifest.xml | 1 + demos/main/src/main/assets/media.exolist.json | 92 +++++++++++++++++++ .../media3/demo/main/PlayerActivity.java | 85 +++++++++++------ 3 files changed, 148 insertions(+), 30 deletions(-) diff --git a/demos/main/src/main/AndroidManifest.xml b/demos/main/src/main/AndroidManifest.xml index f442b9e2a9..3cffe0fa54 100644 --- a/demos/main/src/main/AndroidManifest.xml +++ b/demos/main/src/main/AndroidManifest.xml @@ -76,6 +76,7 @@ + diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index 0b479ff6d5..09688fa73a 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -387,6 +387,98 @@ } ] }, + { + "name": "IMA DAI streams", + "samples": [ + { + "name": "HLS VOD: Demo (skippable pre/post), single ads [30 s]", + "uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1" + }, + { + "name": "HLS VOD: Tears of Steel (pre/mid/mid/mid/post), single ads [10s]", + "uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1" + }, + { + "name": "HLS Live: Big Buck Bunny (mid), 3 ads each [10 s]", + "uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3" + }, + { + "name": "DASH VOD: Tears of Steel (11 periods, pre/mid/post), 2/5/2 ads [5/10s]", + "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" + }, + { + "name": "Playlist: No ads - HLS VOD: Demo (skippable pre/post) - No ads", + "playlist": [ + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + }, + { + "name": "Playlist: No ads - HLS VOD: Tears of steel (pre/mid/mid/mid/post) - No ads", + "playlist": [ + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + }, + { + "name": "Playlist: No ads - HLS Live: Big Buck Bunny (mid) - No ads", + "playlist": [ + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + }, + { + "name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", + "playlist": [ + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + }, + { + "name": "Playlist: Client-side Ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", + "playlist": [ + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv", + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=" + }, + { + "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + } + ] + }, { "name": "Playlists", "samples": [ diff --git a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java index fb93c46238..437fedbdc7 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java @@ -15,8 +15,6 @@ */ package androidx.media3.demo.main; -import static androidx.media3.common.util.Assertions.checkNotNull; - import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; @@ -43,6 +41,7 @@ import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.drm.FrameworkMediaDrm; import androidx.media3.exoplayer.ima.ImaAdsLoader; +import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource; import androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.DecoderInitializationException; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException; import androidx.media3.exoplayer.offline.DownloadRequest; @@ -57,6 +56,7 @@ import androidx.media3.ui.PlayerView; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** An activity that plays media using {@link ExoPlayer}. */ public class PlayerActivity extends AppCompatActivity @@ -65,6 +65,7 @@ public class PlayerActivity extends AppCompatActivity // Saved instance state keys. private static final String KEY_TRACK_SELECTION_PARAMETERS = "track_selection_parameters"; + private static final String KEY_SERVER_SIDE_ADS_LOADER_STATE = "server_side_ads_loader_state"; private static final String KEY_ITEM_INDEX = "item_index"; private static final String KEY_POSITION = "position"; private static final String KEY_AUTO_PLAY = "auto_play"; @@ -88,7 +89,10 @@ public class PlayerActivity extends AppCompatActivity // For ad playback only. - private AdsLoader adsLoader; + @Nullable private AdsLoader clientSideAdsLoader; + @Nullable private ImaServerSideAdInsertionMediaSource.AdsLoader serverSideAdsLoader; + private ImaServerSideAdInsertionMediaSource.AdsLoader.@MonotonicNonNull State + serverSideAdsLoaderState; // Activity lifecycle. @@ -116,6 +120,12 @@ public class PlayerActivity extends AppCompatActivity startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY); startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX); startPosition = savedInstanceState.getLong(KEY_POSITION); + Bundle adsLoaderStateBundle = savedInstanceState.getBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE); + if (adsLoaderStateBundle != null) { + serverSideAdsLoaderState = + ImaServerSideAdInsertionMediaSource.AdsLoader.State.CREATOR.fromBundle( + adsLoaderStateBundle); + } } else { trackSelectionParameters = new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build(); @@ -127,7 +137,7 @@ public class PlayerActivity extends AppCompatActivity public void onNewIntent(Intent intent) { super.onNewIntent(intent); releasePlayer(); - releaseAdsLoader(); + releaseClientSideAdsLoader(); clearStartPosition(); setIntent(intent); } @@ -179,7 +189,7 @@ public class PlayerActivity extends AppCompatActivity @Override public void onDestroy() { super.onDestroy(); - releaseAdsLoader(); + releaseClientSideAdsLoader(); } @Override @@ -208,6 +218,9 @@ public class PlayerActivity extends AppCompatActivity outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay); outState.putInt(KEY_ITEM_INDEX, startItemIndex); outState.putLong(KEY_POSITION, startPosition); + if (serverSideAdsLoaderState != null) { + outState.putBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE, serverSideAdsLoaderState.toBundle()); + } } // Activity input @@ -261,17 +274,13 @@ public class PlayerActivity extends AppCompatActivity intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false); RenderersFactory renderersFactory = DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders); - MediaSource.Factory mediaSourceFactory = - new DefaultMediaSourceFactory(dataSourceFactory) - .setAdsLoaderProvider(this::getAdsLoader) - .setAdViewProvider(playerView); trackSelector = new DefaultTrackSelector(/* context= */ this); lastSeenTracksInfo = TracksInfo.EMPTY; player = new ExoPlayer.Builder(/* context= */ this) .setRenderersFactory(renderersFactory) - .setMediaSourceFactory(mediaSourceFactory) + .setMediaSourceFactory(createMediaSourceFactory()) .setTrackSelector(trackSelector) .build(); player.setTrackSelectionParameters(trackSelectionParameters); @@ -280,6 +289,7 @@ public class PlayerActivity extends AppCompatActivity player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true); player.setPlayWhenReady(startAutoPlay); playerView.setPlayer(player); + serverSideAdsLoader.setPlayer(player); debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper.start(); } @@ -293,6 +303,22 @@ public class PlayerActivity extends AppCompatActivity return true; } + private MediaSource.Factory createMediaSourceFactory() { + ImaServerSideAdInsertionMediaSource.AdsLoader.Builder serverSideAdLoaderBuilder = + new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(/* context= */ this, playerView); + if (serverSideAdsLoaderState != null) { + serverSideAdLoaderBuilder.setAdsLoaderState(serverSideAdsLoaderState); + } + serverSideAdsLoader = serverSideAdLoaderBuilder.build(); + ImaServerSideAdInsertionMediaSource.Factory imaServerSideAdInsertionMediaSourceFactory = + new ImaServerSideAdInsertionMediaSource.Factory( + serverSideAdsLoader, new DefaultMediaSourceFactory(dataSourceFactory)); + return new DefaultMediaSourceFactory(dataSourceFactory) + .setAdsLoaderProvider(this::getClientSideAdsLoader) + .setAdViewProvider(playerView) + .setServerSideAdInsertionMediaSourceFactory(imaServerSideAdInsertionMediaSourceFactory); + } + private List createMediaItems(Intent intent) { String action = intent.getAction(); boolean actionIsListView = IntentUtil.ACTION_VIEW_LIST.equals(action); @@ -304,7 +330,6 @@ public class PlayerActivity extends AppCompatActivity List mediaItems = createMediaItems(intent, DemoUtil.getDownloadTracker(/* context= */ this)); - boolean hasAds = false; for (int i = 0; i < mediaItems.size(); i++) { MediaItem mediaItem = mediaItems.get(i); @@ -318,8 +343,7 @@ public class PlayerActivity extends AppCompatActivity return Collections.emptyList(); } - MediaItem.DrmConfiguration drmConfiguration = - checkNotNull(mediaItem.localConfiguration).drmConfiguration; + MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration; if (drmConfiguration != null) { if (Util.SDK_INT < 18) { showToast(R.string.error_drm_unsupported_before_api_18); @@ -331,43 +355,44 @@ public class PlayerActivity extends AppCompatActivity return Collections.emptyList(); } } - hasAds |= mediaItem.localConfiguration.adsConfiguration != null; - } - if (!hasAds) { - releaseAdsLoader(); } return mediaItems; } - private AdsLoader getAdsLoader(MediaItem.AdsConfiguration adsConfiguration) { + private AdsLoader getClientSideAdsLoader(MediaItem.AdsConfiguration adsConfiguration) { // The ads loader is reused for multiple playbacks, so that ad playback can resume. - if (adsLoader == null) { - adsLoader = new ImaAdsLoader.Builder(/* context= */ this).build(); + if (clientSideAdsLoader == null) { + clientSideAdsLoader = new ImaAdsLoader.Builder(/* context= */ this).build(); } - adsLoader.setPlayer(player); - return adsLoader; + clientSideAdsLoader.setPlayer(player); + return clientSideAdsLoader; } protected void releasePlayer() { if (player != null) { updateTrackSelectorParameters(); updateStartPosition(); + serverSideAdsLoaderState = serverSideAdsLoader.release(); + serverSideAdsLoader = null; debugViewHelper.stop(); debugViewHelper = null; player.release(); player = null; + playerView.setPlayer(/* player= */ null); mediaItems = Collections.emptyList(); } - if (adsLoader != null) { - adsLoader.setPlayer(null); + if (clientSideAdsLoader != null) { + clientSideAdsLoader.setPlayer(null); + } else { + playerView.getAdViewGroup().removeAllViews(); } } - private void releaseAdsLoader() { - if (adsLoader != null) { - adsLoader.release(); - adsLoader = null; - playerView.getOverlayFrameLayout().removeAllViews(); + private void releaseClientSideAdsLoader() { + if (clientSideAdsLoader != null) { + clientSideAdsLoader.release(); + clientSideAdsLoader = null; + playerView.getAdViewGroup().removeAllViews(); } } @@ -490,7 +515,7 @@ public class PlayerActivity extends AppCompatActivity for (MediaItem item : IntentUtil.createMediaItemsFromIntent(intent)) { @Nullable DownloadRequest downloadRequest = - downloadTracker.getDownloadRequest(checkNotNull(item.localConfiguration).uri); + downloadTracker.getDownloadRequest(item.localConfiguration.uri); if (downloadRequest != null) { MediaItem.Builder builder = item.buildUpon(); builder From 6978746f4f16c8bca99a40949eae3435afe73f12 Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 16 Feb 2022 17:30:25 +0000 Subject: [PATCH 232/251] Clear ad playback state map when AdsLoader is released #minor-release PiperOrigin-RevId: 429067634 --- .../ima/ImaServerSideAdInsertionMediaSource.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 6baf54e974..87499ea227 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -293,7 +293,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } } - /** The state of the {@link AdsLoader}. */ + /** The state of the {@link AdsLoader} that can be used when resuming from the background. */ public static class State implements Bundleable { private final ImmutableMap adPlaybackStates; @@ -389,7 +389,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou /** * Releases resources. * - * @return The {@link State} that can be used to resume with. + * @return The {@link State} that can be used when resuming from the background. */ public State release() { for (MediaSourceResourceHolder resourceHolder : mediaSourceResources.values()) { @@ -398,9 +398,11 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou resourceHolder.imaServerSideAdInsertionMediaSource.setStreamManager( /* streamManager= */ null); } + State state = new State(ImmutableMap.copyOf(adPlaybackStateMap)); + adPlaybackStateMap.clear(); mediaSourceResources.clear(); player = null; - return new State(ImmutableMap.copyOf(adPlaybackStateMap)); + return state; } // Internal methods. From b96787d37453e0807189679b42906e7c0b048368 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 16 Feb 2022 17:41:00 +0000 Subject: [PATCH 233/251] Avoid attempting to mux out of order pre-API 25 Calling `MediaMuxer.writeSampleData` can block indefinitely on old API versions. It is better not to call this method to fail quickly with an exception rather than getting stuck. Based on on-device testing media muxer doesn't generally handle out of order samples before API 25. There are a small number of devices where this does succeed but it seems preferable to turn this off everywhere to keep the code simple and have consistent behavior. Once we switch to in-app muxing this limitation will no longer apply. #mse-bug-week PiperOrigin-RevId: 429070255 --- .../media3/transformer/FrameworkMuxer.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java index 1645a4f620..214d7b121f 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java @@ -16,6 +16,7 @@ package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.common.util.Util.castNonNull; @@ -24,6 +25,7 @@ import android.media.MediaCodec; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.ParcelFileDescriptor; +import android.util.SparseLongArray; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.media3.common.C; @@ -119,12 +121,14 @@ import java.nio.ByteBuffer; private final MediaMuxer mediaMuxer; private final MediaCodec.BufferInfo bufferInfo; + private final SparseLongArray trackIndexToLastPresentationTimeUs; private boolean isStarted; private FrameworkMuxer(MediaMuxer mediaMuxer) { this.mediaMuxer = mediaMuxer; bufferInfo = new MediaCodec.BufferInfo(); + trackIndexToLastPresentationTimeUs = new SparseLongArray(); } @Override @@ -172,7 +176,17 @@ import java.nio.ByteBuffer; int size = data.limit() - offset; int flags = isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0; bufferInfo.set(offset, size, presentationTimeUs, flags); + long lastSamplePresentationTimeUs = trackIndexToLastPresentationTimeUs.get(trackIndex); try { + // writeSampleData blocks on old API versions, so check here to avoid calling the method. + checkState( + Util.SDK_INT > 24 || presentationTimeUs >= lastSamplePresentationTimeUs, + "Samples not in presentation order (" + + presentationTimeUs + + " < " + + lastSamplePresentationTimeUs + + ") unsupported on this API version"); + trackIndexToLastPresentationTimeUs.put(trackIndex, presentationTimeUs); mediaMuxer.writeSampleData(trackIndex, data, bufferInfo); } catch (RuntimeException e) { throw new MuxerException( From 15995d3dc57e1722dbc13b168c1ab025f6ab5870 Mon Sep 17 00:00:00 2001 From: bachinger Date: Wed, 16 Feb 2022 20:38:39 +0000 Subject: [PATCH 234/251] Use service context in DefaultActionFactory We need to use a Service to create the pending intents in the `DefaultActionFactory`. PiperOrigin-RevId: 429115746 --- libraries/session/build.gradle | 1 + .../media3/session/DefaultActionFactory.java | 24 +++++++++---------- .../media3/session/MediaSessionService.java | 2 +- .../session/DefaultActionFactoryTest.java | 18 ++++++++++---- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/libraries/session/build.gradle b/libraries/session/build.gradle index dcfd31f616..7627d7d6a2 100644 --- a/libraries/session/build.gradle +++ b/libraries/session/build.gradle @@ -37,6 +37,7 @@ dependencies { androidTestImplementation project(modulePrefix + 'test-utils') androidTestImplementation project(modulePrefix + 'test-data') androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion + testImplementation project(modulePrefix + 'test-utils') testImplementation 'org.robolectric:robolectric:' + robolectricVersion } diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java index fba9b917a9..fff79f9eeb 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java @@ -16,8 +16,8 @@ package androidx.media3.session; import android.app.PendingIntent; +import android.app.Service; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.media.session.PlaybackStateCompat; @@ -39,10 +39,10 @@ import androidx.media3.common.util.Util; public static final String EXTRAS_KEY_ACTION_CUSTOM_EXTRAS = "androidx.media3.session.EXTRAS_KEY_CUSTOM_NOTIFICATION_ACTION_EXTRAS"; - private final Context context; + private final Service service; - public DefaultActionFactory(Context context) { - this.context = context.getApplicationContext(); + public DefaultActionFactory(Service service) { + this.service = service; } @Override @@ -62,13 +62,13 @@ import androidx.media3.common.util.Util; public PendingIntent createMediaActionPendingIntent(@Command long command) { int keyCode = PlaybackStateCompat.toKeyCode(command); Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); - intent.setComponent(new ComponentName(context, context.getClass())); + intent.setComponent(new ComponentName(service, service.getClass())); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); if (Util.SDK_INT >= 26 && command != COMMAND_PAUSE && command != COMMAND_STOP) { - return Api26.createPendingIntent(context, /* requestCode= */ keyCode, intent); + return Api26.createPendingIntent(service, /* requestCode= */ keyCode, intent); } else { return PendingIntent.getService( - context, + service, /* requestCode= */ keyCode, intent, Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0); @@ -77,15 +77,15 @@ import androidx.media3.common.util.Util; private PendingIntent createCustomActionPendingIntent(String action, Bundle extras) { Intent intent = new Intent(ACTION_CUSTOM); - intent.setComponent(new ComponentName(context, context.getClass())); + intent.setComponent(new ComponentName(service, service.getClass())); intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM, action); intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM_EXTRAS, extras); if (Util.SDK_INT >= 26) { return Api26.createPendingIntent( - context, /* requestCode= */ KeyEvent.KEYCODE_UNKNOWN, intent); + service, /* requestCode= */ KeyEvent.KEYCODE_UNKNOWN, intent); } else { return PendingIntent.getService( - context, + service, /* requestCode= */ KeyEvent.KEYCODE_UNKNOWN, intent, Util.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0); @@ -141,9 +141,9 @@ import androidx.media3.common.util.Util; private static final class Api26 { private Api26() {} - public static PendingIntent createPendingIntent(Context context, int keyCode, Intent intent) { + public static PendingIntent createPendingIntent(Service service, int keyCode, Intent intent) { return PendingIntent.getForegroundService( - context, /* requestCode= */ keyCode, intent, PendingIntent.FLAG_IMMUTABLE); + service, /* requestCode= */ keyCode, intent, PendingIntent.FLAG_IMMUTABLE); } } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index cc840c534a..56c3c0e450 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -396,7 +396,7 @@ public abstract class MediaSessionService extends Service { if (mediaNotificationProvider == null) { mediaNotificationProvider = new DefaultMediaNotificationProvider(getApplicationContext()); } - actionFactory = new DefaultActionFactory(getApplicationContext()); + actionFactory = new DefaultActionFactory(/* service= */ this); mediaNotificationManager = new MediaNotificationManager( /* mediaSessionService= */ this, mediaNotificationProvider, actionFactory); diff --git a/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java index 9301b618b0..d1af51c288 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java @@ -20,10 +20,11 @@ import static org.robolectric.Shadows.shadowOf; import android.app.PendingIntent; import android.content.Intent; -import androidx.test.core.app.ApplicationProvider; +import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; import org.robolectric.shadows.ShadowPendingIntent; /** Tests for {@link DefaultActionFactory}. */ @@ -33,7 +34,7 @@ public class DefaultActionFactoryTest { @Test public void createMediaPendingIntent_intentIsMediaAction() { DefaultActionFactory actionFactory = - new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + new DefaultActionFactory(Robolectric.setupService(TestService.class)); PendingIntent pendingIntent = actionFactory.createMediaActionPendingIntent(MediaNotification.ActionFactory.COMMAND_PLAY); @@ -45,7 +46,7 @@ public class DefaultActionFactoryTest { @Test public void isMediaAction_withNonMediaIntent_returnsFalse() { DefaultActionFactory actionFactory = - new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + new DefaultActionFactory(Robolectric.setupService(TestService.class)); Intent intent = new Intent("invalid_action"); @@ -55,10 +56,19 @@ public class DefaultActionFactoryTest { @Test public void isCustomAction_withNonCustomActionIntent_returnsFalse() { DefaultActionFactory actionFactory = - new DefaultActionFactory(ApplicationProvider.getApplicationContext()); + new DefaultActionFactory(Robolectric.setupService(TestService.class)); Intent intent = new Intent("invalid_action"); assertThat(actionFactory.isCustomAction(intent)).isFalse(); } + + /** A test service for unit tests. */ + public static final class TestService extends MediaLibraryService { + @Nullable + @Override + public MediaLibrarySession onGetSession(MediaSession.ControllerInfo controllerInfo) { + return null; + } + } } From 61354497e3921fd283e847fbac6c6f45f1ec4e25 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Thu, 17 Feb 2022 10:01:09 +0000 Subject: [PATCH 235/251] Add enable fallback option to transformer demo. Also invert disableFallback parameter in DefaultEncoderFactory to enableFallback. Fallback is still enabled by default. PiperOrigin-RevId: 429253266 --- .../transformer/ConfigurationActivity.java | 5 ++ .../demo/transformer/TransformerActivity.java | 8 ++- .../res/layout/configuration_activity.xml | 10 +++ .../src/main/res/values/strings.xml | 1 + .../transformer/TransformerEndToEndTest.java | 4 +- .../transformer/DefaultEncoderFactory.java | 18 ++--- .../transformer/TransformerEndToEndTest.java | 66 +++++++++---------- 7 files changed, 67 insertions(+), 45 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java index 1200d6b8c6..ff22776d4f 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java @@ -54,6 +54,7 @@ public final class ConfigurationActivity extends AppCompatActivity { public static final String SCALE_X = "scale_x"; public static final String SCALE_Y = "scale_y"; public static final String ROTATE_DEGREES = "rotate_degrees"; + public static final String ENABLE_FALLBACK = "enable_fallback"; public static final String ENABLE_HDR_EDITING = "enable_hdr_editing"; private static final String[] INPUT_URIS = { "https://html5demos.com/assets/dizzy.mp4", @@ -80,6 +81,7 @@ public final class ConfigurationActivity extends AppCompatActivity { private @MonotonicNonNull Spinner translateSpinner; private @MonotonicNonNull Spinner scaleSpinner; private @MonotonicNonNull Spinner rotateSpinner; + private @MonotonicNonNull CheckBox enableFallbackCheckBox; private @MonotonicNonNull CheckBox enableHdrEditingCheckBox; private int inputUriPosition; @@ -154,6 +156,7 @@ public final class ConfigurationActivity extends AppCompatActivity { rotateSpinner.setAdapter(rotateAdapter); rotateAdapter.addAll(SAME_AS_INPUT_OPTION, "0", "10", "45", "60", "90", "180"); + enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox); enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox); } @@ -183,6 +186,7 @@ public final class ConfigurationActivity extends AppCompatActivity { "translateSpinner", "scaleSpinner", "rotateSpinner", + "enableFallbackCheckBox", "enableHdrEditingCheckBox" }) private void startTransformation(View view) { @@ -221,6 +225,7 @@ public final class ConfigurationActivity extends AppCompatActivity { if (!SAME_AS_INPUT_OPTION.equals(selectedRotate)) { bundle.putFloat(ROTATE_DEGREES, Float.parseFloat(selectedRotate)); } + bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked()); bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked()); transformerIntent.putExtras(bundle); diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index 8810495706..4dc5c0585a 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -39,6 +39,8 @@ import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.util.DebugTextViewHelper; +import androidx.media3.transformer.DefaultEncoderFactory; +import androidx.media3.transformer.EncoderSelector; import androidx.media3.transformer.ProgressHolder; import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationRequest; @@ -224,7 +226,11 @@ public final class TransformerActivity extends AppCompatActivity { transformerBuilder .setTransformationRequest(requestBuilder.build()) .setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO)) - .setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO)); + .setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO)) + .setEncoderFactory( + new DefaultEncoderFactory( + EncoderSelector.DEFAULT, + /* enableFallback= */ bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))); } return transformerBuilder .addListener( diff --git a/demos/transformer/src/main/res/layout/configuration_activity.xml b/demos/transformer/src/main/res/layout/configuration_activity.xml index b50d5eaba0..f58f2f7167 100644 --- a/demos/transformer/src/main/res/layout/configuration_activity.xml +++ b/demos/transformer/src/main/res/layout/configuration_activity.xml @@ -170,6 +170,16 @@ android:layout_gravity="right|center_vertical" android:gravity="right" /> + + + + diff --git a/demos/transformer/src/main/res/values/strings.xml b/demos/transformer/src/main/res/values/strings.xml index 569c25bb80..3b27a515ef 100644 --- a/demos/transformer/src/main/res/values/strings.xml +++ b/demos/transformer/src/main/res/values/strings.xml @@ -27,6 +27,7 @@ Translate video Scale video Rotate video (degrees) + Enable fallback Transform [Experimental] HDR editing Debug preview: diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java index cd3fbc5bc3..104c5e7272 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -48,7 +48,7 @@ public class TransformerEndToEndTest { new TransformationRequest.Builder().setVideoMimeType(MimeTypes.VIDEO_H264).build()) .setMuxerFactory(muxerFactory) .setEncoderFactory( - new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* disableFallback= */ true)) + new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* enableFallback= */ false)) .build(); // Result of the following command: // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames bear-vp9.webm @@ -81,7 +81,7 @@ public class TransformerEndToEndTest { .build()) .setMuxerFactory(muxerFactory) .setEncoderFactory( - new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* disableFallback= */ true)) + new DefaultEncoderFactory(EncoderSelector.DEFAULT, /* enableFallback= */ false)) .build(); // Result of the following command: // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4 diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index 1eafb15ec3..d641e0ab28 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -46,7 +46,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { private static final int DEFAULT_I_FRAME_INTERVAL_SECS = 1; @Nullable private final EncoderSelector videoEncoderSelector; - private final boolean disableFallback; + private final boolean enableFallback; /** * Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}, and @@ -58,14 +58,14 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { * type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc. */ public DefaultEncoderFactory() { - this(EncoderSelector.DEFAULT, /* disableFallback= */ false); + this(EncoderSelector.DEFAULT, /* enableFallback= */ true); } /** Creates a new instance. */ public DefaultEncoderFactory( - @Nullable EncoderSelector videoEncoderSelector, boolean disableFallback) { + @Nullable EncoderSelector videoEncoderSelector, boolean enableFallback) { this.videoEncoderSelector = videoEncoderSelector; - this.disableFallback = disableFallback; + this.enableFallback = enableFallback; } @Override @@ -74,7 +74,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { // TODO(b/210591626) Add encoder selection for audio. checkArgument(!allowedMimeTypes.isEmpty()); if (!allowedMimeTypes.contains(format.sampleMimeType)) { - if (!disableFallback) { + if (enableFallback) { // TODO(b/210591626): Pick fallback MIME type using same strategy as for encoder // capabilities limitations. format = format.buildUpon().setSampleMimeType(allowedMimeTypes.get(0)).build(); @@ -117,7 +117,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { @Nullable Pair encoderAndClosestFormatSupport = findEncoderWithClosestFormatSupport( - format, videoEncoderSelector, allowedMimeTypes, disableFallback); + format, videoEncoderSelector, allowedMimeTypes, enableFallback); if (encoderAndClosestFormatSupport == null) { throw TransformationException.createForCodec( new IllegalArgumentException("The requested output format is not supported."), @@ -213,11 +213,11 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { Format requestedFormat, EncoderSelector encoderSelector, List allowedMimeTypes, - boolean disableFallback) { + boolean enableFallback) { String requestedMimeType = requestedFormat.sampleMimeType; @Nullable String mimeType = findFallbackMimeType(encoderSelector, requestedMimeType, allowedMimeTypes); - if (mimeType == null || (disableFallback && !requestedMimeType.equals(mimeType))) { + if (mimeType == null || (!enableFallback && !requestedMimeType.equals(mimeType))) { return null; } @@ -225,7 +225,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { if (encodersForMimeType.isEmpty()) { return null; } - if (disableFallback) { + if (!enableFallback) { return Pair.create(encodersForMimeType.get(0), requestedFormat); } ImmutableList filteredEncoders = diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java index 773d457b41..000f8c83ed 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -105,7 +105,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_videoOnlyPassthrough_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -116,7 +116,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioOnlyPassthrough_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER); @@ -130,7 +130,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioOnlyTranscoding_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer @@ -147,7 +147,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_audioAndVideo_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -159,7 +159,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_withSubtitles_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build()) .build(); @@ -174,7 +174,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_successiveTransformations_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); // Transform first media item. @@ -191,7 +191,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_concurrentTransformations_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -203,7 +203,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_removeAudio_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true).setRemoveAudio(true).build(); + createTransformerBuilder(/* enableFallback= */ false).setRemoveAudio(true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -216,7 +216,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_removeVideo_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true).setRemoveVideo(true).build(); + createTransformerBuilder(/* enableFallback= */ false).setRemoveVideo(true).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -232,7 +232,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -253,7 +253,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -280,7 +280,7 @@ public final class TransformerEndToEndTest { TransformationRequest fallbackTransformationRequest = new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build(); Transformer transformer = - createTransformerBuilder(/* disableFallback= */ false) + createTransformerBuilder(/* enableFallback= */ true) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -305,7 +305,7 @@ public final class TransformerEndToEndTest { Transformer.Listener mockListener2 = mock(Transformer.Listener.class); Transformer.Listener mockListener3 = mock(Transformer.Listener.class); Transformer transformer1 = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .addListener(mockListener1) .addListener(mockListener2) .addListener(mockListener3) @@ -324,7 +324,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder().setFlattenForSlowMotion(true).build()) .build(); @@ -348,7 +348,7 @@ public final class TransformerEndToEndTest { } }; Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true).addListener(listener).build(); + createTransformerBuilder(/* enableFallback= */ false).addListener(listener).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -364,7 +364,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType( @@ -385,7 +385,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withAudioDecoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder() .setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer @@ -405,7 +405,7 @@ public final class TransformerEndToEndTest { public void startTransformation_withVideoEncoderFormatUnsupported_completesWithError() throws Exception { Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true) + createTransformerBuilder(/* enableFallback= */ false) .setTransformationRequest( new TransformationRequest.Builder() .setVideoMimeType(MimeTypes.VIDEO_H263) // unsupported encoder MIME type @@ -423,7 +423,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_withIoError_completesWithError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri("asset:///non-existing-path.mp4"); transformer.startTransformation(mediaItem, outputPath); @@ -436,7 +436,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_withAudioMuxerFormatUnsupported_completesWithError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER); transformer.startTransformation(mediaItem, outputPath); @@ -457,7 +457,7 @@ public final class TransformerEndToEndTest { TransformationRequest fallbackTransformationRequest = new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build(); Transformer transformer = - createTransformerBuilder(/* disableFallback= */ false).addListener(mockListener).build(); + createTransformerBuilder(/* enableFallback= */ true).addListener(mockListener).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER); transformer.startTransformation(mediaItem, outputPath); @@ -471,7 +471,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_afterCancellation_completesSuccessfully() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); transformer.startTransformation(mediaItem, outputPath); @@ -491,7 +491,7 @@ public final class TransformerEndToEndTest { anotherThread.start(); Looper looper = anotherThread.getLooper(); Transformer transformer = - createTransformerBuilder(/* disableFallback= */ true).setLooper(looper).build(); + createTransformerBuilder(/* enableFallback= */ false).setLooper(looper).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); AtomicReference exception = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -516,7 +516,7 @@ public final class TransformerEndToEndTest { @Test public void startTransformation_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); @@ -543,7 +543,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_knownDuration_returnsConsistentStates() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); @@ -589,7 +589,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_knownDuration_givesIncreasingPercentages() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); List progresses = new ArrayList<>(); Handler progressHandler = @@ -624,7 +624,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_noCurrentTransformation_returnsNoTransformation() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); @Transformer.ProgressState int stateBeforeTransform = transformer.getProgress(progressHolder); @@ -638,7 +638,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_unknownDuration_returnsConsistentStates() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_UNKNOWN_DURATION); AtomicInteger previousProgressState = new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY); @@ -681,7 +681,7 @@ public final class TransformerEndToEndTest { @Test public void getProgress_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -705,7 +705,7 @@ public final class TransformerEndToEndTest { @Test public void cancel_afterCompletion_doesNotThrow() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY); transformer.startTransformation(mediaItem, outputPath); @@ -715,7 +715,7 @@ public final class TransformerEndToEndTest { @Test public void cancel_fromWrongThread_throwsError() throws Exception { - Transformer transformer = createTransformerBuilder(/* disableFallback= */ true).build(); + Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build(); HandlerThread anotherThread = new HandlerThread("AnotherThread"); AtomicReference illegalStateException = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -737,11 +737,11 @@ public final class TransformerEndToEndTest { assertThat(illegalStateException.get()).isNotNull(); } - private Transformer.Builder createTransformerBuilder(boolean disableFallback) { + private Transformer.Builder createTransformerBuilder(boolean enableFallback) { return new Transformer.Builder(context) .setClock(clock) .setMuxerFactory(new TestMuxerFactory()) - .setEncoderFactory(new DefaultEncoderFactory(EncoderSelector.DEFAULT, disableFallback)); + .setEncoderFactory(new DefaultEncoderFactory(EncoderSelector.DEFAULT, enableFallback)); } private static void createEncodersAndDecoders() { From 09772908b3d3728cd742cf3914b8fca5f9d48e98 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 10:33:57 +0000 Subject: [PATCH 236/251] Make sure native library loads use correct class loader Merge of https://github.com/google/ExoPlayer/pull/9934 #minor-release PiperOrigin-RevId: 429259055 --- .../media3/common/util/LibraryLoader.java | 17 +++++++++++++++-- .../media3/decoder/av1/Gav1Library.java | 8 +++++++- .../media3/decoder/ffmpeg/FfmpegLibrary.java | 8 +++++++- .../media3/decoder/flac/FlacLibrary.java | 8 +++++++- .../media3/decoder/opus/OpusLibrary.java | 9 ++++++++- .../media3/decoder/opus/OpusDecoderTest.java | 8 +++++++- .../androidx/media3/decoder/vp9/VpxLibrary.java | 9 ++++++++- 7 files changed, 59 insertions(+), 8 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java b/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java index 5e0c7ee60f..449e684fea 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java @@ -19,7 +19,7 @@ import java.util.Arrays; /** Configurable loader for native libraries. */ @UnstableApi -public final class LibraryLoader { +public abstract class LibraryLoader { private static final String TAG = "LibraryLoader"; @@ -49,7 +49,7 @@ public final class LibraryLoader { loadAttempted = true; try { for (String lib : nativeLibraries) { - System.loadLibrary(lib); + loadLibrary(lib); } isAvailable = true; } catch (UnsatisfiedLinkError exception) { @@ -59,4 +59,17 @@ public final class LibraryLoader { } return isAvailable; } + + /** + * Should be implemented to call {@code System.loadLibrary(name)}. + * + *

    It's necessary for each subclass to implement this method because {@link + * System#loadLibrary(String)} uses reflection to obtain the calling class, which is then used to + * obtain the class loader to use when loading the native library. If this class were to implement + * the method directly, and if a subclass were to have a different class loader, then loading of + * the native library would fail. + * + * @param name The name of the library to load. + */ + protected abstract void loadLibrary(String name); } diff --git a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Library.java b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Library.java index 735c382fa0..7a85882c6a 100644 --- a/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Library.java +++ b/libraries/decoder_av1/src/main/java/androidx/media3/decoder/av1/Gav1Library.java @@ -27,7 +27,13 @@ public final class Gav1Library { MediaLibraryInfo.registerModule("media3.decoder.av1"); } - private static final LibraryLoader LOADER = new LibraryLoader("gav1JNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("gav1JNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; private Gav1Library() {} diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java index ff067daf0a..bc86776cac 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java @@ -34,7 +34,13 @@ public final class FfmpegLibrary { private static final String TAG = "FfmpegLibrary"; - private static final LibraryLoader LOADER = new LibraryLoader("ffmpegJNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("ffmpegJNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; private static @MonotonicNonNull String version; private static int inputBufferPaddingSize = C.LENGTH_UNSET; diff --git a/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacLibrary.java b/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacLibrary.java index a9b14cc3cc..7a398ae3ad 100644 --- a/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacLibrary.java +++ b/libraries/decoder_flac/src/main/java/androidx/media3/decoder/flac/FlacLibrary.java @@ -27,7 +27,13 @@ public final class FlacLibrary { MediaLibraryInfo.registerModule("media3.decoder.flac"); } - private static final LibraryLoader LOADER = new LibraryLoader("flacJNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("flacJNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; private FlacLibrary() {} diff --git a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusLibrary.java b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusLibrary.java index 7c0b9cb543..4016ae1866 100644 --- a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusLibrary.java +++ b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusLibrary.java @@ -29,7 +29,14 @@ public final class OpusLibrary { MediaLibraryInfo.registerModule("media3.decoder.opus"); } - private static final LibraryLoader LOADER = new LibraryLoader("opusV2JNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("opusV2JNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; + private static @C.CryptoType int cryptoType = C.CRYPTO_TYPE_UNSUPPORTED; private OpusLibrary() {} diff --git a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java index 63cc0d072c..261ec3febd 100644 --- a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java +++ b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java @@ -34,7 +34,13 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class OpusDecoderTest { - private static final LibraryLoader LOADER = new LibraryLoader("opusV2JNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("opusV2JNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; private static final byte[] HEADER = new byte[] {79, 112, 117, 115, 72, 101, 97, 100, 0, 2, 1, 56, 0, 0, -69, -128, 0, 0, 0}; diff --git a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxLibrary.java b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxLibrary.java index f4fe5d37e6..360bdc71c8 100644 --- a/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxLibrary.java +++ b/libraries/decoder_vp9/src/main/java/androidx/media3/decoder/vp9/VpxLibrary.java @@ -29,7 +29,14 @@ public final class VpxLibrary { MediaLibraryInfo.registerModule("media3.decoder.vpx"); } - private static final LibraryLoader LOADER = new LibraryLoader("vpx", "vpxV2JNI"); + private static final LibraryLoader LOADER = + new LibraryLoader("vpx", "vpxV2JNI") { + @Override + protected void loadLibrary(String name) { + System.loadLibrary(name); + } + }; + private static @C.CryptoType int cryptoType = C.CRYPTO_TYPE_UNSUPPORTED; private VpxLibrary() {} From 7a5b3b3e5c0211b559ba6c9b2c15655c02e3279d Mon Sep 17 00:00:00 2001 From: samrobinson Date: Thu, 17 Feb 2022 14:19:19 +0000 Subject: [PATCH 237/251] Save the bitmap produced by the FrameEditorDataProcessingTest to cache. The bitmap can then be retrieved through ADB. #mse-bug-week PiperOrigin-RevId: 429293231 --- .../FrameEditorDataProcessingTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java index 2c3256ba83..09b7f5efdd 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameEditorDataProcessingTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.lang.Math.abs; import static java.lang.Math.max; +import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -34,7 +35,10 @@ import android.media.MediaExtractor; import android.media.MediaFormat; import androidx.annotation.Nullable; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; @@ -51,6 +55,8 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class FrameEditorDataProcessingTest { + private static final String TAG = "FrameEditorDataProcessingTest"; + // Input MP4 file to transform. private static final String INPUT_MP4_ASSET_STRING = "media/mp4/sample.mp4"; @@ -119,6 +125,7 @@ public final class FrameEditorDataProcessingTest { // TODO(b/207848601): switch to using proper tooling for testing against golden data. float averagePixelAbsoluteDifference = getAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, editedBitmap); + saveTestBitmapToCacheDirectory("processData_noEdits", editedBitmap); assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } @@ -137,6 +144,7 @@ public final class FrameEditorDataProcessingTest { // data.simple float averagePixelAbsoluteDifference = getAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, editedBitmap); + saveTestBitmapToCacheDirectory("processData_translateRight", editedBitmap); assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } @@ -154,6 +162,7 @@ public final class FrameEditorDataProcessingTest { // TODO(b/207848601): switch to using proper tooling for testing against golden data. float averagePixelAbsoluteDifference = getAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, editedBitmap); + saveTestBitmapToCacheDirectory("processData_scaleNarrow", editedBitmap); assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } @@ -174,6 +183,7 @@ public final class FrameEditorDataProcessingTest { // TODO(b/207848601): switch to using proper tooling for testing against golden data. float averagePixelAbsoluteDifference = getAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, editedBitmap); + saveTestBitmapToCacheDirectory("processData_rotate90", editedBitmap); assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } @@ -328,4 +338,21 @@ public final class FrameEditorDataProcessingTest { } return (float) sumMaximumAbsoluteDifferences / (width * height); } + + /** + * Saves the {@link Bitmap} to the {@link Context#getCacheDir() cache directory} as a PNG. + * + *

    File name will be {@code _output.png}. + * + * @param testId Name of the test that produced the {@link Bitmap}. + * @param bitmap The {@link Bitmap} to save. + */ + private static void saveTestBitmapToCacheDirectory(String testId, Bitmap bitmap) { + File file = new File(getApplicationContext().getExternalCacheDir(), testId + "_output.png"); + try (FileOutputStream outputStream = new FileOutputStream(file)) { + bitmap.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, outputStream); + } catch (IOException e) { + Log.e(TAG, "Could not write Bitmap to file path: " + file.getAbsolutePath(), e); + } + } } From 6b606b9987e2b4a3e01aa38d41df53223d9f67c7 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 17 Feb 2022 14:59:39 +0000 Subject: [PATCH 238/251] Depend on llvm-xy bin utils instead of GNU's binutils NDK 23 removes GNU's bin utils. More info in https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#binutils. LLVM bin utils are available in all modern NDK versions. #minor-release Issue: google/ExoPlayer#9933 PiperOrigin-RevId: 429299806 --- .../src/main/jni/build_ffmpeg.sh | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh b/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh index 49f1d028d6..fef653bf6e 100755 --- a/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh +++ b/libraries/decoder_ffmpeg/src/main/jni/build_ffmpeg.sh @@ -49,10 +49,10 @@ cd "${FFMPEG_MODULE_PATH}/jni/ffmpeg" --arch=arm \ --cpu=armv7-a \ --cross-prefix="${TOOLCHAIN_PREFIX}/armv7a-linux-androideabi16-" \ - --nm="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-nm" \ - --ar="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-ar" \ - --ranlib="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-ranlib" \ - --strip="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-strip" \ + --nm="${TOOLCHAIN_PREFIX}/llvm-nm" \ + --ar="${TOOLCHAIN_PREFIX}/llvm-ar" \ + --ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \ + --strip="${TOOLCHAIN_PREFIX}/llvm-strip" \ --extra-cflags="-march=armv7-a -mfloat-abi=softfp" \ --extra-ldflags="-Wl,--fix-cortex-a8" \ ${COMMON_OPTIONS} @@ -64,10 +64,10 @@ make clean --arch=aarch64 \ --cpu=armv8-a \ --cross-prefix="${TOOLCHAIN_PREFIX}/aarch64-linux-android21-" \ - --nm="${TOOLCHAIN_PREFIX}/aarch64-linux-android-nm" \ - --ar="${TOOLCHAIN_PREFIX}/aarch64-linux-android-ar" \ - --ranlib="${TOOLCHAIN_PREFIX}/aarch64-linux-android-ranlib" \ - --strip="${TOOLCHAIN_PREFIX}/aarch64-linux-android-strip" \ + --nm="${TOOLCHAIN_PREFIX}/llvm-nm" \ + --ar="${TOOLCHAIN_PREFIX}/llvm-ar" \ + --ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \ + --strip="${TOOLCHAIN_PREFIX}/llvm-strip" \ ${COMMON_OPTIONS} make -j$JOBS make install-libs @@ -77,10 +77,10 @@ make clean --arch=x86 \ --cpu=i686 \ --cross-prefix="${TOOLCHAIN_PREFIX}/i686-linux-android16-" \ - --nm="${TOOLCHAIN_PREFIX}/i686-linux-android-nm" \ - --ar="${TOOLCHAIN_PREFIX}/i686-linux-android-ar" \ - --ranlib="${TOOLCHAIN_PREFIX}/i686-linux-android-ranlib" \ - --strip="${TOOLCHAIN_PREFIX}/i686-linux-android-strip" \ + --nm="${TOOLCHAIN_PREFIX}/llvm-nm" \ + --ar="${TOOLCHAIN_PREFIX}/llvm-ar" \ + --ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \ + --strip="${TOOLCHAIN_PREFIX}/llvm-strip" \ --disable-asm \ ${COMMON_OPTIONS} make -j$JOBS @@ -91,10 +91,10 @@ make clean --arch=x86_64 \ --cpu=x86_64 \ --cross-prefix="${TOOLCHAIN_PREFIX}/x86_64-linux-android21-" \ - --nm="${TOOLCHAIN_PREFIX}/x86_64-linux-android-nm" \ - --ar="${TOOLCHAIN_PREFIX}/x86_64-linux-android-ar" \ - --ranlib="${TOOLCHAIN_PREFIX}/x86_64-linux-android-ranlib" \ - --strip="${TOOLCHAIN_PREFIX}/x86_64-linux-android-strip" \ + --nm="${TOOLCHAIN_PREFIX}/llvm-nm" \ + --ar="${TOOLCHAIN_PREFIX}/llvm-ar" \ + --ranlib="${TOOLCHAIN_PREFIX}/llvm-ranlib" \ + --strip="${TOOLCHAIN_PREFIX}/llvm-strip" \ --disable-asm \ ${COMMON_OPTIONS} make -j$JOBS From 265dd079e99a8a1ca9b71a2255fc9c43d3f59198 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 15:20:46 +0000 Subject: [PATCH 239/251] Move track type from TrackGroupInfo to TrackGroup The track type is derived solely from the content. It does not depend on any runtime properties such as the player's capabilities of user track selection. Hence it belongs in TrackGroup rather than TrackGroupInfo. Note that this avoids TrackSelectionOverride from having to re-derive the track type internally. PiperOrigin-RevId: 429303312 --- .../java/androidx/media3/cast/CastPlayer.java | 6 +-- .../androidx/media3/common/TrackGroup.java | 9 ++++- .../media3/common/TrackSelectionOverride.java | 2 +- .../androidx/media3/common/TracksInfo.java | 28 +++++--------- .../media3/common/TracksInfoTest.java | 38 ++++++++++++++----- .../trackselection/MappingTrackSelector.java | 12 ++---- .../MappingTrackSelectorTest.java | 13 +++++-- 7 files changed, 59 insertions(+), 49 deletions(-) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 071f38c59a..d60a7452f7 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -1038,11 +1038,7 @@ public final class CastPlayer extends BasePlayer { final boolean[] trackSelected = new boolean[] {selected}; trackGroupInfos[i] = new TracksInfo.TrackGroupInfo( - trackType, - trackGroups[i], - /* adaptiveSupported= */ false, - trackSupport, - trackSelected); + trackGroups[i], /* adaptiveSupported= */ false, trackSupport, trackSelected); } TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java index ad0bb81c23..8631aac4ab 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackGroup.java @@ -43,6 +43,8 @@ public final class TrackGroup implements Bundleable { public final int length; /** An identifier for the track group. */ public final String id; + /** The type of tracks in the group. */ + public final @C.TrackType int type; private final Format[] formats; @@ -71,6 +73,11 @@ public final class TrackGroup implements Bundleable { this.id = id; this.formats = formats; this.length = formats.length; + @C.TrackType int type = MimeTypes.getTrackType(formats[0].sampleMimeType); + if (type == C.TRACK_TYPE_UNKNOWN) { + type = MimeTypes.getTrackType(formats[0].containerMimeType); + } + this.type = type; verifyCorrectness(); } @@ -134,7 +141,7 @@ public final class TrackGroup implements Bundleable { return false; } TrackGroup other = (TrackGroup) obj; - return length == other.length && id.equals(other.id) && Arrays.equals(formats, other.formats); + return id.equals(other.id) && Arrays.equals(formats, other.formats); } // Bundleable implementation. diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java index 77b9c2bbf1..791f4f7082 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionOverride.java @@ -88,7 +88,7 @@ public final class TrackSelectionOverride implements Bundleable { /** Returns the {@link C.TrackType} of the overridden track group. */ public @C.TrackType int getTrackType() { - return MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); + return trackGroup.type; } @Override diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index bf98c0f5ef..9dd0885788 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -49,7 +49,6 @@ public final class TracksInfo implements Bundleable { /** The number of tracks in the group. */ public final int length; - private final @C.TrackType int trackType; private final TrackGroup trackGroup; private final boolean adaptiveSupported; private final @C.FormatSupport int[] trackSupport; @@ -58,23 +57,20 @@ public final class TracksInfo implements Bundleable { /** * Constructs a TrackGroupInfo. * - * @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}. * @param trackGroup The {@link TrackGroup} described. - * @param adaptiveSupported Whether adaptive selections containing more than one track are - * supported. + * @param adaptiveSupported Whether adaptive selections containing more than one track in the + * {@code trackGroup} are supported. * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. - * @param tracksSelected Whether a track is selected for each track in {@code trackGroup}. + * @param tracksSelected Whether each track in the {@code trackGroup} is selected. */ @UnstableApi public TrackGroupInfo( - @C.TrackType int trackType, TrackGroup trackGroup, boolean adaptiveSupported, @C.FormatSupport int[] trackSupport, boolean[] tracksSelected) { length = trackGroup.length; checkArgument(length == trackSupport.length && length == tracksSelected.length); - this.trackType = trackType; this.trackGroup = trackGroup; this.adaptiveSupported = adaptiveSupported && length > 1; this.trackSupport = trackSupport.clone(); @@ -191,7 +187,7 @@ public final class TracksInfo implements Bundleable { /** Returns the {@link C.TrackType} of the group. */ public @C.TrackType int getTrackType() { - return trackType; + return trackGroup.type; } @Override @@ -203,7 +199,7 @@ public final class TracksInfo implements Bundleable { return false; } TrackGroupInfo that = (TrackGroupInfo) other; - return trackType == that.trackType + return adaptiveSupported == that.adaptiveSupported && trackGroup.equals(that.trackGroup) && Arrays.equals(trackSupport, that.trackSupport) && Arrays.equals(trackSelected, that.trackSelected); @@ -212,8 +208,8 @@ public final class TracksInfo implements Bundleable { @Override public int hashCode() { int result = trackGroup.hashCode(); + result = 31 * result + (adaptiveSupported ? 1 : 0); result = 31 * result + Arrays.hashCode(trackSupport); - result = 31 * result + trackType; result = 31 * result + Arrays.hashCode(trackSelected); return result; } @@ -225,7 +221,6 @@ public final class TracksInfo implements Bundleable { @IntDef({ FIELD_TRACK_GROUP, FIELD_TRACK_SUPPORT, - FIELD_TRACK_TYPE, FIELD_TRACK_SELECTED, FIELD_ADAPTIVE_SUPPORTED, }) @@ -233,7 +228,6 @@ public final class TracksInfo implements Bundleable { private static final int FIELD_TRACK_GROUP = 0; private static final int FIELD_TRACK_SUPPORT = 1; - private static final int FIELD_TRACK_TYPE = 2; private static final int FIELD_TRACK_SELECTED = 3; private static final int FIELD_ADAPTIVE_SUPPORTED = 4; @@ -242,7 +236,6 @@ public final class TracksInfo implements Bundleable { Bundle bundle = new Bundle(); bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle()); bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport); - bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType); bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected); bundle.putBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), adaptiveSupported); return bundle; @@ -259,16 +252,13 @@ public final class TracksInfo implements Bundleable { final @C.FormatSupport int[] trackSupport = MoreObjects.firstNonNull( bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]); - @C.TrackType - int trackType = bundle.getInt(keyForField(FIELD_TRACK_TYPE), C.TRACK_TYPE_UNKNOWN); boolean[] selected = MoreObjects.firstNonNull( bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)), new boolean[trackGroup.length]); boolean adaptiveSupported = bundle.getBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), false); - return new TrackGroupInfo( - trackType, trackGroup, adaptiveSupported, trackSupport, selected); + return new TrackGroupInfo(trackGroup, adaptiveSupported, trackSupport, selected); }; private static String keyForField(@FieldNumber int field) { @@ -300,7 +290,7 @@ public final class TracksInfo implements Bundleable { /** Returns true if there are tracks of type {@code trackType}, and false otherwise. */ public boolean hasTracksOfType(@C.TrackType int trackType) { for (int i = 0; i < trackGroupInfos.size(); i++) { - if (trackGroupInfos.get(i).trackType == trackType) { + if (trackGroupInfos.get(i).getTrackType() == trackType) { return true; } } @@ -330,7 +320,7 @@ public final class TracksInfo implements Bundleable { @C.TrackType int trackType, boolean allowExceedsCapabilities) { boolean supported = true; for (int i = 0; i < trackGroupInfos.size(); i++) { - if (trackGroupInfos.get(i).trackType == trackType) { + if (trackGroupInfos.get(i).getTrackType() == trackType) { if (trackGroupInfos.get(i).isSupported(allowExceedsCapabilities)) { return true; } else { diff --git a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java index b2eb0c80fe..c3f4182e29 100644 --- a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java @@ -15,6 +15,8 @@ */ package androidx.media3.common; +import static androidx.media3.common.MimeTypes.AUDIO_AAC; +import static androidx.media3.common.MimeTypes.VIDEO_H264; import static com.google.common.truth.Truth.assertThat; import androidx.media3.common.TracksInfo.TrackGroupInfo; @@ -40,14 +42,14 @@ public class TracksInfoTest { new TracksInfo( ImmutableList.of( new TrackGroupInfo( - C.TRACK_TYPE_AUDIO, - new TrackGroup(new Format.Builder().build()), + new TrackGroup(new Format.Builder().setSampleMimeType(AUDIO_AAC).build()), /* adaptiveSupported= */ false, new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, /* tracksSelected= */ new boolean[] {true}), new TrackGroupInfo( - C.TRACK_TYPE_VIDEO, - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + new TrackGroup( + new Format.Builder().setSampleMimeType(VIDEO_H264).build(), + new Format.Builder().setSampleMimeType(VIDEO_H264).build()), /* adaptiveSupported= */ true, new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE}, /* tracksSelected= */ new boolean[] {false, true}))); @@ -59,7 +61,12 @@ public class TracksInfoTest { public void tracksInfoGetters_withoutTrack_returnExpectedValues() { TracksInfo tracksInfo = new TracksInfo(ImmutableList.of()); + assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue(); + assertThat( + tracksInfo.isTypeSupportedOrEmpty( + C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) + .isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).isEmpty(); @@ -77,15 +84,15 @@ public class TracksInfoTest { public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() { TrackGroupInfo trackGroupInfo0 = new TrackGroupInfo( - C.TRACK_TYPE_AUDIO, - new TrackGroup(new Format.Builder().build()), + new TrackGroup(new Format.Builder().setSampleMimeType(AUDIO_AAC).build()), /* adaptiveSupported= */ false, new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, /* tracksSelected= */ new boolean[] {false}); TrackGroupInfo trackGroupInfo1 = new TrackGroupInfo( - C.TRACK_TYPE_VIDEO, - new TrackGroup(new Format.Builder().build(), new Format.Builder().build()), + new TrackGroup( + new Format.Builder().setSampleMimeType(VIDEO_H264).build(), + new Format.Builder().setSampleMimeType(VIDEO_H264).build()), /* adaptiveSupported= */ true, new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED}, /* tracksSelected= */ new boolean[] {false, true}); @@ -97,6 +104,18 @@ public class TracksInfoTest { assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue(); assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue(); + assertThat( + tracksInfo.isTypeSupportedOrEmpty( + C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) + .isTrue(); + assertThat( + tracksInfo.isTypeSupportedOrEmpty( + C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) + .isTrue(); + assertThat( + tracksInfo.isTypeSupportedOrEmpty( + C.TRACK_TYPE_TEXT, /* allowExceedsCapabilities= */ true)) + .isTrue(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue(); ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); @@ -124,8 +143,7 @@ public class TracksInfoTest { public void trackGroupInfo_withSingleTrack_isNotAdaptive() { TrackGroupInfo trackGroupInfo0 = new TrackGroupInfo( - C.TRACK_TYPE_AUDIO, - new TrackGroup(new Format.Builder().build()), + new TrackGroup(new Format.Builder().setSampleMimeType(AUDIO_AAC).build()), /* adaptiveSupported= */ true, new int[] {C.FORMAT_EXCEEDS_CAPABILITIES}, /* tracksSelected= */ new boolean[] {false}); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java index 11f381e734..5f031720de 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/MappingTrackSelector.java @@ -29,7 +29,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.media3.common.C; import androidx.media3.common.C.FormatSupport; -import androidx.media3.common.MimeTypes; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; @@ -373,8 +372,7 @@ public abstract class MappingTrackSelector extends TrackSelector { for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { TrackGroup group = trackGroups.get(groupIndex); // Associate the group to a preferred renderer. - boolean preferUnassociatedRenderer = - MimeTypes.getTrackType(group.getFormat(0).sampleMimeType) == C.TRACK_TYPE_METADATA; + boolean preferUnassociatedRenderer = group.type == C.TRACK_TYPE_METADATA; int rendererIndex = findRenderer( rendererCapabilities, group, rendererTrackGroupCounts, preferUnassociatedRenderer); @@ -588,10 +586,8 @@ public abstract class MappingTrackSelector extends TrackSelector { && trackSelection.indexOf(trackIndex) != C.INDEX_UNSET; selected[trackIndex] = isTrackSelected; } - @C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex); builder.add( - new TracksInfo.TrackGroupInfo( - trackGroupType, trackGroup, adaptiveSupported, trackSupport, selected)); + new TracksInfo.TrackGroupInfo(trackGroup, adaptiveSupported, trackSupport, selected)); } } TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups(); @@ -600,12 +596,10 @@ public abstract class MappingTrackSelector extends TrackSelector { @C.FormatSupport int[] trackSupport = new int[trackGroup.length]; Arrays.fill(trackSupport, C.FORMAT_UNSUPPORTED_TYPE); // A track group only contains tracks of the same type, thus only consider the first track. - @C.TrackType - int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType); boolean[] selected = new boolean[trackGroup.length]; // Initialized to false. builder.add( new TracksInfo.TrackGroupInfo( - trackGroupType, trackGroup, /* adaptiveSupported= */ false, trackSupport, selected)); + trackGroup, /* adaptiveSupported= */ false, trackSupport, selected)); } return new TracksInfo(builder.build()); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java index 32023b11e2..6badef1484 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/MappingTrackSelectorTest.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer.trackselection; +import static androidx.media3.common.MimeTypes.AUDIO_AAC; +import static androidx.media3.common.MimeTypes.VIDEO_H264; import static com.google.common.truth.Truth.assertThat; import android.util.Pair; @@ -53,7 +55,7 @@ public final class MappingTrackSelectorTest { new FakeRendererCapabilities(C.TRACK_TYPE_METADATA); private static final TrackGroup VIDEO_TRACK_GROUP = buildTrackGroup(MimeTypes.VIDEO_H264); - private static final TrackGroup AUDIO_TRACK_GROUP = buildTrackGroup(MimeTypes.AUDIO_AAC); + private static final TrackGroup AUDIO_TRACK_GROUP = buildTrackGroup(AUDIO_AAC); private static final TrackGroup METADATA_TRACK_GROUP = buildTrackGroup(MimeTypes.APPLICATION_ID3); private static final Timeline TIMELINE = new FakeTimeline(); @@ -146,10 +148,13 @@ public final class MappingTrackSelectorTest { new int[] {C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO}, new TrackGroupArray[] { new TrackGroupArray( - new TrackGroup("0", new Format.Builder().build()), - new TrackGroup("1", new Format.Builder().build())), + new TrackGroup("0", new Format.Builder().setSampleMimeType(AUDIO_AAC).build()), + new TrackGroup("1", new Format.Builder().setSampleMimeType(AUDIO_AAC).build())), new TrackGroupArray( - new TrackGroup("2", new Format.Builder().build(), new Format.Builder().build())) + new TrackGroup( + "2", + new Format.Builder().setSampleMimeType(VIDEO_H264).build(), + new Format.Builder().setSampleMimeType(VIDEO_H264).build())) }, new int[] { RendererCapabilities.ADAPTIVE_SEAMLESS, RendererCapabilities.ADAPTIVE_NOT_SUPPORTED From f22025cc5e83c528438e000194e27764f20faf57 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 16:12:15 +0000 Subject: [PATCH 240/251] Simplify TracksInfo API isTypeSupportedOrEmpty is very specific and a little hard to understand unless you know the one thing it's useful for. This commit replaces it with isTypeSupported, which can be used in conjunction with the recently added containsType method. PiperOrigin-RevId: 429312712 --- .../media3/demo/cast/PlayerManager.java | 8 ++--- .../media3/demo/main/PlayerActivity.java | 10 +++--- .../androidx/media3/common/TracksInfo.java | 35 +++++++++++------- .../media3/common/TracksInfoTest.java | 36 ++++++++----------- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java b/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java index 89910104f6..8f4d122f92 100644 --- a/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java +++ b/demos/cast/src/main/java/androidx/media3/demo/cast/PlayerManager.java @@ -223,12 +223,12 @@ import java.util.ArrayList; if (currentPlayer != localPlayer || tracksInfo == lastSeenTrackGroupInfo) { return; } - if (!tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) { + if (tracksInfo.containsType(C.TRACK_TYPE_VIDEO) + && !tracksInfo.isTypeSupported(C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) { listener.onUnsupportedTrack(C.TRACK_TYPE_VIDEO); } - if (!tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) { + if (tracksInfo.containsType(C.TRACK_TYPE_AUDIO) + && !tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) { listener.onUnsupportedTrack(C.TRACK_TYPE_AUDIO); } lastSeenTrackGroupInfo = tracksInfo; diff --git a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java index 437fedbdc7..fc82d5aa1b 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java @@ -466,12 +466,14 @@ public class PlayerActivity extends AppCompatActivity if (tracksInfo == lastSeenTracksInfo) { return; } - if (!tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) { + if (tracksInfo.containsType(C.TRACK_TYPE_VIDEO) + && !tracksInfo.isTypeSupported( + C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) { showToast(R.string.error_unsupported_video); } - if (!tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) { + if (tracksInfo.containsType(C.TRACK_TYPE_AUDIO) + && !tracksInfo.isTypeSupported( + C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) { showToast(R.string.error_unsupported_audio); } lastSeenTracksInfo = tracksInfo; diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 9dd0885788..2405bb207c 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -288,7 +288,7 @@ public final class TracksInfo implements Bundleable { } /** Returns true if there are tracks of type {@code trackType}, and false otherwise. */ - public boolean hasTracksOfType(@C.TrackType int trackType) { + public boolean containsType(@C.TrackType int trackType) { for (int i = 0; i < trackGroupInfos.size(); i++) { if (trackGroupInfos.get(i).getTrackType() == trackType) { return true; @@ -299,16 +299,15 @@ public final class TracksInfo implements Bundleable { /** * Returns true if at least one track of type {@code trackType} is {@link - * TrackGroupInfo#isTrackSupported(int) supported} or if there are no tracks of this type. + * TrackGroupInfo#isTrackSupported(int) supported}. */ - public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) { - return isTypeSupportedOrEmpty(trackType, /* allowExceedsCapabilities= */ false); + public boolean isTypeSupported(@C.TrackType int trackType) { + return isTypeSupported(trackType, /* allowExceedsCapabilities= */ false); } /** * Returns true if at least one track of type {@code trackType} is {@link - * TrackGroupInfo#isTrackSupported(int, boolean) supported} or if there are no tracks of this - * type. + * TrackGroupInfo#isTrackSupported(int, boolean) supported}. * * @param allowExceedsCapabilities Whether to consider the track as supported if it has a * supported {@link Format#sampleMimeType MIME type}, but otherwise exceeds the advertised @@ -316,19 +315,31 @@ public final class TracksInfo implements Bundleable { * decoder whose maximum advertised resolution is exceeded by the resolution of the track. * Such tracks may be playable in some cases. */ - public boolean isTypeSupportedOrEmpty( - @C.TrackType int trackType, boolean allowExceedsCapabilities) { - boolean supported = true; + public boolean isTypeSupported(@C.TrackType int trackType, boolean allowExceedsCapabilities) { for (int i = 0; i < trackGroupInfos.size(); i++) { if (trackGroupInfos.get(i).getTrackType() == trackType) { if (trackGroupInfos.get(i).isSupported(allowExceedsCapabilities)) { return true; - } else { - supported = false; } } } - return supported; + return false; + } + + /** @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int)}. */ + @Deprecated + @UnstableApi + @SuppressWarnings("deprecation") + public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) { + return isTypeSupportedOrEmpty(trackType, /* allowExceedsCapabilities= */ false); + } + + /** @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int, boolean)}. */ + @Deprecated + @UnstableApi + public boolean isTypeSupportedOrEmpty( + @C.TrackType int trackType, boolean allowExceedsCapabilities) { + return !containsType(trackType) || isTypeSupported(trackType, allowExceedsCapabilities); } /** Returns true if at least one track of the type {@code trackType} is selected for playback. */ diff --git a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java index c3f4182e29..d9e1aced24 100644 --- a/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/TracksInfoTest.java @@ -61,12 +61,10 @@ public class TracksInfoTest { public void tracksInfoGetters_withoutTrack_returnExpectedValues() { TracksInfo tracksInfo = new TracksInfo(ImmutableList.of()); - assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_AUDIO)).isFalse(); - assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue(); - assertThat( - tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) - .isTrue(); + assertThat(tracksInfo.containsType(C.TRACK_TYPE_AUDIO)).isFalse(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO)).isFalse(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) + .isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); assertThat(trackGroupInfos).isEmpty(); @@ -98,24 +96,18 @@ public class TracksInfoTest { /* tracksSelected= */ new boolean[] {false, true}); TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1)); - assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_AUDIO)).isTrue(); - assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_VIDEO)).isTrue(); - assertThat(tracksInfo.hasTracksOfType(C.TRACK_TYPE_TEXT)).isFalse(); - assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse(); - assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue(); - assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue(); - assertThat( - tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) + assertThat(tracksInfo.containsType(C.TRACK_TYPE_AUDIO)).isTrue(); + assertThat(tracksInfo.containsType(C.TRACK_TYPE_VIDEO)).isTrue(); + assertThat(tracksInfo.containsType(C.TRACK_TYPE_TEXT)).isFalse(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO)).isFalse(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_VIDEO)).isTrue(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_TEXT)).isFalse(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) .isTrue(); - assertThat( - tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) - .isTrue(); - assertThat( - tracksInfo.isTypeSupportedOrEmpty( - C.TRACK_TYPE_TEXT, /* allowExceedsCapabilities= */ true)) + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) .isTrue(); + assertThat(tracksInfo.isTypeSupported(C.TRACK_TYPE_TEXT, /* allowExceedsCapabilities= */ true)) + .isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse(); assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue(); ImmutableList trackGroupInfos = tracksInfo.getTrackGroupInfos(); From 061aac7ab281aa6cded62ee22084ac6e9f4e0f3c Mon Sep 17 00:00:00 2001 From: samrobinson Date: Thu, 17 Feb 2022 17:06:04 +0000 Subject: [PATCH 241/251] Add new public 4k60 portrait video to the demo application. #mse-bug-week PiperOrigin-RevId: 429323642 --- .../androidx/media3/demo/transformer/ConfigurationActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java index ff22776d4f..efc9e53d7a 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java @@ -61,12 +61,14 @@ public final class ConfigurationActivity extends AppCompatActivity { "https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4", "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4", "https://html5demos.com/assets/dizzy.webm", + "https://storage.googleapis.com/exoplayer-test-media-1/mp4/portrait_4k60.mp4", }; private static final String[] URI_DESCRIPTIONS = { // same order as INPUT_URIS "MP4 with H264 video and AAC audio", "MP4 with H265 video and AAC audio", "Long MP4 with H264 video and AAC audio", "WebM with VP8 video and Vorbis audio", + "4K 60fps MP4 with H264 video and AAC audio (portrait, timestamps always increase)", }; private static final String SAME_AS_INPUT_OPTION = "same as input"; From 38717ce969f299c761d9e766ca253aea34929f83 Mon Sep 17 00:00:00 2001 From: Ian Baker Date: Fri, 18 Feb 2022 14:54:02 +0000 Subject: [PATCH 242/251] Reformat some javadoc --- .../media3/demo/main/PlayerActivity.java | 4 +- .../media3/cast/CastTrackSelection.java | 4 +- .../androidx/media3/common/AdOverlayInfo.java | 8 +- .../main/java/androidx/media3/common/C.java | 248 +++++++++++++----- .../androidx/media3/common/DrmInitData.java | 8 +- .../java/androidx/media3/common/Format.java | 52 +++- .../androidx/media3/common/MediaItem.java | 56 +++- .../androidx/media3/common/MediaMetadata.java | 8 +- .../java/androidx/media3/common/Metadata.java | 8 +- .../java/androidx/media3/common/Player.java | 80 ++++-- .../androidx/media3/common/StreamKey.java | 4 +- .../java/androidx/media3/common/Timeline.java | 16 +- .../media3/common/TrackSelectionArray.java | 4 +- .../androidx/media3/common/TracksInfo.java | 8 +- .../androidx/media3/common/util/Clock.java | 8 +- .../media3/common/util/LibraryLoader.java | 4 +- .../java/androidx/media3/common/util/Log.java | 32 ++- .../media3/common/util/LongArray.java | 4 +- .../media3/common/util/NotificationUtil.java | 24 +- .../media3/database/ExoDatabaseProvider.java | 4 +- .../media3/datasource/AssetDataSource.java | 8 +- .../datasource/ByteArrayDataSource.java | 4 +- .../media3/datasource/ContentDataSource.java | 8 +- .../datasource/DefaultDataSourceFactory.java | 4 +- .../datasource/DefaultHttpDataSource.java | 16 +- .../media3/datasource/FileDataSource.java | 8 +- .../datasource/PriorityDataSourceFactory.java | 4 +- .../datasource/RawResourceDataSource.java | 12 +- .../cache/CacheFileMetadataIndex.java | 4 +- .../cache/DefaultContentMetadata.java | 4 +- .../datasource/cronet/CronetDataSource.java | 8 +- .../cronet/CronetDataSourceFactory.java | 4 +- .../datasource/okhttp/OkHttpDataSource.java | 12 +- .../okhttp/OkHttpDataSourceFactory.java | 4 +- .../rtmp/RtmpDataSourceFactory.java | 8 +- .../androidx/media3/decoder/CryptoInfo.java | 12 +- .../media3/exoplayer/AudioFocusManager.java | 20 +- .../media3/exoplayer/DefaultLoadControl.java | 4 +- .../exoplayer/DefaultRenderersFactory.java | 4 +- .../androidx/media3/exoplayer/ExoPlayer.java | 144 +++++++--- .../media3/exoplayer/PlayerMessage.java | 4 +- .../exoplayer/RendererCapabilities.java | 24 +- .../exoplayer/RendererConfiguration.java | 4 +- .../media3/exoplayer/SimpleExoPlayer.java | 104 ++++++-- .../analytics/AnalyticsListener.java | 28 +- .../audio/AudioRendererEventListener.java | 4 +- .../audio/AudioTrackPositionTracker.java | 12 +- .../exoplayer/audio/DefaultAudioSink.java | 12 +- .../drm/DrmSessionEventListener.java | 4 +- .../exoplayer/drm/LocalMediaDrmCallback.java | 4 +- .../drm/UnsupportedDrmException.java | 4 +- .../exoplayer/offline/DownloadException.java | 8 +- .../exoplayer/offline/DownloadHelper.java | 8 +- .../exoplayer/offline/DownloadService.java | 4 +- .../exoplayer/scheduler/Requirements.java | 4 +- .../exoplayer/source/ClippingMediaSource.java | 4 +- .../source/DefaultMediaSourceFactory.java | 4 +- .../exoplayer/source/MediaSourceFactory.java | 4 +- .../exoplayer/source/MergingMediaSource.java | 4 +- .../trackselection/DefaultTrackSelector.java | 12 +- .../trackselection/RandomTrackSelection.java | 4 +- .../upstream/DefaultBandwidthMeter.java | 4 +- .../DefaultLoadErrorHandlingPolicy.java | 4 +- .../exoplayer/upstream/SlidingPercentile.java | 4 +- .../video/VideoDecoderGLSurfaceView.java | 8 +- .../video/VideoRendererEventListener.java | 4 +- .../exoplayer/dash/DashMediaSource.java | 4 +- .../dash/manifest/SingleSegmentIndex.java | 4 +- .../hls/DefaultHlsDataSourceFactory.java | 4 +- .../media3/exoplayer/hls/HlsManifest.java | 4 +- .../hls/SampleQueueMappingException.java | 4 +- .../hls/playlist/HlsMasterPlaylist.java | 4 +- .../manifest/SsManifestParser.java | 12 +- .../workmanager/WorkManagerScheduler.java | 4 +- .../media3/extractor/BinarySearchSeeker.java | 4 +- .../androidx/media3/extractor/SeekMap.java | 4 +- .../media3/extractor/amr/AmrExtractor.java | 4 +- .../extractor/flv/TagPayloadReader.java | 4 +- .../extractor/flv/VideoTagPayloadReader.java | 4 +- .../metadata/flac/VorbisComment.java | 4 +- .../media3/extractor/mp3/Mp3Extractor.java | 4 +- .../extractor/mp4/FragmentedMp4Extractor.java | 4 +- .../media3/extractor/ogg/StreamReader.java | 8 +- .../extractor/text/SimpleSubtitleDecoder.java | 4 +- .../text/SubtitleDecoderException.java | 8 +- .../extractor/text/cea/CeaSubtitle.java | 4 +- .../media3/extractor/ts/AdtsReader.java | 4 +- .../extractor/ts/DvbSubtitleReader.java | 4 +- .../media3/extractor/ts/H265Reader.java | 4 +- .../media3/extractor/ts/LatmReader.java | 4 +- .../media3/extractor/ts/SeiReader.java | 4 +- .../test/session/common/PollingCheck.java | 4 +- .../androidx/media3/test/utils/Action.java | 28 +- .../media3/test/utils/ActionSchedule.java | 4 +- .../media3/test/utils/DownloadBuilder.java | 60 +++-- .../media3/test/utils/FakeExoMediaDrm.java | 8 +- .../media3/transformer/Transformer.java | 12 +- .../media3/ui/DefaultTrackNameProvider.java | 4 +- .../main/java/androidx/media3/ui/TimeBar.java | 4 +- 99 files changed, 1050 insertions(+), 350 deletions(-) diff --git a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java index fc82d5aa1b..365bf5422c 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java @@ -260,7 +260,9 @@ public class PlayerActivity extends AppCompatActivity setContentView(R.layout.player_activity); } - /** @return Whether initialization was successful. */ + /** + * @return Whether initialization was successful. + */ protected boolean initializePlayer() { if (player == null) { Intent intent = getIntent(); diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastTrackSelection.java b/libraries/cast/src/main/java/androidx/media3/cast/CastTrackSelection.java index 55961ddf79..fa4cdc16aa 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastTrackSelection.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastTrackSelection.java @@ -31,7 +31,9 @@ import androidx.media3.common.util.Assertions; private final TrackGroup trackGroup; - /** @param trackGroup The {@link TrackGroup} from which the first track will only be selected. */ + /** + * @param trackGroup The {@link TrackGroup} from which the first track will only be selected. + */ public CastTrackSelection(TrackGroup trackGroup) { this.trackGroup = trackGroup; } diff --git a/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java b/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java index 44259fcbc3..40d367aee9 100644 --- a/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/AdOverlayInfo.java @@ -97,14 +97,18 @@ public final class AdOverlayInfo { /** An optional, detailed reason that the overlay view is needed. */ @Nullable public final String reasonDetail; - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public AdOverlayInfo(View view, @Purpose int purpose) { this(view, purpose, /* detailedReason= */ null); } - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public AdOverlayInfo(View view, @Purpose int purpose, @Nullable String detailedReason) { diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index a83ca8798f..649c648207 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -170,11 +170,17 @@ public final class C { @IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC}) @UnstableApi public @interface CryptoMode {} - /** @see MediaCodec#CRYPTO_MODE_UNENCRYPTED */ + /** + * @see MediaCodec#CRYPTO_MODE_UNENCRYPTED + */ @UnstableApi public static final int CRYPTO_MODE_UNENCRYPTED = MediaCodec.CRYPTO_MODE_UNENCRYPTED; - /** @see MediaCodec#CRYPTO_MODE_AES_CTR */ + /** + * @see MediaCodec#CRYPTO_MODE_AES_CTR + */ @UnstableApi public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR; - /** @see MediaCodec#CRYPTO_MODE_AES_CBC */ + /** + * @see MediaCodec#CRYPTO_MODE_AES_CBC + */ @UnstableApi public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC; /** @@ -243,11 +249,17 @@ public final class C { ENCODING_PCM_FLOAT }) public @interface PcmEncoding {} - /** @see AudioFormat#ENCODING_INVALID */ + /** + * @see AudioFormat#ENCODING_INVALID + */ @UnstableApi public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID; - /** @see AudioFormat#ENCODING_PCM_8BIT */ + /** + * @see AudioFormat#ENCODING_PCM_8BIT + */ @UnstableApi public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT; - /** @see AudioFormat#ENCODING_PCM_16BIT */ + /** + * @see AudioFormat#ENCODING_PCM_16BIT + */ @UnstableApi public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT; /** Like {@link #ENCODING_PCM_16BIT}, but with the bytes in big endian order. */ @UnstableApi public static final int ENCODING_PCM_16BIT_BIG_ENDIAN = 0x10000000; @@ -255,35 +267,63 @@ public final class C { @UnstableApi public static final int ENCODING_PCM_24BIT = 0x20000000; /** PCM encoding with 32 bits per sample. */ @UnstableApi public static final int ENCODING_PCM_32BIT = 0x30000000; - /** @see AudioFormat#ENCODING_PCM_FLOAT */ + /** + * @see AudioFormat#ENCODING_PCM_FLOAT + */ @UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT; - /** @see AudioFormat#ENCODING_MP3 */ + /** + * @see AudioFormat#ENCODING_MP3 + */ @UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3; - /** @see AudioFormat#ENCODING_AAC_LC */ + /** + * @see AudioFormat#ENCODING_AAC_LC + */ @UnstableApi public static final int ENCODING_AAC_LC = AudioFormat.ENCODING_AAC_LC; - /** @see AudioFormat#ENCODING_AAC_HE_V1 */ + /** + * @see AudioFormat#ENCODING_AAC_HE_V1 + */ @UnstableApi public static final int ENCODING_AAC_HE_V1 = AudioFormat.ENCODING_AAC_HE_V1; - /** @see AudioFormat#ENCODING_AAC_HE_V2 */ + /** + * @see AudioFormat#ENCODING_AAC_HE_V2 + */ @UnstableApi public static final int ENCODING_AAC_HE_V2 = AudioFormat.ENCODING_AAC_HE_V2; - /** @see AudioFormat#ENCODING_AAC_XHE */ + /** + * @see AudioFormat#ENCODING_AAC_XHE + */ @UnstableApi public static final int ENCODING_AAC_XHE = AudioFormat.ENCODING_AAC_XHE; - /** @see AudioFormat#ENCODING_AAC_ELD */ + /** + * @see AudioFormat#ENCODING_AAC_ELD + */ @UnstableApi public static final int ENCODING_AAC_ELD = AudioFormat.ENCODING_AAC_ELD; /** AAC Error Resilient Bit-Sliced Arithmetic Coding. */ @UnstableApi public static final int ENCODING_AAC_ER_BSAC = 0x40000000; - /** @see AudioFormat#ENCODING_AC3 */ + /** + * @see AudioFormat#ENCODING_AC3 + */ @UnstableApi public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3; - /** @see AudioFormat#ENCODING_E_AC3 */ + /** + * @see AudioFormat#ENCODING_E_AC3 + */ @UnstableApi public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3; - /** @see AudioFormat#ENCODING_E_AC3_JOC */ + /** + * @see AudioFormat#ENCODING_E_AC3_JOC + */ @UnstableApi public static final int ENCODING_E_AC3_JOC = AudioFormat.ENCODING_E_AC3_JOC; - /** @see AudioFormat#ENCODING_AC4 */ + /** + * @see AudioFormat#ENCODING_AC4 + */ @UnstableApi public static final int ENCODING_AC4 = AudioFormat.ENCODING_AC4; - /** @see AudioFormat#ENCODING_DTS */ + /** + * @see AudioFormat#ENCODING_DTS + */ @UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS; - /** @see AudioFormat#ENCODING_DTS_HD */ + /** + * @see AudioFormat#ENCODING_DTS_HD + */ @UnstableApi public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD; - /** @see AudioFormat#ENCODING_DOLBY_TRUEHD */ + /** + * @see AudioFormat#ENCODING_DOLBY_TRUEHD + */ @UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD; /** Represents the behavior affecting whether spatialization will be used. */ @@ -324,19 +364,33 @@ public final class C { STREAM_TYPE_DEFAULT }) public @interface StreamType {} - /** @see AudioManager#STREAM_ALARM */ + /** + * @see AudioManager#STREAM_ALARM + */ @UnstableApi public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM; - /** @see AudioManager#STREAM_DTMF */ + /** + * @see AudioManager#STREAM_DTMF + */ @UnstableApi public static final int STREAM_TYPE_DTMF = AudioManager.STREAM_DTMF; - /** @see AudioManager#STREAM_MUSIC */ + /** + * @see AudioManager#STREAM_MUSIC + */ @UnstableApi public static final int STREAM_TYPE_MUSIC = AudioManager.STREAM_MUSIC; - /** @see AudioManager#STREAM_NOTIFICATION */ + /** + * @see AudioManager#STREAM_NOTIFICATION + */ @UnstableApi public static final int STREAM_TYPE_NOTIFICATION = AudioManager.STREAM_NOTIFICATION; - /** @see AudioManager#STREAM_RING */ + /** + * @see AudioManager#STREAM_RING + */ @UnstableApi public static final int STREAM_TYPE_RING = AudioManager.STREAM_RING; - /** @see AudioManager#STREAM_SYSTEM */ + /** + * @see AudioManager#STREAM_SYSTEM + */ @UnstableApi public static final int STREAM_TYPE_SYSTEM = AudioManager.STREAM_SYSTEM; - /** @see AudioManager#STREAM_VOICE_CALL */ + /** + * @see AudioManager#STREAM_VOICE_CALL + */ @UnstableApi public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL; /** The default stream type used by audio renderers. Equal to {@link #STREAM_TYPE_MUSIC}. */ @UnstableApi public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC; @@ -359,16 +413,26 @@ public final class C { CONTENT_TYPE_UNKNOWN }) public @interface AudioContentType {} - /** @see android.media.AudioAttributes#CONTENT_TYPE_MOVIE */ + /** + * @see android.media.AudioAttributes#CONTENT_TYPE_MOVIE + */ public static final int CONTENT_TYPE_MOVIE = android.media.AudioAttributes.CONTENT_TYPE_MOVIE; - /** @see android.media.AudioAttributes#CONTENT_TYPE_MUSIC */ + /** + * @see android.media.AudioAttributes#CONTENT_TYPE_MUSIC + */ public static final int CONTENT_TYPE_MUSIC = android.media.AudioAttributes.CONTENT_TYPE_MUSIC; - /** @see android.media.AudioAttributes#CONTENT_TYPE_SONIFICATION */ + /** + * @see android.media.AudioAttributes#CONTENT_TYPE_SONIFICATION + */ public static final int CONTENT_TYPE_SONIFICATION = android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION; - /** @see android.media.AudioAttributes#CONTENT_TYPE_SPEECH */ + /** + * @see android.media.AudioAttributes#CONTENT_TYPE_SPEECH + */ public static final int CONTENT_TYPE_SPEECH = android.media.AudioAttributes.CONTENT_TYPE_SPEECH; - /** @see android.media.AudioAttributes#CONTENT_TYPE_UNKNOWN */ + /** + * @see android.media.AudioAttributes#CONTENT_TYPE_UNKNOWN + */ public static final int CONTENT_TYPE_UNKNOWN = android.media.AudioAttributes.CONTENT_TYPE_UNKNOWN; /** @@ -386,7 +450,9 @@ public final class C { flag = true, value = {FLAG_AUDIBILITY_ENFORCED}) public @interface AudioFlags {} - /** @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED */ + /** + * @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED + */ public static final int FLAG_AUDIBILITY_ENFORCED = android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED; @@ -424,46 +490,78 @@ public final class C { USAGE_VOICE_COMMUNICATION_SIGNALLING }) public @interface AudioUsage {} - /** @see android.media.AudioAttributes#USAGE_ALARM */ + /** + * @see android.media.AudioAttributes#USAGE_ALARM + */ public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM; - /** @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY */ + /** + * @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY + */ public static final int USAGE_ASSISTANCE_ACCESSIBILITY = android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; - /** @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ + /** + * @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE + */ public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - /** @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION */ + /** + * @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION + */ public static final int USAGE_ASSISTANCE_SONIFICATION = android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; - /** @see android.media.AudioAttributes#USAGE_ASSISTANT */ + /** + * @see android.media.AudioAttributes#USAGE_ASSISTANT + */ public static final int USAGE_ASSISTANT = android.media.AudioAttributes.USAGE_ASSISTANT; - /** @see android.media.AudioAttributes#USAGE_GAME */ + /** + * @see android.media.AudioAttributes#USAGE_GAME + */ public static final int USAGE_GAME = android.media.AudioAttributes.USAGE_GAME; - /** @see android.media.AudioAttributes#USAGE_MEDIA */ + /** + * @see android.media.AudioAttributes#USAGE_MEDIA + */ public static final int USAGE_MEDIA = android.media.AudioAttributes.USAGE_MEDIA; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION + */ public static final int USAGE_NOTIFICATION = android.media.AudioAttributes.USAGE_NOTIFICATION; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED + */ public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT + */ public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST + */ public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT + */ public static final int USAGE_NOTIFICATION_EVENT = android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT; - /** @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE */ + /** + * @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE + */ public static final int USAGE_NOTIFICATION_RINGTONE = android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; - /** @see android.media.AudioAttributes#USAGE_UNKNOWN */ + /** + * @see android.media.AudioAttributes#USAGE_UNKNOWN + */ public static final int USAGE_UNKNOWN = android.media.AudioAttributes.USAGE_UNKNOWN; - /** @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION */ + /** + * @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION + */ public static final int USAGE_VOICE_COMMUNICATION = android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION; - /** @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING */ + /** + * @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING + */ public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; @@ -901,11 +999,17 @@ public final class C { @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_SPACE_BT709, COLOR_SPACE_BT601, COLOR_SPACE_BT2020}) public @interface ColorSpace {} - /** @see MediaFormat#COLOR_STANDARD_BT709 */ + /** + * @see MediaFormat#COLOR_STANDARD_BT709 + */ @UnstableApi public static final int COLOR_SPACE_BT709 = MediaFormat.COLOR_STANDARD_BT709; - /** @see MediaFormat#COLOR_STANDARD_BT601_PAL */ + /** + * @see MediaFormat#COLOR_STANDARD_BT601_PAL + */ @UnstableApi public static final int COLOR_SPACE_BT601 = MediaFormat.COLOR_STANDARD_BT601_PAL; - /** @see MediaFormat#COLOR_STANDARD_BT2020 */ + /** + * @see MediaFormat#COLOR_STANDARD_BT2020 + */ @UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020; /** @@ -918,11 +1022,17 @@ public final class C { @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_TRANSFER_SDR, COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG}) public @interface ColorTransfer {} - /** @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO */ + /** + * @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO + */ @UnstableApi public static final int COLOR_TRANSFER_SDR = MediaFormat.COLOR_TRANSFER_SDR_VIDEO; - /** @see MediaFormat#COLOR_TRANSFER_ST2084 */ + /** + * @see MediaFormat#COLOR_TRANSFER_ST2084 + */ @UnstableApi public static final int COLOR_TRANSFER_ST2084 = MediaFormat.COLOR_TRANSFER_ST2084; - /** @see MediaFormat#COLOR_TRANSFER_HLG */ + /** + * @see MediaFormat#COLOR_TRANSFER_HLG + */ @UnstableApi public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG; /** @@ -935,9 +1045,13 @@ public final class C { @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL}) public @interface ColorRange {} - /** @see MediaFormat#COLOR_RANGE_LIMITED */ + /** + * @see MediaFormat#COLOR_RANGE_LIMITED + */ @UnstableApi public static final int COLOR_RANGE_LIMITED = MediaFormat.COLOR_RANGE_LIMITED; - /** @see MediaFormat#COLOR_RANGE_FULL */ + /** + * @see MediaFormat#COLOR_RANGE_FULL + */ @UnstableApi public static final int COLOR_RANGE_FULL = MediaFormat.COLOR_RANGE_FULL; /** Video projection types. */ @@ -1203,7 +1317,9 @@ public final class C { */ @UnstableApi public static final int FORMAT_UNSUPPORTED_TYPE = 0b000; - /** @deprecated Use {@link Util#usToMs(long)}. */ + /** + * @deprecated Use {@link Util#usToMs(long)}. + */ @UnstableApi @InlineMe( replacement = "Util.usToMs(timeUs)", @@ -1213,7 +1329,9 @@ public final class C { return Util.usToMs(timeUs); } - /** @deprecated Use {@link Util#msToUs(long)}. */ + /** + * @deprecated Use {@link Util#msToUs(long)}. + */ @UnstableApi @InlineMe( replacement = "Util.msToUs(timeMs)", @@ -1223,7 +1341,9 @@ public final class C { return Util.msToUs(timeMs); } - /** @deprecated Use {@link Util#generateAudioSessionIdV21(Context)}. */ + /** + * @deprecated Use {@link Util#generateAudioSessionIdV21(Context)}. + */ @UnstableApi @InlineMe( replacement = "Util.generateAudioSessionIdV21(context)", @@ -1234,7 +1354,9 @@ public final class C { return Util.generateAudioSessionIdV21(context); } - /** @deprecated Use {@link Util#getFormatSupportString(int)}. */ + /** + * @deprecated Use {@link Util#getFormatSupportString(int)}. + */ @UnstableApi @InlineMe( replacement = "Util.getFormatSupportString(formatSupport)", @@ -1244,7 +1366,9 @@ public final class C { return Util.getFormatSupportString(formatSupport); } - /** @deprecated Use {@link Util#getErrorCodeForMediaDrmErrorCode(int)}. */ + /** + * @deprecated Use {@link Util#getErrorCodeForMediaDrmErrorCode(int)}. + */ @UnstableApi @InlineMe( replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)", diff --git a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java index 2d9e19caab..7a971ef980 100644 --- a/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java +++ b/libraries/common/src/main/java/androidx/media3/common/DrmInitData.java @@ -92,7 +92,9 @@ public final class DrmInitData implements Comparator, Parcelable { /** Number of {@link SchemeData}s. */ public final int schemeDataCount; - /** @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. */ + /** + * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. + */ public DrmInitData(List schemeDatas) { this(null, false, schemeDatas.toArray(new SchemeData[0])); } @@ -105,7 +107,9 @@ public final class DrmInitData implements Comparator, Parcelable { this(schemeType, false, schemeDatas.toArray(new SchemeData[0])); } - /** @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. */ + /** + * @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. + */ public DrmInitData(SchemeData... schemeDatas) { this(null, schemeDatas); } diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index 773b9fab37..bdb48ab639 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -769,7 +769,9 @@ public final class Format implements Bundleable { // Video. - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createVideoSampleFormat( @@ -798,7 +800,9 @@ public final class Format implements Bundleable { .build(); } - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createVideoSampleFormat( @@ -833,7 +837,9 @@ public final class Format implements Bundleable { // Audio. - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createAudioSampleFormat( @@ -864,7 +870,9 @@ public final class Format implements Bundleable { .build(); } - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createAudioSampleFormat( @@ -899,7 +907,9 @@ public final class Format implements Bundleable { // Generic. - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createContainerFormat( @@ -926,7 +936,9 @@ public final class Format implements Bundleable { .build(); } - /** @deprecated Use {@link Format.Builder}. */ + /** + * @deprecated Use {@link Format.Builder}. + */ @UnstableApi @Deprecated public static Format createSampleFormat(@Nullable String id, @Nullable String sampleMimeType) { @@ -986,28 +998,36 @@ public final class Format implements Bundleable { return new Builder(this); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}. */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}. + */ @UnstableApi @Deprecated public Format copyWithMaxInputSize(int maxInputSize) { return buildUpon().setMaxInputSize(maxInputSize).build(); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}. */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}. + */ @UnstableApi @Deprecated public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { return buildUpon().setSubsampleOffsetUs(subsampleOffsetUs).build(); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} . */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} . + */ @UnstableApi @Deprecated public Format copyWithLabel(@Nullable String label) { return buildUpon().setLabel(label).build(); } - /** @deprecated Use {@link #withManifestFormatInfo(Format)}. */ + /** + * @deprecated Use {@link #withManifestFormatInfo(Format)}. + */ @UnstableApi @Deprecated public Format copyWithManifestFormatInfo(Format manifestFormat) { @@ -1092,21 +1112,27 @@ public final class Format implements Bundleable { return buildUpon().setEncoderDelay(encoderDelay).setEncoderPadding(encoderPadding).build(); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}. */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}. + */ @UnstableApi @Deprecated public Format copyWithFrameRate(float frameRate) { return buildUpon().setFrameRate(frameRate).build(); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setDrmInitData(DrmInitData)}. */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setDrmInitData(DrmInitData)}. + */ @UnstableApi @Deprecated public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { return buildUpon().setDrmInitData(drmInitData).build(); } - /** @deprecated Use {@link #buildUpon()} and {@link Builder#setMetadata(Metadata)}. */ + /** + * @deprecated Use {@link #buildUpon()} and {@link Builder#setMetadata(Metadata)}. + */ @UnstableApi @Deprecated public Format copyWithMetadata(@Nullable Metadata metadata) { diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java index 29e85b22b1..94be49086d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java @@ -712,7 +712,9 @@ public final class MediaItem implements Bundleable { /** The UUID of the protection scheme. */ public final UUID scheme; - /** @deprecated Use {@link #scheme} instead. */ + /** + * @deprecated Use {@link #scheme} instead. + */ @UnstableApi @Deprecated public final UUID uuid; /** @@ -721,7 +723,9 @@ public final class MediaItem implements Bundleable { */ @Nullable public final Uri licenseUri; - /** @deprecated Use {@link #licenseRequestHeaders} instead. */ + /** + * @deprecated Use {@link #licenseRequestHeaders} instead. + */ @UnstableApi @Deprecated public final ImmutableMap requestHeaders; /** The headers to attach to requests sent to the DRM license server. */ @@ -742,7 +746,9 @@ public final class MediaItem implements Bundleable { */ public final boolean forceDefaultLicenseUri; - /** @deprecated Use {@link #forcedSessionTrackTypes}. */ + /** + * @deprecated Use {@link #forcedSessionTrackTypes}. + */ @UnstableApi @Deprecated public final ImmutableList<@C.TrackType Integer> sessionForClearTypes; /** * The types of tracks for which to always use a DRM session even if the content is unencrypted. @@ -929,7 +935,9 @@ public final class MediaItem implements Bundleable { /** Optional subtitles to be sideloaded. */ public final ImmutableList subtitleConfigurations; - /** @deprecated Use {@link #subtitleConfigurations} instead. */ + /** + * @deprecated Use {@link #subtitleConfigurations} instead. + */ @UnstableApi @Deprecated public final List subtitles; /** @@ -998,7 +1006,9 @@ public final class MediaItem implements Bundleable { } } - /** @deprecated Use {@link LocalConfiguration}. */ + /** + * @deprecated Use {@link LocalConfiguration}. + */ @UnstableApi @Deprecated public static final class PlaybackProperties extends LocalConfiguration { @@ -1160,7 +1170,9 @@ public final class MediaItem implements Bundleable { builder.maxPlaybackSpeed); } - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public LiveConfiguration( @@ -1427,19 +1439,25 @@ public final class MediaItem implements Bundleable { } } - /** @deprecated Use {@link MediaItem.SubtitleConfiguration} instead */ + /** + * @deprecated Use {@link MediaItem.SubtitleConfiguration} instead + */ @UnstableApi @Deprecated public static final class Subtitle extends SubtitleConfiguration { - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public Subtitle(Uri uri, String mimeType, @Nullable String language) { this(uri, mimeType, language, /* selectionFlags= */ 0); } - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public Subtitle( @@ -1447,7 +1465,9 @@ public final class MediaItem implements Bundleable { this(uri, mimeType, language, selectionFlags, /* roleFlags= */ 0, /* label= */ null); } - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @UnstableApi @Deprecated public Subtitle( @@ -1550,7 +1570,9 @@ public final class MediaItem implements Bundleable { return buildClippingProperties(); } - /** @deprecated Use {@link #build()} instead. */ + /** + * @deprecated Use {@link #build()} instead. + */ @UnstableApi @Deprecated public ClippingProperties buildClippingProperties() { @@ -1680,7 +1702,9 @@ public final class MediaItem implements Bundleable { } } - /** @deprecated Use {@link ClippingConfiguration} instead. */ + /** + * @deprecated Use {@link ClippingConfiguration} instead. + */ @UnstableApi @Deprecated public static final class ClippingProperties extends ClippingConfiguration { @@ -1709,7 +1733,9 @@ public final class MediaItem implements Bundleable { * boundaries. */ @Nullable public final LocalConfiguration localConfiguration; - /** @deprecated Use {@link #localConfiguration} instead. */ + /** + * @deprecated Use {@link #localConfiguration} instead. + */ @UnstableApi @Deprecated @Nullable public final PlaybackProperties playbackProperties; /** The live playback configuration. */ @@ -1720,7 +1746,9 @@ public final class MediaItem implements Bundleable { /** The clipping properties. */ public final ClippingConfiguration clippingConfiguration; - /** @deprecated Use {@link #clippingConfiguration} instead. */ + /** + * @deprecated Use {@link #clippingConfiguration} instead. + */ @UnstableApi @Deprecated public final ClippingProperties clippingProperties; // Using PlaybackProperties and ClippingProperties until they're deleted. diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java index 4d257fe7d4..91657f83bc 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaMetadata.java @@ -248,7 +248,9 @@ public final class MediaMetadata implements Bundleable { return this; } - /** @deprecated Use {@link #setRecordingYear(Integer)} instead. */ + /** + * @deprecated Use {@link #setRecordingYear(Integer)} instead. + */ @UnstableApi @Deprecated public Builder setYear(@Nullable Integer year) { @@ -654,7 +656,9 @@ public final class MediaMetadata implements Bundleable { @Nullable public final @FolderType Integer folderType; /** Optional boolean for media playability. */ @Nullable public final Boolean isPlayable; - /** @deprecated Use {@link #recordingYear} instead. */ + /** + * @deprecated Use {@link #recordingYear} instead. + */ @UnstableApi @Deprecated @Nullable public final Integer year; /** Optional year of the recording date. */ @Nullable public final Integer recordingYear; diff --git a/libraries/common/src/main/java/androidx/media3/common/Metadata.java b/libraries/common/src/main/java/androidx/media3/common/Metadata.java index ebb0621d50..1af08378f7 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Metadata.java +++ b/libraries/common/src/main/java/androidx/media3/common/Metadata.java @@ -62,12 +62,16 @@ public final class Metadata implements Parcelable { private final Entry[] entries; - /** @param entries The metadata entries. */ + /** + * @param entries The metadata entries. + */ public Metadata(Entry... entries) { this.entries = entries; } - /** @param entries The metadata entries. */ + /** + * @param entries The metadata entries. + */ public Metadata(List entries) { this.entries = entries.toArray(new Entry[0]); } diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index d37e70869f..3f0a47f167 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -142,7 +142,9 @@ public interface Player { * The UID of the window, or {@code null} if the timeline is {@link Timeline#isEmpty() empty}. */ @Nullable public final Object windowUid; - /** @deprecated Use {@link #mediaItemIndex} instead. */ + /** + * @deprecated Use {@link #mediaItemIndex} instead. + */ @UnstableApi @Deprecated public final int windowIndex; /** The media item index. */ public final int mediaItemIndex; @@ -732,7 +734,9 @@ public interface Player { */ default void onIsLoadingChanged(boolean isLoading) {} - /** @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. */ + /** + * @deprecated Use {@link #onIsLoadingChanged(boolean)} instead. + */ @Deprecated @UnstableApi default void onLoadingChanged(boolean isLoading) {} @@ -1446,24 +1450,32 @@ public interface Player { int COMMAND_SEEK_TO_DEFAULT_POSITION = 4; /** Command to seek anywhere into the current {@link MediaItem}. */ int COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM = 5; - /** @deprecated Use {@link #COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM} instead. */ + /** + * @deprecated Use {@link #COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM} instead. + */ @UnstableApi @Deprecated int COMMAND_SEEK_IN_CURRENT_WINDOW = COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; /** Command to seek to the default position of the previous {@link MediaItem}. */ int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 6; - /** @deprecated Use {@link #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM} instead. */ + /** + * @deprecated Use {@link #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM} instead. + */ @UnstableApi @Deprecated int COMMAND_SEEK_TO_PREVIOUS_WINDOW = COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; /** Command to seek to an earlier position in the current or previous {@link MediaItem}. */ int COMMAND_SEEK_TO_PREVIOUS = 7; /** Command to seek to the default position of the next {@link MediaItem}. */ int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 8; - /** @deprecated Use {@link #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM} instead. */ + /** + * @deprecated Use {@link #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM} instead. + */ @UnstableApi @Deprecated int COMMAND_SEEK_TO_NEXT_WINDOW = COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; /** Command to seek to a later position in the current or next {@link MediaItem}. */ int COMMAND_SEEK_TO_NEXT = 9; /** Command to seek anywhere in any {@link MediaItem}. */ int COMMAND_SEEK_TO_MEDIA_ITEM = 10; - /** @deprecated Use {@link #COMMAND_SEEK_TO_MEDIA_ITEM} instead. */ + /** + * @deprecated Use {@link #COMMAND_SEEK_TO_MEDIA_ITEM} instead. + */ @UnstableApi @Deprecated int COMMAND_SEEK_TO_WINDOW = COMMAND_SEEK_TO_MEDIA_ITEM; /** Command to seek back by a fixed increment into the current {@link MediaItem}. */ int COMMAND_SEEK_BACK = 11; @@ -1889,12 +1901,16 @@ public interface Player { */ void seekForward(); - /** @deprecated Use {@link #hasPreviousMediaItem()} instead. */ + /** + * @deprecated Use {@link #hasPreviousMediaItem()} instead. + */ @UnstableApi @Deprecated boolean hasPrevious(); - /** @deprecated Use {@link #hasPreviousMediaItem()} instead. */ + /** + * @deprecated Use {@link #hasPreviousMediaItem()} instead. + */ @UnstableApi @Deprecated boolean hasPreviousWindow(); @@ -1909,12 +1925,16 @@ public interface Player { */ boolean hasPreviousMediaItem(); - /** @deprecated Use {@link #seekToPreviousMediaItem()} instead. */ + /** + * @deprecated Use {@link #seekToPreviousMediaItem()} instead. + */ @UnstableApi @Deprecated void previous(); - /** @deprecated Use {@link #seekToPreviousMediaItem()} instead. */ + /** + * @deprecated Use {@link #seekToPreviousMediaItem()} instead. + */ @UnstableApi @Deprecated void seekToPreviousWindow(); @@ -1961,12 +1981,16 @@ public interface Player { */ void seekToPrevious(); - /** @deprecated Use {@link #hasNextMediaItem()} instead. */ + /** + * @deprecated Use {@link #hasNextMediaItem()} instead. + */ @UnstableApi @Deprecated boolean hasNext(); - /** @deprecated Use {@link #hasNextMediaItem()} instead. */ + /** + * @deprecated Use {@link #hasNextMediaItem()} instead. + */ @UnstableApi @Deprecated boolean hasNextWindow(); @@ -1981,12 +2005,16 @@ public interface Player { */ boolean hasNextMediaItem(); - /** @deprecated Use {@link #seekToNextMediaItem()} instead. */ + /** + * @deprecated Use {@link #seekToNextMediaItem()} instead. + */ @UnstableApi @Deprecated void next(); - /** @deprecated Use {@link #seekToNextMediaItem()} instead. */ + /** + * @deprecated Use {@link #seekToNextMediaItem()} instead. + */ @UnstableApi @Deprecated void seekToNextWindow(); @@ -2171,7 +2199,9 @@ public interface Player { /** Returns the index of the period currently being played. */ int getCurrentPeriodIndex(); - /** @deprecated Use {@link #getCurrentMediaItemIndex()} instead. */ + /** + * @deprecated Use {@link #getCurrentMediaItemIndex()} instead. + */ @UnstableApi @Deprecated int getCurrentWindowIndex(); @@ -2183,7 +2213,9 @@ public interface Player { */ int getCurrentMediaItemIndex(); - /** @deprecated Use {@link #getNextMediaItemIndex()} instead. */ + /** + * @deprecated Use {@link #getNextMediaItemIndex()} instead. + */ @UnstableApi @Deprecated int getNextWindowIndex(); @@ -2200,7 +2232,9 @@ public interface Player { */ int getNextMediaItemIndex(); - /** @deprecated Use {@link #getPreviousMediaItemIndex()} instead. */ + /** + * @deprecated Use {@link #getPreviousMediaItemIndex()} instead. + */ @UnstableApi @Deprecated int getPreviousWindowIndex(); @@ -2262,7 +2296,9 @@ public interface Player { */ long getTotalBufferedDuration(); - /** @deprecated Use {@link #isCurrentMediaItemDynamic()} instead. */ + /** + * @deprecated Use {@link #isCurrentMediaItemDynamic()} instead. + */ @UnstableApi @Deprecated boolean isCurrentWindowDynamic(); @@ -2275,7 +2311,9 @@ public interface Player { */ boolean isCurrentMediaItemDynamic(); - /** @deprecated Use {@link #isCurrentMediaItemLive()} instead. */ + /** + * @deprecated Use {@link #isCurrentMediaItemLive()} instead. + */ @UnstableApi @Deprecated boolean isCurrentWindowLive(); @@ -2301,7 +2339,9 @@ public interface Player { */ long getCurrentLiveOffset(); - /** @deprecated Use {@link #isCurrentMediaItemSeekable()} instead. */ + /** + * @deprecated Use {@link #isCurrentMediaItemSeekable()} instead. + */ @UnstableApi @Deprecated boolean isCurrentWindowSeekable(); diff --git a/libraries/common/src/main/java/androidx/media3/common/StreamKey.java b/libraries/common/src/main/java/androidx/media3/common/StreamKey.java index eda6e51729..939575b562 100644 --- a/libraries/common/src/main/java/androidx/media3/common/StreamKey.java +++ b/libraries/common/src/main/java/androidx/media3/common/StreamKey.java @@ -44,7 +44,9 @@ public final class StreamKey implements Comparable, Parcelable { /** The stream index. */ public final int streamIndex; - /** @deprecated Use {@link #streamIndex}. */ + /** + * @deprecated Use {@link #streamIndex}. + */ @Deprecated public final int trackIndex; /** diff --git a/libraries/common/src/main/java/androidx/media3/common/Timeline.java b/libraries/common/src/main/java/androidx/media3/common/Timeline.java index 498eed4870..3f665f8bfc 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Timeline.java +++ b/libraries/common/src/main/java/androidx/media3/common/Timeline.java @@ -169,7 +169,9 @@ public abstract class Timeline implements Bundleable { */ public Object uid; - /** @deprecated Use {@link #mediaItem} instead. */ + /** + * @deprecated Use {@link #mediaItem} instead. + */ @UnstableApi @Deprecated @Nullable public Object tag; /** The {@link MediaItem} associated to the window. Not necessarily unique. */ @@ -212,7 +214,9 @@ public abstract class Timeline implements Bundleable { /** Whether this window may change when the timeline is updated. */ public boolean isDynamic; - /** @deprecated Use {@link #isLive()} instead. */ + /** + * @deprecated Use {@link #isLive()} instead. + */ @UnstableApi @Deprecated public boolean isLive; /** @@ -1178,7 +1182,9 @@ public abstract class Timeline implements Bundleable { == C.INDEX_UNSET; } - /** @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long)} instead. */ + /** + * @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long)} instead. + */ @UnstableApi @Deprecated @InlineMe(replacement = "this.getPeriodPositionUs(window, period, windowIndex, windowPositionUs)") @@ -1186,7 +1192,9 @@ public abstract class Timeline implements Bundleable { Window window, Period period, int windowIndex, long windowPositionUs) { return getPeriodPositionUs(window, period, windowIndex, windowPositionUs); } - /** @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long, long)} instead. */ + /** + * @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long, long)} instead. + */ @UnstableApi @Deprecated @Nullable diff --git a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionArray.java b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionArray.java index 9d6372759d..4867d8029a 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TrackSelectionArray.java +++ b/libraries/common/src/main/java/androidx/media3/common/TrackSelectionArray.java @@ -32,7 +32,9 @@ public final class TrackSelectionArray { // Lazily initialized hashcode. private int hashCode; - /** @param trackSelections The selections. Must not be null, but may contain null elements. */ + /** + * @param trackSelections The selections. Must not be null, but may contain null elements. + */ public TrackSelectionArray(@NullableType TrackSelection... trackSelections) { this.trackSelections = trackSelections; this.length = trackSelections.length; diff --git a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java index 2405bb207c..793da2a831 100644 --- a/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/TracksInfo.java @@ -326,7 +326,9 @@ public final class TracksInfo implements Bundleable { return false; } - /** @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int)}. */ + /** + * @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int)}. + */ @Deprecated @UnstableApi @SuppressWarnings("deprecation") @@ -334,7 +336,9 @@ public final class TracksInfo implements Bundleable { return isTypeSupportedOrEmpty(trackType, /* allowExceedsCapabilities= */ false); } - /** @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int, boolean)}. */ + /** + * @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int, boolean)}. + */ @Deprecated @UnstableApi public boolean isTypeSupportedOrEmpty( diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Clock.java b/libraries/common/src/main/java/androidx/media3/common/util/Clock.java index 18a4e74dac..27a87d0918 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Clock.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Clock.java @@ -36,10 +36,14 @@ public interface Clock { */ long currentTimeMillis(); - /** @see android.os.SystemClock#elapsedRealtime() */ + /** + * @see android.os.SystemClock#elapsedRealtime() + */ long elapsedRealtime(); - /** @see android.os.SystemClock#uptimeMillis() */ + /** + * @see android.os.SystemClock#uptimeMillis() + */ long uptimeMillis(); /** diff --git a/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java b/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java index 449e684fea..f2a176ce8d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/LibraryLoader.java @@ -27,7 +27,9 @@ public abstract class LibraryLoader { private boolean loadAttempted; private boolean isAvailable; - /** @param libraries The names of the libraries to load. */ + /** + * @param libraries The names of the libraries to load. + */ public LibraryLoader(String... libraries) { nativeLibraries = libraries; } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Log.java b/libraries/common/src/main/java/androidx/media3/common/util/Log.java index 98b61a6290..ce0d25dfad 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Log.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Log.java @@ -82,7 +82,9 @@ public final class Log { Log.logStackTraces = logStackTraces; } - /** @see android.util.Log#d(String, String) */ + /** + * @see android.util.Log#d(String, String) + */ @Pure public static void d(@Size(max = 23) String tag, String message) { if (logLevel == LOG_LEVEL_ALL) { @@ -90,13 +92,17 @@ public final class Log { } } - /** @see android.util.Log#d(String, String, Throwable) */ + /** + * @see android.util.Log#d(String, String, Throwable) + */ @Pure public static void d(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { d(tag, appendThrowableString(message, throwable)); } - /** @see android.util.Log#i(String, String) */ + /** + * @see android.util.Log#i(String, String) + */ @Pure public static void i(@Size(max = 23) String tag, String message) { if (logLevel <= LOG_LEVEL_INFO) { @@ -104,13 +110,17 @@ public final class Log { } } - /** @see android.util.Log#i(String, String, Throwable) */ + /** + * @see android.util.Log#i(String, String, Throwable) + */ @Pure public static void i(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { i(tag, appendThrowableString(message, throwable)); } - /** @see android.util.Log#w(String, String) */ + /** + * @see android.util.Log#w(String, String) + */ @Pure public static void w(@Size(max = 23) String tag, String message) { if (logLevel <= LOG_LEVEL_WARNING) { @@ -118,13 +128,17 @@ public final class Log { } } - /** @see android.util.Log#w(String, String, Throwable) */ + /** + * @see android.util.Log#w(String, String, Throwable) + */ @Pure public static void w(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { w(tag, appendThrowableString(message, throwable)); } - /** @see android.util.Log#e(String, String) */ + /** + * @see android.util.Log#e(String, String) + */ @Pure public static void e(@Size(max = 23) String tag, String message) { if (logLevel <= LOG_LEVEL_ERROR) { @@ -132,7 +146,9 @@ public final class Log { } } - /** @see android.util.Log#e(String, String, Throwable) */ + /** + * @see android.util.Log#e(String, String, Throwable) + */ @Pure public static void e(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { e(tag, appendThrowableString(message, throwable)); diff --git a/libraries/common/src/main/java/androidx/media3/common/util/LongArray.java b/libraries/common/src/main/java/androidx/media3/common/util/LongArray.java index 5642afaeda..bca5d9bfb2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/LongArray.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/LongArray.java @@ -30,7 +30,9 @@ public final class LongArray { this(DEFAULT_INITIAL_CAPACITY); } - /** @param initialCapacity The initial capacity of the array. */ + /** + * @param initialCapacity The initial capacity of the array. + */ public LongArray(int initialCapacity) { values = new long[initialCapacity]; } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java index e857b0817c..bfec3b4548 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NotificationUtil.java @@ -54,17 +54,29 @@ public final class NotificationUtil { IMPORTANCE_HIGH }) public @interface Importance {} - /** @see NotificationManager#IMPORTANCE_UNSPECIFIED */ + /** + * @see NotificationManager#IMPORTANCE_UNSPECIFIED + */ public static final int IMPORTANCE_UNSPECIFIED = NotificationManager.IMPORTANCE_UNSPECIFIED; - /** @see NotificationManager#IMPORTANCE_NONE */ + /** + * @see NotificationManager#IMPORTANCE_NONE + */ public static final int IMPORTANCE_NONE = NotificationManager.IMPORTANCE_NONE; - /** @see NotificationManager#IMPORTANCE_MIN */ + /** + * @see NotificationManager#IMPORTANCE_MIN + */ public static final int IMPORTANCE_MIN = NotificationManager.IMPORTANCE_MIN; - /** @see NotificationManager#IMPORTANCE_LOW */ + /** + * @see NotificationManager#IMPORTANCE_LOW + */ public static final int IMPORTANCE_LOW = NotificationManager.IMPORTANCE_LOW; - /** @see NotificationManager#IMPORTANCE_DEFAULT */ + /** + * @see NotificationManager#IMPORTANCE_DEFAULT + */ public static final int IMPORTANCE_DEFAULT = NotificationManager.IMPORTANCE_DEFAULT; - /** @see NotificationManager#IMPORTANCE_HIGH */ + /** + * @see NotificationManager#IMPORTANCE_HIGH + */ public static final int IMPORTANCE_HIGH = NotificationManager.IMPORTANCE_HIGH; /** diff --git a/libraries/database/src/main/java/androidx/media3/database/ExoDatabaseProvider.java b/libraries/database/src/main/java/androidx/media3/database/ExoDatabaseProvider.java index c5232a0b49..5015e61bad 100644 --- a/libraries/database/src/main/java/androidx/media3/database/ExoDatabaseProvider.java +++ b/libraries/database/src/main/java/androidx/media3/database/ExoDatabaseProvider.java @@ -18,7 +18,9 @@ package androidx.media3.database; import android.content.Context; import androidx.media3.common.util.UnstableApi; -/** @deprecated Use {@link StandaloneDatabaseProvider}. */ +/** + * @deprecated Use {@link StandaloneDatabaseProvider}. + */ @Deprecated @UnstableApi public final class ExoDatabaseProvider extends StandaloneDatabaseProvider { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/AssetDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/AssetDataSource.java index 88899b7312..83e8ee228f 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/AssetDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/AssetDataSource.java @@ -37,7 +37,9 @@ public final class AssetDataSource extends BaseDataSource { /** Thrown when an {@link IOException} is encountered reading a local asset. */ public static final class AssetDataSourceException extends DataSourceException { - /** @deprecated Use {@link #AssetDataSourceException(Throwable, int)}. */ + /** + * @deprecated Use {@link #AssetDataSourceException(Throwable, int)}. + */ @Deprecated public AssetDataSourceException(IOException cause) { super(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); @@ -62,7 +64,9 @@ public final class AssetDataSource extends BaseDataSource { private long bytesRemaining; private boolean opened; - /** @param context A context. */ + /** + * @param context A context. + */ public AssetDataSource(Context context) { super(/* isNetwork= */ false); this.assetManager = context.getAssets(); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/ByteArrayDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/ByteArrayDataSource.java index c3aa5b9a88..d62445a886 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/ByteArrayDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/ByteArrayDataSource.java @@ -36,7 +36,9 @@ public final class ByteArrayDataSource extends BaseDataSource { private int bytesRemaining; private boolean opened; - /** @param data The data to be read. */ + /** + * @param data The data to be read. + */ public ByteArrayDataSource(byte[] data) { super(/* isNetwork= */ false); Assertions.checkNotNull(data); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java index 100b79caf0..32bdeebca4 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java @@ -46,7 +46,9 @@ public final class ContentDataSource extends BaseDataSource { /** Thrown when an {@link IOException} is encountered reading from a content URI. */ public static class ContentDataSourceException extends DataSourceException { - /** @deprecated Use {@link #ContentDataSourceException(IOException, int)}. */ + /** + * @deprecated Use {@link #ContentDataSourceException(IOException, int)}. + */ @Deprecated public ContentDataSourceException(IOException cause) { this(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); @@ -67,7 +69,9 @@ public final class ContentDataSource extends BaseDataSource { private long bytesRemaining; private boolean opened; - /** @param context A context. */ + /** + * @param context A context. + */ public ContentDataSource(Context context) { super(/* isNetwork= */ false); this.resolver = context.getContentResolver(); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultDataSourceFactory.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultDataSourceFactory.java index b5f15b7063..a57e715bf7 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultDataSourceFactory.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultDataSourceFactory.java @@ -20,7 +20,9 @@ import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource.Factory; -/** @deprecated Use {@link DefaultDataSource.Factory} instead. */ +/** + * @deprecated Use {@link DefaultDataSource.Factory} instead. + */ @UnstableApi @Deprecated public final class DefaultDataSourceFactory implements Factory { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java index 2e8be29c17..4b6a9f0772 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java @@ -229,21 +229,27 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou private long bytesToRead; private long bytesRead; - /** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link DefaultHttpDataSource.Factory} instead. + */ @SuppressWarnings("deprecation") @Deprecated public DefaultHttpDataSource() { this(/* userAgent= */ null, DEFAULT_CONNECT_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS); } - /** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link DefaultHttpDataSource.Factory} instead. + */ @SuppressWarnings("deprecation") @Deprecated public DefaultHttpDataSource(@Nullable String userAgent) { this(userAgent, DEFAULT_CONNECT_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS); } - /** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link DefaultHttpDataSource.Factory} instead. + */ @SuppressWarnings("deprecation") @Deprecated public DefaultHttpDataSource( @@ -256,7 +262,9 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou /* defaultRequestProperties= */ null); } - /** @deprecated Use {@link DefaultHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link DefaultHttpDataSource.Factory} instead. + */ @Deprecated public DefaultHttpDataSource( @Nullable String userAgent, diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java index df643fc6f3..555651f86e 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/FileDataSource.java @@ -41,13 +41,17 @@ public final class FileDataSource extends BaseDataSource { /** Thrown when a {@link FileDataSource} encounters an error reading a file. */ public static class FileDataSourceException extends DataSourceException { - /** @deprecated Use {@link #FileDataSourceException(Throwable, int)} */ + /** + * @deprecated Use {@link #FileDataSourceException(Throwable, int)} + */ @Deprecated public FileDataSourceException(Exception cause) { super(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); } - /** @deprecated Use {@link #FileDataSourceException(String, Throwable, int)} */ + /** + * @deprecated Use {@link #FileDataSourceException(String, Throwable, int)} + */ @Deprecated public FileDataSourceException(String message, IOException cause) { super(message, cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/PriorityDataSourceFactory.java b/libraries/datasource/src/main/java/androidx/media3/datasource/PriorityDataSourceFactory.java index f8d6dcc868..cf86a22164 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/PriorityDataSourceFactory.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/PriorityDataSourceFactory.java @@ -19,7 +19,9 @@ import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource.Factory; -/** @deprecated Use {@link PriorityDataSource.Factory}. */ +/** + * @deprecated Use {@link PriorityDataSource.Factory}. + */ @Deprecated @UnstableApi public final class PriorityDataSourceFactory implements Factory { diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java index bc31706db1..b406cd8303 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java @@ -58,13 +58,17 @@ public final class RawResourceDataSource extends BaseDataSource { /** Thrown when an {@link IOException} is encountered reading from a raw resource. */ public static class RawResourceDataSourceException extends DataSourceException { - /** @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. */ + /** + * @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. + */ @Deprecated public RawResourceDataSourceException(String message) { super(message, /* cause= */ null, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); } - /** @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. */ + /** + * @deprecated Use {@link #RawResourceDataSourceException(String, Throwable, int)}. + */ @Deprecated public RawResourceDataSourceException(Throwable cause) { super(cause, PlaybackException.ERROR_CODE_IO_UNSPECIFIED); @@ -101,7 +105,9 @@ public final class RawResourceDataSource extends BaseDataSource { private long bytesRemaining; private boolean opened; - /** @param context A context. */ + /** + * @param context A context. + */ public RawResourceDataSource(Context context) { super(/* isNetwork= */ false); this.resources = context.getResources(); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheFileMetadataIndex.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheFileMetadataIndex.java index 1938df7d7b..c98353e58f 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheFileMetadataIndex.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CacheFileMetadataIndex.java @@ -94,7 +94,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } } - /** @param databaseProvider Provides the database in which the index is stored. */ + /** + * @param databaseProvider Provides the database in which the index is stored. + */ public CacheFileMetadataIndex(DatabaseProvider databaseProvider) { this.databaseProvider = databaseProvider; } diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/DefaultContentMetadata.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/DefaultContentMetadata.java index 745ccca54f..43f81a19e0 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/DefaultContentMetadata.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/DefaultContentMetadata.java @@ -43,7 +43,9 @@ public final class DefaultContentMetadata implements ContentMetadata { this(Collections.emptyMap()); } - /** @param metadata The metadata entries in their raw byte array form. */ + /** + * @param metadata The metadata entries in their raw byte array form. + */ public DefaultContentMetadata(Map metadata) { this.metadata = Collections.unmodifiableMap(metadata); } diff --git a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java index 74d863301f..4d5988398e 100644 --- a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java +++ b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSource.java @@ -345,7 +345,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { */ public final int cronetConnectionStatus; - /** @deprecated Use {@link #OpenException(IOException, DataSpec, int, int)}. */ + /** + * @deprecated Use {@link #OpenException(IOException, DataSpec, int, int)}. + */ @Deprecated public OpenException(IOException cause, DataSpec dataSpec, int cronetConnectionStatus) { super(cause, dataSpec, PlaybackException.ERROR_CODE_IO_UNSPECIFIED, TYPE_OPEN); @@ -361,7 +363,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { this.cronetConnectionStatus = cronetConnectionStatus; } - /** @deprecated Use {@link #OpenException(String, DataSpec, int, int)}. */ + /** + * @deprecated Use {@link #OpenException(String, DataSpec, int, int)}. + */ @Deprecated public OpenException(String errorMessage, DataSpec dataSpec, int cronetConnectionStatus) { super(errorMessage, dataSpec, PlaybackException.ERROR_CODE_IO_UNSPECIFIED, TYPE_OPEN); diff --git a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java index b1a22e912e..bf1ad26cb5 100644 --- a/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java +++ b/libraries/datasource_cronet/src/main/java/androidx/media3/datasource/cronet/CronetDataSourceFactory.java @@ -26,7 +26,9 @@ import androidx.media3.datasource.TransferListener; import java.util.concurrent.Executor; import org.chromium.net.CronetEngine; -/** @deprecated Use {@link CronetDataSource.Factory} instead. */ +/** + * @deprecated Use {@link CronetDataSource.Factory} instead. + */ @Deprecated @UnstableApi public final class CronetDataSourceFactory extends BaseFactory { diff --git a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java index 6980c6bea3..889c5f6d6a 100644 --- a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java +++ b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSource.java @@ -181,21 +181,27 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource { private long bytesToRead; private long bytesRead; - /** @deprecated Use {@link OkHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link OkHttpDataSource.Factory} instead. + */ @SuppressWarnings("deprecation") @Deprecated public OkHttpDataSource(Call.Factory callFactory) { this(callFactory, /* userAgent= */ null); } - /** @deprecated Use {@link OkHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link OkHttpDataSource.Factory} instead. + */ @SuppressWarnings("deprecation") @Deprecated public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent) { this(callFactory, userAgent, /* cacheControl= */ null, /* defaultRequestProperties= */ null); } - /** @deprecated Use {@link OkHttpDataSource.Factory} instead. */ + /** + * @deprecated Use {@link OkHttpDataSource.Factory} instead. + */ @Deprecated public OkHttpDataSource( Call.Factory callFactory, diff --git a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSourceFactory.java b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSourceFactory.java index 3deff7b4a8..1a476fd5be 100644 --- a/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSourceFactory.java +++ b/libraries/datasource_okhttp/src/main/java/androidx/media3/datasource/okhttp/OkHttpDataSourceFactory.java @@ -23,7 +23,9 @@ import androidx.media3.datasource.TransferListener; import okhttp3.CacheControl; import okhttp3.Call; -/** @deprecated Use {@link OkHttpDataSource.Factory} instead. */ +/** + * @deprecated Use {@link OkHttpDataSource.Factory} instead. + */ @Deprecated @UnstableApi public final class OkHttpDataSourceFactory extends BaseFactory { diff --git a/libraries/datasource_rtmp/src/main/java/androidx/media3/datasource/rtmp/RtmpDataSourceFactory.java b/libraries/datasource_rtmp/src/main/java/androidx/media3/datasource/rtmp/RtmpDataSourceFactory.java index 1042f35135..9852807078 100644 --- a/libraries/datasource_rtmp/src/main/java/androidx/media3/datasource/rtmp/RtmpDataSourceFactory.java +++ b/libraries/datasource_rtmp/src/main/java/androidx/media3/datasource/rtmp/RtmpDataSourceFactory.java @@ -20,7 +20,9 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.TransferListener; -/** @deprecated Use {@link RtmpDataSource.Factory} instead. */ +/** + * @deprecated Use {@link RtmpDataSource.Factory} instead. + */ @Deprecated @UnstableApi public final class RtmpDataSourceFactory implements DataSource.Factory { @@ -31,7 +33,9 @@ public final class RtmpDataSourceFactory implements DataSource.Factory { this(null); } - /** @param listener An optional listener. */ + /** + * @param listener An optional listener. + */ public RtmpDataSourceFactory(@Nullable TransferListener listener) { this.listener = listener; } diff --git a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java index 4663b38788..8ddaef5d9b 100644 --- a/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java +++ b/libraries/decoder/src/main/java/androidx/media3/decoder/CryptoInfo.java @@ -69,9 +69,13 @@ public final class CryptoInfo { * @see android.media.MediaCodec.CryptoInfo#numSubSamples */ public int numSubSamples; - /** @see android.media.MediaCodec.CryptoInfo.Pattern */ + /** + * @see android.media.MediaCodec.CryptoInfo.Pattern + */ public int encryptedBlocks; - /** @see android.media.MediaCodec.CryptoInfo.Pattern */ + /** + * @see android.media.MediaCodec.CryptoInfo.Pattern + */ public int clearBlocks; private final android.media.MediaCodec.CryptoInfo frameworkCryptoInfo; @@ -82,7 +86,9 @@ public final class CryptoInfo { patternHolder = Util.SDK_INT >= 24 ? new PatternHolderV24(frameworkCryptoInfo) : null; } - /** @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int) */ + /** + * @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int) + */ public void set( int numSubSamples, int[] numBytesOfClearData, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java index e07c434534..0fcd77ca57 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioFocusManager.java @@ -114,17 +114,27 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE }) private @interface AudioFocusGain {} - /** @see AudioManager#AUDIOFOCUS_NONE */ + /** + * @see AudioManager#AUDIOFOCUS_NONE + */ @SuppressWarnings("InlinedApi") private static final int AUDIOFOCUS_NONE = AudioManager.AUDIOFOCUS_NONE; - /** @see AudioManager#AUDIOFOCUS_GAIN */ + /** + * @see AudioManager#AUDIOFOCUS_GAIN + */ private static final int AUDIOFOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT */ + /** + * @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT + */ private static final int AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK */ + /** + * @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + */ private static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; - /** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE */ + /** + * @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE + */ @SuppressWarnings("InlinedApi") private static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLoadControl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLoadControl.java index 468532b0c0..978a9bc829 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLoadControl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultLoadControl.java @@ -224,7 +224,9 @@ public class DefaultLoadControl implements LoadControl { return this; } - /** @deprecated use {@link #build} instead. */ + /** + * @deprecated use {@link #build} instead. + */ @Deprecated public DefaultLoadControl createDefaultLoadControl() { return build(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 451c0ee3a1..230fc2e03b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -103,7 +103,9 @@ public class DefaultRenderersFactory implements RenderersFactory { private boolean enableAudioTrackPlaybackParams; private boolean enableOffload; - /** @param context A {@link Context}. */ + /** + * @param context A {@link Context}. + */ public DefaultRenderersFactory(Context context) { this.context = context; codecAdapterFactory = new DefaultMediaCodecAdapterFactory(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 23b88faf3d..d367de7648 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -160,43 +160,63 @@ public interface ExoPlayer extends Player { @Deprecated interface AudioComponent { - /** @deprecated Use {@link ExoPlayer#setAudioAttributes(AudioAttributes, boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setAudioAttributes(AudioAttributes, boolean)} instead. + */ @Deprecated void setAudioAttributes(AudioAttributes audioAttributes, boolean handleAudioFocus); - /** @deprecated Use {@link Player#getAudioAttributes()} instead. */ + /** + * @deprecated Use {@link Player#getAudioAttributes()} instead. + */ @Deprecated AudioAttributes getAudioAttributes(); - /** @deprecated Use {@link ExoPlayer#setAudioSessionId(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setAudioSessionId(int)} instead. + */ @Deprecated void setAudioSessionId(int audioSessionId); - /** @deprecated Use {@link ExoPlayer#getAudioSessionId()} instead. */ + /** + * @deprecated Use {@link ExoPlayer#getAudioSessionId()} instead. + */ @Deprecated int getAudioSessionId(); - /** @deprecated Use {@link ExoPlayer#setAuxEffectInfo(AuxEffectInfo)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setAuxEffectInfo(AuxEffectInfo)} instead. + */ @Deprecated void setAuxEffectInfo(AuxEffectInfo auxEffectInfo); - /** @deprecated Use {@link ExoPlayer#clearAuxEffectInfo()} instead. */ + /** + * @deprecated Use {@link ExoPlayer#clearAuxEffectInfo()} instead. + */ @Deprecated void clearAuxEffectInfo(); - /** @deprecated Use {@link Player#setVolume(float)} instead. */ + /** + * @deprecated Use {@link Player#setVolume(float)} instead. + */ @Deprecated void setVolume(float audioVolume); - /** @deprecated Use {@link Player#getVolume()} instead. */ + /** + * @deprecated Use {@link Player#getVolume()} instead. + */ @Deprecated float getVolume(); - /** @deprecated Use {@link ExoPlayer#setSkipSilenceEnabled(boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setSkipSilenceEnabled(boolean)} instead. + */ @Deprecated void setSkipSilenceEnabled(boolean skipSilenceEnabled); - /** @deprecated Use {@link ExoPlayer#getSkipSilenceEnabled()} instead. */ + /** + * @deprecated Use {@link ExoPlayer#getSkipSilenceEnabled()} instead. + */ @Deprecated boolean getSkipSilenceEnabled(); } @@ -209,21 +229,29 @@ public interface ExoPlayer extends Player { @Deprecated interface VideoComponent { - /** @deprecated Use {@link ExoPlayer#setVideoScalingMode(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setVideoScalingMode(int)} instead. + */ @Deprecated void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode); - /** @deprecated Use {@link ExoPlayer#getVideoScalingMode()} instead. */ + /** + * @deprecated Use {@link ExoPlayer#getVideoScalingMode()} instead. + */ @Deprecated @C.VideoScalingMode int getVideoScalingMode(); - /** @deprecated Use {@link ExoPlayer#setVideoChangeFrameRateStrategy(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setVideoChangeFrameRateStrategy(int)} instead. + */ @Deprecated void setVideoChangeFrameRateStrategy( @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy); - /** @deprecated Use {@link ExoPlayer#getVideoChangeFrameRateStrategy()} instead. */ + /** + * @deprecated Use {@link ExoPlayer#getVideoChangeFrameRateStrategy()} instead. + */ @Deprecated @C.VideoChangeFrameRateStrategy int getVideoChangeFrameRateStrategy(); @@ -242,7 +270,9 @@ public interface ExoPlayer extends Player { @Deprecated void clearVideoFrameMetadataListener(VideoFrameMetadataListener listener); - /** @deprecated Use {@link ExoPlayer#setCameraMotionListener(CameraMotionListener)} instead. */ + /** + * @deprecated Use {@link ExoPlayer#setCameraMotionListener(CameraMotionListener)} instead. + */ @Deprecated void setCameraMotionListener(CameraMotionListener listener); @@ -252,43 +282,63 @@ public interface ExoPlayer extends Player { @Deprecated void clearCameraMotionListener(CameraMotionListener listener); - /** @deprecated Use {@link Player#clearVideoSurface()} instead. */ + /** + * @deprecated Use {@link Player#clearVideoSurface()} instead. + */ @Deprecated void clearVideoSurface(); - /** @deprecated Use {@link Player#clearVideoSurface(Surface)} instead. */ + /** + * @deprecated Use {@link Player#clearVideoSurface(Surface)} instead. + */ @Deprecated void clearVideoSurface(@Nullable Surface surface); - /** @deprecated Use {@link Player#setVideoSurface(Surface)} instead. */ + /** + * @deprecated Use {@link Player#setVideoSurface(Surface)} instead. + */ @Deprecated void setVideoSurface(@Nullable Surface surface); - /** @deprecated Use {@link Player#setVideoSurfaceHolder(SurfaceHolder)} instead. */ + /** + * @deprecated Use {@link Player#setVideoSurfaceHolder(SurfaceHolder)} instead. + */ @Deprecated void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); - /** @deprecated Use {@link Player#clearVideoSurfaceHolder(SurfaceHolder)} instead. */ + /** + * @deprecated Use {@link Player#clearVideoSurfaceHolder(SurfaceHolder)} instead. + */ @Deprecated void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder); - /** @deprecated Use {@link Player#setVideoSurfaceView(SurfaceView)} instead. */ + /** + * @deprecated Use {@link Player#setVideoSurfaceView(SurfaceView)} instead. + */ @Deprecated void setVideoSurfaceView(@Nullable SurfaceView surfaceView); - /** @deprecated Use {@link Player#clearVideoSurfaceView(SurfaceView)} instead. */ + /** + * @deprecated Use {@link Player#clearVideoSurfaceView(SurfaceView)} instead. + */ @Deprecated void clearVideoSurfaceView(@Nullable SurfaceView surfaceView); - /** @deprecated Use {@link Player#setVideoTextureView(TextureView)} instead. */ + /** + * @deprecated Use {@link Player#setVideoTextureView(TextureView)} instead. + */ @Deprecated void setVideoTextureView(@Nullable TextureView textureView); - /** @deprecated Use {@link Player#clearVideoTextureView(TextureView)} instead. */ + /** + * @deprecated Use {@link Player#clearVideoTextureView(TextureView)} instead. + */ @Deprecated void clearVideoTextureView(@Nullable TextureView textureView); - /** @deprecated Use {@link Player#getVideoSize()} instead. */ + /** + * @deprecated Use {@link Player#getVideoSize()} instead. + */ @Deprecated VideoSize getVideoSize(); } @@ -301,7 +351,9 @@ public interface ExoPlayer extends Player { @Deprecated interface TextComponent { - /** @deprecated Use {@link Player#getCurrentCues()} instead. */ + /** + * @deprecated Use {@link Player#getCurrentCues()} instead. + */ @Deprecated List getCurrentCues(); } @@ -314,31 +366,45 @@ public interface ExoPlayer extends Player { @Deprecated interface DeviceComponent { - /** @deprecated Use {@link Player#getDeviceInfo()} instead. */ + /** + * @deprecated Use {@link Player#getDeviceInfo()} instead. + */ @Deprecated DeviceInfo getDeviceInfo(); - /** @deprecated Use {@link Player#getDeviceVolume()} instead. */ + /** + * @deprecated Use {@link Player#getDeviceVolume()} instead. + */ @Deprecated int getDeviceVolume(); - /** @deprecated Use {@link Player#isDeviceMuted()} instead. */ + /** + * @deprecated Use {@link Player#isDeviceMuted()} instead. + */ @Deprecated boolean isDeviceMuted(); - /** @deprecated Use {@link Player#setDeviceVolume(int)} instead. */ + /** + * @deprecated Use {@link Player#setDeviceVolume(int)} instead. + */ @Deprecated void setDeviceVolume(int volume); - /** @deprecated Use {@link Player#increaseDeviceVolume()} instead. */ + /** + * @deprecated Use {@link Player#increaseDeviceVolume()} instead. + */ @Deprecated void increaseDeviceVolume(); - /** @deprecated Use {@link Player#decreaseDeviceVolume()} instead. */ + /** + * @deprecated Use {@link Player#decreaseDeviceVolume()} instead. + */ @Deprecated void decreaseDeviceVolume(); - /** @deprecated Use {@link Player#setDeviceMuted(boolean)} instead. */ + /** + * @deprecated Use {@link Player#setDeviceMuted(boolean)} instead. + */ @Deprecated void setDeviceMuted(boolean muted); } @@ -1158,12 +1224,16 @@ public interface ExoPlayer extends Player { @UnstableApi Clock getClock(); - /** @deprecated Use {@link #prepare()} instead. */ + /** + * @deprecated Use {@link #prepare()} instead. + */ @UnstableApi @Deprecated void retry(); - /** @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead. */ + /** + * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead. + */ @UnstableApi @Deprecated void prepare(MediaSource mediaSource); @@ -1522,7 +1592,9 @@ public interface ExoPlayer extends Player { */ void setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy); - /** @deprecated Use {@link #setWakeMode(int)} instead. */ + /** + * @deprecated Use {@link #setWakeMode(int)} instead. + */ @UnstableApi @Deprecated void setHandleWakeLock(boolean handleWakeLock); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlayerMessage.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlayerMessage.java index 5b4acaf980..328416025a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlayerMessage.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/PlayerMessage.java @@ -155,7 +155,9 @@ public final class PlayerMessage { return payload; } - /** @deprecated Use {@link #setLooper(Looper)} instead. */ + /** + * @deprecated Use {@link #setLooper(Looper)} instead. + */ @Deprecated public PlayerMessage setHandler(Handler handler) { return setLooper(handler.getLooper()); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java index a51f245cc3..78dcf082f7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererCapabilities.java @@ -31,7 +31,9 @@ import java.lang.annotation.Target; @UnstableApi public interface RendererCapabilities { - /** @deprecated Use {@link C.FormatSupport} instead. */ + /** + * @deprecated Use {@link C.FormatSupport} instead. + */ @SuppressWarnings("deprecation") @Documented @Retention(RetentionPolicy.SOURCE) @@ -47,15 +49,25 @@ public interface RendererCapabilities { @interface FormatSupport {} /** A mask to apply to {@link Capabilities} to obtain the {@link C.FormatSupport} only. */ int FORMAT_SUPPORT_MASK = 0b111; - /** @deprecated Use {@link C#FORMAT_HANDLED} instead. */ + /** + * @deprecated Use {@link C#FORMAT_HANDLED} instead. + */ @Deprecated int FORMAT_HANDLED = C.FORMAT_HANDLED; - /** @deprecated Use {@link C#FORMAT_EXCEEDS_CAPABILITIES} instead. */ + /** + * @deprecated Use {@link C#FORMAT_EXCEEDS_CAPABILITIES} instead. + */ @Deprecated int FORMAT_EXCEEDS_CAPABILITIES = C.FORMAT_EXCEEDS_CAPABILITIES; - /** @deprecated Use {@link C#FORMAT_UNSUPPORTED_DRM} instead. */ + /** + * @deprecated Use {@link C#FORMAT_UNSUPPORTED_DRM} instead. + */ @Deprecated int FORMAT_UNSUPPORTED_DRM = C.FORMAT_UNSUPPORTED_DRM; - /** @deprecated Use {@link C#FORMAT_UNSUPPORTED_SUBTYPE} instead. */ + /** + * @deprecated Use {@link C#FORMAT_UNSUPPORTED_SUBTYPE} instead. + */ @Deprecated int FORMAT_UNSUPPORTED_SUBTYPE = C.FORMAT_UNSUPPORTED_SUBTYPE; - /** @deprecated Use {@link C#FORMAT_UNSUPPORTED_TYPE} instead. */ + /** + * @deprecated Use {@link C#FORMAT_UNSUPPORTED_TYPE} instead. + */ @Deprecated int FORMAT_UNSUPPORTED_TYPE = C.FORMAT_UNSUPPORTED_TYPE; /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererConfiguration.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererConfiguration.java index 2677d8fc4f..452ec4fcaf 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererConfiguration.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/RendererConfiguration.java @@ -29,7 +29,9 @@ public final class RendererConfiguration { /** Whether to enable tunneling. */ public final boolean tunneling; - /** @param tunneling Whether to enable tunneling. */ + /** + * @param tunneling Whether to enable tunneling. + */ public RendererConfiguration(boolean tunneling) { this.tunneling = tunneling; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 3bd76fc6e6..d791222b9d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -56,7 +56,9 @@ import androidx.media3.exoplayer.video.spherical.CameraMotionListener; import androidx.media3.extractor.ExtractorsFactory; import java.util.List; -/** @deprecated Use {@link ExoPlayer} instead. */ +/** + * @deprecated Use {@link ExoPlayer} instead. + */ @UnstableApi @Deprecated public class SimpleExoPlayer extends BasePlayer @@ -66,20 +68,26 @@ public class SimpleExoPlayer extends BasePlayer ExoPlayer.TextComponent, ExoPlayer.DeviceComponent { - /** @deprecated Use {@link ExoPlayer.Builder} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder} instead. + */ @Deprecated @SuppressWarnings("deprecation") public static final class Builder { private final ExoPlayer.Builder wrappedBuilder; - /** @deprecated Use {@link ExoPlayer.Builder#Builder(Context)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#Builder(Context)} instead. + */ @Deprecated public Builder(Context context) { wrappedBuilder = new ExoPlayer.Builder(context); } - /** @deprecated Use {@link ExoPlayer.Builder#Builder(Context, RenderersFactory)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#Builder(Context, RenderersFactory)} instead. + */ @Deprecated public Builder(Context context, RenderersFactory renderersFactory) { wrappedBuilder = new ExoPlayer.Builder(context, renderersFactory); @@ -143,7 +151,9 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setTrackSelector(TrackSelector)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setTrackSelector(TrackSelector)} instead. + */ @Deprecated public Builder setTrackSelector(TrackSelector trackSelector) { wrappedBuilder.setTrackSelector(trackSelector); @@ -159,21 +169,27 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setLoadControl(LoadControl)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setLoadControl(LoadControl)} instead. + */ @Deprecated public Builder setLoadControl(LoadControl loadControl) { wrappedBuilder.setLoadControl(loadControl); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setBandwidthMeter(BandwidthMeter)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setBandwidthMeter(BandwidthMeter)} instead. + */ @Deprecated public Builder setBandwidthMeter(BandwidthMeter bandwidthMeter) { wrappedBuilder.setBandwidthMeter(bandwidthMeter); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setLooper(Looper)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setLooper(Looper)} instead. + */ @Deprecated public Builder setLooper(Looper looper) { wrappedBuilder.setLooper(looper); @@ -209,35 +225,45 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setWakeMode(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setWakeMode(int)} instead. + */ @Deprecated public Builder setWakeMode(@C.WakeMode int wakeMode) { wrappedBuilder.setWakeMode(wakeMode); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setHandleAudioBecomingNoisy(boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setHandleAudioBecomingNoisy(boolean)} instead. + */ @Deprecated public Builder setHandleAudioBecomingNoisy(boolean handleAudioBecomingNoisy) { wrappedBuilder.setHandleAudioBecomingNoisy(handleAudioBecomingNoisy); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setSkipSilenceEnabled(boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setSkipSilenceEnabled(boolean)} instead. + */ @Deprecated public Builder setSkipSilenceEnabled(boolean skipSilenceEnabled) { wrappedBuilder.setSkipSilenceEnabled(skipSilenceEnabled); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setVideoScalingMode(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setVideoScalingMode(int)} instead. + */ @Deprecated public Builder setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { wrappedBuilder.setVideoScalingMode(videoScalingMode); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setVideoChangeFrameRateStrategy(int)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setVideoChangeFrameRateStrategy(int)} instead. + */ @Deprecated public Builder setVideoChangeFrameRateStrategy( @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy) { @@ -245,49 +271,63 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setUseLazyPreparation(boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setUseLazyPreparation(boolean)} instead. + */ @Deprecated public Builder setUseLazyPreparation(boolean useLazyPreparation) { wrappedBuilder.setUseLazyPreparation(useLazyPreparation); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setSeekParameters(SeekParameters)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setSeekParameters(SeekParameters)} instead. + */ @Deprecated public Builder setSeekParameters(SeekParameters seekParameters) { wrappedBuilder.setSeekParameters(seekParameters); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setSeekBackIncrementMs(long)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setSeekBackIncrementMs(long)} instead. + */ @Deprecated public Builder setSeekBackIncrementMs(@IntRange(from = 1) long seekBackIncrementMs) { wrappedBuilder.setSeekBackIncrementMs(seekBackIncrementMs); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setSeekForwardIncrementMs(long)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setSeekForwardIncrementMs(long)} instead. + */ @Deprecated public Builder setSeekForwardIncrementMs(@IntRange(from = 1) long seekForwardIncrementMs) { wrappedBuilder.setSeekForwardIncrementMs(seekForwardIncrementMs); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setReleaseTimeoutMs(long)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setReleaseTimeoutMs(long)} instead. + */ @Deprecated public Builder setReleaseTimeoutMs(long releaseTimeoutMs) { wrappedBuilder.setReleaseTimeoutMs(releaseTimeoutMs); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setDetachSurfaceTimeoutMs(long)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setDetachSurfaceTimeoutMs(long)} instead. + */ @Deprecated public Builder setDetachSurfaceTimeoutMs(long detachSurfaceTimeoutMs) { wrappedBuilder.setDetachSurfaceTimeoutMs(detachSurfaceTimeoutMs); return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setPauseAtEndOfMediaItems(boolean)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setPauseAtEndOfMediaItems(boolean)} instead. + */ @Deprecated public Builder setPauseAtEndOfMediaItems(boolean pauseAtEndOfMediaItems) { wrappedBuilder.setPauseAtEndOfMediaItems(pauseAtEndOfMediaItems); @@ -304,7 +344,9 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#setClock(Clock)} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#setClock(Clock)} instead. + */ @Deprecated @VisibleForTesting public Builder setClock(Clock clock) { @@ -312,7 +354,9 @@ public class SimpleExoPlayer extends BasePlayer return this; } - /** @deprecated Use {@link ExoPlayer.Builder#build()} instead. */ + /** + * @deprecated Use {@link ExoPlayer.Builder#build()} instead. + */ @Deprecated public SimpleExoPlayer build() { return wrappedBuilder.buildSimpleExoPlayer(); @@ -322,7 +366,9 @@ public class SimpleExoPlayer extends BasePlayer private final ExoPlayerImpl player; private final ConditionVariable constructorFinished; - /** @deprecated Use the {@link ExoPlayer.Builder}. */ + /** + * @deprecated Use the {@link ExoPlayer.Builder}. + */ @Deprecated protected SimpleExoPlayer( Context context, @@ -349,12 +395,16 @@ public class SimpleExoPlayer extends BasePlayer .setLooper(applicationLooper)); } - /** @param builder The {@link Builder} to obtain all construction parameters. */ + /** + * @param builder The {@link Builder} to obtain all construction parameters. + */ protected SimpleExoPlayer(Builder builder) { this(builder.wrappedBuilder); } - /** @param builder The {@link ExoPlayer.Builder} to obtain all construction parameters. */ + /** + * @param builder The {@link ExoPlayer.Builder} to obtain all construction parameters. + */ /* package */ SimpleExoPlayer(ExoPlayer.Builder builder) { constructorFinished = new ConditionVariable(); try { @@ -696,7 +746,9 @@ public class SimpleExoPlayer extends BasePlayer return player.getPlayerError(); } - /** @deprecated Use {@link #prepare()} instead. */ + /** + * @deprecated Use {@link #prepare()} instead. + */ @Deprecated @Override @SuppressWarnings("deprecation") // Calling deprecated method. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java index 7195661a2a..b4900c904a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/analytics/AnalyticsListener.java @@ -669,7 +669,9 @@ public interface AnalyticsListener { */ default void onIsLoadingChanged(EventTime eventTime, boolean isLoading) {} - /** @deprecated Use {@link #onIsLoadingChanged(EventTime, boolean)} instead. */ + /** + * @deprecated Use {@link #onIsLoadingChanged(EventTime, boolean)} instead. + */ @Deprecated default void onLoadingChanged(EventTime eventTime, boolean isLoading) {} @@ -840,7 +842,9 @@ public interface AnalyticsListener { */ default void onCues(EventTime eventTime, List cues) {} - /** @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. */ + /** + * @deprecated Use {@link #onAudioEnabled} and {@link #onVideoEnabled} instead. + */ @Deprecated default void onDecoderEnabled( EventTime eventTime, int trackType, DecoderCounters decoderCounters) {} @@ -860,7 +864,9 @@ public interface AnalyticsListener { @Deprecated default void onDecoderInputFormatChanged(EventTime eventTime, int trackType, Format format) {} - /** @deprecated Use {@link #onAudioDisabled} and {@link #onVideoDisabled} instead. */ + /** + * @deprecated Use {@link #onAudioDisabled} and {@link #onVideoDisabled} instead. + */ @Deprecated default void onDecoderDisabled( EventTime eventTime, int trackType, DecoderCounters decoderCounters) {} @@ -889,7 +895,9 @@ public interface AnalyticsListener { long initializedTimestampMs, long initializationDurationMs) {} - /** @deprecated Use {@link #onAudioDecoderInitialized(EventTime, String, long, long)}. */ + /** + * @deprecated Use {@link #onAudioDecoderInitialized(EventTime, String, long, long)}. + */ @Deprecated default void onAudioDecoderInitialized( EventTime eventTime, String decoderName, long initializationDurationMs) {} @@ -1058,7 +1066,9 @@ public interface AnalyticsListener { long initializedTimestampMs, long initializationDurationMs) {} - /** @deprecated Use {@link #onVideoDecoderInitialized(EventTime, String, long, long)}. */ + /** + * @deprecated Use {@link #onVideoDecoderInitialized(EventTime, String, long, long)}. + */ @Deprecated default void onVideoDecoderInitialized( EventTime eventTime, String decoderName, long initializationDurationMs) {} @@ -1164,7 +1174,9 @@ public interface AnalyticsListener { */ default void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {} - /** @deprecated Implement {@link #onVideoSizeChanged(EventTime eventTime, VideoSize)} instead. */ + /** + * @deprecated Implement {@link #onVideoSizeChanged(EventTime eventTime, VideoSize)} instead. + */ @Deprecated default void onVideoSizeChanged( EventTime eventTime, @@ -1184,7 +1196,9 @@ public interface AnalyticsListener { */ default void onSurfaceSizeChanged(EventTime eventTime, int width, int height) {} - /** @deprecated Implement {@link #onDrmSessionAcquired(EventTime, int)} instead. */ + /** + * @deprecated Implement {@link #onDrmSessionAcquired(EventTime, int)} instead. + */ @Deprecated default void onDrmSessionAcquired(EventTime eventTime) {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java index a01dcf614b..2ffc957d43 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioRendererEventListener.java @@ -59,7 +59,9 @@ public interface AudioRendererEventListener { default void onAudioDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs) {} - /** @deprecated Use {@link #onAudioInputFormatChanged(Format, DecoderReuseEvaluation)}. */ + /** + * @deprecated Use {@link #onAudioInputFormatChanged(Format, DecoderReuseEvaluation)}. + */ @Deprecated default void onAudioInputFormatChanged(Format format) {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java index 1101ac09bf..2844cace37 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java @@ -116,11 +116,17 @@ import java.lang.reflect.Method; @Target(TYPE_USE) @IntDef({PLAYSTATE_STOPPED, PLAYSTATE_PAUSED, PLAYSTATE_PLAYING}) private @interface PlayState {} - /** @see AudioTrack#PLAYSTATE_STOPPED */ + /** + * @see AudioTrack#PLAYSTATE_STOPPED + */ private static final int PLAYSTATE_STOPPED = AudioTrack.PLAYSTATE_STOPPED; - /** @see AudioTrack#PLAYSTATE_PAUSED */ + /** + * @see AudioTrack#PLAYSTATE_PAUSED + */ private static final int PLAYSTATE_PAUSED = AudioTrack.PLAYSTATE_PAUSED; - /** @see AudioTrack#PLAYSTATE_PLAYING */ + /** + * @see AudioTrack#PLAYSTATE_PLAYING + */ private static final int PLAYSTATE_PLAYING = AudioTrack.PLAYSTATE_PLAYING; /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index 7186ca79a5..620278baad 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -533,7 +533,9 @@ public final class DefaultAudioSink implements AudioSink { private boolean offloadDisabledUntilNextConfiguration; private boolean isWaitingForOffloadEndOfStreamHandled; - /** @deprecated Use {@link Builder}. */ + /** + * @deprecated Use {@link Builder}. + */ @Deprecated @InlineMeValidationDisabled("Migrate constructor to Builder") @InlineMe( @@ -551,7 +553,9 @@ public final class DefaultAudioSink implements AudioSink { .setAudioProcessors(audioProcessors)); } - /** @deprecated Use {@link Builder}. */ + /** + * @deprecated Use {@link Builder}. + */ @Deprecated @InlineMeValidationDisabled("Migrate constructor to Builder") @InlineMe( @@ -573,7 +577,9 @@ public final class DefaultAudioSink implements AudioSink { .setEnableFloatOutput(enableFloatOutput)); } - /** @deprecated Use {@link Builder}. */ + /** + * @deprecated Use {@link Builder}. + */ @Deprecated @InlineMeValidationDisabled("Migrate constructor to Builder") @InlineMe( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionEventListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionEventListener.java index 811b7e6b93..414c4ae58d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionEventListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DrmSessionEventListener.java @@ -30,7 +30,9 @@ import java.util.concurrent.CopyOnWriteArrayList; @UnstableApi public interface DrmSessionEventListener { - /** @deprecated Implement {@link #onDrmSessionAcquired(int, MediaPeriodId, int)} instead. */ + /** + * @deprecated Implement {@link #onDrmSessionAcquired(int, MediaPeriodId, int)} instead. + */ @Deprecated default void onDrmSessionAcquired(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/LocalMediaDrmCallback.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/LocalMediaDrmCallback.java index 10caf21ea0..ca6ce6adab 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/LocalMediaDrmCallback.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/LocalMediaDrmCallback.java @@ -32,7 +32,9 @@ public final class LocalMediaDrmCallback implements MediaDrmCallback { private final byte[] keyResponse; - /** @param keyResponse The fixed response for all key requests. */ + /** + * @param keyResponse The fixed response for all key requests. + */ public LocalMediaDrmCallback(byte[] keyResponse) { this.keyResponse = Assertions.checkNotNull(keyResponse); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java index 9b360d2776..4530eab122 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/UnsupportedDrmException.java @@ -51,7 +51,9 @@ public final class UnsupportedDrmException extends Exception { /** Either {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ public final @Reason int reason; - /** @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ + /** + * @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. + */ public UnsupportedDrmException(@Reason int reason) { this.reason = reason; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadException.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadException.java index eef2c58e25..ea377974c7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadException.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadException.java @@ -22,12 +22,16 @@ import java.io.IOException; @UnstableApi public final class DownloadException extends IOException { - /** @param message The message for the exception. */ + /** + * @param message The message for the exception. + */ public DownloadException(String message) { super(message); } - /** @param cause The cause for the exception. */ + /** + * @param cause The cause for the exception. + */ public DownloadException(Throwable cause) { super(cause); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java index 0d1ffa7937..bf67d93ff5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java @@ -173,13 +173,17 @@ public final class DownloadHelper { return capabilities; } - /** @deprecated Use {@link #forMediaItem(Context, MediaItem)} */ + /** + * @deprecated Use {@link #forMediaItem(Context, MediaItem)} + */ @Deprecated public static DownloadHelper forProgressive(Context context, Uri uri) { return forMediaItem(context, new MediaItem.Builder().setUri(uri).build()); } - /** @deprecated Use {@link #forMediaItem(Context, MediaItem)} */ + /** + * @deprecated Use {@link #forMediaItem(Context, MediaItem)} + */ @Deprecated public static DownloadHelper forProgressive(Context context, Uri uri, @Nullable String cacheKey) { return forMediaItem( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java index a4a65730f6..e17fa4a6fc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java @@ -225,7 +225,9 @@ public abstract class DownloadService extends Service { /* channelDescriptionResourceId= */ 0); } - /** @deprecated Use {@link #DownloadService(int, long, String, int, int)}. */ + /** + * @deprecated Use {@link #DownloadService(int, long, String, int, int)}. + */ @Deprecated protected DownloadService( int foregroundNotificationId, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index 4bdd389fbc..53ad113710 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -76,7 +76,9 @@ public final class Requirements implements Parcelable { private final @RequirementFlags int requirements; - /** @param requirements A combination of requirement flags. */ + /** + * @param requirements A combination of requirement flags. + */ public Requirements(@RequirementFlags int requirements) { if ((requirements & NETWORK_UNMETERED) != 0) { // Make sure network requirement flags are consistent. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java index 6a734a752b..fc63321d45 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ClippingMediaSource.java @@ -65,7 +65,9 @@ public final class ClippingMediaSource extends CompositeMediaSource { /** The reason clipping failed. */ public final @Reason int reason; - /** @param reason The reason clipping failed. */ + /** + * @param reason The reason clipping failed. + */ public IllegalClippingException(@Reason int reason) { super("Illegal clipping: " + getReasonDescription(reason)); this.reason = reason; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java index 5cbc5628d4..c64201c8b8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/DefaultMediaSourceFactory.java @@ -97,7 +97,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @SuppressWarnings("deprecation") // Implement deprecated type for backwards compatibility. public final class DefaultMediaSourceFactory implements MediaSourceFactory { - /** @deprecated Use {@link AdsLoader.Provider} instead. */ + /** + * @deprecated Use {@link AdsLoader.Provider} instead. + */ @UnstableApi @Deprecated public interface AdsLoaderProvider extends AdsLoader.Provider {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java index af47342210..5a46691c99 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MediaSourceFactory.java @@ -22,7 +22,9 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.drm.DrmSessionManagerProvider; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; -/** @deprecated Use {@link MediaSource.Factory}. */ +/** + * @deprecated Use {@link MediaSource.Factory}. + */ @UnstableApi @Deprecated public interface MediaSourceFactory extends MediaSource.Factory { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java index 8822b24268..ad33c458af 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaSource.java @@ -63,7 +63,9 @@ public final class MergingMediaSource extends CompositeMediaSource { /** The reason the merge failed. */ public final @Reason int reason; - /** @param reason The reason the merge failed. */ + /** + * @param reason The reason the merge failed. + */ public IllegalMergeException(@Reason int reason) { this.reason = reason; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index d59db6a91e..6361c53e45 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -1484,19 +1484,25 @@ public class DefaultTrackSelector extends MappingTrackSelector { private final ExoTrackSelection.Factory trackSelectionFactory; private final AtomicReference parametersReference; - /** @deprecated Use {@link #DefaultTrackSelector(Context)} instead. */ + /** + * @deprecated Use {@link #DefaultTrackSelector(Context)} instead. + */ @Deprecated public DefaultTrackSelector() { this(Parameters.DEFAULT_WITHOUT_CONTEXT, new AdaptiveTrackSelection.Factory()); } - /** @deprecated Use {@link #DefaultTrackSelector(Context, ExoTrackSelection.Factory)}. */ + /** + * @deprecated Use {@link #DefaultTrackSelector(Context, ExoTrackSelection.Factory)}. + */ @Deprecated public DefaultTrackSelector(ExoTrackSelection.Factory trackSelectionFactory) { this(Parameters.DEFAULT_WITHOUT_CONTEXT, trackSelectionFactory); } - /** @param context Any {@link Context}. */ + /** + * @param context Any {@link Context}. + */ public DefaultTrackSelector(Context context) { this(context, new AdaptiveTrackSelection.Factory()); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/RandomTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/RandomTrackSelection.java index bddba8f7af..0db95a86bc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/RandomTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/RandomTrackSelection.java @@ -42,7 +42,9 @@ public final class RandomTrackSelection extends BaseTrackSelection { random = new Random(); } - /** @param seed A seed for the {@link Random} instance used by the factory. */ + /** + * @param seed A seed for the {@link Random} instance used by the factory. + */ public Factory(int seed) { random = new Random(seed); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java index 654a34d975..5a7c7e268b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java @@ -290,7 +290,9 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList private boolean networkTypeOverrideSet; private @C.NetworkType int networkTypeOverride; - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @Deprecated public DefaultBandwidthMeter() { this( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultLoadErrorHandlingPolicy.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultLoadErrorHandlingPolicy.java index 6c6689fb7b..6c5d66857a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultLoadErrorHandlingPolicy.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultLoadErrorHandlingPolicy.java @@ -41,7 +41,9 @@ public class DefaultLoadErrorHandlingPolicy implements LoadErrorHandlingPolicy { public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT_PROGRESSIVE_LIVE = 6; /** The default duration for which a track is excluded in milliseconds. */ public static final long DEFAULT_TRACK_EXCLUSION_MS = 60_000; - /** @deprecated Use {@link #DEFAULT_TRACK_EXCLUSION_MS} instead. */ + /** + * @deprecated Use {@link #DEFAULT_TRACK_EXCLUSION_MS} instead. + */ @Deprecated public static final long DEFAULT_TRACK_BLACKLIST_MS = DEFAULT_TRACK_EXCLUSION_MS; /** The default duration for which a location is excluded in milliseconds. */ public static final long DEFAULT_LOCATION_EXCLUSION_MS = 5 * 60_000; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java index 0427ef831f..ca3d0fadbc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java @@ -57,7 +57,9 @@ public class SlidingPercentile { private int totalWeight; private int recycledSampleCount; - /** @param maxWeight The maximum weight. */ + /** + * @param maxWeight The maximum weight. + */ public SlidingPercentile(int maxWeight) { this.maxWeight = maxWeight; recycledSamples = new Sample[MAX_RECYCLED_SAMPLES]; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java index a24d17096b..cc3898032f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java @@ -50,7 +50,9 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView private final Renderer renderer; - /** @param context A {@link Context}. */ + /** + * @param context A {@link Context}. + */ public VideoDecoderGLSurfaceView(Context context) { this(context, /* attrs= */ null); } @@ -74,7 +76,9 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView renderer.setOutputBuffer(outputBuffer); } - /** @deprecated This class implements {@link VideoDecoderOutputBufferRenderer} directly. */ + /** + * @deprecated This class implements {@link VideoDecoderOutputBufferRenderer} directly. + */ @Deprecated public VideoDecoderOutputBufferRenderer getVideoDecoderOutputBufferRenderer() { return this; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoRendererEventListener.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoRendererEventListener.java index 7990ac0a9d..d4f5e70caa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoRendererEventListener.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoRendererEventListener.java @@ -59,7 +59,9 @@ public interface VideoRendererEventListener { default void onVideoDecoderInitialized( String decoderName, long initializedTimestampMs, long initializationDurationMs) {} - /** @deprecated Use {@link #onVideoInputFormatChanged(Format, DecoderReuseEvaluation)}. */ + /** + * @deprecated Use {@link #onVideoInputFormatChanged(Format, DecoderReuseEvaluation)}. + */ @Deprecated default void onVideoInputFormatChanged(Format format) {} diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java index 038b4191ef..eded3b48f8 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java @@ -301,7 +301,9 @@ public final class DashMediaSource extends BaseMediaSource { * if no value is defined in the {@link MediaItem} or the manifest. */ public static final long DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS = 30_000; - /** @deprecated Use {@link #DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS} instead. */ + /** + * @deprecated Use {@link #DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS} instead. + */ @Deprecated public static final long DEFAULT_LIVE_PRESENTATION_DELAY_MS = 30_000; /** The media id used by media items of dash media sources without a manifest URI. */ public static final String DEFAULT_MEDIA_ID = "DashMediaSource"; diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/SingleSegmentIndex.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/SingleSegmentIndex.java index 6c31e63258..73bff6bc64 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/SingleSegmentIndex.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/SingleSegmentIndex.java @@ -23,7 +23,9 @@ import androidx.media3.exoplayer.dash.DashSegmentIndex; private final RangedUri uri; - /** @param uri A {@link RangedUri} defining the location of the segment data. */ + /** + * @param uri A {@link RangedUri} defining the location of the segment data. + */ public SingleSegmentIndex(RangedUri uri) { this.uri = uri; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsDataSourceFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsDataSourceFactory.java index 238223533d..d37f36bd8a 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsDataSourceFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsDataSourceFactory.java @@ -24,7 +24,9 @@ public final class DefaultHlsDataSourceFactory implements HlsDataSourceFactory { private final DataSource.Factory dataSourceFactory; - /** @param dataSourceFactory The {@link DataSource.Factory} to use for all data types. */ + /** + * @param dataSourceFactory The {@link DataSource.Factory} to use for all data types. + */ public DefaultHlsDataSourceFactory(DataSource.Factory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsManifest.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsManifest.java index 5132195c3e..1b6864d1f0 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsManifest.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsManifest.java @@ -24,7 +24,9 @@ import androidx.media3.exoplayer.hls.playlist.HlsMultivariantPlaylist; @UnstableApi public final class HlsManifest { - /** @deprecated Use {@link #multivariantPlaylist} instead. */ + /** + * @deprecated Use {@link #multivariantPlaylist} instead. + */ @Deprecated @SuppressWarnings("deprecation") // Keeping deprecated field with deprecated class. public final HlsMasterPlaylist masterPlaylist; diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/SampleQueueMappingException.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/SampleQueueMappingException.java index c5ba8d09a3..c19939f65c 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/SampleQueueMappingException.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/SampleQueueMappingException.java @@ -25,7 +25,9 @@ import java.io.IOException; @UnstableApi public final class SampleQueueMappingException extends IOException { - /** @param mimeType The mime type of the track group whose mapping failed. */ + /** + * @param mimeType The mime type of the track group whose mapping failed. + */ public SampleQueueMappingException(@Nullable String mimeType) { super("Unable to bind a sample queue to TrackGroup with mime type " + mimeType + "."); } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMasterPlaylist.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMasterPlaylist.java index 6ddc2c4387..f4054dc79e 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMasterPlaylist.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsMasterPlaylist.java @@ -22,7 +22,9 @@ import androidx.media3.common.util.UnstableApi; import java.util.List; import java.util.Map; -/** @deprecated Use {@link HlsMultivariantPlaylist} instead. */ +/** + * @deprecated Use {@link HlsMultivariantPlaylist} instead. + */ @UnstableApi @Deprecated public final class HlsMasterPlaylist extends HlsMultivariantPlaylist { diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java index ca98b688e6..6ac0a24560 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java @@ -225,17 +225,23 @@ public class SsManifestParser implements ParsingLoadable.Parser { // Do nothing. } - /** @param xmlParser The underlying {@link XmlPullParser} */ + /** + * @param xmlParser The underlying {@link XmlPullParser} + */ protected void parseText(XmlPullParser xmlParser) { // Do nothing. } - /** @param xmlParser The underlying {@link XmlPullParser} */ + /** + * @param xmlParser The underlying {@link XmlPullParser} + */ protected void parseEndTag(XmlPullParser xmlParser) { // Do nothing. } - /** @param parsedChild A parsed child object. */ + /** + * @param parsedChild A parsed child object. + */ protected void addChild(Object parsedChild) { // Do nothing. } diff --git a/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java b/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java index 8429e80f48..0d9f748f90 100644 --- a/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java +++ b/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java @@ -56,7 +56,9 @@ public final class WorkManagerScheduler implements Scheduler { private final WorkManager workManager; private final String workName; - /** @deprecated Call {@link #WorkManagerScheduler(Context, String)} instead. */ + /** + * @deprecated Call {@link #WorkManagerScheduler(Context, String)} instead. + */ @Deprecated @SuppressWarnings("deprecation") public WorkManagerScheduler(String workName) { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java index a8bcb676a6..691ee94319 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/BinarySearchSeeker.java @@ -534,7 +534,9 @@ public abstract class BinarySearchSeeker { return durationUs; } - /** @see SeekTimestampConverter#timeUsToTargetTime(long) */ + /** + * @see SeekTimestampConverter#timeUsToTargetTime(long) + */ public long timeUsToTargetTime(long timeUs) { return seekTimestampConverter.timeUsToTargetTime(timeUs); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/SeekMap.java b/libraries/extractor/src/main/java/androidx/media3/extractor/SeekMap.java index c1c15680f5..037f8d7cb9 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/SeekMap.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/SeekMap.java @@ -75,7 +75,9 @@ public interface SeekMap { /** The second seek point, or {@link #first} if there's only one seek point. */ public final SeekPoint second; - /** @param point The single seek point. */ + /** + * @param point The single seek point. + */ public SeekPoints(SeekPoint point) { this(point, point); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java index 5a4b3e1f52..551fc75739 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/amr/AmrExtractor.java @@ -171,7 +171,9 @@ public final class AmrExtractor implements Extractor { this(/* flags= */ 0); } - /** @param flags Flags that control the extractor's behavior. */ + /** + * @param flags Flags that control the extractor's behavior. + */ public AmrExtractor(@Flags int flags) { if ((flags & FLAG_ENABLE_CONSTANT_BITRATE_SEEKING_ALWAYS) != 0) { flags |= FLAG_ENABLE_CONSTANT_BITRATE_SEEKING; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/TagPayloadReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/TagPayloadReader.java index 18cd29c746..6106a65b8f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/TagPayloadReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/TagPayloadReader.java @@ -33,7 +33,9 @@ import androidx.media3.extractor.TrackOutput; protected final TrackOutput output; - /** @param output A {@link TrackOutput} to which samples should be written. */ + /** + * @param output A {@link TrackOutput} to which samples should be written. + */ protected TagPayloadReader(TrackOutput output) { this.output = output; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java index a819947ee5..c2709461e8 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java @@ -48,7 +48,9 @@ import androidx.media3.extractor.TrackOutput; private boolean hasOutputKeyframe; private int frameType; - /** @param output A {@link TrackOutput} to which samples should be written. */ + /** + * @param output A {@link TrackOutput} to which samples should be written. + */ public VideoTagPayloadReader(TrackOutput output) { super(output); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/flac/VorbisComment.java b/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/flac/VorbisComment.java index 51e94f0473..7bce2d15d7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/flac/VorbisComment.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/flac/VorbisComment.java @@ -24,7 +24,9 @@ import androidx.media3.common.MediaMetadata; import androidx.media3.common.Metadata; import androidx.media3.common.util.UnstableApi; -/** @deprecated Use {@link androidx.media3.extractor.metadata.vorbis.VorbisComment} instead. */ +/** + * @deprecated Use {@link androidx.media3.extractor.metadata.vorbis.VorbisComment} instead. + */ @Deprecated @UnstableApi public class VorbisComment implements Metadata.Entry { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java index 53ae30f9ee..6279da9e63 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp3/Mp3Extractor.java @@ -171,7 +171,9 @@ public final class Mp3Extractor implements Extractor { this(0); } - /** @param flags Flags that control the extractor's behavior. */ + /** + * @param flags Flags that control the extractor's behavior. + */ public Mp3Extractor(@Flags int flags) { this(flags, C.TIME_UNSET); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 36284771d1..361871f51d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -188,7 +188,9 @@ public class FragmentedMp4Extractor implements Extractor { this(0); } - /** @param flags Flags that control the extractor's behavior. */ + /** + * @param flags Flags that control the extractor's behavior. + */ public FragmentedMp4Extractor(@Flags int flags) { this(flags, /* timestampAdjuster= */ null); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/StreamReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/StreamReader.java index 9de683b331..6add005122 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/StreamReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/StreamReader.java @@ -90,7 +90,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; currentGranule = 0; } - /** @see Extractor#seek(long, long) */ + /** + * @see Extractor#seek(long, long) + */ final void seek(long position, long timeUs) { oggPacket.reset(); if (position == 0) { @@ -104,7 +106,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } } - /** @see Extractor#read(ExtractorInput, PositionHolder) */ + /** + * @see Extractor#read(ExtractorInput, PositionHolder) + */ final int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { assertInitialized(); switch (state) { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SimpleSubtitleDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SimpleSubtitleDecoder.java index 2f06d6e948..7d0689a631 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SimpleSubtitleDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SimpleSubtitleDecoder.java @@ -30,7 +30,9 @@ public abstract class SimpleSubtitleDecoder private final String name; - /** @param name The name of the decoder. */ + /** + * @param name The name of the decoder. + */ @SuppressWarnings("nullness:method.invocation") protected SimpleSubtitleDecoder(String name) { super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleDecoderException.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleDecoderException.java index b5fe73595d..a9b3713c24 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleDecoderException.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleDecoderException.java @@ -23,12 +23,16 @@ import androidx.media3.decoder.DecoderException; @UnstableApi public class SubtitleDecoderException extends DecoderException { - /** @param message The detail message for this exception. */ + /** + * @param message The detail message for this exception. + */ public SubtitleDecoderException(String message) { super(message); } - /** @param cause The cause of this exception, or {@code null}. */ + /** + * @param cause The cause of this exception, or {@code null}. + */ public SubtitleDecoderException(@Nullable Throwable cause) { super(cause); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/cea/CeaSubtitle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/cea/CeaSubtitle.java index f85b8f1997..f9ce17fabc 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/cea/CeaSubtitle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/cea/CeaSubtitle.java @@ -27,7 +27,9 @@ import java.util.List; private final List cues; - /** @param cues The subtitle cues. */ + /** + * @param cues The subtitle cues. + */ public CeaSubtitle(List cues) { this.cues = cues; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java index baf9ebdca3..10e14ee4f6 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java @@ -100,7 +100,9 @@ public final class AdtsReader implements ElementaryStreamReader { private @MonotonicNonNull TrackOutput currentOutput; private long currentSampleDuration; - /** @param exposeId3 True if the reader should expose ID3 information. */ + /** + * @param exposeId3 True if the reader should expose ID3 information. + */ public AdtsReader(boolean exposeId3) { this(exposeId3, null); } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DvbSubtitleReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DvbSubtitleReader.java index 986e033881..d41fe859b7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DvbSubtitleReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DvbSubtitleReader.java @@ -41,7 +41,9 @@ public final class DvbSubtitleReader implements ElementaryStreamReader { private int sampleBytesWritten; private long sampleTimeUs; - /** @param subtitleInfos Information about the DVB subtitles associated to the stream. */ + /** + * @param subtitleInfos Information about the DVB subtitles associated to the stream. + */ public DvbSubtitleReader(List subtitleInfos) { this.subtitleInfos = subtitleInfos; outputs = new TrackOutput[subtitleInfos.size()]; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java index f8c1953bf8..a0ab3aaa8d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java @@ -78,7 +78,9 @@ public final class H265Reader implements ElementaryStreamReader { // Scratch variables to avoid allocations. private final ParsableByteArray seiWrapper; - /** @param seiReader An SEI reader for consuming closed caption channels. */ + /** + * @param seiReader An SEI reader for consuming closed caption channels. + */ public H265Reader(SeiReader seiReader) { this.seiReader = seiReader; prefixFlags = new boolean[3]; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/LatmReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/LatmReader.java index 1197276c1b..a9a4ff7603 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/LatmReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/LatmReader.java @@ -75,7 +75,9 @@ public final class LatmReader implements ElementaryStreamReader { private int channelCount; @Nullable private String codecs; - /** @param language Track language. */ + /** + * @param language Track language. + */ public LatmReader(@Nullable String language) { this.language = language; sampleDataBuffer = new ParsableByteArray(INITIAL_BUFFER_SIZE); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/SeiReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/SeiReader.java index c0f80c4f68..582b772060 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/SeiReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/SeiReader.java @@ -35,7 +35,9 @@ public final class SeiReader { private final List closedCaptionFormats; private final TrackOutput[] outputs; - /** @param closedCaptionFormats A list of formats for the closed caption channels to expose. */ + /** + * @param closedCaptionFormats A list of formats for the closed caption channels to expose. + */ public SeiReader(List closedCaptionFormats) { this.closedCaptionFormats = closedCaptionFormats; outputs = new TrackOutput[closedCaptionFormats.size()]; diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/PollingCheck.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/PollingCheck.java index ef9c58b1cd..cf4df31057 100644 --- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/PollingCheck.java +++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/PollingCheck.java @@ -30,7 +30,9 @@ public abstract class PollingCheck { /** The condition that the PollingCheck should use to proceed successfully. */ public interface PollingCheckCondition { - /** @return Whether the polling condition has been met. */ + /** + * @return Whether the polling condition has been met. + */ boolean canProceed() throws Exception; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java index 6aaf678a37..f025836ba2 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/Action.java @@ -323,7 +323,9 @@ public abstract class Action { /** Calls {@link ExoPlayer#clearMediaItems()}}. */ public static class ClearMediaItems extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public ClearMediaItems(String tag) { super(tag, "ClearMediaItems"); } @@ -426,7 +428,9 @@ public abstract class Action { /** Calls {@link ExoPlayer#clearVideoSurface()}. */ public static final class ClearVideoSurface extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public ClearVideoSurface(String tag) { super(tag, "ClearVideoSurface"); } @@ -441,7 +445,9 @@ public abstract class Action { /** Calls {@link ExoPlayer#setVideoSurface(Surface)}. */ public static final class SetVideoSurface extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public SetVideoSurface(String tag) { super(tag, "SetVideoSurface"); } @@ -480,7 +486,9 @@ public abstract class Action { /** Calls {@link ExoPlayer#prepare()}. */ public static final class Prepare extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public Prepare(String tag) { super(tag, "Prepare"); } @@ -817,7 +825,9 @@ public abstract class Action { */ public static final class WaitForPositionDiscontinuity extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public WaitForPositionDiscontinuity(String tag) { super(tag, "WaitForPositionDiscontinuity"); } @@ -1046,7 +1056,9 @@ public abstract class Action { /** Waits until the player acknowledged all pending player commands. */ public static final class WaitForPendingPlayerCommands extends Action { - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public WaitForPendingPlayerCommands(String tag) { super(tag, "WaitForPendingPlayerCommands"); } @@ -1083,7 +1095,9 @@ public abstract class Action { private final Runnable runnable; - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public ExecuteRunnable(@Size(max = 23) String tag, Runnable runnable) { super(tag, "ExecuteRunnable"); this.runnable = runnable; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/ActionSchedule.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/ActionSchedule.java index a1d9784eac..fb4402d60d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/ActionSchedule.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/ActionSchedule.java @@ -114,7 +114,9 @@ public final class ActionSchedule { private long currentDelayMs; private ActionNode previousNode; - /** @param tag A tag to use for logging. */ + /** + * @param tag A tag to use for logging. + */ public Builder(String tag) { this.tag = tag; rootNode = new ActionNode(new RootAction(tag), 0); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java index fa9c146a8e..b330c94786 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DownloadBuilder.java @@ -108,91 +108,121 @@ public final class DownloadBuilder { this.progress = new DownloadProgress(); } - /** @see DownloadRequest#uri */ + /** + * @see DownloadRequest#uri + */ public DownloadBuilder setUri(String uri) { this.uri = Uri.parse(uri); return this; } - /** @see DownloadRequest#uri */ + /** + * @see DownloadRequest#uri + */ public DownloadBuilder setUri(Uri uri) { this.uri = uri; return this; } - /** @see DownloadRequest#mimeType */ + /** + * @see DownloadRequest#mimeType + */ public DownloadBuilder setMimeType(String mimeType) { this.mimeType = mimeType; return this; } - /** @see DownloadRequest#keySetId */ + /** + * @see DownloadRequest#keySetId + */ public DownloadBuilder setKeySetId(byte[] keySetId) { this.keySetId = keySetId; return this; } - /** @see DownloadRequest#customCacheKey */ + /** + * @see DownloadRequest#customCacheKey + */ public DownloadBuilder setCacheKey(@Nullable String cacheKey) { this.cacheKey = cacheKey; return this; } - /** @see Download#state */ + /** + * @see Download#state + */ public DownloadBuilder setState(@Download.State int state) { this.state = state; return this; } - /** @see DownloadProgress#percentDownloaded */ + /** + * @see DownloadProgress#percentDownloaded + */ public DownloadBuilder setPercentDownloaded(float percentDownloaded) { progress.percentDownloaded = percentDownloaded; return this; } - /** @see DownloadProgress#bytesDownloaded */ + /** + * @see DownloadProgress#bytesDownloaded + */ public DownloadBuilder setBytesDownloaded(long bytesDownloaded) { progress.bytesDownloaded = bytesDownloaded; return this; } - /** @see Download#contentLength */ + /** + * @see Download#contentLength + */ public DownloadBuilder setContentLength(long contentLength) { this.contentLength = contentLength; return this; } - /** @see Download#failureReason */ + /** + * @see Download#failureReason + */ public DownloadBuilder setFailureReason(int failureReason) { this.failureReason = failureReason; return this; } - /** @see Download#stopReason */ + /** + * @see Download#stopReason + */ public DownloadBuilder setStopReason(int stopReason) { this.stopReason = stopReason; return this; } - /** @see Download#startTimeMs */ + /** + * @see Download#startTimeMs + */ public DownloadBuilder setStartTimeMs(long startTimeMs) { this.startTimeMs = startTimeMs; return this; } - /** @see Download#updateTimeMs */ + /** + * @see Download#updateTimeMs + */ public DownloadBuilder setUpdateTimeMs(long updateTimeMs) { this.updateTimeMs = updateTimeMs; return this; } - /** @see DownloadRequest#streamKeys */ + /** + * @see DownloadRequest#streamKeys + */ public DownloadBuilder setStreamKeys(StreamKey... streamKeys) { this.streamKeys = Arrays.asList(streamKeys); return this; } - /** @see DownloadRequest#data */ + /** + * @see DownloadRequest#data + */ public DownloadBuilder setCustomMetadata(byte[] customMetadata) { this.customMetadata = customMetadata; return this; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java index fca4296d6d..f7eb8d6978 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java @@ -173,13 +173,17 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { private int referenceCount; @Nullable private OnEventListener onEventListener; - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @Deprecated public FakeExoMediaDrm() { this(/* maxConcurrentSessions= */ Integer.MAX_VALUE); } - /** @deprecated Use {@link Builder} instead. */ + /** + * @deprecated Use {@link Builder} instead. + */ @Deprecated public FakeExoMediaDrm(int maxConcurrentSessions) { this( diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index 8b26a65dcb..e8219e9723 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -108,7 +108,9 @@ public final class Transformer { private Clock clock; private Codec.EncoderFactory encoderFactory; - /** @deprecated Use {@link #Builder(Context)} instead. */ + /** + * @deprecated Use {@link #Builder(Context)} instead. + */ @Deprecated public Builder() { muxerFactory = new FrameworkMuxer.Factory(); @@ -154,7 +156,9 @@ public final class Transformer { this.clock = transformer.clock; } - /** @deprecated Use {@link #Builder(Context)} instead. */ + /** + * @deprecated Use {@link #Builder(Context)} instead. + */ @Deprecated public Builder setContext(Context context) { this.context = context.getApplicationContext(); @@ -427,7 +431,9 @@ public final class Transformer { onTransformationCompleted(inputMediaItem); } - /** @deprecated Use {@link #onTransformationError(MediaItem, TransformationException)}. */ + /** + * @deprecated Use {@link #onTransformationError(MediaItem, TransformationException)}. + */ @Deprecated default void onTransformationError(MediaItem inputMediaItem, Exception exception) { onTransformationError(inputMediaItem, (TransformationException) exception); diff --git a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java index 4f65300bd2..5132b93f38 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/DefaultTrackNameProvider.java @@ -32,7 +32,9 @@ public class DefaultTrackNameProvider implements TrackNameProvider { private final Resources resources; - /** @param resources Resources from which to obtain strings. */ + /** + * @param resources Resources from which to obtain strings. + */ public DefaultTrackNameProvider(Resources resources) { this.resources = Assertions.checkNotNull(resources); } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/TimeBar.java b/libraries/ui/src/main/java/androidx/media3/ui/TimeBar.java index e94b5a5e08..4b85a4a2c1 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/TimeBar.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/TimeBar.java @@ -40,7 +40,9 @@ public interface TimeBar { */ void removeListener(OnScrubListener listener); - /** @see View#isEnabled() */ + /** + * @see View#isEnabled() + */ void setEnabled(boolean enabled); /** From fdd3973a094f4de5fb14f4c34bc7d88bd629efc3 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 18:11:38 +0000 Subject: [PATCH 243/251] Fix potential NPE in test_utils WebServerDispatcher PiperOrigin-RevId: 429338648 --- .../java/androidx/media3/test/utils/WebServerDispatcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java index 63b20dfc7c..7471b075fb 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/WebServerDispatcher.java @@ -29,6 +29,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.base.Joiner; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -241,7 +242,7 @@ public class WebServerDispatcher extends Dispatcher { /** Returns the path for a given {@link RecordedRequest}, stripping any query parameters. */ public static String getRequestPath(RecordedRequest request) { - return Util.splitAtFirst(request.getPath(), "\\?")[0]; + return Util.splitAtFirst(Strings.nullToEmpty(request.getPath()), "\\?")[0]; } /** From cf85d1bdc8c2518815ca78aa018157a2e755a667 Mon Sep 17 00:00:00 2001 From: hschlueter Date: Thu, 17 Feb 2022 18:30:41 +0000 Subject: [PATCH 244/251] Only allow HEVC output MIME selection in demo for API>=24. The muxer doesn't support HEVC below API 24. The is documented in the TransformationRequest javadoc and the Transformer.Builder will throw if HEVC is requested below API 24 so the option should not be part of the demo for those devices. #mse-bug-week PiperOrigin-RevId: 429343805 --- .../media3/demo/transformer/ConfigurationActivity.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java index efc9e53d7a..b6a447768c 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java @@ -33,6 +33,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Util; import java.util.Arrays; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -122,11 +123,10 @@ public final class ConfigurationActivity extends AppCompatActivity { videoMimeSpinner = findViewById(R.id.video_mime_spinner); videoMimeSpinner.setAdapter(videoMimeAdapter); videoMimeAdapter.addAll( - SAME_AS_INPUT_OPTION, - MimeTypes.VIDEO_H263, - MimeTypes.VIDEO_H264, - MimeTypes.VIDEO_H265, - MimeTypes.VIDEO_MP4V); + SAME_AS_INPUT_OPTION, MimeTypes.VIDEO_H263, MimeTypes.VIDEO_H264, MimeTypes.VIDEO_MP4V); + if (Util.SDK_INT >= 24) { + videoMimeAdapter.add(MimeTypes.VIDEO_H265); + } ArrayAdapter resolutionHeightAdapter = new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item); From f1e59f8001ea22df0b007034305ecc1f7e4adeda Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 19:49:23 +0000 Subject: [PATCH 245/251] Libopus Support For WebM DiscardPadding PiperOrigin-RevId: 429364728 --- .../decoder/opus/LibopusAudioRenderer.java | 11 ++++ .../media3/decoder/opus/OpusDecoder.java | 38 +++++++++++ .../media3/decoder/opus/OpusDecoderTest.java | 63 ++++++++++++++++++ .../extractor/mkv/MatroskaExtractor.java | 64 +++++++++++++++---- 4 files changed, 162 insertions(+), 14 deletions(-) diff --git a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/LibopusAudioRenderer.java b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/LibopusAudioRenderer.java index dc49060405..ec9717366c 100644 --- a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/LibopusAudioRenderer.java +++ b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/LibopusAudioRenderer.java @@ -115,6 +115,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { format.initializationData, cryptoConfig, outputFloat); + decoder.experimentalSetDiscardPaddingEnabled(experimentalGetDiscardPaddingEnabled()); TraceUtil.endSection(); return decoder; @@ -126,4 +127,14 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { int pcmEncoding = decoder.outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT; return Util.getPcmFormat(pcmEncoding, decoder.channelCount, OpusDecoder.SAMPLE_RATE); } + + /** + * Returns true if support for padding removal from the end of decoder output buffer should be + * enabled. + * + *

    This method is experimental, and will be renamed or removed in a future release. + */ + protected boolean experimentalGetDiscardPaddingEnabled() { + return false; + } } diff --git a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java index 47f4c7a758..a381b49a4c 100644 --- a/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java +++ b/libraries/decoder_opus/src/main/java/androidx/media3/decoder/opus/OpusDecoder.java @@ -56,6 +56,7 @@ public final class OpusDecoder private final int preSkipSamples; private final int seekPreRollSamples; private final long nativeDecoderContext; + private boolean experimentalDiscardPaddingEnabled; private int skipSamples; @@ -145,6 +146,16 @@ public final class OpusDecoder } } + /** + * Sets whether discard padding is enabled. When enabled, discard padding samples (provided as + * supplemental data on the input buffer) will be removed from the end of the decoder output. + * + *

    This method is experimental, and will be renamed or removed in a future release. + */ + public void experimentalSetDiscardPaddingEnabled(boolean enabled) { + this.experimentalDiscardPaddingEnabled = enabled; + } + @Override public String getName() { return "libopus" + OpusLibrary.getVersion(); @@ -224,6 +235,14 @@ public final class OpusDecoder skipSamples = 0; outputData.position(skipBytes); } + } else if (experimentalDiscardPaddingEnabled && inputBuffer.hasSupplementalData()) { + int discardPaddingSamples = getDiscardPaddingSamples(inputBuffer.supplementalData); + if (discardPaddingSamples > 0) { + int discardBytes = samplesToBytes(discardPaddingSamples, channelCount, outputFloat); + if (result >= discardBytes) { + outputData.limit(result - discardBytes); + } + } } return null; } @@ -281,6 +300,25 @@ public final class OpusDecoder return DEFAULT_SEEK_PRE_ROLL_SAMPLES; } + /** + * Returns the number of discard padding samples specified by the supplemental data attached to an + * input buffer. + * + * @param supplementalData Supplemental data related to the an input buffer. + * @return The number of discard padding samples to remove from the decoder output. + */ + @VisibleForTesting + /* package */ static int getDiscardPaddingSamples(@Nullable ByteBuffer supplementalData) { + if (supplementalData == null || supplementalData.remaining() != 8) { + return 0; + } + long discardPaddingNs = supplementalData.order(ByteOrder.LITTLE_ENDIAN).getLong(); + if (discardPaddingNs < 0) { + return 0; + } + return (int) ((discardPaddingNs * SAMPLE_RATE) / C.NANOS_PER_SECOND); + } + /** Returns number of bytes to represent {@code samples}. */ private static int samplesToBytes(int samples, int channelCount, boolean outputFloat) { int bytesPerChannel = outputFloat ? 4 : 2; diff --git a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java index 261ec3febd..59581dc6a6 100644 --- a/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java +++ b/libraries/decoder_opus/src/test/java/androidx/media3/decoder/opus/OpusDecoderTest.java @@ -52,6 +52,8 @@ public final class OpusDecoderTest { private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840; + private static final int DISCARD_PADDING_NANOS = 166667; + private static final ImmutableList HEADER_ONLY_INITIALIZATION_DATA = ImmutableList.of(HEADER); @@ -102,6 +104,20 @@ public final class OpusDecoderTest { assertThat(seekPreRollSamples).isEqualTo(DEFAULT_SEEK_PRE_ROLL_SAMPLES); } + @Test + public void getDiscardPaddingSamples_positiveSampleLength_returnSampleLength() { + int discardPaddingSamples = + OpusDecoder.getDiscardPaddingSamples(createSupplementalData(DISCARD_PADDING_NANOS)); + assertThat(discardPaddingSamples).isEqualTo(nanosecondsToSampleCount(DISCARD_PADDING_NANOS)); + } + + @Test + public void getDiscardPaddingSamples_negativeSampleLength_returnZero() { + int discardPaddingSamples = + OpusDecoder.getDiscardPaddingSamples(createSupplementalData(-DISCARD_PADDING_NANOS)); + assertThat(discardPaddingSamples).isEqualTo(0); + } + @Test public void decode_removesPreSkipFromOutput() throws OpusDecoderException { OpusDecoder decoder = @@ -120,6 +136,49 @@ public final class OpusDecoderTest { .isEqualTo(DECODED_DATA_SIZE - nanosecondsToBytes(PRE_SKIP_NANOS)); } + @Test + public void decode_whenDiscardPaddingDisabled_returnsDiscardPadding() + throws OpusDecoderException { + OpusDecoder decoder = + new OpusDecoder( + /* numInputBuffers= */ 0, + /* numOutputBuffers= */ 0, + /* initialInputBufferSize= */ 0, + createInitializationData(/* preSkipNanos= */ 0), + /* cryptoConfig= */ null, + /* outputFloat= */ false); + DecoderInputBuffer input = + createInputBuffer( + decoder, + ENCODED_DATA, + /* supplementalData= */ buildNativeOrderByteArray(DISCARD_PADDING_NANOS)); + SimpleDecoderOutputBuffer output = decoder.createOutputBuffer(); + assertThat(decoder.decode(input, output, false)).isNull(); + assertThat(output.data.remaining()).isEqualTo(DECODED_DATA_SIZE); + } + + @Test + public void decode_whenDiscardPaddingEnabled_removesDiscardPadding() throws OpusDecoderException { + OpusDecoder decoder = + new OpusDecoder( + /* numInputBuffers= */ 0, + /* numOutputBuffers= */ 0, + /* initialInputBufferSize= */ 0, + createInitializationData(/* preSkipNanos= */ 0), + /* cryptoConfig= */ null, + /* outputFloat= */ false); + decoder.experimentalSetDiscardPaddingEnabled(true); + DecoderInputBuffer input = + createInputBuffer( + decoder, + ENCODED_DATA, + /* supplementalData= */ buildNativeOrderByteArray(DISCARD_PADDING_NANOS)); + SimpleDecoderOutputBuffer output = decoder.createOutputBuffer(); + assertThat(decoder.decode(input, output, false)).isNull(); + assertThat(output.data.limit()) + .isEqualTo(DECODED_DATA_SIZE - nanosecondsToBytes(DISCARD_PADDING_NANOS)); + } + private static long sampleCountToNanoseconds(long sampleCount) { return (sampleCount * C.NANOS_PER_SECOND) / OpusDecoder.SAMPLE_RATE; } @@ -141,6 +200,10 @@ public final class OpusDecoderTest { return ImmutableList.of(HEADER, preSkip, CUSTOM_SEEK_PRE_ROLL_BYTES); } + private static ByteBuffer createSupplementalData(long value) { + return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).rewind(); + } + private static DecoderInputBuffer createInputBuffer( OpusDecoder decoder, byte[] data, @Nullable byte[] supplementalData) { DecoderInputBuffer input = decoder.createInputBuffer(); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 63ded1ed1d..9d174c655a 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -193,6 +193,7 @@ public class MatroskaExtractor implements Extractor { private static final int ID_CODEC_PRIVATE = 0x63A2; private static final int ID_CODEC_DELAY = 0x56AA; private static final int ID_SEEK_PRE_ROLL = 0x56BB; + private static final int ID_DISCARD_PADDING = 0x75A2; private static final int ID_VIDEO = 0xE0; private static final int ID_PIXEL_WIDTH = 0xB0; private static final int ID_PIXEL_HEIGHT = 0xBA; @@ -391,7 +392,7 @@ public class MatroskaExtractor implements Extractor { private final ParsableByteArray subtitleSample; private final ParsableByteArray encryptionInitializationVector; private final ParsableByteArray encryptionSubsampleData; - private final ParsableByteArray blockAdditionalData; + private final ParsableByteArray supplementalData; private @MonotonicNonNull ByteBuffer encryptionSubsampleDataBuffer; private long segmentContentSize; @@ -434,6 +435,7 @@ public class MatroskaExtractor implements Extractor { private @C.BufferFlags int blockFlags; private int blockAdditionalId; private boolean blockHasReferenceBlock; + private long blockGroupDiscardPaddingNs; // Sample writing state. private int sampleBytesRead; @@ -472,7 +474,7 @@ public class MatroskaExtractor implements Extractor { subtitleSample = new ParsableByteArray(); encryptionInitializationVector = new ParsableByteArray(ENCRYPTION_IV_SIZE); encryptionSubsampleData = new ParsableByteArray(); - blockAdditionalData = new ParsableByteArray(); + supplementalData = new ParsableByteArray(); blockSampleSizes = new int[1]; } @@ -579,6 +581,7 @@ public class MatroskaExtractor implements Extractor { case ID_BLOCK_ADD_ID_TYPE: case ID_CODEC_DELAY: case ID_SEEK_PRE_ROLL: + case ID_DISCARD_PADDING: case ID_CHANNELS: case ID_AUDIO_BIT_DEPTH: case ID_CONTENT_ENCODING_ORDER: @@ -690,6 +693,7 @@ public class MatroskaExtractor implements Extractor { break; case ID_BLOCK_GROUP: blockHasReferenceBlock = false; + blockGroupDiscardPaddingNs = 0L; break; case ID_CONTENT_ENCODING: // TODO: check and fail if more than one content encoding is present. @@ -750,13 +754,22 @@ public class MatroskaExtractor implements Extractor { // We've skipped this block (due to incompatible track number). return; } + Track track = tracks.get(blockTrackNumber); + track.assertOutputInitialized(); + if (blockGroupDiscardPaddingNs > 0L && CODEC_ID_OPUS.equals(track.codecId)) { + // For Opus, attach DiscardPadding to the block group samples as supplemental data. + supplementalData.reset( + ByteBuffer.allocate(8) + .order(ByteOrder.LITTLE_ENDIAN) + .putLong(blockGroupDiscardPaddingNs) + .array()); + } + // Commit sample metadata. int sampleOffset = 0; for (int i = 0; i < blockSampleCount; i++) { sampleOffset += blockSampleSizes[i]; } - Track track = tracks.get(blockTrackNumber); - track.assertOutputInitialized(); for (int i = 0; i < blockSampleCount; i++) { long sampleTimeUs = blockTimeUs + (i * track.defaultSampleDurationNs) / 1000; int sampleFlags = blockFlags; @@ -888,6 +901,9 @@ public class MatroskaExtractor implements Extractor { case ID_SEEK_PRE_ROLL: getCurrentTrack(id).seekPreRollNs = value; break; + case ID_DISCARD_PADDING: + blockGroupDiscardPaddingNs = value; + break; case ID_CHANNELS: getCurrentTrack(id).channelCount = (int) value; break; @@ -1281,7 +1297,9 @@ public class MatroskaExtractor implements Extractor { // For SimpleBlock, we can write sample data and immediately commit the corresponding // sample metadata. while (blockSampleIndex < blockSampleCount) { - int sampleSize = writeSampleData(input, track, blockSampleSizes[blockSampleIndex]); + int sampleSize = + writeSampleData( + input, track, blockSampleSizes[blockSampleIndex], /* isBlockGroup= */ false); long sampleTimeUs = blockTimeUs + (blockSampleIndex * track.defaultSampleDurationNs) / 1000; commitSampleToOutput(track, sampleTimeUs, blockFlags, sampleSize, /* offset= */ 0); @@ -1296,7 +1314,8 @@ public class MatroskaExtractor implements Extractor { // the sample data, storing the final sample sizes for when we commit the metadata. while (blockSampleIndex < blockSampleCount) { blockSampleSizes[blockSampleIndex] = - writeSampleData(input, track, blockSampleSizes[blockSampleIndex]); + writeSampleData( + input, track, blockSampleSizes[blockSampleIndex], /* isBlockGroup= */ true); blockSampleIndex++; } } @@ -1332,8 +1351,8 @@ public class MatroskaExtractor implements Extractor { throws IOException { if (blockAdditionalId == BLOCK_ADDITIONAL_ID_VP9_ITU_T_35 && CODEC_ID_VP9.equals(track.codecId)) { - blockAdditionalData.reset(contentSize); - input.readFully(blockAdditionalData.getData(), 0, contentSize); + supplementalData.reset(contentSize); + input.readFully(supplementalData.getData(), 0, contentSize); } else { // Unhandled block additional data. input.skipFully(contentSize); @@ -1405,10 +1424,10 @@ public class MatroskaExtractor implements Extractor { flags &= ~C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA; } else { // Append supplemental data. - int blockAdditionalSize = blockAdditionalData.limit(); + int supplementalDataSize = supplementalData.limit(); track.output.sampleData( - blockAdditionalData, blockAdditionalSize, TrackOutput.SAMPLE_DATA_PART_SUPPLEMENTAL); - size += blockAdditionalSize; + supplementalData, supplementalDataSize, TrackOutput.SAMPLE_DATA_PART_SUPPLEMENTAL); + size += supplementalDataSize; } } track.output.sampleMetadata(timeUs, flags, size, offset, track.cryptoData); @@ -1437,11 +1456,13 @@ public class MatroskaExtractor implements Extractor { * @param input The input from which to read sample data. * @param track The track to output the sample to. * @param size The size of the sample data on the input side. + * @param isBlockGroup Whether the samples are from a BlockGroup. * @return The final size of the written sample. * @throws IOException If an error occurs reading from the input. */ @RequiresNonNull("#2.output") - private int writeSampleData(ExtractorInput input, Track track, int size) throws IOException { + private int writeSampleData(ExtractorInput input, Track track, int size, boolean isBlockGroup) + throws IOException { if (CODEC_ID_SUBRIP.equals(track.codecId)) { writeSubtitleSampleData(input, SUBRIP_PREFIX, size); return finishWriteSampleData(); @@ -1548,9 +1569,9 @@ public class MatroskaExtractor implements Extractor { sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length); } - if (track.maxBlockAdditionId > 0) { + if (track.samplesHaveSupplementalData(isBlockGroup)) { blockFlags |= C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA; - blockAdditionalData.reset(/* limit= */ 0); + supplementalData.reset(/* limit= */ 0); // If there is supplemental data, the structure of the sample data is: // encryption data (if any) || sample size (4 bytes) || sample data || supplemental data int sampleSize = size + sampleStrippedBytes.limit() - sampleBytesRead; @@ -2337,6 +2358,21 @@ public class MatroskaExtractor implements Extractor { } } + /** + * Returns true if supplemental data will be attached to the samples. + * + * @param isBlockGroup Whether the samples are from a BlockGroup. + */ + private boolean samplesHaveSupplementalData(boolean isBlockGroup) { + if (CODEC_ID_OPUS.equals(codecId)) { + // At the end of a BlockGroup, a positive DiscardPadding value will be written out as + // supplemental data for Opus codec. Otherwise (i.e. DiscardPadding <= 0) supplemental data + // size will be 0. + return isBlockGroup; + } + return maxBlockAdditionId > 0; + } + /** Returns the HDR Static Info as defined in CTA-861.3. */ @Nullable private byte[] getHdrStaticInfo() { From c80d30ebceea9d38bba2c8c9a3a9fbb72c5e88df Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 20:04:49 +0000 Subject: [PATCH 246/251] Avoid Clearing Supplemental Data Flag In MatroskExtractor PiperOrigin-RevId: 429368911 --- .../java/androidx/media3/extractor/mkv/MatroskaExtractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 9d174c655a..184b6867e6 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -1421,7 +1421,7 @@ public class MatroskaExtractor implements Extractor { if (blockSampleCount > 1) { // There were multiple samples in the block. Appending the additional data to the last // sample doesn't make sense. Skip instead. - flags &= ~C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA; + supplementalData.reset(/* limit= */ 0); } else { // Append supplemental data. int supplementalDataSize = supplementalData.limit(); From 9238dc758d128db1ec0d62592a8fd5797783413e Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 17 Feb 2022 21:21:32 +0000 Subject: [PATCH 247/251] Add Matroska Extractor Test Sample For Opus PiperOrigin-RevId: 429386479 --- .../extractor/mkv/MatroskaExtractorTest.java | 6 +++ .../mkv/sample_with_opus_audio.mkv.0.dump | 36 ++++++++++++++++++ .../mkv/sample_with_opus_audio.mkv.1.dump | 36 ++++++++++++++++++ .../mkv/sample_with_opus_audio.mkv.2.dump | 36 ++++++++++++++++++ .../mkv/sample_with_opus_audio.mkv.3.dump | 36 ++++++++++++++++++ ...le_with_opus_audio.mkv.unknown_length.dump | 36 ++++++++++++++++++ .../media/mkv/sample_with_opus_audio.mkv | Bin 0 -> 1464 bytes 7 files changed, 186 insertions(+) create mode 100644 libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.0.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.1.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.2.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.3.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.unknown_length.dump create mode 100644 libraries/test_data/src/test/assets/media/mkv/sample_with_opus_audio.mkv diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java index 3aa8d4cfe2..69ad2ee7ab 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java @@ -87,6 +87,12 @@ public final class MatroskaExtractorTest { MatroskaExtractor::new, "media/mkv/sample_with_vorbis_audio.mkv", simulationConfig); } + @Test + public void mkvSample_withOpusAudio() throws Exception { + ExtractorAsserts.assertBehavior( + MatroskaExtractor::new, "media/mkv/sample_with_opus_audio.mkv", simulationConfig); + } + @Test public void mkvSample_withHtcRotationInfoInTrackName() throws Exception { ExtractorAsserts.assertBehavior( diff --git a/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.0.dump b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.0.dump new file mode 100644 index 0000000000..162d49f061 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.0.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 58000 + getPosition(0) = [[timeUs=0, position=569]] + getPosition(1) = [[timeUs=0, position=569]] + getPosition(29000) = [[timeUs=0, position=569]] + getPosition(58000) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 1: + total output bytes = 828 + sample count = 3 + format 0: + id = 1 + sampleMimeType = audio/opus + maxInputSize = 5760 + channelCount = 1 + sampleRate = 48000 + selectionFlags = 1 + language = und + initializationData: + data = length 19, hash CB9E23BC + data = length 8, hash CA22068C + data = length 8, hash 79C07075 + sample 0: + time = 0 + flags = 1 + data = length 283, hash 686EA90 + sample 1: + time = 21000 + flags = 1 + data = length 217, hash 7D03A2A6 + sample 2: + time = 41000 + flags = 268435457 + data = length 328, hash BB4A36F0 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.1.dump b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.1.dump new file mode 100644 index 0000000000..162d49f061 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.1.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 58000 + getPosition(0) = [[timeUs=0, position=569]] + getPosition(1) = [[timeUs=0, position=569]] + getPosition(29000) = [[timeUs=0, position=569]] + getPosition(58000) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 1: + total output bytes = 828 + sample count = 3 + format 0: + id = 1 + sampleMimeType = audio/opus + maxInputSize = 5760 + channelCount = 1 + sampleRate = 48000 + selectionFlags = 1 + language = und + initializationData: + data = length 19, hash CB9E23BC + data = length 8, hash CA22068C + data = length 8, hash 79C07075 + sample 0: + time = 0 + flags = 1 + data = length 283, hash 686EA90 + sample 1: + time = 21000 + flags = 1 + data = length 217, hash 7D03A2A6 + sample 2: + time = 41000 + flags = 268435457 + data = length 328, hash BB4A36F0 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.2.dump b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.2.dump new file mode 100644 index 0000000000..162d49f061 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.2.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 58000 + getPosition(0) = [[timeUs=0, position=569]] + getPosition(1) = [[timeUs=0, position=569]] + getPosition(29000) = [[timeUs=0, position=569]] + getPosition(58000) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 1: + total output bytes = 828 + sample count = 3 + format 0: + id = 1 + sampleMimeType = audio/opus + maxInputSize = 5760 + channelCount = 1 + sampleRate = 48000 + selectionFlags = 1 + language = und + initializationData: + data = length 19, hash CB9E23BC + data = length 8, hash CA22068C + data = length 8, hash 79C07075 + sample 0: + time = 0 + flags = 1 + data = length 283, hash 686EA90 + sample 1: + time = 21000 + flags = 1 + data = length 217, hash 7D03A2A6 + sample 2: + time = 41000 + flags = 268435457 + data = length 328, hash BB4A36F0 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.3.dump b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.3.dump new file mode 100644 index 0000000000..162d49f061 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.3.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 58000 + getPosition(0) = [[timeUs=0, position=569]] + getPosition(1) = [[timeUs=0, position=569]] + getPosition(29000) = [[timeUs=0, position=569]] + getPosition(58000) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 1: + total output bytes = 828 + sample count = 3 + format 0: + id = 1 + sampleMimeType = audio/opus + maxInputSize = 5760 + channelCount = 1 + sampleRate = 48000 + selectionFlags = 1 + language = und + initializationData: + data = length 19, hash CB9E23BC + data = length 8, hash CA22068C + data = length 8, hash 79C07075 + sample 0: + time = 0 + flags = 1 + data = length 283, hash 686EA90 + sample 1: + time = 21000 + flags = 1 + data = length 217, hash 7D03A2A6 + sample 2: + time = 41000 + flags = 268435457 + data = length 328, hash BB4A36F0 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.unknown_length.dump new file mode 100644 index 0000000000..162d49f061 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mkv/sample_with_opus_audio.mkv.unknown_length.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 58000 + getPosition(0) = [[timeUs=0, position=569]] + getPosition(1) = [[timeUs=0, position=569]] + getPosition(29000) = [[timeUs=0, position=569]] + getPosition(58000) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 1: + total output bytes = 828 + sample count = 3 + format 0: + id = 1 + sampleMimeType = audio/opus + maxInputSize = 5760 + channelCount = 1 + sampleRate = 48000 + selectionFlags = 1 + language = und + initializationData: + data = length 19, hash CB9E23BC + data = length 8, hash CA22068C + data = length 8, hash 79C07075 + sample 0: + time = 0 + flags = 1 + data = length 283, hash 686EA90 + sample 1: + time = 21000 + flags = 1 + data = length 217, hash 7D03A2A6 + sample 2: + time = 41000 + flags = 268435457 + data = length 328, hash BB4A36F0 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/media/mkv/sample_with_opus_audio.mkv b/libraries/test_data/src/test/assets/media/mkv/sample_with_opus_audio.mkv new file mode 100644 index 0000000000000000000000000000000000000000..c17923bb28d9c85750f5a8466042c3eb93b3f023 GIT binary patch literal 1464 zcmb1gy}x*|Q(GgW({~{L)X3uWxsk)EsUtVBq$s~QJJG2fDAd}>BoW+@&d2})tSy4R zvr7)_Z|UOKlJwo(9lW|l)N^H8@S4Vj5T01bx@;itqwnq>pqx<1;q>4&O^k66u1xT5 zpkxzk#v6!f48dSSsY`&|b9#S^k3$T%*7c3e{7w$O4ZS{zWof1sdgf+&h6V=Vj;Q?N zB@;O3c4tIhXLhi!HCk2jDu&smv%|p`;x3SnUhHpqu${|z9n{&8*Bcp&k9Me}`afYf z-+zBjBZJb`InAYcDQ#_z@%{m!!C|YKll2ya?QUT?vW20!k?G;Y`HhTQI~<-DfXz%w zX%t9aG}*tPwAdpxF@=%Qg0W#YSOyee4h#3Ucsw_-E-r>zr=EPEA^ilDBjvid-PO<8 z-^De^r5(*5FbxUG2fC9H?t|-9M%FtG7$(WUFflYX0R@UePG(X*&_U@Z7P&6&a0v}^ z4Ds~$b7`MqU|5jP*l6-aSjE_n0xPSVW!vxEw#4{&k z=KZfTH~GBKW5ap1PrADWN>6q6hlIUezx}kUR><|2LFtDWmwZV2DE;NW&8v^nA5)oU zpUB<1I+m@zDS$uaT-t?>;+N8MUUU?{aGp@Nb$$BYAl2RCE4XIsgz55_%l&sMTs`5P z*+XX5KxJ=`Z9E<}=9)@5JQp+I+Z0ytlZyJteZ}qfg|yCT-md zM}N4Ts8?jUuD70H@rC~HT;iv<{r!~v$ZdO3Z$qw6eK92{gCcAS2& zu{t86z~+(YmJt6n_p1zksx=#{Ot{?gc=pEw_q9HM;aR=gnpb(pm;Y*EQ-l?A=fBv! z_N-l~Pnzf+!4G?~9_debIsNwH@Bj81Tn)Xo=umCu_2u_o3H*xC7S#T(7`S-vlebHH zLjC0%Q%`T)BX#h@j7JWwS>_9)dLQ0aKAXe2 zcE`_0VM$>H?tF=VdE3Q*vRs=#s|ARs-hO@6;vKhS`U~^R%C`@mbkn%Mtfp6W?eTqA z!@kdD;=26vgV0lp0z*Efh^FG*PXtyxQEd8|y`#e`@6SpHt8)t^k32EnuzcONf{zRZ zzgMXhHm&{r&g3h1>BeVz-0MD{?T&w#-JWJq^(rjNWg>?`(5;Q7i<(W-AArir>HAv* Z72kR6?%&+Vu)PadMt^K#V*Sv_2>^}eWBdRB literal 0 HcmV?d00001 From 99074f703a54f97c04f8262fa83084103abd8af1 Mon Sep 17 00:00:00 2001 From: claincly Date: Fri, 18 Feb 2022 14:46:32 +0000 Subject: [PATCH 248/251] Improve exception message. We use the `createForCodec` method that does not take a `MediaFormat` during transformation, the error message always includes "no configured MediaFormat", which is false. PiperOrigin-RevId: 429553573 --- .../media3/transformer/DefaultCodec.java | 22 +++++++------ .../transformer/DefaultEncoderFactory.java | 4 +-- .../transformer/TransformationException.java | 31 +++++++++---------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java index da53c2ba19..72c3155424 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java @@ -45,6 +45,9 @@ public final class DefaultCodec implements Codec { private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT; private final BufferInfo outputBufferInfo; + /** The {@link MediaFormat} used to configure the underlying {@link MediaCodec}. */ + private final MediaFormat configurationMediaFormat; + private final Format configurationFormat; private final MediaCodec mediaCodec; @Nullable private final Surface inputSurface; @@ -63,7 +66,8 @@ public final class DefaultCodec implements Codec { * @param configurationFormat The {@link Format} to configure the {@code DefaultCodec}. See {@link * #getConfigurationFormat()}. The {@link Format#sampleMimeType sampleMimeType} must not be * {@code null}. - * @param mediaFormat The {@link MediaFormat} to configure the underlying {@link MediaCodec}. + * @param configurationMediaFormat The {@link MediaFormat} to configure the underlying {@link + * MediaCodec}. * @param mediaCodecName The name of a specific {@link MediaCodec} to instantiate. If {@code * null}, {@code DefaultCodec} uses {@link Format#sampleMimeType * configurationFormat.sampleMimeType} to create the underlying {@link MediaCodec codec}. @@ -72,12 +76,13 @@ public final class DefaultCodec implements Codec { */ public DefaultCodec( Format configurationFormat, - MediaFormat mediaFormat, + MediaFormat configurationMediaFormat, @Nullable String mediaCodecName, boolean isDecoder, @Nullable Surface outputSurface) throws TransformationException { this.configurationFormat = configurationFormat; + this.configurationMediaFormat = configurationMediaFormat; outputBufferInfo = new BufferInfo(); inputBufferIndex = C.INDEX_UNSET; outputBufferIndex = C.INDEX_UNSET; @@ -93,7 +98,7 @@ public final class DefaultCodec implements Codec { : isDecoder ? MediaCodec.createDecoderByType(sampleMimeType) : MediaCodec.createEncoderByType(sampleMimeType); - configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface); + configureCodec(mediaCodec, configurationMediaFormat, isDecoder, outputSurface); if (isVideo && !isDecoder) { inputSurface = mediaCodec.createInputSurface(); } @@ -108,7 +113,7 @@ public final class DefaultCodec implements Codec { } throw createInitializationTransformationException( - e, mediaFormat, configurationFormat, isVideo, isDecoder, mediaCodecName); + e, configurationMediaFormat, isVideo, isDecoder, mediaCodecName); } this.mediaCodec = mediaCodec; this.inputSurface = inputSurface; @@ -294,9 +299,9 @@ public final class DefaultCodec implements Codec { boolean isVideo = MimeTypes.isVideo(configurationFormat.sampleMimeType); return TransformationException.createForCodec( cause, - configurationFormat, isVideo, isDecoder, + configurationMediaFormat, mediaCodec.getName(), isDecoder ? TransformationException.ERROR_CODE_DECODING_FAILED @@ -306,17 +311,15 @@ public final class DefaultCodec implements Codec { private static TransformationException createInitializationTransformationException( Exception cause, MediaFormat mediaFormat, - Format format, boolean isVideo, boolean isDecoder, @Nullable String mediaCodecName) { if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) { return TransformationException.createForCodec( cause, - mediaFormat, - format, isVideo, isDecoder, + mediaFormat, mediaCodecName, isDecoder ? TransformationException.ERROR_CODE_DECODER_INIT_FAILED @@ -325,10 +328,9 @@ public final class DefaultCodec implements Codec { if (cause instanceof IllegalArgumentException) { return TransformationException.createForCodec( cause, - mediaFormat, - format, isVideo, isDecoder, + mediaFormat, mediaCodecName, isDecoder ? TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index d641e0ab28..2c6fe623a2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -81,9 +81,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { } else { throw TransformationException.createForCodec( new IllegalArgumentException("The requested output format is not supported."), - format, /* isVideo= */ false, /* isDecoder= */ false, + format, /* mediaCodecName= */ null, TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); } @@ -121,9 +121,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { if (encoderAndClosestFormatSupport == null) { throw TransformationException.createForCodec( new IllegalArgumentException("The requested output format is not supported."), - format, /* isVideo= */ true, /* isDecoder= */ false, + format, /* mediaCodecName= */ null, TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java index 0a57e3edfa..bf4bfa7ebb 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationException.java @@ -207,56 +207,55 @@ public final class TransformationException extends Exception { /** * Creates an instance for a decoder or encoder related exception. * + *

    Use this method after the {@link MediaFormat} used to configure the {@link Codec} is known. + * * @param cause The cause of the failure. - * @param mediaFormat The {@link MediaFormat} used for configuring the underlying {@link - * MediaCodec}, if known. - * @param format The {@link Format} used for configuring the {@link Codec}. * @param isVideo Whether the decoder or encoder is configured for video. * @param isDecoder Whether the exception is created for a decoder. + * @param mediaFormat The {@link MediaFormat} used for configuring the underlying {@link + * MediaCodec}. * @param mediaCodecName The name of the {@link MediaCodec} used, if known. * @param errorCode See {@link #errorCode}. * @return The created instance. */ public static TransformationException createForCodec( Throwable cause, - @Nullable MediaFormat mediaFormat, - Format format, boolean isVideo, boolean isDecoder, + MediaFormat mediaFormat, @Nullable String mediaCodecName, int errorCode) { String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); String errorMessage = - componentName - + " error, format=" - + format - + ", mediaCodecName=" - + mediaCodecName - + ", mediaFormat=" - + (mediaFormat == null ? "no configured MediaFormat" : mediaFormat.toString()); + componentName + ", mediaFormat=" + mediaFormat + ", mediaCodecName=" + mediaCodecName; return new TransformationException(errorMessage, cause, errorCode); } /** * Creates an instance for a decoder or encoder related exception. * + *

    Use this method before configuring the {@link Codec}, or when the {@link Codec} is not + * configured with a {@link MediaFormat}. + * * @param cause The cause of the failure. - * @param format The {@link Format} used for configuring the {@link Codec}. * @param isVideo Whether the decoder or encoder is configured for video. * @param isDecoder Whether the exception is created for a decoder. + * @param format The {@link Format} used for configuring the {@link Codec}. * @param mediaCodecName The name of the {@link MediaCodec} used, if known. * @param errorCode See {@link #errorCode}. * @return The created instance. */ public static TransformationException createForCodec( Throwable cause, - Format format, boolean isVideo, boolean isDecoder, + Format format, @Nullable String mediaCodecName, int errorCode) { - return createForCodec( - cause, /* mediaFormat= */ null, format, isVideo, isDecoder, mediaCodecName, errorCode); + String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); + String errorMessage = + componentName + " error, format=" + format + ", mediaCodecName=" + mediaCodecName; + return new TransformationException(errorMessage, cause, errorCode); } /** From 4b72335d51f0ffe52f7f0287d463b07d08de9bff Mon Sep 17 00:00:00 2001 From: samrobinson Date: Fri, 18 Feb 2022 16:05:43 +0000 Subject: [PATCH 249/251] Add a TestTransformationResult class for additional test values. This class will contain additional details such as frame count, once implemented. #mse-bug-week PiperOrigin-RevId: 429567678 --- .../media3/transformer/AndroidTestUtil.java | 25 ++++++++++------- .../transformer/TestTransformationResult.java | 27 +++++++++++++++++++ .../RepeatedTranscodeTransformationTest.java | 16 +++++------ 3 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/TestTransformationResult.java diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 2db87e4187..9a75a1360d 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -53,18 +53,20 @@ public final class AndroidTestUtil { * @param transformer The {@link Transformer} that performs the transformation. * @param uriString The uri (as a {@link String}) that will be transformed. * @param timeoutSeconds The transformer timeout. An exception is thrown if this is exceeded. - * @return The {@link TransformationResult}. + * @return The {@link TestTransformationResult}. * @throws Exception The cause of the transformation not completing. */ - public static TransformationResult runTransformer( + public static TestTransformationResult runTransformer( Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) throws Exception { JSONObject resultJson = new JSONObject(); try { - TransformationResult transformationResult = + TestTransformationResult testTransformationResult = runTransformerInternal(context, testId, transformer, uriString, timeoutSeconds); - resultJson.put("transformationResult", getTransformationResultJson(transformationResult)); - return transformationResult; + resultJson.put( + "transformationResult", + getTransformationResultJson(testTransformationResult.transformationResult)); + return testTransformationResult; } catch (Exception e) { resultJson.put("exception", getExceptionJson(e)); throw e; @@ -73,7 +75,7 @@ public final class AndroidTestUtil { } } - private static TransformationResult runTransformerInternal( + private static TestTransformationResult runTransformerInternal( Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) throws Exception { AtomicReference<@NullableType TransformationException> transformationExceptionReference = @@ -137,10 +139,13 @@ public final class AndroidTestUtil { // If both exceptions are null, the Transformation must have succeeded, and a // transformationResult will be available. - return checkNotNull(transformationResultReference.get()) - .buildUpon() - .setFileSizeBytes(outputVideoFile.length()) - .build(); + TransformationResult transformationResult = + checkNotNull(transformationResultReference.get()) + .buildUpon() + .setFileSizeBytes(outputVideoFile.length()) + .build(); + + return new TestTransformationResult(transformationResult, outputVideoFile.getPath()); } private static void writeTestSummaryToFile(Context context, String testId, JSONObject resultJson) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TestTransformationResult.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TestTransformationResult.java new file mode 100644 index 0000000000..da45d55714 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TestTransformationResult.java @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +/** A test only class for holding additional details alongside a {@link TransformationResult}. */ +public class TestTransformationResult { + public final TransformationResult transformationResult; + public final String filePath; + + public TestTransformationResult(TransformationResult transformationResult, String filePath) { + this.transformationResult = transformationResult; + this.filePath = filePath; + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java index 6de689a8df..a893c4c420 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -15,16 +15,16 @@ */ package androidx.media3.transformer.mh; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.graphics.Matrix; import androidx.media3.common.MimeTypes; -import androidx.media3.common.util.Assertions; import androidx.media3.transformer.AndroidTestUtil; +import androidx.media3.transformer.TestTransformationResult; import androidx.media3.transformer.TransformationRequest; -import androidx.media3.transformer.TransformationResult; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -58,14 +58,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - TransformationResult result = + TestTransformationResult testResult = runTransformer( context, /* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, transformer, AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, /* timeoutSeconds= */ 120); - differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); + differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); } assertWithMessage( @@ -92,14 +92,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - TransformationResult result = + TestTransformationResult testResult = runTransformer( context, /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, transformer, AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, /* timeoutSeconds= */ 120); - differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); + differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); } assertWithMessage( @@ -123,14 +123,14 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - TransformationResult result = + TestTransformationResult testResult = runTransformer( context, /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i, transformer, AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, /* timeoutSeconds= */ 120); - differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes)); + differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); } assertWithMessage( From d1317b60fc6ecd909c73da256f2b83200b690f67 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 4 Feb 2022 01:08:09 +0530 Subject: [PATCH 250/251] Add support for RTSP AMR-NB/WB Added AMR-NB/WB RTP packet reader and added support for AMR-NB/WB playback through RTSP Change-Id: I0a975fa1e1aa8450bda1c828599a523ba796bc48 --- .../exoplayer/rtsp/RtpPayloadFormat.java | 8 + .../media3/exoplayer/rtsp/RtspMediaTrack.java | 12 ++ .../DefaultRtpPayloadReaderFactory.java | 3 + .../exoplayer/rtsp/reader/RtpAmrReader.java | 193 ++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java index 297353167b..9d7c354b11 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java @@ -37,6 +37,8 @@ import java.util.Map; public final class RtpPayloadFormat { private static final String RTP_MEDIA_AC3 = "AC3"; + private static final String RTP_MEDIA_AMR = "AMR"; + private static final String RTP_MEDIA_AMR_WB = "AMR-WB"; private static final String RTP_MEDIA_MPEG4_GENERIC = "MPEG4-GENERIC"; private static final String RTP_MEDIA_H264 = "H264"; private static final String RTP_MEDIA_H265 = "H265"; @@ -45,6 +47,8 @@ public final class RtpPayloadFormat { public static boolean isFormatSupported(MediaDescription mediaDescription) { switch (Ascii.toUpperCase(mediaDescription.rtpMapAttribute.mediaEncoding)) { case RTP_MEDIA_AC3: + case RTP_MEDIA_AMR: + case RTP_MEDIA_AMR_WB: case RTP_MEDIA_H264: case RTP_MEDIA_H265: case RTP_MEDIA_MPEG4_GENERIC: @@ -71,6 +75,10 @@ public final class RtpPayloadFormat { return MimeTypes.VIDEO_H265; case RTP_MEDIA_MPEG4_GENERIC: return MimeTypes.AUDIO_AAC; + case RTP_MEDIA_AMR: + return MimeTypes.AUDIO_AMR_NB; + case RTP_MEDIA_AMR_WB: + return MimeTypes.AUDIO_AMR_WB; default: throw new IllegalArgumentException(mediaType); } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index 7547f1ea18..236c921fca 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -48,6 +48,8 @@ import com.google.common.collect.ImmutableMap; private static final String PARAMETER_H265_SPROP_PPS = "sprop-pps"; private static final String PARAMETER_H265_SPROP_VPS = "sprop-vps"; private static final String PARAMETER_H265_SPROP_MAX_DON_DIFF = "sprop-max-don-diff"; + private static final String PARAMETER_AMR_OCTET_ALIGN = "octet-align"; + private static final String PARAMETER_AMR_INTERLEAVING = "interleaving"; /** Prefix for the RFC6381 codecs string for AAC formats. */ private static final String AAC_CODECS_PREFIX = "mp4a.40."; @@ -121,6 +123,16 @@ import com.google.common.collect.ImmutableMap; checkArgument(!fmtpParameters.isEmpty()); processAacFmtpAttribute(formatBuilder, fmtpParameters, channelCount, clockRate); break; + case MimeTypes.AUDIO_AMR_NB: + case MimeTypes.AUDIO_AMR_WB: + checkArgument(channelCount == 1, "multi channel is not supported currently"); + checkArgument(!fmtpParameters.isEmpty()); + checkArgument( + fmtpParameters.containsKey(PARAMETER_AMR_OCTET_ALIGN), "mode not supported currently"); + checkArgument( + !fmtpParameters.containsKey(PARAMETER_AMR_INTERLEAVING), + "mode not supported currently"); + break; case MimeTypes.VIDEO_H264: checkArgument(!fmtpParameters.isEmpty()); processH264FmtpAttribute(formatBuilder, fmtpParameters); diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java index 888939b7e8..f24793ff5d 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java @@ -36,6 +36,9 @@ import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; return new RtpAc3Reader(payloadFormat); case MimeTypes.AUDIO_AAC: return new RtpAacReader(payloadFormat); + case MimeTypes.AUDIO_AMR_NB: + case MimeTypes.AUDIO_AMR_WB: + return new RtpAmrReader(payloadFormat); case MimeTypes.VIDEO_H264: return new RtpH264Reader(payloadFormat); case MimeTypes.VIDEO_H265: diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java new file mode 100644 index 0000000000..8c462e7952 --- /dev/null +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java @@ -0,0 +1,193 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.rtsp.reader; + +import static androidx.media3.common.util.Assertions.checkArgument; +import static androidx.media3.common.util.Assertions.checkNotNull; + +import androidx.media3.common.C; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.Util; +import androidx.media3.exoplayer.rtsp.RtpPacket; +import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; +import androidx.media3.extractor.ExtractorOutput; +import androidx.media3.extractor.TrackOutput; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * Parses an AMR byte stream carried on RTP packets and extracts individual samples. Interleaving + * mode is not supported. + */ +/* package */ final class RtpAmrReader implements RtpPayloadReader { + private static final String TAG = "RtpAmrReader"; + /** + * The frame size in bytes, including header (1 byte), for each of the 16 frame types for AMR + * narrow band. + */ + private static final int[] frameSizeBytesByTypeNb = { + 13, + 14, + 16, + 18, + 20, + 21, + 27, + 32, + 6, // AMR SID + 7, // GSM-EFR SID + 6, // TDMA-EFR SID + 6, // PDC-EFR SID + 1, // Future use + 1, // Future use + 1, // Future use + 1 // No data + }; + + /** + * The frame size in bytes, including header (1 byte), for each of the 16 frame types for AMR wide + * band. + */ + private static final int[] frameSizeBytesByTypeWb = { + 18, + 24, + 33, + 37, + 41, + 47, + 51, + 59, + 61, + 6, // AMR-WB SID + 1, // Future use + 1, // Future use + 1, // Future use + 1, // Future use + 1, // speech lost + 1 // No data + }; + + private final RtpPayloadFormat payloadFormat; + private @MonotonicNonNull TrackOutput trackOutput; + private long firstReceivedTimestamp; + private int previousSequenceNumber; + private long startTimeOffsetUs; + private final int sampleRate; + private boolean isWideBand; + + public RtpAmrReader(RtpPayloadFormat payloadFormat) { + this.payloadFormat = payloadFormat; + firstReceivedTimestamp = C.TIME_UNSET; + previousSequenceNumber = C.INDEX_UNSET; + this.sampleRate = this.payloadFormat.clockRate; + this.isWideBand = (payloadFormat.format.sampleMimeType == MimeTypes.AUDIO_AMR_WB); + } + + // RtpPayloadReader implementation. + @Override + public void createTracks(ExtractorOutput extractorOutput, int trackId) { + trackOutput = extractorOutput.track(trackId, C.TRACK_TYPE_AUDIO); + trackOutput.format(payloadFormat.format); + } + + @Override + public void onReceivingFirstPacket(long timestamp, int sequenceNumber) { + this.firstReceivedTimestamp = timestamp; + } + + @Override + public void consume( + ParsableByteArray data, long timestamp, int sequenceNumber, boolean rtpMarker) { + // Check that this packet is in the sequence of the previous packet. + if (previousSequenceNumber != C.INDEX_UNSET) { + int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber); + if (sequenceNumber != expectedSequenceNumber) { + Log.w( + TAG, + Util.formatInvariant( + "Received RTP packet with unexpected sequence number. Expected: %d; received: %d.", + expectedSequenceNumber, sequenceNumber)); + } + } + checkNotNull(trackOutput); + /** + * AMR as RTP payload RFC4867 Section-4.2 + * +----------------+-------------------+---------------- + * | payload header | table of contents | speech data ... + * +----------------+-------------------+---------------- + * + * Payload header RFC4867 Section-4.4.1 + * As interleaving is not supported currently, our header won't contain ILL and ILP + * +-+-+-+-+-+-+-+ + * | CMR |R|R|R|R| + * +-+-+-+-+-+-+-+ + */ + // skip CMR and reserved bits + data.skipBytes(1); + // Loop over sampleSize to send multiple frames along with appropriate timestamp when compound + // payload support is added + int frameType = (data.peekUnsignedByte() >> 3) & 0x0f; + int frameSize = getFrameSize(frameType, isWideBand); + int sampleSize = data.bytesLeft(); + checkArgument(sampleSize == frameSize, "compound payload not supported currently"); + trackOutput.sampleData(data, sampleSize); + long sampleTimeUs = + toSampleTimeUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp, sampleRate); + trackOutput.sampleMetadata( + sampleTimeUs, + C.BUFFER_FLAG_KEY_FRAME, + sampleSize, + /* offset= */ 0, + /* encryptionData= */ null); + previousSequenceNumber = sequenceNumber; + } + + @Override + public void seek(long nextRtpTimestamp, long timeUs) { + firstReceivedTimestamp = nextRtpTimestamp; + startTimeOffsetUs = timeUs; + } + + // Internal methods. + + private boolean isValidFrameType(int frameType) { + if (frameType < 0 || frameType > 15) { + return false; + } + // For wide band, type 10-13 are for future use. + // For narrow band, type 12-14 are for future use. + return isWideBand ? (frameType < 10 || frameType > 13) : (frameType < 12 || frameType > 14); + } + + public int getFrameSize(int frameType, boolean isWideBand) { + checkArgument( + isValidFrameType(frameType), + "Illegal AMR " + (isWideBand ? "WB" : "NB") + " frame type " + frameType); + + return isWideBand ? frameSizeBytesByTypeWb[frameType] : frameSizeBytesByTypeNb[frameType]; + } + + /** Returns the correct sample time from RTP timestamp, accounting for the AMR sampling rate. */ + private static long toSampleTimeUs( + long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp, int sampleRate) { + return startTimeOffsetUs + + Util.scaleLargeTimestamp( + rtpTimestamp - firstReceivedRtpTimestamp, + /* multiplier= */ C.MICROS_PER_SECOND, + /* divisor= */ sampleRate); + } +} From 1761b423caaafd133fed72d3669badd6e0fd9ea6 Mon Sep 17 00:00:00 2001 From: Manisha Jajoo Date: Fri, 4 Mar 2022 19:25:09 +0530 Subject: [PATCH 251/251] Fix review comments in RtpAmrReader --- .../media3/exoplayer/rtsp/RtspMediaTrack.java | 5 +- .../exoplayer/rtsp/reader/RtpAmrReader.java | 85 ++++++++++--------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index 236c921fca..6d789603bb 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -128,10 +128,11 @@ import com.google.common.collect.ImmutableMap; checkArgument(channelCount == 1, "multi channel is not supported currently"); checkArgument(!fmtpParameters.isEmpty()); checkArgument( - fmtpParameters.containsKey(PARAMETER_AMR_OCTET_ALIGN), "mode not supported currently"); + fmtpParameters.containsKey(PARAMETER_AMR_OCTET_ALIGN), + "modes other than octet align is not supported currently"); checkArgument( !fmtpParameters.containsKey(PARAMETER_AMR_INTERLEAVING), - "mode not supported currently"); + "interleaving mode is not supported currently"); break; case MimeTypes.VIDEO_H264: checkArgument(!fmtpParameters.isEmpty()); diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java index 8c462e7952..b0cad4e1c7 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpAmrReader.java @@ -31,23 +31,26 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Parses an AMR byte stream carried on RTP packets and extracts individual samples. Interleaving - * mode is not supported. + * mode is not supported. Refer to RFC4867 for more details. */ /* package */ final class RtpAmrReader implements RtpPayloadReader { private static final String TAG = "RtpAmrReader"; /** * The frame size in bytes, including header (1 byte), for each of the 16 frame types for AMR * narrow band. + * AMR-NB is a multi-mode codec that supports eight narrow band speech encoding modes + * with bit rates between 4.75 and 12.2 kbps RFC4867 Section 3.1 + * Refer to table 1a in 3GPP TS 26.101 */ - private static final int[] frameSizeBytesByTypeNb = { - 13, - 14, - 16, - 18, - 20, - 21, - 27, - 32, + private static final int[] amrNbFrameTypeIndexToFrameSize = { + 13, // 4.75kbps + 14, // 5.15kbps + 16, // 5.90kbps + 18, // 6.70kbps PDC-EFR + 20, // 7.40kbps TDMA-EFR + 21, // 7.95kbps + 27, // 10.2kbps + 32, // 12.2kbps GSM-EFR 6, // AMR SID 7, // GSM-EFR SID 6, // TDMA-EFR SID @@ -61,17 +64,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * The frame size in bytes, including header (1 byte), for each of the 16 frame types for AMR wide * band. + * AMR-WB is a multi-mode codec that supports nine wide band speech encoding modes + * with bit rates between 6.6 to 23.85 kbps RFC4867 Section 3.2 + * Refer to table 1a in 3GPP TS 26.201 */ - private static final int[] frameSizeBytesByTypeWb = { - 18, - 24, - 33, - 37, - 41, - 47, - 51, - 59, - 61, + private static final int[] amrWbFrameTypeIndexToFrameSize = { + 18, // 6.60kbps + 24, // 8.85kbps + 33, // 12.65kbps + 37, // 14.25kbps + 41, // 15.85kbps + 47, // 18.25kbps + 51, // 19.85kbps + 59, // 23.05kbps + 61, // 23.85kbps 6, // AMR-WB SID 1, // Future use 1, // Future use @@ -82,19 +88,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; }; private final RtpPayloadFormat payloadFormat; + private final int sampleRate; + private @MonotonicNonNull TrackOutput trackOutput; private long firstReceivedTimestamp; - private int previousSequenceNumber; private long startTimeOffsetUs; - private final int sampleRate; + private int previousSequenceNumber; private boolean isWideBand; public RtpAmrReader(RtpPayloadFormat payloadFormat) { this.payloadFormat = payloadFormat; - firstReceivedTimestamp = C.TIME_UNSET; - previousSequenceNumber = C.INDEX_UNSET; + this.firstReceivedTimestamp = C.TIME_UNSET; + this.previousSequenceNumber = C.INDEX_UNSET; this.sampleRate = this.payloadFormat.clockRate; - this.isWideBand = (payloadFormat.format.sampleMimeType == MimeTypes.AUDIO_AMR_WB); + + checkNotNull(this.payloadFormat.format.sampleMimeType); + this.isWideBand = this.payloadFormat.format.sampleMimeType == MimeTypes.AUDIO_AMR_WB; } // RtpPayloadReader implementation. @@ -125,12 +134,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } checkNotNull(trackOutput); /** - * AMR as RTP payload RFC4867 Section-4.2 + * AMR as RTP payload RFC4867 Section 4.2 * +----------------+-------------------+---------------- * | payload header | table of contents | speech data ... * +----------------+-------------------+---------------- * - * Payload header RFC4867 Section-4.4.1 + * Payload header RFC4867 Section 4.4.1 * As interleaving is not supported currently, our header won't contain ILL and ILP * +-+-+-+-+-+-+-+ * | CMR |R|R|R|R| @@ -164,21 +173,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // Internal methods. - private boolean isValidFrameType(int frameType) { - if (frameType < 0 || frameType > 15) { - return false; - } - // For wide band, type 10-13 are for future use. - // For narrow band, type 12-14 are for future use. - return isWideBand ? (frameType < 10 || frameType > 13) : (frameType < 12 || frameType > 14); - } - - public int getFrameSize(int frameType, boolean isWideBand) { + public static int getFrameSize(int frameType, boolean isWideBand) { checkArgument( isValidFrameType(frameType), "Illegal AMR " + (isWideBand ? "WB" : "NB") + " frame type " + frameType); - return isWideBand ? frameSizeBytesByTypeWb[frameType] : frameSizeBytesByTypeNb[frameType]; + return isWideBand + ? amrWbFrameTypeIndexToFrameSize[frameType] + : amrNbFrameTypeIndexToFrameSize[frameType]; + } + + private static boolean isValidFrameType(int frameType) { + if (frameType < 0 || frameType > 15) { + return false; + } + return (frameType < 9 || frameType > 14); } /** Returns the correct sample time from RTP timestamp, accounting for the AMR sampling rate. */