Enable passthrough based on the input MIME type.
This commit is contained in:
parent
a1083d360a
commit
3360f5eda5
@ -59,7 +59,6 @@ import com.google.android.exoplayer.util.Util;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioFormat;
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.UnsupportedSchemeException;
|
import android.media.UnsupportedSchemeException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -249,7 +248,6 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
// Build the audio chunk sources.
|
// Build the audio chunk sources.
|
||||||
List<ChunkSource> audioChunkSourceList = new ArrayList<ChunkSource>();
|
List<ChunkSource> audioChunkSourceList = new ArrayList<ChunkSource>();
|
||||||
List<String> audioTrackNameList = new ArrayList<String>();
|
List<String> audioTrackNameList = new ArrayList<String>();
|
||||||
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
|
|
||||||
if (audioAdaptationSet != null) {
|
if (audioAdaptationSet != null) {
|
||||||
DataSource audioDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter);
|
DataSource audioDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter);
|
||||||
FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator();
|
FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator();
|
||||||
@ -275,7 +273,6 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioEncoding = encoding;
|
|
||||||
for (int j = audioRepresentations.size() - 1; j >= 0; j--) {
|
for (int j = audioRepresentations.size() - 1; j >= 0; j--) {
|
||||||
if (!audioRepresentations.get(j).format.codecs.equals(codec)) {
|
if (!audioRepresentations.get(j).format.codecs.equals(codec)) {
|
||||||
audioTrackNameList.remove(j);
|
audioTrackNameList.remove(j);
|
||||||
@ -303,7 +300,7 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player,
|
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player,
|
||||||
DemoPlayer.TYPE_AUDIO);
|
DemoPlayer.TYPE_AUDIO);
|
||||||
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, drmSessionManager, true,
|
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, drmSessionManager, true,
|
||||||
mainHandler, player, audioEncoding);
|
mainHandler, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the text chunk sources.
|
// Build the text chunk sources.
|
||||||
|
@ -21,9 +21,7 @@ import com.google.android.exoplayer.drm.DrmSessionManager;
|
|||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.media.AudioFormat;
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaFormat;
|
|
||||||
import android.media.audiofx.Virtualizer;
|
import android.media.audiofx.Virtualizer;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
||||||
@ -71,7 +69,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
|
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
private final AudioTrack audioTrack;
|
private final AudioTrack audioTrack;
|
||||||
private final int encoding;
|
|
||||||
|
|
||||||
private int audioSessionId;
|
private int audioSessionId;
|
||||||
private long currentPositionUs;
|
private long currentPositionUs;
|
||||||
@ -124,50 +121,27 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
*/
|
*/
|
||||||
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
|
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
|
||||||
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
|
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
|
||||||
this(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener,
|
|
||||||
AudioFormat.ENCODING_PCM_16BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param source The upstream source from which the renderer obtains samples.
|
|
||||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
|
||||||
* content is not required.
|
|
||||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
|
||||||
* For example a media file may start with a short clear region so as to allow playback to
|
|
||||||
* begin in parallel with key acquisision. This parameter specifies whether the renderer is
|
|
||||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
|
||||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
|
||||||
* null if delivery of events is not required.
|
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
* @param encoding One of the {@code AudioFormat.ENCODING_*} constants specifying the audio
|
|
||||||
* encoding.
|
|
||||||
*/
|
|
||||||
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
|
|
||||||
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener,
|
|
||||||
int encoding) {
|
|
||||||
super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
|
super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
this.audioTrack = new AudioTrack();
|
this.audioTrack = new AudioTrack();
|
||||||
this.encoding = encoding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||||
throws DecoderQueryException {
|
throws DecoderQueryException {
|
||||||
if (encoding == AudioFormat.ENCODING_AC3 || encoding == AudioFormat.ENCODING_E_AC3) {
|
if (MimeTypes.isPassthroughAudio(mimeType)) {
|
||||||
return new DecoderInfo(RAW_DECODER_NAME, true);
|
return new DecoderInfo(RAW_DECODER_NAME, true);
|
||||||
}
|
}
|
||||||
return super.getDecoderInfo(mimeType, requiresSecureDecoder);
|
return super.getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureCodec(MediaCodec codec, String codecName, MediaFormat format,
|
protected void configureCodec(MediaCodec codec, String codecName,
|
||||||
android.media.MediaCrypto crypto) {
|
android.media.MediaFormat format, android.media.MediaCrypto crypto) {
|
||||||
if (RAW_DECODER_NAME.equals(codecName)) {
|
if (RAW_DECODER_NAME.equals(codecName)) {
|
||||||
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
|
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
|
||||||
String mimeType = format.getString(MediaFormat.KEY_MIME);
|
String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
|
||||||
format.setString(android.media.MediaFormat.KEY_MIME, MimeTypes.AUDIO_RAW);
|
format.setString(android.media.MediaFormat.KEY_MIME, MimeTypes.AUDIO_RAW);
|
||||||
codec.configure(format, null, crypto, 0);
|
codec.configure(format, null, crypto, 0);
|
||||||
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
|
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
|
||||||
@ -193,8 +167,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onOutputFormatChanged(MediaFormat format) {
|
protected void onOutputFormatChanged(MediaFormat inputFormat,
|
||||||
audioTrack.reconfigure(format, encoding, 0);
|
android.media.MediaFormat outputFormat) {
|
||||||
|
if (MimeTypes.isPassthroughAudio(inputFormat.mimeType)) {
|
||||||
|
audioTrack.reconfigure(inputFormat.getFrameworkMediaFormatV16());
|
||||||
|
} else {
|
||||||
|
audioTrack.reconfigure(outputFormat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
* <p>
|
* <p>
|
||||||
* The default implementation is a no-op.
|
* The default implementation is a no-op.
|
||||||
*
|
*
|
||||||
* @param format The new output format.
|
* @param inputFormat The format of media input to the codec.
|
||||||
|
* @param outputFormat The new output format.
|
||||||
*/
|
*/
|
||||||
protected void onOutputFormatChanged(android.media.MediaFormat format) {
|
protected void onOutputFormatChanged(MediaFormat inputFormat,
|
||||||
|
android.media.MediaFormat outputFormat) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,7 +820,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||||
onOutputFormatChanged(codec.getOutputFormat());
|
onOutputFormatChanged(format, codec.getOutputFormat());
|
||||||
codecCounters.outputFormatChangedCount++;
|
codecCounters.outputFormatChangedCount++;
|
||||||
return true;
|
return true;
|
||||||
} else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
} else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||||
|
@ -381,15 +381,17 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onOutputFormatChanged(android.media.MediaFormat format) {
|
protected void onOutputFormatChanged(MediaFormat inputFormat,
|
||||||
boolean hasCrop = format.containsKey(KEY_CROP_RIGHT) && format.containsKey(KEY_CROP_LEFT)
|
android.media.MediaFormat outputFormat) {
|
||||||
&& format.containsKey(KEY_CROP_BOTTOM) && format.containsKey(KEY_CROP_TOP);
|
boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT)
|
||||||
|
&& outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM)
|
||||||
|
&& outputFormat.containsKey(KEY_CROP_TOP);
|
||||||
currentWidth = hasCrop
|
currentWidth = hasCrop
|
||||||
? format.getInteger(KEY_CROP_RIGHT) - format.getInteger(KEY_CROP_LEFT) + 1
|
? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1
|
||||||
: format.getInteger(android.media.MediaFormat.KEY_WIDTH);
|
: outputFormat.getInteger(android.media.MediaFormat.KEY_WIDTH);
|
||||||
currentHeight = hasCrop
|
currentHeight = hasCrop
|
||||||
? format.getInteger(KEY_CROP_BOTTOM) - format.getInteger(KEY_CROP_TOP) + 1
|
? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1
|
||||||
: format.getInteger(android.media.MediaFormat.KEY_HEIGHT);
|
: outputFormat.getInteger(android.media.MediaFormat.KEY_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer.audio;
|
|||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.util.Ac3Util;
|
import com.google.android.exoplayer.util.Ac3Util;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
@ -315,24 +316,21 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconfigures the audio track to play back media in {@code format}. The encoding is assumed to
|
* Reconfigures the audio track to play back media in {@code format}, inferring a buffer size from
|
||||||
* be {@link AudioFormat#ENCODING_PCM_16BIT}.
|
* the format.
|
||||||
*/
|
*/
|
||||||
public void reconfigure(MediaFormat format) {
|
public void reconfigure(MediaFormat format) {
|
||||||
reconfigure(format, AudioFormat.ENCODING_PCM_16BIT, 0);
|
reconfigure(format, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconfigures the audio track to play back media in {@code format}. Buffers passed to
|
* Reconfigures the audio track to play back media in {@code format}.
|
||||||
* {@link #handleBuffer} must use the specified {@code encoding}, which should be a constant from
|
|
||||||
* {@link AudioFormat}.
|
|
||||||
*
|
*
|
||||||
* @param format Specifies the channel count and sample rate to play back.
|
* @param format Specifies the channel count and sample rate to play back.
|
||||||
* @param encoding The format in which audio is represented.
|
|
||||||
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
|
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
|
||||||
* size inferred from the format.
|
* size inferred from the format.
|
||||||
*/
|
*/
|
||||||
public void reconfigure(MediaFormat format, int encoding, int specifiedBufferSize) {
|
public void reconfigure(MediaFormat format, int specifiedBufferSize) {
|
||||||
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
||||||
int channelConfig;
|
int channelConfig;
|
||||||
switch (channelCount) {
|
switch (channelCount) {
|
||||||
@ -353,8 +351,10 @@ public final class AudioTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
|
||||||
|
String mimeType = format.getString(MediaFormat.KEY_MIME);
|
||||||
|
|
||||||
// TODO: Does channelConfig determine channelCount?
|
// TODO: Does channelConfig determine channelCount?
|
||||||
|
int encoding = MimeTypes.getEncodingForMimeType(mimeType);
|
||||||
boolean isAc3 = encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3;
|
boolean isAc3 = encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3;
|
||||||
if (isInitialized() && this.sampleRate == sampleRate && this.channelConfig == channelConfig
|
if (isInitialized() && this.sampleRate == sampleRate && this.channelConfig == channelConfig
|
||||||
&& !this.isAc3 && !isAc3) {
|
&& !this.isAc3 && !isAc3) {
|
||||||
|
@ -534,8 +534,18 @@ import java.util.List;
|
|||||||
childPosition += childAtomSize;
|
childPosition += childAtomSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.mediaFormat = MediaFormat.createAudioFormat(
|
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing.
|
||||||
MimeTypes.AUDIO_AAC, sampleSize, durationUs, channelCount, sampleRate,
|
String mimeType;
|
||||||
|
if (atomType == Atom.TYPE_ac_3) {
|
||||||
|
mimeType = MimeTypes.AUDIO_AC3;
|
||||||
|
} else if (atomType == Atom.TYPE_ec_3) {
|
||||||
|
mimeType = MimeTypes.AUDIO_EC3;
|
||||||
|
} else {
|
||||||
|
mimeType = MimeTypes.AUDIO_AAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.mediaFormat = MediaFormat.createAudioFormat(mimeType, sampleSize, durationUs, channelCount,
|
||||||
|
sampleRate,
|
||||||
initializationData == null ? null : Collections.singletonList(initializationData));
|
initializationData == null ? null : Collections.singletonList(initializationData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.util;
|
package com.google.android.exoplayer.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.C;
|
||||||
|
import com.google.android.exoplayer.audio.AudioCapabilities;
|
||||||
|
|
||||||
|
import android.media.AudioFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines common MIME types and helper methods.
|
* Defines common MIME types and helper methods.
|
||||||
*/
|
*/
|
||||||
@ -119,4 +124,37 @@ public class MimeTypes {
|
|||||||
return mimeType.equals(APPLICATION_TTML);
|
return mimeType.equals(APPLICATION_TTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the output audio encoding that will result from processing input in {@code mimeType}.
|
||||||
|
* For non-passthrough audio formats, this is always {@link AudioFormat#ENCODING_PCM_16BIT}. For
|
||||||
|
* passthrough formats it will be one of {@link AudioFormat}'s other {@code ENCODING_*} constants.
|
||||||
|
* For non-audio formats, {@link AudioFormat#ENCODING_INVALID} will be returned.
|
||||||
|
*
|
||||||
|
* @param mimeType The MIME type of media that will be decoded (or passed through).
|
||||||
|
* @return The corresponding {@link AudioFormat} encoding.
|
||||||
|
*/
|
||||||
|
public static int getEncodingForMimeType(String mimeType) {
|
||||||
|
if (AUDIO_AC3.equals(mimeType)) {
|
||||||
|
return C.ENCODING_AC3;
|
||||||
|
}
|
||||||
|
if (AUDIO_EC3.equals(mimeType)) {
|
||||||
|
return C.ENCODING_E_AC3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other audio formats will be decoded to 16-bit PCM.
|
||||||
|
return isAudio(mimeType) ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the specified {@code mimeType} represents audio that can be played via
|
||||||
|
* passthrough if the device supports it.
|
||||||
|
*
|
||||||
|
* @param mimeType The MIME type of input media.
|
||||||
|
* @return Whether the audio can be played via passthrough. If this method returns {@code true},
|
||||||
|
* it is still necessary to check the {@link AudioCapabilities} for device support.
|
||||||
|
*/
|
||||||
|
public static boolean isPassthroughAudio(String mimeType) {
|
||||||
|
return AUDIO_AC3.equals(mimeType) || AUDIO_EC3.equals(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user