diff --git a/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java index 319e206ddf..549b9dcd55 100644 --- a/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/Ac3PassthroughAudioTrackRenderer.java @@ -73,9 +73,6 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { /** Default buffer size for AC-3 packets from the sample source */ private static final int DEFAULT_BUFFER_SIZE = 16384 * 2; - /** Multiplication factor for the audio track's buffer size. */ - private static final int MIN_BUFFER_MULTIPLICATION_FACTOR = 3; - private final Handler eventHandler; private final EventListener eventListener; @@ -103,15 +100,15 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer { * 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) { + 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_NORMAL); sampleHolder.data = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE); formatHolder = new MediaFormatHolder(); - audioTrack = new AudioTrack(MIN_BUFFER_MULTIPLICATION_FACTOR); + audioTrack = new AudioTrack(); shouldReadInputBuffer = true; } 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 b12cb6cb2a..a6ff3b0a44 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.drm.DrmSessionManager; -import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.MimeTypes; import android.annotation.TargetApi; @@ -64,10 +63,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { public static final int MSG_SET_VOLUME = 1; private final EventListener eventListener; - private final AudioTrack audioTrack; - private int audioSessionId; + private int audioSessionId; private long currentPositionUs; /** @@ -118,72 +116,10 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer { */ public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) { - this(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener, - new AudioTrack()); - } - - /** - * @param source The upstream source from which the renderer obtains samples. - * @param minBufferMultiplicationFactor When instantiating an underlying - * {@link android.media.AudioTrack}, the size of the track is calculated as this value - * multiplied by the minimum buffer size obtained from - * {@link android.media.AudioTrack#getMinBufferSize(int, int, int)}. The multiplication - * factor must be greater than or equal to 1. - * @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 MediaCodecAudioTrackRenderer(SampleSource source, float minBufferMultiplicationFactor, - Handler eventHandler, EventListener eventListener) { - this(source, null, true, minBufferMultiplicationFactor, eventHandler, eventListener); - } - - /** - * @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 minBufferMultiplicationFactor When instantiating an underlying - * {@link android.media.AudioTrack}, the size of the track is calculated as this value - * multiplied by the minimum buffer size obtained from - * {@link android.media.AudioTrack#getMinBufferSize(int, int, int)}. The multiplication - * factor must be greater than or equal to 1. - * @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 MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager, - boolean playClearSamplesWithoutKeys, float minBufferMultiplicationFactor, - Handler eventHandler, EventListener eventListener) { - this(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener, - new AudioTrack(minBufferMultiplicationFactor)); - } - - /** - * @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 audioTrack Used for playing back decoded audio samples. - */ - public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager, - boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener, - AudioTrack audioTrack) { super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener); this.eventListener = eventListener; - this.audioTrack = Assertions.checkNotNull(audioTrack); this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + this.audioTrack = new AudioTrack(); } @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 ccc3943b28..d331ce9b7c 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 @@ -16,7 +16,6 @@ package com.google.android.exoplayer.audio; import com.google.android.exoplayer.C; -import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.annotation.SuppressLint; @@ -89,12 +88,19 @@ public final class AudioTrack { /** Represents an unset {@link android.media.AudioTrack} session identifier. */ public static final int SESSION_ID_NOT_SET = 0; - /** The default multiplication factor used when determining the size of the track's buffer. */ - public static final float DEFAULT_MIN_BUFFER_MULTIPLICATION_FACTOR = 4; - /** Returned by {@link #getCurrentPositionUs} when the position is not set. */ public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE; + /** A minimum length for the {@link android.media.AudioTrack} buffer, in microseconds. */ + private static final long MIN_BUFFER_DURATION_US = 250000; + /** A maximum length for the {@link android.media.AudioTrack} buffer, in microseconds. */ + private static final long MAX_BUFFER_DURATION_US = 750000; + /** + * A multiplication factor to apply to the minimum buffer size requested by the underlying + * {@link android.media.AudioTrack}. + */ + private static final int BUFFER_MULTIPLICATION_FACTOR = 4; + private static final String TAG = "AudioTrack"; /** @@ -126,7 +132,6 @@ public final class AudioTrack { private final ConditionVariable releasingConditionVariable; private final AudioTimestampCompat audioTimestampCompat; private final long[] playheadOffsets; - private final float minBufferMultiplicationFactor; private android.media.AudioTrack audioTrack; private int sampleRate; @@ -162,15 +167,7 @@ public final class AudioTrack { /** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */ private int ac3Bitrate; - /** Constructs an audio track using the default minimum buffer size multiplier. */ public AudioTrack() { - this(DEFAULT_MIN_BUFFER_MULTIPLICATION_FACTOR); - } - - /** Constructs an audio track using the specified minimum buffer size multiplier. */ - public AudioTrack(float minBufferMultiplicationFactor) { - Assertions.checkArgument(minBufferMultiplicationFactor >= 1); - this.minBufferMultiplicationFactor = minBufferMultiplicationFactor; releasingConditionVariable = new ConditionVariable(true); if (Util.SDK_INT >= 19) { audioTimestampCompat = new AudioTimestampCompatV19(); @@ -297,11 +294,11 @@ public final class AudioTrack { * * @param format Specifies the channel count and sample rate to play back. * @param encoding The format in which audio is represented. - * @param bufferSize The total size of the playback buffer in bytes. Specify 0 to use a buffer - * size based on the minimum for format. + * @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a + * size inferred from the format. */ @SuppressLint("InlinedApi") - public void reconfigure(MediaFormat format, int encoding, int bufferSize) { + public void reconfigure(MediaFormat format, int encoding, int specifiedBufferSize) { int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); int channelConfig; switch (channelCount) { @@ -333,16 +330,25 @@ public final class AudioTrack { reset(); - minBufferSize = android.media.AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding); - this.encoding = encoding; - this.bufferSize = - bufferSize == 0 ? (int) (minBufferMultiplicationFactor * minBufferSize) : bufferSize; this.sampleRate = sampleRate; this.channelConfig = channelConfig; this.isAc3 = isAc3; ac3Bitrate = UNKNOWN_AC3_BITRATE; // Calculated on receiving the first buffer if isAc3 is true. frameSize = 2 * channelCount; // 2 bytes per 16 bit sample * number of channels. + minBufferSize = android.media.AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding); + + if (specifiedBufferSize != 0) { + bufferSize = specifiedBufferSize; + } else { + int multipliedBufferSize = minBufferSize * BUFFER_MULTIPLICATION_FACTOR; + int minAppBufferSize = (int) durationUsToFrames(MIN_BUFFER_DURATION_US) * frameSize; + int maxAppBufferSize = (int) Math.max(minBufferSize, + durationUsToFrames(MAX_BUFFER_DURATION_US) * frameSize); + bufferSize = multipliedBufferSize < minAppBufferSize ? minAppBufferSize + : multipliedBufferSize > maxAppBufferSize ? maxAppBufferSize + : multipliedBufferSize; + } } /** Starts/resumes playing audio if the audio track has been initialized. */ @@ -434,7 +440,7 @@ public final class AudioTrack { int bytesWritten = 0; if (Util.SDK_INT < 21) { // Work out how many bytes we can write without the risk of blocking. - int bytesPending = (int) (submittedBytes - framesToBytes(getPlaybackPositionFrames())); + int bytesPending = (int) (submittedBytes - (getPlaybackPositionFrames() * frameSize)); int bytesToWrite = bufferSize - bytesPending; if (bytesToWrite > 0) { bytesToWrite = Math.min(temporaryBufferSize, bytesToWrite); @@ -651,11 +657,6 @@ public final class AudioTrack { return framesToDurationUs(getPlaybackPositionFrames()); } - private long framesToBytes(long frameCount) { - // This method is unused on SDK >= 21. - return frameCount * frameSize; - } - private long bytesToFrames(long byteCount) { if (isAc3) { return