Merge pull request #226 from google/dev
Refine logic for determining AudioTrack size.
This commit is contained in:
commit
a6e94af267
@ -73,9 +73,6 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer {
|
|||||||
/** Default buffer size for AC-3 packets from the sample source */
|
/** Default buffer size for AC-3 packets from the sample source */
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 16384 * 2;
|
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 Handler eventHandler;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
|
|
||||||
@ -103,15 +100,15 @@ public final class Ac3PassthroughAudioTrackRenderer extends TrackRenderer {
|
|||||||
* null if delivery of events is not required.
|
* 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 eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
*/
|
||||||
public Ac3PassthroughAudioTrackRenderer(
|
public Ac3PassthroughAudioTrackRenderer(SampleSource source, Handler eventHandler,
|
||||||
SampleSource source, Handler eventHandler, EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
this.source = Assertions.checkNotNull(source);
|
this.source = Assertions.checkNotNull(source);
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
|
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||||
sampleHolder.data = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE);
|
sampleHolder.data = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE);
|
||||||
formatHolder = new MediaFormatHolder();
|
formatHolder = new MediaFormatHolder();
|
||||||
audioTrack = new AudioTrack(MIN_BUFFER_MULTIPLICATION_FACTOR);
|
audioTrack = new AudioTrack();
|
||||||
shouldReadInputBuffer = true;
|
shouldReadInputBuffer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ package com.google.android.exoplayer;
|
|||||||
|
|
||||||
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.Assertions;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
@ -64,10 +63,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
public static final int MSG_SET_VOLUME = 1;
|
public static final int MSG_SET_VOLUME = 1;
|
||||||
|
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
|
|
||||||
private final AudioTrack audioTrack;
|
private final AudioTrack audioTrack;
|
||||||
private int audioSessionId;
|
|
||||||
|
|
||||||
|
private int audioSessionId;
|
||||||
private long currentPositionUs;
|
private long currentPositionUs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,72 +116,10 @@ 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,
|
|
||||||
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);
|
super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.audioTrack = Assertions.checkNotNull(audioTrack);
|
|
||||||
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
|
this.audioTrack = new AudioTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package com.google.android.exoplayer.audio;
|
package com.google.android.exoplayer.audio;
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@ -89,12 +88,19 @@ public final class AudioTrack {
|
|||||||
/** Represents an unset {@link android.media.AudioTrack} session identifier. */
|
/** Represents an unset {@link android.media.AudioTrack} session identifier. */
|
||||||
public static final int SESSION_ID_NOT_SET = 0;
|
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. */
|
/** Returned by {@link #getCurrentPositionUs} when the position is not set. */
|
||||||
public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE;
|
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";
|
private static final String TAG = "AudioTrack";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,7 +132,6 @@ public final class AudioTrack {
|
|||||||
private final ConditionVariable releasingConditionVariable;
|
private final ConditionVariable releasingConditionVariable;
|
||||||
private final AudioTimestampCompat audioTimestampCompat;
|
private final AudioTimestampCompat audioTimestampCompat;
|
||||||
private final long[] playheadOffsets;
|
private final long[] playheadOffsets;
|
||||||
private final float minBufferMultiplicationFactor;
|
|
||||||
|
|
||||||
private android.media.AudioTrack audioTrack;
|
private android.media.AudioTrack audioTrack;
|
||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
@ -162,15 +167,7 @@ public final class AudioTrack {
|
|||||||
/** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */
|
/** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */
|
||||||
private int ac3Bitrate;
|
private int ac3Bitrate;
|
||||||
|
|
||||||
/** Constructs an audio track using the default minimum buffer size multiplier. */
|
|
||||||
public AudioTrack() {
|
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);
|
releasingConditionVariable = new ConditionVariable(true);
|
||||||
if (Util.SDK_INT >= 19) {
|
if (Util.SDK_INT >= 19) {
|
||||||
audioTimestampCompat = new AudioTimestampCompatV19();
|
audioTimestampCompat = new AudioTimestampCompatV19();
|
||||||
@ -297,11 +294,11 @@ public final class AudioTrack {
|
|||||||
*
|
*
|
||||||
* @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 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
|
* @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to use a
|
||||||
* size based on the minimum for format.
|
* size inferred from the format.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("InlinedApi")
|
@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 channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
|
||||||
int channelConfig;
|
int channelConfig;
|
||||||
switch (channelCount) {
|
switch (channelCount) {
|
||||||
@ -333,16 +330,25 @@ public final class AudioTrack {
|
|||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
minBufferSize = android.media.AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding);
|
|
||||||
|
|
||||||
this.encoding = encoding;
|
this.encoding = encoding;
|
||||||
this.bufferSize =
|
|
||||||
bufferSize == 0 ? (int) (minBufferMultiplicationFactor * minBufferSize) : bufferSize;
|
|
||||||
this.sampleRate = sampleRate;
|
this.sampleRate = sampleRate;
|
||||||
this.channelConfig = channelConfig;
|
this.channelConfig = channelConfig;
|
||||||
this.isAc3 = isAc3;
|
this.isAc3 = isAc3;
|
||||||
ac3Bitrate = UNKNOWN_AC3_BITRATE; // Calculated on receiving the first buffer if isAc3 is true.
|
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.
|
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. */
|
/** Starts/resumes playing audio if the audio track has been initialized. */
|
||||||
@ -434,7 +440,7 @@ public final class AudioTrack {
|
|||||||
int bytesWritten = 0;
|
int bytesWritten = 0;
|
||||||
if (Util.SDK_INT < 21) {
|
if (Util.SDK_INT < 21) {
|
||||||
// Work out how many bytes we can write without the risk of blocking.
|
// 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;
|
int bytesToWrite = bufferSize - bytesPending;
|
||||||
if (bytesToWrite > 0) {
|
if (bytesToWrite > 0) {
|
||||||
bytesToWrite = Math.min(temporaryBufferSize, bytesToWrite);
|
bytesToWrite = Math.min(temporaryBufferSize, bytesToWrite);
|
||||||
@ -651,11 +657,6 @@ public final class AudioTrack {
|
|||||||
return framesToDurationUs(getPlaybackPositionFrames());
|
return framesToDurationUs(getPlaybackPositionFrames());
|
||||||
}
|
}
|
||||||
|
|
||||||
private long framesToBytes(long frameCount) {
|
|
||||||
// This method is unused on SDK >= 21.
|
|
||||||
return frameCount * frameSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long bytesToFrames(long byteCount) {
|
private long bytesToFrames(long byteCount) {
|
||||||
if (isAc3) {
|
if (isAc3) {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user