Unconditionally sleep for offload

Unconditionally sleep for offload, if the audio buffer is full.
Previously ExoPlayer would not sleep if the expected wake-up was
in 2s. This was to prevent underrun if the wake-up was delayed.

Experiments have shown that the wakup audio buffer is far more
than 2s (around 1min). Additionally,
the metric was incorrect because it measured both,
AudioTrack + DSP.

Finally, this metric was erroneous after a gapless transition,
when the head position would reset to 0 and thus the computed
delay until next wakeup was too large.

PiperOrigin-RevId: 451383701
This commit is contained in:
bachinger 2022-05-27 13:42:44 +00:00 committed by Marc Baechinger
parent 9649411a4f
commit 01412f4a57
7 changed files with 10 additions and 40 deletions

View File

@ -168,15 +168,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int ACTIVE_INTERVAL_MS = 10;
private static final int IDLE_INTERVAL_MS = 1000;
/**
* Duration under which pausing the main DO_SOME_WORK loop is not expected to yield significant
* power saving.
*
* <p>This value is probably too high, power measurements are needed adjust it, but as renderer
* sleep is currently only implemented for audio offload, which uses buffer much bigger than 2s,
* this does not matter for now.
*/
private static final long MIN_RENDERER_SLEEP_DURATION_MS = 2000;
/**
* Duration for which the player needs to appear stuck before the playback is failed on the
* assumption that no further progress will be made. To appear stuck, the player's renderers must
@ -2486,11 +2477,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
Renderer.MSG_SET_WAKEUP_LISTENER,
new Renderer.WakeupListener() {
@Override
public void onSleep(long wakeupDeadlineMs) {
// Do not sleep if the expected sleep time is not long enough to save significant power.
if (wakeupDeadlineMs >= MIN_RENDERER_SLEEP_DURATION_MS) {
requestForRendererSleep = true;
}
public void onSleep() {
requestForRendererSleep = true;
}
@Override

View File

@ -66,11 +66,8 @@ public interface Renderer extends PlayerMessage.Target {
* The renderer no longer needs to render until the next wakeup.
*
* <p>Must be called from the thread ExoPlayer invokes the renderer from.
*
* @param wakeupDeadlineMs Maximum time in milliseconds until {@link #onWakeup()} will be
* called.
*/
void onSleep(long wakeupDeadlineMs);
void onSleep();
/**
* The renderer needs to render some frames. The client should call {@link #render(long, long)}

View File

@ -108,13 +108,8 @@ public interface AudioSink {
/** Called when the offload buffer has been partially emptied. */
default void onOffloadBufferEmptying() {}
/**
* Called when the offload buffer has been filled completely.
*
* @param bufferEmptyingDeadlineMs Maximum time in milliseconds until {@link
* #onOffloadBufferEmptying()} will be called.
*/
default void onOffloadBufferFull(long bufferEmptyingDeadlineMs) {}
/** Called when the offload buffer has been filled completely. */
default void onOffloadBufferFull() {}
/**
* Called when {@link AudioSink} has encountered an error.

View File

@ -386,11 +386,6 @@ import java.lang.reflect.Method;
return bufferSize - bytesPending;
}
/** Returns the duration of audio that is buffered but unplayed. */
public long getPendingBufferDurationMs(long writtenFrames) {
return Util.usToMs(framesToDurationUs(writtenFrames - getPlaybackHeadPosition()));
}
/** Returns whether the track is in an invalid state and must be recreated. */
public boolean isStalled(long writtenFrames) {
return forceResetWorkaroundTimeMs != C.TIME_UNSET

View File

@ -1202,9 +1202,7 @@ public final class DefaultAudioSink implements AudioSink {
&& listener != null
&& bytesWritten < bytesRemaining
&& !isWaitingForOffloadEndOfStreamHandled) {
long pendingDurationMs =
audioTrackPositionTracker.getPendingBufferDurationMs(writtenEncodedFrames);
listener.onOffloadBufferFull(pendingDurationMs);
listener.onOffloadBufferFull();
}
}

View File

@ -930,9 +930,9 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@Override
public void onOffloadBufferFull(long bufferEmptyingDeadlineMs) {
public void onOffloadBufferFull() {
if (wakeupListener != null) {
wakeupListener.onSleep(bufferEmptyingDeadlineMs);
wakeupListener.onSleep();
}
}

View File

@ -12154,7 +12154,6 @@ public final class ExoPlayerTest {
/** {@link FakeRenderer} that can sleep and be woken-up. */
private static class FakeSleepRenderer extends FakeRenderer {
private static final long WAKEUP_DEADLINE_MS = 60 * C.MICROS_PER_SECOND;
private final AtomicBoolean sleepOnNextRender;
private final AtomicReference<Renderer.WakeupListener> wakeupListenerReceiver;
@ -12168,9 +12167,7 @@ public final class ExoPlayerTest {
wakeupListenerReceiver.get().onWakeup();
}
/**
* Call {@link Renderer.WakeupListener#onSleep(long)} on the next {@link #render(long, long)}
*/
/** Call {@link Renderer.WakeupListener#onSleep()} on the next {@link #render(long, long)} */
public FakeSleepRenderer sleepOnNextRender() {
sleepOnNextRender.set(true);
return this;
@ -12190,7 +12187,7 @@ public final class ExoPlayerTest {
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
super.render(positionUs, elapsedRealtimeUs);
if (sleepOnNextRender.compareAndSet(/* expectedValue= */ true, /* newValue= */ false)) {
wakeupListenerReceiver.get().onSleep(WAKEUP_DEADLINE_MS);
wakeupListenerReceiver.get().onSleep();
}
}
}