diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java index a29aeb68ae..e8eaf641eb 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java @@ -136,7 +136,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { .setSampleMimeType(MimeTypes.AUDIO_RAW) .setChannelCount(decoder.getChannelCount()) .setSampleRate(decoder.getSampleRate()) - .setEncoding(decoder.getEncoding()) + .setPcmEncoding(decoder.getEncoding()) .build(); } diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index 1d6ba75ce8..615b60c3e7 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -279,7 +279,7 @@ public final class FlacExtractor implements Extractor { .setMaxInputSize(streamMetadata.getMaxDecodedFrameSize()) .setChannelCount(streamMetadata.channels) .setSampleRate(streamMetadata.sampleRate) - .setEncoding(getPcmEncoding(streamMetadata.bitsPerSample)) + .setPcmEncoding(getPcmEncoding(streamMetadata.bitsPerSample)) .setMetadata(metadata) .build(); output.format(mediaFormat); diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Format.java b/library/common/src/main/java/com/google/android/exoplayer2/Format.java index 749dea28a7..d564b2318e 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Format.java @@ -96,7 +96,7 @@ import java.util.List; * @@ -157,7 +157,7 @@ public final class Format implements Parcelable { private int channelCount; private int sampleRate; - @C.Encoding private int encoding; + @C.PcmEncoding private int pcmEncoding; private int encoderDelay; private int encoderPadding; @@ -185,7 +185,7 @@ public final class Format implements Parcelable { // Audio specific. channelCount = NO_VALUE; sampleRate = NO_VALUE; - encoding = NO_VALUE; + pcmEncoding = NO_VALUE; // Text specific. accessibilityChannel = NO_VALUE; } @@ -225,7 +225,7 @@ public final class Format implements Parcelable { // Audio specific. this.channelCount = format.channelCount; this.sampleRate = format.sampleRate; - this.encoding = format.encoding; + this.pcmEncoding = format.pcmEncoding; this.encoderDelay = format.encoderDelay; this.encoderPadding = format.encoderPadding; // Text specific. @@ -530,13 +530,13 @@ public final class Format implements Parcelable { } /** - * Sets {@link Format#encoding}. The default value is {@link #NO_VALUE}. + * Sets {@link Format#pcmEncoding}. The default value is {@link #NO_VALUE}. * - * @param encoding The {@link Format#encoding}. + * @param pcmEncoding The {@link Format#pcmEncoding}. * @return The builder. */ - public Builder setEncoding(@C.Encoding int encoding) { - this.encoding = encoding; + public Builder setPcmEncoding(@C.PcmEncoding int pcmEncoding) { + this.pcmEncoding = pcmEncoding; return this; } @@ -618,7 +618,7 @@ public final class Format implements Parcelable { colorInfo, channelCount, sampleRate, - encoding, + pcmEncoding, encoderDelay, encoderPadding, accessibilityChannel, @@ -769,8 +769,6 @@ public final class Format implements Parcelable { public final int sampleRate; /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ @C.PcmEncoding public final int pcmEncoding; - /** The {@link C.Encoding} for audio. Set to {@link #NO_VALUE} for other media types. */ - @C.Encoding public final int encoding; /** * The number of frames to trim from the start of the decoded audio stream, or 0 if not * applicable. @@ -1008,7 +1006,7 @@ public final class Format implements Parcelable { int maxInputSize, int channelCount, int sampleRate, - @C.Encoding int encoding, + @C.PcmEncoding int pcmEncoding, @Nullable List initializationData, @Nullable DrmInitData drmInitData, @C.SelectionFlags int selectionFlags, @@ -1026,7 +1024,7 @@ public final class Format implements Parcelable { .setDrmInitData(drmInitData) .setChannelCount(channelCount) .setSampleRate(sampleRate) - .setEncoding(encoding) + .setPcmEncoding(pcmEncoding) .build(); } @@ -1040,7 +1038,7 @@ public final class Format implements Parcelable { int maxInputSize, int channelCount, int sampleRate, - @C.Encoding int encoding, + @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, @Nullable List initializationData, @@ -1062,7 +1060,7 @@ public final class Format implements Parcelable { .setDrmInitData(drmInitData) .setChannelCount(channelCount) .setSampleRate(sampleRate) - .setEncoding(encoding) + .setPcmEncoding(pcmEncoding) .setEncoderDelay(encoderDelay) .setEncoderPadding(encoderPadding) .build(); @@ -1244,7 +1242,7 @@ public final class Format implements Parcelable { // Audio specific. int channelCount, int sampleRate, - @C.Encoding int encoding, + @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, // Text specific. @@ -1282,8 +1280,7 @@ public final class Format implements Parcelable { // Audio specific. this.channelCount = channelCount; this.sampleRate = sampleRate; - this.encoding = encoding; - this.pcmEncoding = toPcmEncoding(encoding); + this.pcmEncoding = pcmEncoding; this.encoderDelay = encoderDelay == NO_VALUE ? 0 : encoderDelay; this.encoderPadding = encoderPadding == NO_VALUE ? 0 : encoderPadding; // Text specific. @@ -1337,8 +1334,7 @@ public final class Format implements Parcelable { // Audio specific. channelCount = in.readInt(); sampleRate = in.readInt(); - encoding = in.readInt(); - pcmEncoding = toPcmEncoding(encoding); + pcmEncoding = in.readInt(); encoderDelay = in.readInt(); encoderPadding = in.readInt(); // Text specific. @@ -1569,7 +1565,7 @@ public final class Format implements Parcelable { // Audio specific. result = 31 * result + channelCount; result = 31 * result + sampleRate; - result = 31 * result + encoding; + result = 31 * result + pcmEncoding; result = 31 * result + encoderDelay; result = 31 * result + encoderPadding; // Text specific. @@ -1606,7 +1602,7 @@ public final class Format implements Parcelable { && stereoMode == other.stereoMode && channelCount == other.channelCount && sampleRate == other.sampleRate - && encoding == other.encoding + && pcmEncoding == other.pcmEncoding && encoderDelay == other.encoderDelay && encoderPadding == other.encoderPadding && accessibilityChannel == other.accessibilityChannel @@ -1727,7 +1723,7 @@ public final class Format implements Parcelable { // Audio specific. dest.writeInt(channelCount); dest.writeInt(sampleRate); - dest.writeInt(encoding); + dest.writeInt(pcmEncoding); dest.writeInt(encoderDelay); dest.writeInt(encoderPadding); // Text specific. @@ -1747,9 +1743,4 @@ public final class Format implements Parcelable { } }; - - @C.PcmEncoding - private static int toPcmEncoding(@C.Encoding int encoding) { - return Util.isEncodingLinearPcm(encoding) ? encoding : NO_VALUE; - } } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java index 1a5cec63d5..2b24d93232 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -1446,7 +1446,7 @@ public final class Util { .setSampleMimeType(MimeTypes.AUDIO_RAW) .setChannelCount(channels) .setSampleRate(sampleRate) - .setEncoding(pcmEncoding) + .setPcmEncoding(pcmEncoding) .build(); } @@ -1503,7 +1503,7 @@ public final class Util { /** * Returns the audio track channel configuration for the given channel count, or {@link - * AudioFormat#CHANNEL_INVALID} if output is not poossible. + * AudioFormat#CHANNEL_INVALID} if output is not possible. * * @param channelCount The number of channels in the input audio. * @return The channel configuration or {@link AudioFormat#CHANNEL_INVALID} if output is not diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index 2a6e1ba7c0..1f2444a816 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -22,6 +22,7 @@ import android.media.AudioTrack; import android.os.ConditionVariable; import android.os.Handler; import android.os.SystemClock; +import android.util.Pair; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -31,6 +32,7 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; +import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -441,12 +443,13 @@ public final class DefaultAudioSink implements AudioSink { @Override @SinkFormatSupport public int getFormatSupport(Format format) { - if (format.encoding == C.ENCODING_INVALID) { - return SINK_FORMAT_UNSUPPORTED; - } - if (Util.isEncodingLinearPcm(format.encoding)) { - if (format.encoding == C.ENCODING_PCM_16BIT - || (enableFloatOutput && format.encoding == C.ENCODING_PCM_FLOAT)) { + if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) { + if (!Util.isEncodingLinearPcm(format.pcmEncoding)) { + Log.w(TAG, "Invalid PCM encoding: " + format.pcmEncoding); + return SINK_FORMAT_UNSUPPORTED; + } + if (format.pcmEncoding == C.ENCODING_PCM_16BIT + || (enableFloatOutput && format.pcmEncoding == C.ENCODING_PCM_FLOAT)) { return SINK_FORMAT_SUPPORTED_DIRECTLY; } // We can resample all linear PCM encodings to 16-bit integer PCM, which AudioTrack is @@ -456,7 +459,7 @@ public final class DefaultAudioSink implements AudioSink { if (enableOffload && isOffloadedPlaybackSupported(format, audioAttributes)) { return SINK_FORMAT_SUPPORTED_DIRECTLY; } - if (isPassthroughPlaybackSupported(format)) { + if (isPassthroughPlaybackSupported(format, audioCapabilities)) { return SINK_FORMAT_SUPPORTED_DIRECTLY; } return SINK_FORMAT_UNSUPPORTED; @@ -482,15 +485,15 @@ public final class DefaultAudioSink implements AudioSink { @OutputMode int outputMode; @C.Encoding int outputEncoding; int outputSampleRate; - int outputChannelCount; int outputChannelConfig; int outputPcmFrameSize; - if (Util.isEncodingLinearPcm(inputFormat.encoding)) { - inputPcmFrameSize = Util.getPcmFrameSize(inputFormat.encoding, inputFormat.channelCount); + if (MimeTypes.AUDIO_RAW.equals(inputFormat.sampleMimeType)) { + Assertions.checkArgument(Util.isEncodingLinearPcm(inputFormat.pcmEncoding)); + inputPcmFrameSize = Util.getPcmFrameSize(inputFormat.pcmEncoding, inputFormat.channelCount); boolean useFloatOutput = - enableFloatOutput && Util.isEncodingHighResolutionPcm(inputFormat.encoding); + enableFloatOutput && Util.isEncodingHighResolutionPcm(inputFormat.pcmEncoding); availableAudioProcessors = useFloatOutput ? toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors; canApplyPlaybackParameters = !useFloatOutput; @@ -510,7 +513,7 @@ public final class DefaultAudioSink implements AudioSink { AudioProcessor.AudioFormat outputFormat = new AudioProcessor.AudioFormat( - inputFormat.sampleRate, inputFormat.channelCount, inputFormat.encoding); + inputFormat.sampleRate, inputFormat.channelCount, inputFormat.pcmEncoding); for (AudioProcessor audioProcessor : availableAudioProcessors) { try { AudioProcessor.AudioFormat nextFormat = audioProcessor.configure(outputFormat); @@ -525,30 +528,41 @@ public final class DefaultAudioSink implements AudioSink { outputMode = OUTPUT_MODE_PCM; outputEncoding = outputFormat.encoding; outputSampleRate = outputFormat.sampleRate; - outputChannelCount = outputFormat.channelCount; - outputChannelConfig = Util.getAudioTrackChannelConfig(outputChannelCount); - outputPcmFrameSize = Util.getPcmFrameSize(outputEncoding, outputChannelCount); + outputChannelConfig = Util.getAudioTrackChannelConfig(outputFormat.channelCount); + outputPcmFrameSize = Util.getPcmFrameSize(outputEncoding, outputFormat.channelCount); } else { inputPcmFrameSize = C.LENGTH_UNSET; availableAudioProcessors = new AudioProcessor[0]; canApplyPlaybackParameters = false; - outputEncoding = inputFormat.encoding; outputSampleRate = inputFormat.sampleRate; - outputChannelCount = inputFormat.channelCount; outputPcmFrameSize = C.LENGTH_UNSET; if (enableOffload && isOffloadedPlaybackSupported(inputFormat, audioAttributes)) { outputMode = OUTPUT_MODE_OFFLOAD; - outputChannelConfig = Util.getAudioTrackChannelConfig(outputChannelCount); + outputEncoding = + MimeTypes.getEncoding( + Assertions.checkNotNull(inputFormat.sampleMimeType), inputFormat.codecs); + outputChannelConfig = Util.getAudioTrackChannelConfig(inputFormat.channelCount); } else { outputMode = OUTPUT_MODE_PASSTHROUGH; - outputChannelConfig = getChannelConfigForPassthrough(inputFormat.channelCount); + @Nullable + Pair encodingAndChannelConfig = + getEncodingAndChannelConfigForPassthrough(inputFormat, audioCapabilities); + if (encodingAndChannelConfig == null) { + throw new ConfigurationException("Unable to configure passthrough for: " + inputFormat); + } + outputEncoding = encodingAndChannelConfig.first; + outputChannelConfig = encodingAndChannelConfig.second; } } - if (outputChannelConfig == AudioFormat.CHANNEL_INVALID) { - throw new ConfigurationException("Unsupported channel count: " + outputChannelCount); + if (outputEncoding == C.ENCODING_INVALID) { + throw new ConfigurationException( + "Invalid output encoding (mode=" + outputMode + ") for: " + inputFormat); + } + if (outputChannelConfig == AudioFormat.CHANNEL_INVALID) { + throw new ConfigurationException( + "Invalid output channel config (mode=" + outputMode + ") for: " + inputFormat); } - Configuration pendingConfiguration = new Configuration( inputPcmFrameSize, @@ -1276,60 +1290,67 @@ public final class DefaultAudioSink implements AudioSink { : writtenEncodedFrames; } - private boolean isPassthroughPlaybackSupported(Format format) { - // Check for encodings that are known to work for passthrough with the implementation in this - // class. This avoids trying to use passthrough with an encoding where the device/app reports - // it's capable but it is untested or known to be broken (for example AAC-LC). - return audioCapabilities != null - && audioCapabilities.supportsEncoding(format.encoding) - && (format.encoding == C.ENCODING_AC3 - || format.encoding == C.ENCODING_E_AC3 - || format.encoding == C.ENCODING_E_AC3_JOC - || format.encoding == C.ENCODING_AC4 - || format.encoding == C.ENCODING_DTS - || format.encoding == C.ENCODING_DTS_HD - || format.encoding == C.ENCODING_DOLBY_TRUEHD) - && (format.channelCount == Format.NO_VALUE - || format.channelCount <= audioCapabilities.getMaxChannelCount()); - } - - private static boolean isOffloadedPlaybackSupported( - Format format, AudioAttributes audioAttributes) { - if (Util.SDK_INT < 29) { - return false; - } - int channelConfig = Util.getAudioTrackChannelConfig(format.channelCount); - AudioFormat audioFormat = getAudioFormat(format.sampleRate, channelConfig, format.encoding); - if (!AudioManager.isOffloadedPlaybackSupported( - audioFormat, audioAttributes.getAudioAttributesV21())) { - return false; - } - boolean notGapless = format.encoderDelay == 0 && format.encoderPadding == 0; - return notGapless || isOffloadGaplessSupported(); + private static boolean isPassthroughPlaybackSupported( + Format format, @Nullable AudioCapabilities audioCapabilities) { + return getEncodingAndChannelConfigForPassthrough(format, audioCapabilities) != null; } /** - * Returns if the device supports gapless in offload playback. + * Returns the encoding and channel config to use when configuring an {@link AudioTrack} in + * passthrough mode for the specified {@link Format}. Returns {@code null} if passthrough of the + * format is unsupported. * - *

Gapless offload is not supported by all devices and there is no API to query its support. As - * a result this detection is currently based on manual testing. TODO(internal b/158191844): Add - * an SDK API to query offload gapless support. + * @param format The {@link Format}. + * @param audioCapabilities The device audio capabilities. + * @return The encoding and channel config to use, or {@code null} if passthrough of the format is + * unsupported. */ - private static boolean isOffloadGaplessSupported() { - return Util.SDK_INT >= 30 && Util.MODEL.startsWith("Pixel"); - } + @Nullable + private static Pair getEncodingAndChannelConfigForPassthrough( + Format format, @Nullable AudioCapabilities audioCapabilities) { + if (audioCapabilities == null) { + return null; + } - private static boolean isOffloadedPlayback(AudioTrack audioTrack) { - return Util.SDK_INT >= 29 && audioTrack.isOffloadedPlayback(); - } + @C.Encoding + int encoding = + MimeTypes.getEncoding(Assertions.checkNotNull(format.sampleMimeType), format.codecs); + // Check for encodings that are known to work for passthrough with the implementation in this + // class. This avoids trying to use passthrough with an encoding where the device/app reports + // it's capable but it is untested or known to be broken (for example AAC-LC). + boolean supportedEncoding = + encoding == C.ENCODING_AC3 + || encoding == C.ENCODING_E_AC3 + || encoding == C.ENCODING_E_AC3_JOC + || encoding == C.ENCODING_AC4 + || encoding == C.ENCODING_DTS + || encoding == C.ENCODING_DTS_HD + || encoding == C.ENCODING_DOLBY_TRUEHD; + if (!supportedEncoding) { + return null; + } - private static AudioTrack initializeKeepSessionIdAudioTrack(int audioSessionId) { - int sampleRate = 4000; // Equal to private AudioTrack.MIN_SAMPLE_RATE. - 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. - return new AudioTrack(C.STREAM_TYPE_DEFAULT, sampleRate, channelConfig, encoding, bufferSize, - MODE_STATIC, audioSessionId); + // E-AC3 JOC is object based, so any channel count specified in the format is arbitrary. Use 6, + // since the E-AC3 compatible part of the stream is 5.1. + int channelCount = encoding == C.ENCODING_E_AC3_JOC ? 6 : format.channelCount; + if (channelCount > audioCapabilities.getMaxChannelCount()) { + return null; + } + + int channelConfig = getChannelConfigForPassthrough(channelCount); + if (channelConfig == AudioFormat.CHANNEL_INVALID) { + return null; + } + + if (audioCapabilities.supportsEncoding(encoding)) { + return Pair.create(encoding, channelConfig); + } else if (encoding == C.ENCODING_E_AC3_JOC + && audioCapabilities.supportsEncoding(C.ENCODING_E_AC3)) { + // E-AC3 receivers support E-AC3 JOC streams (but decode in 2-D rather than 3-D). + return Pair.create(C.ENCODING_E_AC3, channelConfig); + } + + return null; } private static int getChannelConfigForPassthrough(int channelCount) { @@ -1354,6 +1375,60 @@ public final class DefaultAudioSink implements AudioSink { return Util.getAudioTrackChannelConfig(channelCount); } + private static boolean isOffloadedPlaybackSupported( + Format format, AudioAttributes audioAttributes) { + if (Util.SDK_INT < 29) { + return false; + } + @C.Encoding + int encoding = + MimeTypes.getEncoding(Assertions.checkNotNull(format.sampleMimeType), format.codecs); + if (encoding == C.ENCODING_INVALID) { + return false; + } + int channelConfig = Util.getAudioTrackChannelConfig(format.channelCount); + if (channelConfig == AudioFormat.CHANNEL_INVALID) { + return false; + } + AudioFormat audioFormat = getAudioFormat(format.sampleRate, channelConfig, encoding); + if (!AudioManager.isOffloadedPlaybackSupported( + audioFormat, audioAttributes.getAudioAttributesV21())) { + return false; + } + boolean notGapless = format.encoderDelay == 0 && format.encoderPadding == 0; + return notGapless || isOffloadedGaplessPlaybackSupported(); + } + + /** + * Returns whether the device supports gapless in offload playback. + * + *

Gapless offload is not supported by all devices and there is no API to query its support. As + * a result this detection is currently based on manual testing. + */ + // TODO(internal b/158191844): Add an SDK API to query offload gapless support. + private static boolean isOffloadedGaplessPlaybackSupported() { + return Util.SDK_INT >= 30 && Util.MODEL.startsWith("Pixel"); + } + + private static boolean isOffloadedPlayback(AudioTrack audioTrack) { + return Util.SDK_INT >= 29 && audioTrack.isOffloadedPlayback(); + } + + private static AudioTrack initializeKeepSessionIdAudioTrack(int audioSessionId) { + int sampleRate = 4000; // Equal to private AudioTrack.MIN_SAMPLE_RATE. + 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. + return new AudioTrack( + C.STREAM_TYPE_DEFAULT, + sampleRate, + channelConfig, + encoding, + bufferSize, + MODE_STATIC, + audioSessionId); + } + private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) { switch (encoding) { case C.ENCODING_MP3: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index 9e3fbe58f1..4af503e845 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -290,7 +290,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media @Override protected boolean usePassthrough(Format format) { - return getPassthroughEncoding(format) != C.ENCODING_INVALID; + @Nullable String mimeType = format.sampleMimeType; + if (MimeTypes.AUDIO_RAW.equals(mimeType)) { + // PCM passthrough is not yet supported. + return false; + } + return audioSink.supportsFormat(format); } @Override @@ -345,7 +350,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType) && oldFormat.channelCount == newFormat.channelCount && oldFormat.sampleRate == newFormat.sampleRate - && oldFormat.encoding == newFormat.encoding + && oldFormat.pcmEncoding == newFormat.pcmEncoding && oldFormat.initializationDataEquals(newFormat) && !MimeTypes.AUDIO_OPUS.equals(oldFormat.sampleMimeType); } @@ -394,21 +399,26 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media Format audioSinkInputFormat; @Nullable int[] channelMap = null; if (codecPassthroughFormat != null) { // Raw codec passthrough - audioSinkInputFormat = getFormatWithEncodingForPassthrough(codecPassthroughFormat); + audioSinkInputFormat = codecPassthroughFormat; } else if (getCodec() == null) { // Codec bypass passthrough - audioSinkInputFormat = getFormatWithEncodingForPassthrough(outputFormat); + audioSinkInputFormat = outputFormat; } else { MediaFormat mediaFormat = getCodec().getOutputFormat(); @C.PcmEncoding int pcmEncoding; if (mediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) { pcmEncoding = Util.getPcmEncoding(mediaFormat.getInteger(VIVO_BITS_PER_SAMPLE_KEY)); } else { - pcmEncoding = getPcmEncoding(outputFormat); + // If the format is anything other than PCM then we assume that the audio decoder will + // output 16-bit PCM. + pcmEncoding = + MimeTypes.AUDIO_RAW.equals(outputFormat.sampleMimeType) + ? outputFormat.pcmEncoding + : C.ENCODING_PCM_16BIT; } audioSinkInputFormat = new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_RAW) - .setEncoding(pcmEncoding) + .setPcmEncoding(pcmEncoding) .setEncoderDelay(outputFormat.encoderDelay) .setEncoderPadding(outputFormat.encoderPadding) .setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)) @@ -430,45 +440,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } } - /** - * Returns the {@link C.Encoding} constant to use for passthrough of the given format, or {@link - * C#ENCODING_INVALID} if passthrough is not possible. - * - * @param format The format for which to get the encoding. - * @return The {@link C.Encoding} corresponding to the format, or {@link C#ENCODING_INVALID} if - * the format is not supported. - */ - @C.Encoding - protected int getPassthroughEncoding(Format format) { - @Nullable String mimeType = format.sampleMimeType; - if (MimeTypes.AUDIO_RAW.equals(mimeType)) { - // PCM passthrough is not supported. - return C.ENCODING_INVALID; - } - if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) { - // E-AC3 JOC is object-based so the output channel count is arbitrary. - Format eAc3JocFormat = - format - .buildUpon() - .setChannelCount(Format.NO_VALUE) - .setEncoding(C.ENCODING_E_AC3_JOC) - .build(); - if (audioSink.supportsFormat(eAc3JocFormat)) { - return C.ENCODING_E_AC3_JOC; - } - // E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back. - mimeType = MimeTypes.AUDIO_E_AC3; - } - - @C.Encoding int encoding = MimeTypes.getEncoding(mimeType, format.codecs); - Format passthroughFormat = format.buildUpon().setEncoding(encoding).build(); - if (audioSink.supportsFormat(passthroughFormat)) { - return encoding; - } else { - return C.ENCODING_INVALID; - } - } - /** * Called when the audio session id becomes known. The default implementation is a no-op. One * reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in @@ -782,13 +753,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media } } - private Format getFormatWithEncodingForPassthrough(Format outputFormat) { - @C.Encoding int passthroughEncoding = getPassthroughEncoding(outputFormat); - // TODO(b/112299307): Passthrough can have become unavailable since usePassthrough was called. - Assertions.checkState(passthroughEncoding != C.ENCODING_INVALID); - return outputFormat.buildUpon().setEncoding(passthroughEncoding).build(); - } - /** * Returns whether the device's decoders are known to not support setting the codec operating * rate. @@ -832,15 +796,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media || Util.DEVICE.startsWith("ms01")); } - @C.PcmEncoding - private static int getPcmEncoding(Format format) { - // If the format is anything other than PCM then we assume that the audio decoder will output - // 16-bit PCM. - return MimeTypes.AUDIO_RAW.equals(format.sampleMimeType) - ? format.encoding - : C.ENCODING_PCM_16BIT; - } - private final class AudioSinkListener implements AudioSink.Listener { @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java index 535e917299..f392e770bd 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java @@ -88,7 +88,7 @@ public final class SilenceMediaSource extends BaseMediaSource { .setSampleMimeType(MimeTypes.AUDIO_RAW) .setChannelCount(CHANNEL_COUNT) .setSampleRate(SAMPLE_RATE_HZ) - .setEncoding(PCM_ENCODING) + .setPcmEncoding(PCM_ENCODING) .build(); private static final MediaItem MEDIA_ITEM = new MediaItem.Builder() diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java index b64b14e501..e42ec07837 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/DefaultAudioSinkTest.java @@ -24,6 +24,7 @@ import static org.robolectric.annotation.Config.TARGET_SDK; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.util.MimeTypes; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; @@ -213,7 +214,12 @@ public final class DefaultAudioSinkTest { AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES, new AudioProcessor[0], /* enableFloatOutput= */ false); - Format floatFormat = STEREO_44_1_FORMAT.buildUpon().setEncoding(C.ENCODING_PCM_FLOAT).build(); + Format floatFormat = + STEREO_44_1_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_RAW) + .setPcmEncoding(C.ENCODING_PCM_FLOAT) + .build(); assertThat(defaultAudioSink.getFormatSupport(floatFormat)) .isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING); } @@ -226,7 +232,12 @@ public final class DefaultAudioSinkTest { AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES, new AudioProcessor[0], /* enableFloatOutput= */ true); - Format floatFormat = STEREO_44_1_FORMAT.buildUpon().setEncoding(C.ENCODING_PCM_FLOAT).build(); + Format floatFormat = + STEREO_44_1_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_RAW) + .setPcmEncoding(C.ENCODING_PCM_FLOAT) + .build(); assertThat(defaultAudioSink.getFormatSupport(floatFormat)) .isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING); } @@ -239,14 +250,24 @@ public final class DefaultAudioSinkTest { AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES, new AudioProcessor[0], /* enableFloatOutput= */ true); - Format floatFormat = STEREO_44_1_FORMAT.buildUpon().setEncoding(C.ENCODING_PCM_FLOAT).build(); + Format floatFormat = + STEREO_44_1_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_RAW) + .setPcmEncoding(C.ENCODING_PCM_FLOAT) + .build(); assertThat(defaultAudioSink.getFormatSupport(floatFormat)) .isEqualTo(SINK_FORMAT_SUPPORTED_DIRECTLY); } @Test public void supportsFloatPcm() { - Format floatFormat = STEREO_44_1_FORMAT.buildUpon().setEncoding(C.ENCODING_PCM_FLOAT).build(); + Format floatFormat = + STEREO_44_1_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_RAW) + .setPcmEncoding(C.ENCODING_PCM_FLOAT) + .build(); assertThat(defaultAudioSink.supportsFormat(floatFormat)).isTrue(); } @@ -255,7 +276,12 @@ public final class DefaultAudioSinkTest { DefaultAudioSink defaultAudioSink = new DefaultAudioSink( new AudioCapabilities(new int[] {C.ENCODING_AAC_LC}, 2), new AudioProcessor[0]); - Format aacLcFormat = STEREO_44_1_FORMAT.buildUpon().setEncoding(C.ENCODING_AAC_LC).build(); + Format aacLcFormat = + STEREO_44_1_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_AAC) + .setPcmEncoding(C.ENCODING_AAC_LC) + .build(); assertThat(defaultAudioSink.supportsFormat(aacLcFormat)).isFalse(); } @@ -267,7 +293,8 @@ public final class DefaultAudioSinkTest { throws AudioSink.ConfigurationException { Format format = new Format.Builder() - .setEncoding(C.ENCODING_PCM_16BIT) + .setSampleMimeType(MimeTypes.AUDIO_RAW) + .setPcmEncoding(C.ENCODING_PCM_16BIT) .setChannelCount(channelCount) .setSampleRate(SAMPLE_RATE_44_1) .setEncoderDelay(trimStartFrames) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java index 081b591b1f..305cb5b54e 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java @@ -56,7 +56,7 @@ public class MediaCodecAudioRendererTest { private static final Format AUDIO_AAC = new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_AAC) - .setEncoding(C.ENCODING_PCM_16BIT) + .setPcmEncoding(C.ENCODING_PCM_16BIT) .setChannelCount(2) .setSampleRate(44100) .setEncoderDelay(100) @@ -274,7 +274,7 @@ public class MediaCodecAudioRendererTest { private static Format getAudioSinkFormat(Format inputFormat) { return new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_RAW) - .setEncoding(C.ENCODING_PCM_16BIT) + .setPcmEncoding(C.ENCODING_PCM_16BIT) .setChannelCount(inputFormat.channelCount) .setSampleRate(inputFormat.sampleRate) .setEncoderDelay(inputFormat.encoderDelay) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/C2Mp3TimestampTrackerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/C2Mp3TimestampTrackerTest.java index ca5ba9a878..1108b882e4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/C2Mp3TimestampTrackerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/C2Mp3TimestampTrackerTest.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.mediacodec; import static com.google.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.util.MimeTypes; @@ -34,7 +33,6 @@ public final class C2Mp3TimestampTrackerTest { private static final Format AUDIO_MP3 = new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_MPEG) - .setEncoding(C.ENCODING_PCM_16BIT) .setChannelCount(2) .setSampleRate(44_100) .build(); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index f566493ada..0d3965321c 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -2098,7 +2098,7 @@ public class MatroskaExtractor implements Extractor { formatBuilder .setChannelCount(channelCount) .setSampleRate(sampleRate) - .setEncoding(pcmEncoding); + .setPcmEncoding(pcmEncoding); } else if (MimeTypes.isVideo(mimeType)) { type = C.TRACK_TYPE_VIDEO; if (displayUnit == Track.DISPLAY_UNIT_PIXELS) { diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index de2f4b5d9b..1593b90bea 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -404,7 +404,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset; chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples; } - int fixedSampleSize = Util.getPcmFrameSize(track.format.encoding, track.format.channelCount); + int fixedSampleSize = + Util.getPcmFrameSize(track.format.pcmEncoding, track.format.channelCount); FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk( fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits); @@ -1325,7 +1326,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; .setCodecs(codecs) .setChannelCount(channelCount) .setSampleRate(sampleRate) - .setEncoding(pcmEncoding) + .setPcmEncoding(pcmEncoding) .setInitializationData( initializationData == null ? null : Collections.singletonList(initializationData)) .setDrmInitData(drmInitData) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java index ffd5a19e0d..1d7b6b9c6e 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavExtractor.java @@ -231,7 +231,7 @@ public final class WavExtractor implements Extractor { .setMaxInputSize(targetSampleSizeBytes) .setChannelCount(header.numChannels) .setSampleRate(header.frameRateHz) - .setEncoding(pcmEncoding) + .setPcmEncoding(pcmEncoding) .build(); } @@ -373,7 +373,7 @@ public final class WavExtractor implements Extractor { .setMaxInputSize(numOutputFramesToBytes(targetSampleSizeFrames, numChannels)) .setChannelCount(header.numChannels) .setSampleRate(header.frameRateHz) - .setEncoding(C.ENCODING_PCM_16BIT) + .setPcmEncoding(C.ENCODING_PCM_16BIT) .build(); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingAudioSink.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingAudioSink.java index 3ea2211ac2..ead5bd24d9 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingAudioSink.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingAudioSink.java @@ -57,7 +57,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum throws ConfigurationException { interceptedData.add( new DumpableConfiguration( - inputFormat.encoding, inputFormat.channelCount, inputFormat.sampleRate)); + inputFormat.pcmEncoding, inputFormat.channelCount, inputFormat.sampleRate)); super.configure(inputFormat, specifiedBufferSize, outputChannels); }