Use OMX.google.raw.decoder for passthrough playback.
The OMX component needs to be configured with a format that has a MIME type of audio/raw. Remove Ac3PassthroughAudioTrackRenderer, which is no longer used.
This commit is contained in:
parent
ed1dbddc75
commit
d9b3582baa
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.demo.player;
|
package com.google.android.exoplayer.demo.player;
|
||||||
|
|
||||||
import com.google.android.exoplayer.Ac3PassthroughAudioTrackRenderer;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.DefaultLoadControl;
|
import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||||
@ -61,6 +61,7 @@ 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;
|
||||||
|
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;
|
||||||
@ -298,13 +299,8 @@ public class DashRendererBuilder implements RendererBuilder,
|
|||||||
// TODO: There needs to be some logic to filter out non-AC3 tracks when selecting to use AC3.
|
// TODO: There needs to be some logic to filter out non-AC3 tracks when selecting to use AC3.
|
||||||
boolean useAc3Passthrough = haveAc3Tracks && audioCapabilities != null
|
boolean useAc3Passthrough = haveAc3Tracks && audioCapabilities != null
|
||||||
&& (audioCapabilities.supportsAc3() || audioCapabilities.supportsEAc3());
|
&& (audioCapabilities.supportsAc3() || audioCapabilities.supportsEAc3());
|
||||||
if (useAc3Passthrough) {
|
|
||||||
audioRenderer =
|
|
||||||
new Ac3PassthroughAudioTrackRenderer(audioSampleSource, mainHandler, player);
|
|
||||||
} else {
|
|
||||||
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, drmSessionManager, true,
|
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, drmSessionManager, true,
|
||||||
mainHandler, player);
|
mainHandler, player, useAc3Passthrough ? C.ENCODING_AC3 : AudioFormat.ENCODING_DEFAULT);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the text chunk sources.
|
// Build the text chunk sources.
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.demo.player;
|
package com.google.android.exoplayer.demo.player;
|
||||||
|
|
||||||
import com.google.android.exoplayer.Ac3PassthroughAudioTrackRenderer;
|
|
||||||
import com.google.android.exoplayer.DummyTrackRenderer;
|
import com.google.android.exoplayer.DummyTrackRenderer;
|
||||||
import com.google.android.exoplayer.ExoPlaybackException;
|
import com.google.android.exoplayer.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer.ExoPlayer;
|
import com.google.android.exoplayer.ExoPlayer;
|
||||||
@ -51,8 +50,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener,
|
public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener,
|
||||||
HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
|
HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
|
||||||
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
|
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
|
||||||
Ac3PassthroughAudioTrackRenderer.EventListener, StreamingDrmSessionManager.EventListener,
|
StreamingDrmSessionManager.EventListener, TextRenderer {
|
||||||
TextRenderer {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds renderers for the player.
|
* Builds renderers for the player.
|
||||||
|
@ -1,330 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.google.android.exoplayer;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
|
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.media.AudioFormat;
|
|
||||||
import android.os.Handler;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders encoded AC-3/enhanced AC-3 data to an {@link AudioTrack} for decoding on the playback
|
|
||||||
* device.
|
|
||||||
*
|
|
||||||
* <p>To determine whether the playback device supports passthrough, receive an audio configuration
|
|
||||||
* using {@link AudioCapabilitiesReceiver} and check whether the audio capabilities include
|
|
||||||
* AC-3/enhanced AC-3 passthrough.
|
|
||||||
*/
|
|
||||||
@TargetApi(21)
|
|
||||||
public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface definition for a callback to be notified of {@link Ac3PassthroughAudioTrackRenderer}
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
public interface EventListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when an {@link AudioTrack} fails to initialize.
|
|
||||||
*
|
|
||||||
* @param e The corresponding exception.
|
|
||||||
*/
|
|
||||||
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when an {@link AudioTrack} write fails.
|
|
||||||
*
|
|
||||||
* @param e The corresponding exception.
|
|
||||||
*/
|
|
||||||
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of a message that can be passed to an instance of this class via
|
|
||||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
|
||||||
* should be a {@link Float} with 0 being silence and 1 being unity gain.
|
|
||||||
*/
|
|
||||||
public static final int MSG_SET_VOLUME = 1;
|
|
||||||
|
|
||||||
private static final int SOURCE_STATE_NOT_READY = 0;
|
|
||||||
private static final int SOURCE_STATE_READY = 1;
|
|
||||||
|
|
||||||
/** Default buffer size for AC-3 packets from the sample source */
|
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 16384 * 2;
|
|
||||||
|
|
||||||
private final Handler eventHandler;
|
|
||||||
private final EventListener eventListener;
|
|
||||||
|
|
||||||
private final SampleSource source;
|
|
||||||
private final SampleHolder sampleHolder;
|
|
||||||
private final MediaFormatHolder formatHolder;
|
|
||||||
|
|
||||||
private int trackIndex;
|
|
||||||
private MediaFormat format;
|
|
||||||
|
|
||||||
private int sourceState;
|
|
||||||
private boolean inputStreamEnded;
|
|
||||||
private boolean shouldReadInputBuffer;
|
|
||||||
|
|
||||||
private long currentPositionUs;
|
|
||||||
|
|
||||||
private AudioTrack audioTrack;
|
|
||||||
private int audioSessionId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new track renderer that passes AC-3 samples directly to an audio track.
|
|
||||||
*
|
|
||||||
* @param source The upstream source from which the renderer obtains samples.
|
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
public Ac3PassthroughAudioTrackRenderer(SampleSource source, Handler eventHandler,
|
|
||||||
EventListener eventListener) {
|
|
||||||
this.source = Assertions.checkNotNull(source);
|
|
||||||
this.eventHandler = eventHandler;
|
|
||||||
this.eventListener = eventListener;
|
|
||||||
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
|
|
||||||
sampleHolder.replaceBuffer(DEFAULT_BUFFER_SIZE);
|
|
||||||
formatHolder = new MediaFormatHolder();
|
|
||||||
audioTrack = new AudioTrack();
|
|
||||||
shouldReadInputBuffer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isTimeSource() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int doPrepare() throws ExoPlaybackException {
|
|
||||||
try {
|
|
||||||
boolean sourcePrepared = source.prepare();
|
|
||||||
if (!sourcePrepared) {
|
|
||||||
return TrackRenderer.STATE_UNPREPARED;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ExoPlaybackException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < source.getTrackCount(); i++) {
|
|
||||||
// TODO(andrewlewis): Choose best format here after checking playout formats from HDMI config.
|
|
||||||
if (handlesMimeType(source.getTrackInfo(i).mimeType)) {
|
|
||||||
trackIndex = i;
|
|
||||||
return TrackRenderer.STATE_PREPARED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TrackRenderer.STATE_IGNORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean handlesMimeType(String mimeType) {
|
|
||||||
return MimeTypes.AUDIO_MP4.equals(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onEnabled(long positionUs, boolean joining) {
|
|
||||||
source.enable(trackIndex, positionUs);
|
|
||||||
sourceState = SOURCE_STATE_NOT_READY;
|
|
||||||
inputStreamEnded = false;
|
|
||||||
currentPositionUs = positionUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
|
||||||
try {
|
|
||||||
sourceState = source.continueBuffering(positionUs)
|
|
||||||
? (sourceState == SOURCE_STATE_NOT_READY ? SOURCE_STATE_READY : sourceState)
|
|
||||||
: SOURCE_STATE_NOT_READY;
|
|
||||||
|
|
||||||
if (format == null) {
|
|
||||||
readFormat();
|
|
||||||
} else {
|
|
||||||
// Initialize and start the audio track now.
|
|
||||||
if (!audioTrack.isInitialized()) {
|
|
||||||
int oldAudioSessionId = audioSessionId;
|
|
||||||
try {
|
|
||||||
audioSessionId = audioTrack.initialize(oldAudioSessionId);
|
|
||||||
} catch (AudioTrack.InitializationException e) {
|
|
||||||
notifyAudioTrackInitializationError(e);
|
|
||||||
throw new ExoPlaybackException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getState() == TrackRenderer.STATE_STARTED) {
|
|
||||||
audioTrack.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
feedInputBuffer();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ExoPlaybackException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFormat() throws IOException {
|
|
||||||
int result = source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, false);
|
|
||||||
if (result == SampleSource.FORMAT_READ) {
|
|
||||||
format = formatHolder.format;
|
|
||||||
// TODO: For E-AC-3 input, reconfigure with AudioFormat.ENCODING_E_AC3.
|
|
||||||
audioTrack.reconfigure(format.getFrameworkMediaFormatV16(), AudioFormat.ENCODING_AC3, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void feedInputBuffer() throws IOException, ExoPlaybackException {
|
|
||||||
if (!audioTrack.isInitialized() || inputStreamEnded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get more data if we have run out.
|
|
||||||
if (shouldReadInputBuffer) {
|
|
||||||
sampleHolder.clearData();
|
|
||||||
int result =
|
|
||||||
source.readData(trackIndex, currentPositionUs, formatHolder, sampleHolder, false);
|
|
||||||
if (result == SampleSource.FORMAT_READ) {
|
|
||||||
format = formatHolder.format;
|
|
||||||
audioTrack.reconfigure(format.getFrameworkMediaFormatV16(), AudioFormat.ENCODING_AC3, 0);
|
|
||||||
}
|
|
||||||
if (result == SampleSource.END_OF_STREAM) {
|
|
||||||
inputStreamEnded = true;
|
|
||||||
}
|
|
||||||
if (result != SampleSource.SAMPLE_READ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
shouldReadInputBuffer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handleBufferResult;
|
|
||||||
try {
|
|
||||||
handleBufferResult =
|
|
||||||
audioTrack.handleBuffer(sampleHolder.data, 0, sampleHolder.size, sampleHolder.timeUs);
|
|
||||||
} catch (AudioTrack.WriteException e) {
|
|
||||||
notifyAudioTrackWriteError(e);
|
|
||||||
throw new ExoPlaybackException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are out of sync, allow currentPositionUs to jump backwards.
|
|
||||||
if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) {
|
|
||||||
currentPositionUs = Long.MIN_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get another input buffer if this one was consumed.
|
|
||||||
shouldReadInputBuffer = (handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStarted() {
|
|
||||||
audioTrack.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStopped() {
|
|
||||||
audioTrack.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isEnded() {
|
|
||||||
// We've exhausted the input stream, and the AudioTrack has either played all of the data
|
|
||||||
// submitted, or has been fed insufficient data to begin playback.
|
|
||||||
return inputStreamEnded && (!audioTrack.hasPendingData()
|
|
||||||
|| !audioTrack.hasEnoughDataToBeginPlayback());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isReady() {
|
|
||||||
return audioTrack.hasPendingData() || (format != null && sourceState != SOURCE_STATE_NOT_READY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getCurrentPositionUs() {
|
|
||||||
long audioTrackCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded());
|
|
||||||
if (audioTrackCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) {
|
|
||||||
// Make sure we don't ever report time moving backwards.
|
|
||||||
currentPositionUs = Math.max(currentPositionUs, audioTrackCurrentPositionUs);
|
|
||||||
}
|
|
||||||
return currentPositionUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getDurationUs() {
|
|
||||||
return source.getTrackInfo(trackIndex).durationUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getBufferedPositionUs() {
|
|
||||||
long sourceBufferedPosition = source.getBufferedPositionUs();
|
|
||||||
return sourceBufferedPosition == UNKNOWN_TIME_US || sourceBufferedPosition == END_OF_TRACK_US
|
|
||||||
? sourceBufferedPosition : Math.max(sourceBufferedPosition, getCurrentPositionUs());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDisabled() {
|
|
||||||
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
|
||||||
shouldReadInputBuffer = true;
|
|
||||||
audioTrack.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void seekTo(long positionUs) throws ExoPlaybackException {
|
|
||||||
source.seekToUs(positionUs);
|
|
||||||
sourceState = SOURCE_STATE_NOT_READY;
|
|
||||||
inputStreamEnded = false;
|
|
||||||
shouldReadInputBuffer = true;
|
|
||||||
|
|
||||||
// TODO: Try and re-use the same AudioTrack instance once [Internal: b/7941810] is fixed.
|
|
||||||
audioTrack.reset();
|
|
||||||
currentPositionUs = Long.MIN_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
|
||||||
if (messageType == MSG_SET_VOLUME) {
|
|
||||||
audioTrack.setVolume((Float) message);
|
|
||||||
} else {
|
|
||||||
super.handleMessage(messageType, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyAudioTrackInitializationError(final AudioTrack.InitializationException e) {
|
|
||||||
if (eventHandler != null && eventListener != null) {
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onAudioTrackInitializationError(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) {
|
|
||||||
if (eventHandler != null && eventListener != null) {
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onAudioTrackWriteError(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import android.media.AudioFormat;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaExtractor;
|
import android.media.MediaExtractor;
|
||||||
|
|
||||||
@ -49,6 +50,12 @@ public final class C {
|
|||||||
@SuppressWarnings("InlinedApi")
|
@SuppressWarnings("InlinedApi")
|
||||||
public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR;
|
public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see AudioFormat#ENCODING_AC3
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("InlinedApi")
|
||||||
|
public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see MediaExtractor#SAMPLE_FLAG_SYNC
|
* @see MediaExtractor#SAMPLE_FLAG_SYNC
|
||||||
*/
|
*/
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
import com.google.android.exoplayer.audio.AudioTrack;
|
||||||
import com.google.android.exoplayer.drm.DrmSessionManager;
|
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.MediaFormat;
|
||||||
import android.media.audiofx.Virtualizer;
|
import android.media.audiofx.Virtualizer;
|
||||||
@ -62,8 +64,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
*/
|
*/
|
||||||
public static final int MSG_SET_VOLUME = 1;
|
public static final int MSG_SET_VOLUME = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name for the raw (passthrough) decoder OMX component.
|
||||||
|
*/
|
||||||
|
private static final String RAW_DECODER_NAME = "OMX.google.raw.decoder";
|
||||||
|
|
||||||
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;
|
||||||
@ -116,10 +124,56 @@ 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_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||||
|
throws DecoderQueryException {
|
||||||
|
if (encoding == AudioFormat.ENCODING_AC3 || encoding == AudioFormat.ENCODING_E_AC3) {
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
format.setString(android.media.MediaFormat.KEY_MIME, MimeTypes.AUDIO_RAW);
|
||||||
|
codec.configure(format, null, crypto, 0);
|
||||||
|
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
|
||||||
|
} else {
|
||||||
|
codec.configure(format, null, crypto, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -140,7 +194,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onOutputFormatChanged(MediaFormat format) {
|
protected void onOutputFormatChanged(MediaFormat format) {
|
||||||
audioTrack.reconfigure(format);
|
audioTrack.reconfigure(format, encoding, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -289,12 +289,25 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures a newly created {@link MediaCodec}. Sub-classes should
|
* Returns a {@link DecoderInfo} for decoding media in the specified MIME type.
|
||||||
* override this method if they wish to configure the codec with a
|
*
|
||||||
* non-null surface.
|
* @param mimeType The type of media to decode.
|
||||||
**/
|
* @param requiresSecureDecoder Whether a secure decoder is needed for decoding {@code mimeType}.
|
||||||
protected void configureCodec(MediaCodec codec, android.media.MediaFormat x, MediaCrypto crypto) {
|
* @return {@link DecoderInfo} for decoding media in the specified MIME type, or {@code null} if
|
||||||
codec.configure(x, null, crypto, 0);
|
* no suitable decoder is available.
|
||||||
|
*/
|
||||||
|
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||||
|
throws DecoderQueryException {
|
||||||
|
return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a newly created {@link MediaCodec}. Sub-classes should override this method if they
|
||||||
|
* wish to configure the codec with a non-null surface.
|
||||||
|
*/
|
||||||
|
protected void configureCodec(MediaCodec codec, String codecName,
|
||||||
|
android.media.MediaFormat format, MediaCrypto crypto) {
|
||||||
|
codec.configure(format, null, crypto, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@ -329,7 +342,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
|
|
||||||
DecoderInfo decoderInfo = null;
|
DecoderInfo decoderInfo = null;
|
||||||
try {
|
try {
|
||||||
decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
|
decoderInfo = getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||||
} catch (DecoderQueryException e) {
|
} catch (DecoderQueryException e) {
|
||||||
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
|
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
|
||||||
DecoderInitializationException.DECODER_QUERY_ERROR));
|
DecoderInitializationException.DECODER_QUERY_ERROR));
|
||||||
@ -345,7 +358,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
try {
|
try {
|
||||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||||
codec = MediaCodec.createByCodecName(decoderName);
|
codec = MediaCodec.createByCodecName(decoderName);
|
||||||
configureCodec(codec, format.getFrameworkMediaFormatV16(), mediaCrypto);
|
configureCodec(codec, decoderName, format.getFrameworkMediaFormatV16(), mediaCrypto);
|
||||||
codec.start();
|
codec.start();
|
||||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
notifyDecoderInitialized(decoderName, codecInitializedTimestamp,
|
notifyDecoderInitialized(decoderName, codecInitializedTimestamp,
|
||||||
|
@ -358,8 +358,8 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
|
|
||||||
// Override configureCodec to provide the surface.
|
// Override configureCodec to provide the surface.
|
||||||
@Override
|
@Override
|
||||||
protected void configureCodec(MediaCodec codec, android.media.MediaFormat format,
|
protected void configureCodec(MediaCodec codec, String codecName,
|
||||||
MediaCrypto crypto) {
|
android.media.MediaFormat format, MediaCrypto crypto) {
|
||||||
codec.configure(format, surface, crypto, 0);
|
codec.configure(format, surface, crypto, 0);
|
||||||
codec.setVideoScalingMode(videoScalingMode);
|
codec.setVideoScalingMode(videoScalingMode);
|
||||||
}
|
}
|
||||||
|
@ -313,10 +313,10 @@ 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}. The encoding is assumed to
|
||||||
* be {@link AudioFormat#ENCODING_PCM_16BIT}.
|
* be {@link AudioFormat#ENCODING_DEFAULT}.
|
||||||
*/
|
*/
|
||||||
public void reconfigure(MediaFormat format) {
|
public void reconfigure(MediaFormat format) {
|
||||||
reconfigure(format, AudioFormat.ENCODING_PCM_16BIT, 0);
|
reconfigure(format, AudioFormat.ENCODING_DEFAULT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,13 +33,15 @@ public class MimeTypes {
|
|||||||
|
|
||||||
public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4";
|
public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4";
|
||||||
public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
|
public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
|
||||||
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
|
||||||
public static final String AUDIO_EC3 = BASE_TYPE_AUDIO + "/eac3";
|
|
||||||
public static final String AUDIO_WEBM = BASE_TYPE_AUDIO + "/webm";
|
public static final String AUDIO_WEBM = BASE_TYPE_AUDIO + "/webm";
|
||||||
public static final String AUDIO_MPEG = BASE_TYPE_AUDIO + "/mpeg";
|
public static final String AUDIO_MPEG = BASE_TYPE_AUDIO + "/mpeg";
|
||||||
public static final String AUDIO_MPEG_L1 = BASE_TYPE_AUDIO + "/mpeg-L1";
|
public static final String AUDIO_MPEG_L1 = BASE_TYPE_AUDIO + "/mpeg-L1";
|
||||||
public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2";
|
public static final String AUDIO_MPEG_L2 = BASE_TYPE_AUDIO + "/mpeg-L2";
|
||||||
|
|
||||||
|
public static final String AUDIO_RAW = BASE_TYPE_AUDIO + "/raw";
|
||||||
|
public static final String AUDIO_AC3 = BASE_TYPE_AUDIO + "/ac3";
|
||||||
|
public static final String AUDIO_EC3 = BASE_TYPE_AUDIO + "/eac3";
|
||||||
|
|
||||||
public static final String AUDIO_VORBIS = BASE_TYPE_AUDIO + "/vorbis";
|
public static final String AUDIO_VORBIS = BASE_TYPE_AUDIO + "/vorbis";
|
||||||
public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
|
public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user