Commit efff7a9d by krocard Committed by Oliver Woodman

Propagate sample rate and format deeper

#exo-offload

PiperOrigin-RevId: 310150780
parent 8077fe1b
...@@ -143,12 +143,14 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { ...@@ -143,12 +143,14 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
private boolean isOutputSupported(Format inputFormat) { private boolean isOutputSupported(Format inputFormat) {
return shouldUseFloatOutput(inputFormat) return shouldUseFloatOutput(inputFormat)
|| supportsOutput(inputFormat.channelCount, C.ENCODING_PCM_16BIT); || supportsOutput(inputFormat.channelCount, inputFormat.sampleRate, C.ENCODING_PCM_16BIT);
} }
private boolean shouldUseFloatOutput(Format inputFormat) { private boolean shouldUseFloatOutput(Format inputFormat) {
Assertions.checkNotNull(inputFormat.sampleMimeType); Assertions.checkNotNull(inputFormat.sampleMimeType);
if (!enableFloatOutput || !supportsOutput(inputFormat.channelCount, C.ENCODING_PCM_FLOAT)) { if (!enableFloatOutput
|| !supportsOutput(
inputFormat.channelCount, inputFormat.sampleRate, C.ENCODING_PCM_FLOAT)) {
return false; return false;
} }
switch (inputFormat.sampleMimeType) { switch (inputFormat.sampleMimeType) {
......
...@@ -100,7 +100,7 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer { ...@@ -100,7 +100,7 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer {
new FlacStreamMetadata(format.initializationData.get(0), streamMetadataOffset); new FlacStreamMetadata(format.initializationData.get(0), streamMetadataOffset);
pcmEncoding = Util.getPcmEncoding(streamMetadata.bitsPerSample); pcmEncoding = Util.getPcmEncoding(streamMetadata.bitsPerSample);
} }
if (!supportsOutput(format.channelCount, pcmEncoding)) { if (!supportsOutput(format.channelCount, format.sampleRate, pcmEncoding)) {
return FORMAT_UNSUPPORTED_SUBTYPE; return FORMAT_UNSUPPORTED_SUBTYPE;
} else if (format.drmInitData != null && format.exoMediaCryptoType == null) { } else if (format.drmInitData != null && format.exoMediaCryptoType == null) {
return FORMAT_UNSUPPORTED_DRM; return FORMAT_UNSUPPORTED_DRM;
......
...@@ -69,7 +69,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { ...@@ -69,7 +69,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer {
if (!OpusLibrary.isAvailable() if (!OpusLibrary.isAvailable()
|| !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) { || !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) {
return FORMAT_UNSUPPORTED_TYPE; return FORMAT_UNSUPPORTED_TYPE;
} else if (!supportsOutput(format.channelCount, C.ENCODING_PCM_16BIT)) { } else if (!supportsOutput(format.channelCount, format.sampleRate, C.ENCODING_PCM_16BIT)) {
return FORMAT_UNSUPPORTED_SUBTYPE; return FORMAT_UNSUPPORTED_SUBTYPE;
} else if (!drmIsSupported) { } else if (!drmIsSupported) {
return FORMAT_UNSUPPORTED_DRM; return FORMAT_UNSUPPORTED_DRM;
......
...@@ -177,10 +177,11 @@ public interface AudioSink { ...@@ -177,10 +177,11 @@ public interface AudioSink {
* Returns whether the sink supports the audio format. * Returns whether the sink supports the audio format.
* *
* @param channelCount The number of channels, or {@link Format#NO_VALUE} if not known. * @param channelCount The number of channels, or {@link Format#NO_VALUE} if not known.
* @param sampleRate The sample rate, or {@link Format#NO_VALUE} if not known.
* @param encoding The audio encoding, or {@link Format#NO_VALUE} if not known. * @param encoding The audio encoding, or {@link Format#NO_VALUE} if not known.
* @return Whether the sink supports the audio format. * @return Whether the sink supports the audio format.
*/ */
boolean supportsOutput(int channelCount, @C.Encoding int encoding); boolean supportsOutput(int channelCount, int sampleRate, @C.Encoding int encoding);
/** /**
* Returns the playback position in the stream starting at zero, in microseconds, or * Returns the playback position in the stream starting at zero, in microseconds, or
......
...@@ -214,10 +214,11 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media ...@@ -214,10 +214,11 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media
/** /**
* Returns whether the sink supports the audio format. * Returns whether the sink supports the audio format.
* *
* @see AudioSink#supportsOutput(int, int) * @see AudioSink#supportsOutput(int, int, int)
*/ */
protected final boolean supportsOutput(int channelCount, @C.Encoding int encoding) { protected final boolean supportsOutput(
return audioSink.supportsOutput(channelCount, encoding); int channelCount, int sampleRateHz, @C.Encoding int encoding) {
return audioSink.supportsOutput(channelCount, sampleRateHz, encoding);
} }
@Override @Override
......
...@@ -395,7 +395,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -395,7 +395,7 @@ public final class DefaultAudioSink implements AudioSink {
} }
@Override @Override
public boolean supportsOutput(int channelCount, @C.Encoding int encoding) { public boolean supportsOutput(int channelCount, int sampleRateHz, @C.Encoding int encoding) {
if (Util.isEncodingLinearPcm(encoding)) { if (Util.isEncodingLinearPcm(encoding)) {
// AudioTrack supports 16-bit integer PCM output in all platform API versions, and float // AudioTrack supports 16-bit integer PCM output in all platform API versions, and float
// output from platform API version 21 only. Other integer PCM encodings are resampled by this // output from platform API version 21 only. Other integer PCM encodings are resampled by this
...@@ -446,7 +446,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -446,7 +446,7 @@ public final class DefaultAudioSink implements AudioSink {
@C.Encoding int encoding = inputEncoding; @C.Encoding int encoding = inputEncoding;
boolean useFloatOutput = boolean useFloatOutput =
enableFloatOutput enableFloatOutput
&& supportsOutput(inputChannelCount, C.ENCODING_PCM_FLOAT) && supportsOutput(inputChannelCount, inputSampleRate, C.ENCODING_PCM_FLOAT)
&& Util.isEncodingHighResolutionPcm(inputEncoding); && Util.isEncodingHighResolutionPcm(inputEncoding);
AudioProcessor[] availableAudioProcessors = AudioProcessor[] availableAudioProcessors =
useFloatOutput ? toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors; useFloatOutput ? toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.audio; package com.google.android.exoplayer2.audio;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -34,8 +35,8 @@ public class ForwardingAudioSink implements AudioSink { ...@@ -34,8 +35,8 @@ public class ForwardingAudioSink implements AudioSink {
} }
@Override @Override
public boolean supportsOutput(int channelCount, int encoding) { public boolean supportsOutput(int channelCount, int sampleRate, @C.Encoding int encoding) {
return sink.supportsOutput(channelCount, encoding); return sink.supportsOutput(channelCount, sampleRate, encoding);
} }
@Override @Override
......
...@@ -85,7 +85,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -85,7 +85,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private boolean passthroughEnabled; private boolean passthroughEnabled;
private boolean codecNeedsDiscardChannelsWorkaround; private boolean codecNeedsDiscardChannelsWorkaround;
private boolean codecNeedsEosBufferTimestampWorkaround; private boolean codecNeedsEosBufferTimestampWorkaround;
private android.media.MediaFormat passthroughMediaFormat; @Nullable private Format passthroughFormat;
@Nullable private Format inputFormat; @Nullable private Format inputFormat;
private long currentPositionUs; private long currentPositionUs;
private boolean allowFirstBufferPositionDiscontinuity; private boolean allowFirstBufferPositionDiscontinuity;
...@@ -210,8 +210,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -210,8 +210,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Capabilities @Capabilities
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
throws DecoderQueryException { throws DecoderQueryException {
String mimeType = format.sampleMimeType; if (!MimeTypes.isAudio(format.sampleMimeType)) {
if (!MimeTypes.isAudio(mimeType)) {
return RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE); return RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE);
} }
@TunnelingSupport @TunnelingSupport
...@@ -220,13 +219,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -220,13 +219,15 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// In passthrough mode, if DRM init data is present we need to use a passthrough decoder to // In passthrough mode, if DRM init data is present we need to use a passthrough decoder to
// decrypt the content. For passthrough of clear content we don't need a decoder at all. // decrypt the content. For passthrough of clear content we don't need a decoder at all.
if (supportsFormatDrm if (supportsFormatDrm
&& usePassthrough(format.channelCount, mimeType) && usePassthrough(format)
&& (format.drmInitData == null || MediaCodecUtil.getPassthroughDecoderInfo() != null)) { && (format.drmInitData == null || MediaCodecUtil.getPassthroughDecoderInfo() != null)) {
return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport); return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
} }
if ((MimeTypes.AUDIO_RAW.equals(mimeType) if ((MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)
&& !audioSink.supportsOutput(format.channelCount, format.pcmEncoding)) && !audioSink.supportsOutput(
|| !audioSink.supportsOutput(format.channelCount, C.ENCODING_PCM_16BIT)) { format.channelCount, format.sampleRate, format.pcmEncoding))
|| !audioSink.supportsOutput(
format.channelCount, format.sampleRate, C.ENCODING_PCM_16BIT)) {
// Assume the decoder outputs 16-bit PCM, unless the input is raw. // Assume the decoder outputs 16-bit PCM, unless the input is raw.
return RendererCapabilities.create(FORMAT_UNSUPPORTED_SUBTYPE); return RendererCapabilities.create(FORMAT_UNSUPPORTED_SUBTYPE);
} }
...@@ -259,7 +260,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -259,7 +260,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (mimeType == null) { if (mimeType == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
if (usePassthrough(format.channelCount, mimeType)) { if (usePassthrough(format)) {
@Nullable MediaCodecInfo codecInfo = MediaCodecUtil.getPassthroughDecoderInfo(); @Nullable MediaCodecInfo codecInfo = MediaCodecUtil.getPassthroughDecoderInfo();
if (codecInfo != null) { if (codecInfo != null) {
return Collections.singletonList(codecInfo); return Collections.singletonList(codecInfo);
...@@ -281,8 +282,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -281,8 +282,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@Override @Override
protected boolean usePassthrough(int channelCount, String mimeType) { protected boolean usePassthrough(Format format) {
return getPassthroughEncoding(channelCount, mimeType) != C.ENCODING_INVALID; return getPassthroughEncoding(format) != C.ENCODING_INVALID;
} }
@Override @Override
...@@ -301,13 +302,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -301,13 +302,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
MediaFormat mediaFormat = MediaFormat mediaFormat =
getMediaFormat(format, codecInfo.codecMimeType, codecMaxInputSize, codecOperatingRate); getMediaFormat(format, codecInfo.codecMimeType, codecMaxInputSize, codecOperatingRate);
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0); codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
if (passthroughEnabled) { // Store the input MIME type if we're using the passthrough codec.
// Store the input MIME type if we're using the passthrough codec. passthroughFormat = passthroughEnabled ? format : null;
passthroughMediaFormat = mediaFormat;
passthroughMediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
} else {
passthroughMediaFormat = null;
}
} }
@Override @Override
...@@ -394,23 +390,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -394,23 +390,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
protected void onOutputMediaFormatChanged(MediaCodec codec, MediaFormat outputMediaFormat) protected void onOutputMediaFormatChanged(MediaCodec codec, MediaFormat outputMediaFormat)
throws ExoPlaybackException { throws ExoPlaybackException {
@C.Encoding int encoding; @C.Encoding int encoding;
MediaFormat mediaFormat; int channelCount;
if (passthroughMediaFormat != null) { int sampleRate;
mediaFormat = passthroughMediaFormat; if (passthroughFormat != null) {
encoding = encoding = getPassthroughEncoding(passthroughFormat);
getPassthroughEncoding( channelCount = passthroughFormat.channelCount;
mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT), sampleRate = passthroughFormat.sampleRate;
mediaFormat.getString(MediaFormat.KEY_MIME));
} else { } else {
mediaFormat = outputMediaFormat;
if (outputMediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) { if (outputMediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) {
encoding = Util.getPcmEncoding(outputMediaFormat.getInteger(VIVO_BITS_PER_SAMPLE_KEY)); encoding = Util.getPcmEncoding(outputMediaFormat.getInteger(VIVO_BITS_PER_SAMPLE_KEY));
} else { } else {
encoding = getPcmEncoding(inputFormat); encoding = getPcmEncoding(inputFormat);
} }
channelCount = outputMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
sampleRate = outputMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
} }
int channelCount = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
int sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
@Nullable int[] channelMap = null; @Nullable int[] channelMap = null;
if (codecNeedsDiscardChannelsWorkaround && channelCount == 6 && inputFormat.channelCount < 6) { if (codecNeedsDiscardChannelsWorkaround && channelCount == 6 && inputFormat.channelCount < 6) {
channelMap = new int[inputFormat.channelCount]; channelMap = new int[inputFormat.channelCount];
...@@ -435,8 +429,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -435,8 +429,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onOutputPassthroughFormatChanged(Format outputFormat) throws ExoPlaybackException { protected void onOutputPassthroughFormatChanged(Format outputFormat) throws ExoPlaybackException {
@C.Encoding @C.Encoding int encoding = getPassthroughEncoding(outputFormat);
int encoding = getPassthroughEncoding(outputFormat.channelCount, outputFormat.sampleMimeType);
try { try {
audioSink.configure( audioSink.configure(
encoding, encoding,
...@@ -454,16 +447,22 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -454,16 +447,22 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
/** /**
* Returns the {@link C.Encoding} constant to use for passthrough of the given format, or {@link * Returns the {@link C.Encoding} constant to use for passthrough of the given format, or {@link
* C#ENCODING_INVALID} if passthrough is not possible. * 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 @C.Encoding
protected int getPassthroughEncoding(int channelCount, String mimeType) { protected int getPassthroughEncoding(Format format) {
@Nullable String mimeType = format.sampleMimeType;
if (MimeTypes.AUDIO_RAW.equals(mimeType)) { if (MimeTypes.AUDIO_RAW.equals(mimeType)) {
// PCM passthrough is not supported. // PCM passthrough is not supported.
return C.ENCODING_INVALID; return C.ENCODING_INVALID;
} }
if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) { if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) {
// E-AC3 JOC is object-based so the output channel count is arbitrary. // E-AC3 JOC is object-based so the output channel count is arbitrary.
if (audioSink.supportsOutput(/* channelCount= */ Format.NO_VALUE, C.ENCODING_E_AC3_JOC)) { if (audioSink.supportsOutput(
/* channelCount= */ Format.NO_VALUE, format.sampleRate, C.ENCODING_E_AC3_JOC)) {
return MimeTypes.getEncoding(MimeTypes.AUDIO_E_AC3_JOC); return MimeTypes.getEncoding(MimeTypes.AUDIO_E_AC3_JOC);
} }
// E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back. // E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back.
...@@ -471,7 +470,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -471,7 +470,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@C.Encoding int encoding = MimeTypes.getEncoding(mimeType); @C.Encoding int encoding = MimeTypes.getEncoding(mimeType);
if (audioSink.supportsOutput(channelCount, encoding)) { if (audioSink.supportsOutput(format.channelCount, format.sampleRate, encoding)) {
return encoding; return encoding;
} else { } else {
return C.ENCODING_INVALID; return C.ENCODING_INVALID;
......
...@@ -578,8 +578,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -578,8 +578,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return; return;
} }
if (inputFormat.drmInitData == null if (inputFormat.drmInitData == null && usePassthrough(inputFormat)) {
&& usePassthrough(inputFormat.channelCount, inputFormat.sampleMimeType)) {
initPassthrough(inputFormat); initPassthrough(inputFormat);
return; return;
} }
...@@ -632,12 +631,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -632,12 +631,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
/** /**
* Returns whether encoded passthrough should be used for playing back the input format. * Returns whether encoded passthrough should be used for playing back the input format.
* *
* @param channelCount The number of channels in the input media, or {@link Format#NO_VALUE} if * @param format The input {@link Format}.
* not known.
* @param mimeType The type of input media.
* @return Whether passthrough playback is supported. * @return Whether passthrough playback is supported.
*/ */
protected boolean usePassthrough(int channelCount, String mimeType) { protected boolean usePassthrough(Format format) {
return false; return false;
} }
......
...@@ -200,14 +200,18 @@ public final class DefaultAudioSinkTest { ...@@ -200,14 +200,18 @@ public final class DefaultAudioSinkTest {
@Config(minSdk = OLDEST_SDK, maxSdk = 20) @Config(minSdk = OLDEST_SDK, maxSdk = 20)
@Test @Test
public void doesNotSupportFloatOutputBeforeApi21() { public void doesNotSupportFloatOutputBeforeApi21() {
assertThat(defaultAudioSink.supportsOutput(CHANNEL_COUNT_STEREO, C.ENCODING_PCM_FLOAT)) assertThat(
defaultAudioSink.supportsOutput(
CHANNEL_COUNT_STEREO, SAMPLE_RATE_44_1, C.ENCODING_PCM_FLOAT))
.isFalse(); .isFalse();
} }
@Config(minSdk = 21, maxSdk = TARGET_SDK) @Config(minSdk = 21, maxSdk = TARGET_SDK)
@Test @Test
public void supportsFloatOutputFromApi21() { public void supportsFloatOutputFromApi21() {
assertThat(defaultAudioSink.supportsOutput(CHANNEL_COUNT_STEREO, C.ENCODING_PCM_FLOAT)) assertThat(
defaultAudioSink.supportsOutput(
CHANNEL_COUNT_STEREO, SAMPLE_RATE_44_1, C.ENCODING_PCM_FLOAT))
.isTrue(); .isTrue();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment