Use "Passthrough" terminology only for non-offload direct playback
The term "passthrough" was heavily overloaded. For clarity, split most of its usage to different terms: * codec "bypass": no MediaCodec is used * "direct playback": no decoding occurs (but decryption may or may not) * "decrypt only codec": a MediaCodec used only to decrypt, not decode * "offload": playback to an offload AudioTrack. * "passthrough" is now only used in the sense of playing encoded audio * to a non offload AudioTrack. PiperOrigin-RevId: 324984612
This commit is contained in:
parent
6e11d32092
commit
d2e50e40dc
@ -90,7 +90,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
private int codecMaxInputSize;
|
private int codecMaxInputSize;
|
||||||
private boolean codecNeedsDiscardChannelsWorkaround;
|
private boolean codecNeedsDiscardChannelsWorkaround;
|
||||||
private boolean codecNeedsEosBufferTimestampWorkaround;
|
private boolean codecNeedsEosBufferTimestampWorkaround;
|
||||||
@Nullable private Format codecPassthroughFormat;
|
/** Codec used for DRM decryption only in passthrough and offload. */
|
||||||
|
@Nullable private Format decryptOnlyCodecFormat;
|
||||||
|
|
||||||
private long currentPositionUs;
|
private long currentPositionUs;
|
||||||
private boolean allowFirstBufferPositionDiscontinuity;
|
private boolean allowFirstBufferPositionDiscontinuity;
|
||||||
private boolean allowPositionDiscontinuity;
|
private boolean allowPositionDiscontinuity;
|
||||||
@ -214,11 +216,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
||||||
boolean formatHasDrm = format.exoMediaCryptoType != null;
|
boolean formatHasDrm = format.exoMediaCryptoType != null;
|
||||||
boolean supportsFormatDrm = supportsFormatDrm(format);
|
boolean supportsFormatDrm = supportsFormatDrm(format);
|
||||||
// In passthrough mode, if the format needs decryption then we need to use a passthrough
|
// In direct mode, if the format has DRM then we need to use a decoder that only decrypts.
|
||||||
// decoder. Else we don't don't need a decoder at all.
|
// Else we don't don't need a decoder at all.
|
||||||
if (supportsFormatDrm
|
if (supportsFormatDrm
|
||||||
&& usePassthrough(format)
|
&& isDirectPlaybackSupported(format)
|
||||||
&& (!formatHasDrm || MediaCodecUtil.getPassthroughDecoderInfo() != null)) {
|
&& (!formatHasDrm || MediaCodecUtil.getDecryptOnlyDecoderInfo() != null)) {
|
||||||
return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
|
return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
|
||||||
}
|
}
|
||||||
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
|
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
|
||||||
@ -260,8 +262,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
if (usePassthrough(format)) {
|
if (isDirectPlaybackSupported(format)) {
|
||||||
@Nullable MediaCodecInfo codecInfo = MediaCodecUtil.getPassthroughDecoderInfo();
|
// The format is supported directly, so a codec is only needed for decryption.
|
||||||
|
@Nullable MediaCodecInfo codecInfo = MediaCodecUtil.getDecryptOnlyDecoderInfo();
|
||||||
if (codecInfo != null) {
|
if (codecInfo != null) {
|
||||||
return Collections.singletonList(codecInfo);
|
return Collections.singletonList(codecInfo);
|
||||||
}
|
}
|
||||||
@ -282,13 +285,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean usePassthrough(Format format) {
|
protected boolean shouldUseBypass(Format format) {
|
||||||
@Nullable String mimeType = format.sampleMimeType;
|
return isDirectPlaybackSupported(format);
|
||||||
if (MimeTypes.AUDIO_RAW.equals(mimeType)) {
|
|
||||||
// PCM passthrough is not yet supported.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return audioSink.supportsFormat(format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -304,11 +302,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
MediaFormat mediaFormat =
|
MediaFormat mediaFormat =
|
||||||
getMediaFormat(format, codecInfo.codecMimeType, codecMaxInputSize, codecOperatingRate);
|
getMediaFormat(format, codecInfo.codecMimeType, codecMaxInputSize, codecOperatingRate);
|
||||||
codecAdapter.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
codecAdapter.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
|
||||||
// Store the input MIME type if we're using the passthrough codec.
|
// Store the input MIME type if we're only using the codec for decryption.
|
||||||
boolean codecPassthroughEnabled =
|
boolean decryptOnlyCodecEnabled =
|
||||||
MimeTypes.AUDIO_RAW.equals(codecInfo.mimeType)
|
MimeTypes.AUDIO_RAW.equals(codecInfo.mimeType)
|
||||||
&& !MimeTypes.AUDIO_RAW.equals(format.sampleMimeType);
|
&& !MimeTypes.AUDIO_RAW.equals(format.sampleMimeType);
|
||||||
codecPassthroughFormat = codecPassthroughEnabled ? format : null;
|
decryptOnlyCodecFormat = decryptOnlyCodecEnabled ? format : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -386,9 +384,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
Format audioSinkInputFormat;
|
Format audioSinkInputFormat;
|
||||||
@Nullable int[] channelMap = null;
|
@Nullable int[] channelMap = null;
|
||||||
if (codecPassthroughFormat != null) { // Raw codec passthrough
|
if (decryptOnlyCodecFormat != null) { // Direct playback with a codec for decryption.
|
||||||
audioSinkInputFormat = codecPassthroughFormat;
|
audioSinkInputFormat = decryptOnlyCodecFormat;
|
||||||
} else if (getCodec() == null) { // Codec bypass passthrough
|
} else if (getCodec() == null) { // Direct playback with codec bypass.
|
||||||
audioSinkInputFormat = format;
|
audioSinkInputFormat = format;
|
||||||
} else {
|
} else {
|
||||||
@C.PcmEncoding int pcmEncoding;
|
@C.PcmEncoding int pcmEncoding;
|
||||||
@ -578,7 +576,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
bufferPresentationTimeUs = getLargestQueuedPresentationTimeUs();
|
bufferPresentationTimeUs = getLargestQueuedPresentationTimeUs();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codecPassthroughFormat != null
|
if (decryptOnlyCodecFormat != null
|
||||||
&& (bufferFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
&& (bufferFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||||
// Discard output buffers from the passthrough (raw) decoder containing codec specific data.
|
// Discard output buffers from the passthrough (raw) decoder containing codec specific data.
|
||||||
checkNotNull(codec).releaseOutputBuffer(bufferIndex, false);
|
checkNotNull(codec).releaseOutputBuffer(bufferIndex, false);
|
||||||
@ -677,6 +675,16 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
return maxInputSize;
|
return maxInputSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns if the format can be played as is to the audio sink. */
|
||||||
|
private boolean isDirectPlaybackSupported(Format format) {
|
||||||
|
@Nullable String mimeType = format.sampleMimeType;
|
||||||
|
if (MimeTypes.AUDIO_RAW.equals(mimeType)) {
|
||||||
|
// Decoding bypass for PCM is not yet supported.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return audioSink.supportsFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a maximum input buffer size for a given {@link Format}.
|
* Returns a maximum input buffer size for a given {@link Format}.
|
||||||
*
|
*
|
||||||
|
@ -537,7 +537,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceDrmSession == null && usePassthrough(inputFormat)) {
|
if (sourceDrmSession == null && shouldUseBypass(inputFormat)) {
|
||||||
initBypass(inputFormat);
|
initBypass(inputFormat);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -588,12 +588,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether encoded passthrough should be used for playing back the input format.
|
* Returns whether buffers in the input format can be processed without a codec.
|
||||||
|
*
|
||||||
|
* <p>This method is only called if the content is not DRM protected, because if the content is
|
||||||
|
* DRM protected use of bypass is never possible.
|
||||||
*
|
*
|
||||||
* @param format The input {@link Format}.
|
* @param format The input {@link Format}.
|
||||||
* @return Whether passthrough playback is supported.
|
* @return Whether playback bypassing {@link MediaCodec} is supported.
|
||||||
*/
|
*/
|
||||||
protected boolean usePassthrough(Format format) {
|
protected boolean shouldUseBypass(Format format) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,13 +106,13 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns information about a decoder suitable for audio passthrough.
|
* Returns information about a decoder that will only decrypt data, without decoding it.
|
||||||
*
|
*
|
||||||
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder exists.
|
* @return A {@link MediaCodecInfo} describing the decoder, or null if no suitable decoder exists.
|
||||||
* @throws DecoderQueryException If there was an error querying the available decoders.
|
* @throws DecoderQueryException If there was an error querying the available decoders.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
public static MediaCodecInfo getDecryptOnlyDecoderInfo() throws DecoderQueryException {
|
||||||
return getDecoderInfo(MimeTypes.AUDIO_RAW, /* secure= */ false, /* tunneling= */ false);
|
return getDecoderInfo(MimeTypes.AUDIO_RAW, /* secure= */ false, /* tunneling= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user