diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java index 26933cf5e4..2b0de68c3d 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java @@ -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 audioChunkSourceList = new ArrayList(); List audioTrackNameList = new ArrayList(); - 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. diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java index ba98373319..48b54db597 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -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); + } } /** diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 364a3f4f79..58bba1d2c1 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -742,9 +742,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { *

* 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) { diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java index b843901543..49949f91a2 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java @@ -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 diff --git a/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java b/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java index 75bf3bcf5f..4fe7c24245 100644 --- a/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java +++ b/library/src/main/java/com/google/android/exoplayer/audio/AudioTrack.java @@ -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) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java index 2f6fb9fc22..42c3f2e70e 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java @@ -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)); } diff --git a/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java index 8ed514174a..3a6ea01c6f 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java @@ -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); + } + }