Enable passthrough based on the input MIME type.

This commit is contained in:
Oliver Woodman 2015-05-08 17:04:21 +01:00
parent a1083d360a
commit 3360f5eda5
7 changed files with 84 additions and 56 deletions

View File

@ -59,7 +59,6 @@ import com.google.android.exoplayer.util.Util;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.UnsupportedSchemeException;
import android.os.Handler;
@ -249,7 +248,6 @@ public class DashRendererBuilder implements RendererBuilder,
// Build the audio chunk sources.
List<ChunkSource> audioChunkSourceList = new ArrayList<ChunkSource>();
List<String> audioTrackNameList = new ArrayList<String>();
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
if (audioAdaptationSet != null) {
DataSource audioDataSource = new DefaultUriDataSource(userAgent, bandwidthMeter);
FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator();
@ -275,7 +273,6 @@ public class DashRendererBuilder implements RendererBuilder,
continue;
}
audioEncoding = encoding;
for (int j = audioRepresentations.size() - 1; j >= 0; j--) {
if (!audioRepresentations.get(j).format.codecs.equals(codec)) {
audioTrackNameList.remove(j);
@ -303,7 +300,7 @@ public class DashRendererBuilder implements RendererBuilder,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player,
DemoPlayer.TYPE_AUDIO);
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, drmSessionManager, true,
mainHandler, player, audioEncoding);
mainHandler, player);
}
// Build the text chunk sources.

View File

@ -21,9 +21,7 @@ import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.MimeTypes;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.audiofx.Virtualizer;
import android.os.Handler;
@ -71,7 +69,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
private final EventListener eventListener;
private final AudioTrack audioTrack;
private final int encoding;
private int audioSessionId;
private long currentPositionUs;
@ -124,50 +121,27 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
*/
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
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);
this.eventListener = eventListener;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
this.audioTrack = new AudioTrack();
this.encoding = encoding;
}
@Override
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
throws DecoderQueryException {
if (encoding == AudioFormat.ENCODING_AC3 || encoding == AudioFormat.ENCODING_E_AC3) {
if (MimeTypes.isPassthroughAudio(mimeType)) {
return new DecoderInfo(RAW_DECODER_NAME, true);
}
return super.getDecoderInfo(mimeType, requiresSecureDecoder);
}
@Override
protected void configureCodec(MediaCodec codec, String codecName, MediaFormat format,
android.media.MediaCrypto crypto) {
protected void configureCodec(MediaCodec codec, String codecName,
android.media.MediaFormat format, android.media.MediaCrypto crypto) {
if (RAW_DECODER_NAME.equals(codecName)) {
// 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);
codec.configure(format, null, crypto, 0);
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
@ -193,8 +167,13 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected void onOutputFormatChanged(MediaFormat format) {
audioTrack.reconfigure(format, encoding, 0);
protected void onOutputFormatChanged(MediaFormat inputFormat,
android.media.MediaFormat outputFormat) {
if (MimeTypes.isPassthroughAudio(inputFormat.mimeType)) {
audioTrack.reconfigure(inputFormat.getFrameworkMediaFormatV16());
} else {
audioTrack.reconfigure(outputFormat);
}
}
/**

View File

@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* <p>
* 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.
}
@ -818,7 +820,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
onOutputFormatChanged(codec.getOutputFormat());
onOutputFormatChanged(format, codec.getOutputFormat());
codecCounters.outputFormatChangedCount++;
return true;
} else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {

View File

@ -381,15 +381,17 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected void onOutputFormatChanged(android.media.MediaFormat format) {
boolean hasCrop = format.containsKey(KEY_CROP_RIGHT) && format.containsKey(KEY_CROP_LEFT)
&& format.containsKey(KEY_CROP_BOTTOM) && format.containsKey(KEY_CROP_TOP);
protected void onOutputFormatChanged(MediaFormat inputFormat,
android.media.MediaFormat outputFormat) {
boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT)
&& outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM)
&& outputFormat.containsKey(KEY_CROP_TOP);
currentWidth = hasCrop
? format.getInteger(KEY_CROP_RIGHT) - format.getInteger(KEY_CROP_LEFT) + 1
: format.getInteger(android.media.MediaFormat.KEY_WIDTH);
? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1
: outputFormat.getInteger(android.media.MediaFormat.KEY_WIDTH);
currentHeight = hasCrop
? format.getInteger(KEY_CROP_BOTTOM) - format.getInteger(KEY_CROP_TOP) + 1
: format.getInteger(android.media.MediaFormat.KEY_HEIGHT);
? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1
: outputFormat.getInteger(android.media.MediaFormat.KEY_HEIGHT);
}
@Override

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer.audio;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Ac3Util;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
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
* be {@link AudioFormat#ENCODING_PCM_16BIT}.
* Reconfigures the audio track to play back media in {@code format}, inferring a buffer size from
* the 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
* {@link #handleBuffer} must use the specified {@code encoding}, which should be a constant from
* {@link AudioFormat}.
* Reconfigures the audio track to play back media in {@code format}.
*
* @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
* 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 channelConfig;
switch (channelCount) {
@ -353,8 +351,10 @@ public final class AudioTrack {
}
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
String mimeType = format.getString(MediaFormat.KEY_MIME);
// TODO: Does channelConfig determine channelCount?
int encoding = MimeTypes.getEncodingForMimeType(mimeType);
boolean isAc3 = encoding == C.ENCODING_AC3 || encoding == C.ENCODING_E_AC3;
if (isInitialized() && this.sampleRate == sampleRate && this.channelConfig == channelConfig
&& !this.isAc3 && !isAc3) {

View File

@ -534,8 +534,18 @@ import java.util.List;
childPosition += childAtomSize;
}
out.mediaFormat = MediaFormat.createAudioFormat(
MimeTypes.AUDIO_AAC, sampleSize, durationUs, channelCount, sampleRate,
// Set the MIME type for ac-3/ec-3 atoms even if the dac3/dec3 child atom is missing.
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));
}

View File

@ -15,6 +15,11 @@
*/
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.
*/
@ -119,4 +124,37 @@ public class MimeTypes {
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);
}
}