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 6b521b2952
commit 74c68b3763
7 changed files with 10 additions and 40 deletions

View File

@ -160,15 +160,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int ACTIVE_INTERVAL_MS = 10; private static final int ACTIVE_INTERVAL_MS = 10;
private static final int IDLE_INTERVAL_MS = 1000; 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 * 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 * assumption that no further progress will be made. To appear stuck, the player's renderers must
@ -2478,11 +2469,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
Renderer.MSG_SET_WAKEUP_LISTENER, Renderer.MSG_SET_WAKEUP_LISTENER,
new Renderer.WakeupListener() { new Renderer.WakeupListener() {
@Override @Override
public void onSleep(long wakeupDeadlineMs) { public void onSleep() {
// Do not sleep if the expected sleep time is not long enough to save significant power. requestForRendererSleep = true;
if (wakeupDeadlineMs >= MIN_RENDERER_SLEEP_DURATION_MS) {
requestForRendererSleep = true;
}
} }
@Override @Override

View File

@ -62,11 +62,8 @@ public interface Renderer extends PlayerMessage.Target {
* The renderer no longer needs to render until the next wakeup. * The renderer no longer needs to render until the next wakeup.
* *
* <p>Must be called from the thread ExoPlayer invokes the renderer from. * <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)} * The renderer needs to render some frames. The client should call {@link #render(long, long)}

View File

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

View File

@ -386,11 +386,6 @@ import java.lang.reflect.Method;
return bufferSize - bytesPending; 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. */ /** Returns whether the track is in an invalid state and must be recreated. */
public boolean isStalled(long writtenFrames) { public boolean isStalled(long writtenFrames) {
return forceResetWorkaroundTimeMs != C.TIME_UNSET return forceResetWorkaroundTimeMs != C.TIME_UNSET

View File

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

View File

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

View File

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