mirror of
https://github.com/samsonjs/media.git
synced 2026-04-20 13:45:47 +00:00
Propagate sample rate and format deeper
#exo-offload PiperOrigin-RevId: 310150780
This commit is contained in:
parent
8077fe1bf1
commit
efff7a9d22
10 changed files with 57 additions and 52 deletions
|
|
@ -143,12 +143,14 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer {
|
|||
|
||||
private boolean isOutputSupported(Format 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) {
|
||||
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;
|
||||
}
|
||||
switch (inputFormat.sampleMimeType) {
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer {
|
|||
new FlacStreamMetadata(format.initializationData.get(0), streamMetadataOffset);
|
||||
pcmEncoding = Util.getPcmEncoding(streamMetadata.bitsPerSample);
|
||||
}
|
||||
if (!supportsOutput(format.channelCount, pcmEncoding)) {
|
||||
if (!supportsOutput(format.channelCount, format.sampleRate, pcmEncoding)) {
|
||||
return FORMAT_UNSUPPORTED_SUBTYPE;
|
||||
} else if (format.drmInitData != null && format.exoMediaCryptoType == null) {
|
||||
return FORMAT_UNSUPPORTED_DRM;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer {
|
|||
if (!OpusLibrary.isAvailable()
|
||||
|| !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) {
|
||||
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;
|
||||
} else if (!drmIsSupported) {
|
||||
return FORMAT_UNSUPPORTED_DRM;
|
||||
|
|
|
|||
|
|
@ -177,10 +177,11 @@ public interface AudioSink {
|
|||
* Returns whether the sink supports the audio format.
|
||||
*
|
||||
* @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.
|
||||
* @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
|
||||
|
|
|
|||
|
|
@ -214,10 +214,11 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media
|
|||
/**
|
||||
* 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) {
|
||||
return audioSink.supportsOutput(channelCount, encoding);
|
||||
protected final boolean supportsOutput(
|
||||
int channelCount, int sampleRateHz, @C.Encoding int encoding) {
|
||||
return audioSink.supportsOutput(channelCount, sampleRateHz, encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
}
|
||||
|
||||
@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)) {
|
||||
// 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
|
||||
|
|
@ -446,7 +446,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
@C.Encoding int encoding = inputEncoding;
|
||||
boolean useFloatOutput =
|
||||
enableFloatOutput
|
||||
&& supportsOutput(inputChannelCount, C.ENCODING_PCM_FLOAT)
|
||||
&& supportsOutput(inputChannelCount, inputSampleRate, C.ENCODING_PCM_FLOAT)
|
||||
&& Util.isEncodingHighResolutionPcm(inputEncoding);
|
||||
AudioProcessor[] availableAudioProcessors =
|
||||
useFloatOutput ? toFloatPcmAvailableAudioProcessors : toIntPcmAvailableAudioProcessors;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
|
@ -34,8 +35,8 @@ public class ForwardingAudioSink implements AudioSink {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOutput(int channelCount, int encoding) {
|
||||
return sink.supportsOutput(channelCount, encoding);
|
||||
public boolean supportsOutput(int channelCount, int sampleRate, @C.Encoding int encoding) {
|
||||
return sink.supportsOutput(channelCount, sampleRate, encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
private boolean passthroughEnabled;
|
||||
private boolean codecNeedsDiscardChannelsWorkaround;
|
||||
private boolean codecNeedsEosBufferTimestampWorkaround;
|
||||
private android.media.MediaFormat passthroughMediaFormat;
|
||||
@Nullable private Format passthroughFormat;
|
||||
@Nullable private Format inputFormat;
|
||||
private long currentPositionUs;
|
||||
private boolean allowFirstBufferPositionDiscontinuity;
|
||||
|
|
@ -210,8 +210,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
@Capabilities
|
||||
protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
|
||||
throws DecoderQueryException {
|
||||
String mimeType = format.sampleMimeType;
|
||||
if (!MimeTypes.isAudio(mimeType)) {
|
||||
if (!MimeTypes.isAudio(format.sampleMimeType)) {
|
||||
return RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE);
|
||||
}
|
||||
@TunnelingSupport
|
||||
|
|
@ -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
|
||||
// decrypt the content. For passthrough of clear content we don't need a decoder at all.
|
||||
if (supportsFormatDrm
|
||||
&& usePassthrough(format.channelCount, mimeType)
|
||||
&& usePassthrough(format)
|
||||
&& (format.drmInitData == null || MediaCodecUtil.getPassthroughDecoderInfo() != null)) {
|
||||
return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
|
||||
}
|
||||
if ((MimeTypes.AUDIO_RAW.equals(mimeType)
|
||||
&& !audioSink.supportsOutput(format.channelCount, format.pcmEncoding))
|
||||
|| !audioSink.supportsOutput(format.channelCount, C.ENCODING_PCM_16BIT)) {
|
||||
if ((MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)
|
||||
&& !audioSink.supportsOutput(
|
||||
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.
|
||||
return RendererCapabilities.create(FORMAT_UNSUPPORTED_SUBTYPE);
|
||||
}
|
||||
|
|
@ -259,7 +260,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
if (mimeType == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (usePassthrough(format.channelCount, mimeType)) {
|
||||
if (usePassthrough(format)) {
|
||||
@Nullable MediaCodecInfo codecInfo = MediaCodecUtil.getPassthroughDecoderInfo();
|
||||
if (codecInfo != null) {
|
||||
return Collections.singletonList(codecInfo);
|
||||
|
|
@ -281,8 +282,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean usePassthrough(int channelCount, String mimeType) {
|
||||
return getPassthroughEncoding(channelCount, mimeType) != C.ENCODING_INVALID;
|
||||
protected boolean usePassthrough(Format format) {
|
||||
return getPassthroughEncoding(format) != C.ENCODING_INVALID;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -301,13 +302,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
MediaFormat mediaFormat =
|
||||
getMediaFormat(format, codecInfo.codecMimeType, codecMaxInputSize, codecOperatingRate);
|
||||
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
||||
if (passthroughEnabled) {
|
||||
// Store the input MIME type if we're using the passthrough codec.
|
||||
passthroughMediaFormat = mediaFormat;
|
||||
passthroughMediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
|
||||
} else {
|
||||
passthroughMediaFormat = null;
|
||||
}
|
||||
// Store the input MIME type if we're using the passthrough codec.
|
||||
passthroughFormat = passthroughEnabled ? format : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -394,23 +390,21 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
protected void onOutputMediaFormatChanged(MediaCodec codec, MediaFormat outputMediaFormat)
|
||||
throws ExoPlaybackException {
|
||||
@C.Encoding int encoding;
|
||||
MediaFormat mediaFormat;
|
||||
if (passthroughMediaFormat != null) {
|
||||
mediaFormat = passthroughMediaFormat;
|
||||
encoding =
|
||||
getPassthroughEncoding(
|
||||
mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT),
|
||||
mediaFormat.getString(MediaFormat.KEY_MIME));
|
||||
int channelCount;
|
||||
int sampleRate;
|
||||
if (passthroughFormat != null) {
|
||||
encoding = getPassthroughEncoding(passthroughFormat);
|
||||
channelCount = passthroughFormat.channelCount;
|
||||
sampleRate = passthroughFormat.sampleRate;
|
||||
} else {
|
||||
mediaFormat = outputMediaFormat;
|
||||
if (outputMediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) {
|
||||
encoding = Util.getPcmEncoding(outputMediaFormat.getInteger(VIVO_BITS_PER_SAMPLE_KEY));
|
||||
} else {
|
||||
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;
|
||||
if (codecNeedsDiscardChannelsWorkaround && channelCount == 6 && inputFormat.channelCount < 6) {
|
||||
channelMap = new int[inputFormat.channelCount];
|
||||
|
|
@ -435,8 +429,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
|
||||
@Override
|
||||
protected void onOutputPassthroughFormatChanged(Format outputFormat) throws ExoPlaybackException {
|
||||
@C.Encoding
|
||||
int encoding = getPassthroughEncoding(outputFormat.channelCount, outputFormat.sampleMimeType);
|
||||
@C.Encoding int encoding = getPassthroughEncoding(outputFormat);
|
||||
try {
|
||||
audioSink.configure(
|
||||
encoding,
|
||||
|
|
@ -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
|
||||
* 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(int channelCount, String mimeType) {
|
||||
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.
|
||||
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);
|
||||
}
|
||||
// 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
|
|||
}
|
||||
|
||||
@C.Encoding int encoding = MimeTypes.getEncoding(mimeType);
|
||||
if (audioSink.supportsOutput(channelCount, encoding)) {
|
||||
if (audioSink.supportsOutput(format.channelCount, format.sampleRate, encoding)) {
|
||||
return encoding;
|
||||
} else {
|
||||
return C.ENCODING_INVALID;
|
||||
|
|
|
|||
|
|
@ -578,8 +578,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
return;
|
||||
}
|
||||
|
||||
if (inputFormat.drmInitData == null
|
||||
&& usePassthrough(inputFormat.channelCount, inputFormat.sampleMimeType)) {
|
||||
if (inputFormat.drmInitData == null && usePassthrough(inputFormat)) {
|
||||
initPassthrough(inputFormat);
|
||||
return;
|
||||
}
|
||||
|
|
@ -632,12 +631,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||
/**
|
||||
* 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
|
||||
* not known.
|
||||
* @param mimeType The type of input media.
|
||||
* @param format The input {@link Format}.
|
||||
* @return Whether passthrough playback is supported.
|
||||
*/
|
||||
protected boolean usePassthrough(int channelCount, String mimeType) {
|
||||
protected boolean usePassthrough(Format format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,14 +200,18 @@ public final class DefaultAudioSinkTest {
|
|||
@Config(minSdk = OLDEST_SDK, maxSdk = 20)
|
||||
@Test
|
||||
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();
|
||||
}
|
||||
|
||||
@Config(minSdk = 21, maxSdk = TARGET_SDK)
|
||||
@Test
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue