mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Move setVideoOutput message handling to playback thread
PiperOrigin-RevId: 688951964
This commit is contained in:
parent
dfb7636138
commit
f11130d59e
@ -35,7 +35,6 @@ import static androidx.media3.exoplayer.Renderer.MSG_SET_SCALING_MODE;
|
|||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED;
|
import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED;
|
||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_EFFECTS;
|
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_EFFECTS;
|
||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER;
|
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER;
|
||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT;
|
|
||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT_RESOLUTION;
|
import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT_RESOLUTION;
|
||||||
import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME;
|
import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
@ -124,7 +123,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
/** The default implementation of {@link ExoPlayer}. */
|
/** The default implementation of {@link ExoPlayer}. */
|
||||||
/* package */ final class ExoPlayerImpl extends BasePlayer
|
/* package */ final class ExoPlayerImpl extends BasePlayer
|
||||||
@ -2702,31 +2700,12 @@ import java.util.concurrent.TimeoutException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setVideoOutputInternal(@Nullable Object videoOutput) {
|
private void setVideoOutputInternal(@Nullable Object videoOutput) {
|
||||||
|
boolean isReplacingVideoOutput = this.videoOutput != null && this.videoOutput != videoOutput;
|
||||||
|
long timeoutMs = isReplacingVideoOutput ? detachSurfaceTimeoutMs : C.TIME_UNSET;
|
||||||
// Note: We don't turn this method into a no-op if the output is being replaced with itself so
|
// Note: We don't turn this method into a no-op if the output is being replaced with itself so
|
||||||
// as to ensure onRenderedFirstFrame callbacks are still called in this case.
|
// as to ensure onRenderedFirstFrame callbacks are still called in this case.
|
||||||
List<PlayerMessage> messages = new ArrayList<>();
|
boolean isSuccess = internalPlayer.setVideoOutput(videoOutput, timeoutMs);
|
||||||
for (Renderer renderer : renderers) {
|
if (isReplacingVideoOutput) {
|
||||||
if (renderer.getTrackType() == TRACK_TYPE_VIDEO) {
|
|
||||||
messages.add(
|
|
||||||
createMessageInternal(renderer)
|
|
||||||
.setType(MSG_SET_VIDEO_OUTPUT)
|
|
||||||
.setPayload(videoOutput)
|
|
||||||
.send());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean messageDeliveryTimedOut = false;
|
|
||||||
if (this.videoOutput != null && this.videoOutput != videoOutput) {
|
|
||||||
// We're replacing an output. Block to ensure that this output will not be accessed by the
|
|
||||||
// renderers after this method returns.
|
|
||||||
try {
|
|
||||||
for (PlayerMessage message : messages) {
|
|
||||||
message.blockUntilDelivered(detachSurfaceTimeoutMs);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
messageDeliveryTimedOut = true;
|
|
||||||
}
|
|
||||||
if (this.videoOutput == ownedSurface) {
|
if (this.videoOutput == ownedSurface) {
|
||||||
// We're replacing a surface that we are responsible for releasing.
|
// We're replacing a surface that we are responsible for releasing.
|
||||||
ownedSurface.release();
|
ownedSurface.release();
|
||||||
@ -2734,7 +2713,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.videoOutput = videoOutput;
|
this.videoOutput = videoOutput;
|
||||||
if (messageDeliveryTimedOut) {
|
if (!isSuccess) {
|
||||||
stopInternal(
|
stopInternal(
|
||||||
ExoPlaybackException.createForUnexpected(
|
ExoPlaybackException.createForUnexpected(
|
||||||
new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE),
|
new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE),
|
||||||
|
@ -154,6 +154,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
private static final int MSG_UPDATE_MEDIA_SOURCES_WITH_MEDIA_ITEMS = 27;
|
private static final int MSG_UPDATE_MEDIA_SOURCES_WITH_MEDIA_ITEMS = 27;
|
||||||
private static final int MSG_SET_PRELOAD_CONFIGURATION = 28;
|
private static final int MSG_SET_PRELOAD_CONFIGURATION = 28;
|
||||||
private static final int MSG_PREPARE = 29;
|
private static final int MSG_PREPARE = 29;
|
||||||
|
private static final int MSG_SET_VIDEO_OUTPUT = 30;
|
||||||
|
|
||||||
private static final long BUFFERING_MAXIMUM_INTERVAL_MS =
|
private static final long BUFFERING_MAXIMUM_INTERVAL_MS =
|
||||||
Util.usToMs(Renderer.DEFAULT_DURATION_TO_PROGRESS_US);
|
Util.usToMs(Renderer.DEFAULT_DURATION_TO_PROGRESS_US);
|
||||||
@ -465,6 +466,32 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the video output.
|
||||||
|
*
|
||||||
|
* <p>If the provided {@code timeoutMs} is {@link C#TIME_UNSET} then this method will not wait on
|
||||||
|
* the message delivery.
|
||||||
|
*
|
||||||
|
* @param videoOutput Surface onto which which video will be rendered.
|
||||||
|
* @param timeoutMs Timeout duration to wait for successful message delivery. If {@link
|
||||||
|
* C#TIME_UNSET} then the method will not block on the message delivery.
|
||||||
|
* @return Whether the operation succeeded. If false, the operation timed out.
|
||||||
|
*/
|
||||||
|
public synchronized boolean setVideoOutput(@Nullable Object videoOutput, long timeoutMs) {
|
||||||
|
if (released || !playbackLooper.getThread().isAlive()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AtomicBoolean processedFlag = new AtomicBoolean();
|
||||||
|
handler
|
||||||
|
.obtainMessage(MSG_SET_VIDEO_OUTPUT, new Pair<>(videoOutput, processedFlag))
|
||||||
|
.sendToTarget();
|
||||||
|
if (timeoutMs != C.TIME_UNSET) {
|
||||||
|
waitUninterruptibly(/* condition= */ processedFlag::get, timeoutMs);
|
||||||
|
return processedFlag.get();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the player.
|
* Releases the player.
|
||||||
*
|
*
|
||||||
@ -566,6 +593,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
setForegroundModeInternal(
|
setForegroundModeInternal(
|
||||||
/* foregroundMode= */ msg.arg1 != 0, /* processedFlag= */ (AtomicBoolean) msg.obj);
|
/* foregroundMode= */ msg.arg1 != 0, /* processedFlag= */ (AtomicBoolean) msg.obj);
|
||||||
break;
|
break;
|
||||||
|
case MSG_SET_VIDEO_OUTPUT:
|
||||||
|
Pair<Object, AtomicBoolean> setVideoOutputPayload = (Pair<Object, AtomicBoolean>) msg.obj;
|
||||||
|
setVideoOutputInternal(
|
||||||
|
/* videoOutput= */ setVideoOutputPayload.first,
|
||||||
|
/* processedFlag= */ setVideoOutputPayload.second);
|
||||||
|
break;
|
||||||
case MSG_STOP:
|
case MSG_STOP:
|
||||||
stopInternal(/* forceResetRenderers= */ false, /* acknowledgeStop= */ true);
|
stopInternal(/* forceResetRenderers= */ false, /* acknowledgeStop= */ true);
|
||||||
break;
|
break;
|
||||||
@ -1513,6 +1546,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setVideoOutputInternal(
|
||||||
|
@Nullable Object videoOutput, @Nullable AtomicBoolean processedFlag)
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
for (RendererHolder renderer : renderers) {
|
||||||
|
renderer.setVideoOutput(videoOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedFlag != null) {
|
||||||
|
synchronized (this) {
|
||||||
|
processedFlag.set(true);
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void stopInternal(boolean forceResetRenderers, boolean acknowledgeStop) {
|
private void stopInternal(boolean forceResetRenderers, boolean acknowledgeStop) {
|
||||||
resetInternal(
|
resetInternal(
|
||||||
/* resetRenderers= */ forceResetRenderers || !foregroundMode,
|
/* resetRenderers= */ forceResetRenderers || !foregroundMode,
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer;
|
package androidx.media3.exoplayer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.C.TRACK_TYPE_VIDEO;
|
||||||
import static androidx.media3.exoplayer.Renderer.STATE_DISABLED;
|
import static androidx.media3.exoplayer.Renderer.STATE_DISABLED;
|
||||||
import static androidx.media3.exoplayer.Renderer.STATE_ENABLED;
|
import static androidx.media3.exoplayer.Renderer.STATE_ENABLED;
|
||||||
import static androidx.media3.exoplayer.Renderer.STATE_STARTED;
|
import static androidx.media3.exoplayer.Renderer.STATE_STARTED;
|
||||||
@ -440,6 +441,12 @@ import java.io.IOException;
|
|||||||
requiresReset = false;
|
requiresReset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setVideoOutput(@Nullable Object videoOutput) throws ExoPlaybackException {
|
||||||
|
if (renderer.getTrackType() == TRACK_TYPE_VIDEO) {
|
||||||
|
renderer.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, videoOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isRendererEnabled(Renderer renderer) {
|
private static boolean isRendererEnabled(Renderer renderer) {
|
||||||
return renderer.getState() != STATE_DISABLED;
|
return renderer.getState() != STATE_DISABLED;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user