mirror of
https://github.com/androidx/media.git
synced 2025-05-07 15:40:37 +08:00
Don't re-buffer when AudioTrack underruns occur.
This commit is contained in:
parent
80d699920c
commit
a4f1e3ce53
@ -159,6 +159,12 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
||||
printInternalError("audioTrackWriteError", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioTrackUnderrun(long audioTrackBufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||
printInternalError("audioTrackUnderrun [" + audioTrackBufferSizeMs + ", "
|
||||
+ elapsedSinceLastFeedMs + "]", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoError(CryptoException e) {
|
||||
printInternalError("cryptoError", e);
|
||||
|
@ -105,6 +105,7 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
void onRendererInitializationError(Exception e);
|
||||
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
||||
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
||||
void onAudioTrackUnderrun(long audioTrackBufferSizeMs, long elapsedSinceLastFeedMs);
|
||||
void onDecoderInitializationError(DecoderInitializationException e);
|
||||
void onCryptoError(CryptoException e);
|
||||
void onLoadError(int sourceId, IOException e);
|
||||
@ -481,6 +482,13 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioTrackUnderrun(long audioTrackBufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||
if (internalErrorListener != null) {
|
||||
internalErrorListener.onAudioTrackUnderrun(audioTrackBufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoError(CryptoException e) {
|
||||
if (internalErrorListener != null) {
|
||||
|
@ -26,6 +26,7 @@ import android.media.AudioManager;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.audiofx.Virtualizer;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@ -55,6 +56,14 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
*/
|
||||
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
||||
|
||||
/**
|
||||
* Invoked when an {@link AudioTrack} underrun occurs.
|
||||
*
|
||||
* @param audioTrackBufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds.
|
||||
* @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data.
|
||||
*/
|
||||
void onAudioTrackUnderrun(long audioTrackBufferSizeMs, long elapsedSinceLastFeedMs);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,6 +86,9 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
private long currentPositionUs;
|
||||
private boolean allowPositionDiscontinuity;
|
||||
|
||||
private boolean audioTrackHasData;
|
||||
private long lastFeedElapsedRealtimeMs;
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
*/
|
||||
@ -272,8 +284,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
|
||||
@Override
|
||||
protected boolean isReady() {
|
||||
return audioTrack.hasPendingData()
|
||||
|| (super.isReady() && getSourceState() == SOURCE_STATE_READY_READ_MAY_FAIL);
|
||||
return audioTrack.hasPendingData() || super.isReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -321,8 +332,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize and start the audio track now.
|
||||
if (!audioTrack.isInitialized()) {
|
||||
// Initialize the AudioTrack now.
|
||||
try {
|
||||
if (audioSessionId != AudioTrack.SESSION_ID_NOT_SET) {
|
||||
audioTrack.initialize(audioSessionId);
|
||||
@ -330,20 +341,29 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
audioSessionId = audioTrack.initialize();
|
||||
onAudioSessionId(audioSessionId);
|
||||
}
|
||||
audioTrackHasData = false;
|
||||
} catch (AudioTrack.InitializationException e) {
|
||||
notifyAudioTrackInitializationError(e);
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
|
||||
if (getState() == TrackRenderer.STATE_STARTED) {
|
||||
audioTrack.play();
|
||||
}
|
||||
} else {
|
||||
// Check for AudioTrack underrun.
|
||||
boolean audioTrackHadData = audioTrackHasData;
|
||||
audioTrackHasData = audioTrack.hasPendingData();
|
||||
if (audioTrackHadData && !audioTrackHasData && getState() == TrackRenderer.STATE_STARTED) {
|
||||
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
||||
notifyAudioTrackUnderrun(audioTrack.getBufferSizeUs() / 1000, elapsedSinceLastFeedMs);
|
||||
}
|
||||
}
|
||||
|
||||
int handleBufferResult;
|
||||
try {
|
||||
handleBufferResult = audioTrack.handleBuffer(
|
||||
buffer, bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs);
|
||||
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
||||
} catch (AudioTrack.WriteException e) {
|
||||
notifyAudioTrackWriteError(e);
|
||||
throw new ExoPlaybackException(e);
|
||||
@ -405,4 +425,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAudioTrackUnderrun(final long audioTrackBufferSizeMs,
|
||||
final long elapsedSinceLastFeedMs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onAudioTrackUnderrun(audioTrackBufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -181,6 +181,7 @@ public final class AudioTrack {
|
||||
private int frameSize;
|
||||
private int minBufferSize;
|
||||
private int bufferSize;
|
||||
private long bufferSizeUs;
|
||||
|
||||
private int nextPlayheadOffsetIndex;
|
||||
private int playheadOffsetCount;
|
||||
@ -433,9 +434,25 @@ public final class AudioTrack {
|
||||
: multipliedBufferSize > maxAppBufferSize ? maxAppBufferSize
|
||||
: multipliedBufferSize;
|
||||
}
|
||||
bufferSizeUs = framesToDurationUs(bytesToFrames(bufferSize));
|
||||
}
|
||||
|
||||
/** Starts/resumes playing audio if the audio track has been initialized. */
|
||||
/**
|
||||
* Returns the size of this {@link AudioTrack}'s buffer in microseconds, given its current
|
||||
* configuration.
|
||||
* <p>
|
||||
* The duration returned from this method may change as a result of calling one of the
|
||||
* {@link #reconfigure} methods.
|
||||
*
|
||||
* @return The size of the buffer in microseconds.
|
||||
*/
|
||||
public long getBufferSizeUs() {
|
||||
return bufferSizeUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or resumes playing audio if the audio track has been initialized.
|
||||
*/
|
||||
public void play() {
|
||||
if (isInitialized()) {
|
||||
resumeSystemTimeUs = System.nanoTime() / 1000;
|
||||
@ -443,7 +460,9 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
/** Signals to the audio track that the next buffer is discontinuous with the previous buffer. */
|
||||
/**
|
||||
* Signals to the audio track that the next buffer is discontinuous with the previous buffer.
|
||||
*/
|
||||
public void handleDiscontinuity() {
|
||||
// Force resynchronization after a skipped buffer.
|
||||
if (startMediaTimeState == START_IN_SYNC) {
|
||||
@ -584,14 +603,18 @@ public final class AudioTrack {
|
||||
return audioTrack.write(buffer, size, android.media.AudioTrack.WRITE_NON_BLOCKING);
|
||||
}
|
||||
|
||||
/** Returns whether the audio track has more data pending that will be played back. */
|
||||
/**
|
||||
* Returns whether the audio track has more data pending that will be played back.
|
||||
*/
|
||||
public boolean hasPendingData() {
|
||||
return isInitialized()
|
||||
&& (bytesToFrames(submittedBytes) > audioTrackUtil.getPlaybackHeadPosition()
|
||||
|| overrideHasPendingData());
|
||||
}
|
||||
|
||||
/** Sets the playback volume. */
|
||||
/**
|
||||
* Sets the playback volume.
|
||||
*/
|
||||
public void setVolume(float volume) {
|
||||
if (this.volume != volume) {
|
||||
this.volume = volume;
|
||||
@ -619,7 +642,9 @@ public final class AudioTrack {
|
||||
audioTrack.setStereoVolume(volume, volume);
|
||||
}
|
||||
|
||||
/** Pauses playback. */
|
||||
/**
|
||||
* Pauses playback.
|
||||
*/
|
||||
public void pause() {
|
||||
if (isInitialized()) {
|
||||
resetSyncParams();
|
||||
@ -662,13 +687,17 @@ public final class AudioTrack {
|
||||
}
|
||||
}
|
||||
|
||||
/** Releases all resources associated with this instance. */
|
||||
/**
|
||||
* Releases all resources associated with this instance.
|
||||
*/
|
||||
public void release() {
|
||||
reset();
|
||||
releaseKeepSessionIdAudioTrack();
|
||||
}
|
||||
|
||||
/** Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}. */
|
||||
/**
|
||||
* Releases {@link #keepSessionIdAudioTrack} asynchronously, if it is non-{@code null}.
|
||||
*/
|
||||
private void releaseKeepSessionIdAudioTrack() {
|
||||
if (keepSessionIdAudioTrack == null) {
|
||||
return;
|
||||
@ -685,7 +714,9 @@ public final class AudioTrack {
|
||||
}.start();
|
||||
}
|
||||
|
||||
/** Returns whether {@link #getCurrentPositionUs} can return the current playback position. */
|
||||
/**
|
||||
* Returns whether {@link #getCurrentPositionUs} can return the current playback position.
|
||||
*/
|
||||
private boolean hasCurrentPositionUs() {
|
||||
return isInitialized() && startMediaTimeState != START_NOT_SET;
|
||||
}
|
||||
@ -757,7 +788,7 @@ public final class AudioTrack {
|
||||
// Compute the audio track latency, excluding the latency due to the buffer (leaving
|
||||
// latency due to the mixer and audio hardware driver).
|
||||
latencyUs = (Integer) getLatencyMethod.invoke(audioTrack, (Object[]) null) * 1000L
|
||||
- framesToDurationUs(bytesToFrames(bufferSize));
|
||||
- bufferSizeUs;
|
||||
// Sanity check that the latency is non-negative.
|
||||
latencyUs = Math.max(latencyUs, 0);
|
||||
// Sanity check that the latency isn't too large.
|
||||
|
@ -104,6 +104,12 @@ public final class LogcatLogger implements ExoPlayer.Listener,
|
||||
Log.e(tag, "Audio track write error", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioTrackUnderrun(long audioTrackBufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||
Log.e(tag, "Audio track underrun (" + audioTrackBufferSizeMs + ", " + elapsedSinceLastFeedMs
|
||||
+ ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDroppedFrames(int count, long elapsed) {
|
||||
Log.w(tag, "Dropped frames (" + count + ")");
|
||||
|
Loading…
x
Reference in New Issue
Block a user