Access finalAudioSink from playback thread

The finalAudioSink was accessed from the main thread but it should only
be accessed from the playback thread.

PiperOrigin-RevId: 743548450
This commit is contained in:
kimvde 2025-04-03 07:06:10 -07:00 committed by Copybara-Service
parent f755c7081f
commit ee611a1ab8
3 changed files with 59 additions and 20 deletions

View File

@ -336,7 +336,6 @@ public final class CompositionPlayer extends SimpleBasePlayer
private LivePositionSupplier positionSupplier;
private LivePositionSupplier bufferedPositionSupplier;
private LivePositionSupplier totalBufferedDurationSupplier;
private boolean isSeeking;
// "this" reference for position suppliers.
@SuppressWarnings("initialization:methodref.receiver.bound.invalid")
@ -506,9 +505,9 @@ public final class CompositionPlayer extends SimpleBasePlayer
playWhenReadyChangeReason = PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST;
if (playbackState == STATE_READY) {
if (playWhenReady) {
finalAudioSink.play();
checkStateNotNull(compositionPlayerInternal).startRendering();
} else {
finalAudioSink.pause();
checkStateNotNull(compositionPlayerInternal).stopRendering();
}
for (int i = 0; i < players.size(); i++) {
players.get(i).setPlayWhenReady(playWhenReady);
@ -587,7 +586,9 @@ public final class CompositionPlayer extends SimpleBasePlayer
@Override
protected ListenableFuture<?> handleSetVolume(float volume) {
this.volume = Util.constrainValue(volume, /* min= */ 0.0f, /* max= */ 1.0f);
finalAudioSink.setVolume(this.volume);
if (compositionPlayerInternal != null) {
compositionPlayerInternal.setVolume(this.volume);
}
return Futures.immediateVoidFuture();
}
@ -597,7 +598,6 @@ public final class CompositionPlayer extends SimpleBasePlayer
resetLivePositionSuppliers();
CompositionPlayerInternal compositionPlayerInternal =
checkStateNotNull(this.compositionPlayerInternal);
isSeeking = true;
compositionPlayerInternal.startSeek(positionMs);
for (int i = 0; i < players.size(); i++) {
players.get(i).seekTo(positionMs);
@ -683,22 +683,17 @@ public final class CompositionPlayer extends SimpleBasePlayer
for (int i = 0; i < players.size(); i++) {
players.get(i).setPlayWhenReady(false);
}
if (!isSeeking) {
// The finalAudioSink cannot be paused more than once. The audio pipeline pauses it during
// a seek, so don't pause here when seeking.
finalAudioSink.pause();
}
checkStateNotNull(compositionPlayerInternal).stopRendering();
}
} else if (endedCount == players.size()) {
playbackState = STATE_ENDED;
} else {
playbackState = STATE_READY;
isSeeking = false;
if (oldPlaybackState != STATE_READY && playWhenReady) {
for (int i = 0; i < players.size(); i++) {
players.get(i).setPlayWhenReady(true);
}
finalAudioSink.play();
checkStateNotNull(compositionPlayerInternal).startRendering();
}
}
}
@ -799,6 +794,7 @@ public final class CompositionPlayer extends SimpleBasePlayer
playbackVideoGraphWrapper,
/* listener= */ this,
compositionInternalListenerHandler);
compositionPlayerInternal.setVolume(volume);
}
private void setPrimaryPlayerSequence(ExoPlayer player, EditedMediaItemSequence sequence) {

View File

@ -47,11 +47,14 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
}
private static final String TAG = "CompPlayerInternal";
private static final int MSG_SET_OUTPUT_SURFACE_INFO = 1;
private static final int MSG_CLEAR_OUTPUT_SURFACE = 2;
private static final int MSG_START_SEEK = 3;
private static final int MSG_END_SEEK = 4;
private static final int MSG_RELEASE = 5;
private static final int MSG_START_RENDERING = 1;
private static final int MSG_STOP_RENDERING = 2;
private static final int MSG_SET_VOLUME = 3;
private static final int MSG_SET_OUTPUT_SURFACE_INFO = 4;
private static final int MSG_CLEAR_OUTPUT_SURFACE = 5;
private static final int MSG_START_SEEK = 6;
private static final int MSG_END_SEEK = 7;
private static final int MSG_RELEASE = 8;
private final Clock clock;
private final HandlerWrapper handler;
@ -94,6 +97,18 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
// Public methods
public void startRendering() {
handler.sendEmptyMessage(MSG_START_RENDERING);
}
public void stopRendering() {
handler.sendEmptyMessage(MSG_STOP_RENDERING);
}
public void setVolume(float volume) {
handler.obtainMessage(MSG_SET_VOLUME, volume).sendToTarget();
}
/** Sets the output surface information on the video pipeline. */
public void setOutputSurfaceInfo(Surface surface, Size size) {
handler
@ -103,7 +118,7 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
/** Clears the output surface from the video pipeline. */
public void clearOutputSurface() {
handler.obtainMessage(MSG_CLEAR_OUTPUT_SURFACE).sendToTarget();
handler.sendEmptyMessage(MSG_CLEAR_OUTPUT_SURFACE);
}
public void startSeek(long positionMs) {
@ -111,7 +126,7 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
}
public void endSeek() {
handler.obtainMessage(MSG_END_SEEK).sendToTarget();
handler.sendEmptyMessage(MSG_END_SEEK);
}
/**
@ -139,6 +154,15 @@ import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
public boolean handleMessage(Message message) {
try {
switch (message.what) {
case MSG_START_RENDERING:
playbackAudioGraphWrapper.startRendering();
break;
case MSG_STOP_RENDERING:
playbackAudioGraphWrapper.stopRendering();
break;
case MSG_SET_VOLUME:
playbackAudioGraphWrapper.setVolume(/* volume= */ (float) message.obj);
break;
case MSG_SET_OUTPUT_SURFACE_INFO:
setOutputSurfaceInfoOnInternalThread(
/* outputSurfaceInfo= */ (OutputSurfaceInfo) message.obj);

View File

@ -47,6 +47,7 @@ import java.util.Objects;
private AudioFormat outputAudioFormat;
private long outputFramesWritten;
private long seekPositionUs;
private boolean isRenderingStarted;
/**
* Creates an instance.
@ -135,6 +136,24 @@ import java.util.Objects;
+ sampleCountToDurationUs(outputFramesWritten, outputAudioFormat.sampleRate);
}
public void startRendering() {
finalAudioSink.play();
isRenderingStarted = true;
}
public void stopRendering() {
if (!isRenderingStarted) {
// The finalAudioSink cannot be paused more than once.
return;
}
finalAudioSink.pause();
isRenderingStarted = false;
}
public void setVolume(float volume) {
finalAudioSink.setVolume(volume);
}
/**
* Handles the steps that need to be executed for a seek before seeking the upstream players.
*
@ -144,7 +163,7 @@ import java.util.Objects;
if (positionUs == C.TIME_UNSET) {
positionUs = 0;
}
finalAudioSink.pause();
stopRendering();
audioGraph.blockInput();
audioGraph.setPendingStartTimeUs(positionUs);
audioGraph.flush();