Fix threading of onFallbackApplied callback

The callback is currently triggered on the ExoPlayer playback thread
instead of the app thread that added the listener.

PiperOrigin-RevId: 492474405
This commit is contained in:
tonihei 2022-12-02 16:24:37 +00:00 committed by Ian Baker
parent 2f8cf947c7
commit f3fc4fb973
4 changed files with 51 additions and 15 deletions

View File

@ -20,6 +20,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkState;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -32,6 +33,7 @@ import com.google.android.exoplayer2.util.Util;
private final MediaItem mediaItem; private final MediaItem mediaItem;
private final TransformationRequest originalTransformationRequest; private final TransformationRequest originalTransformationRequest;
private final ListenerSet<Transformer.Listener> transformerListeners; private final ListenerSet<Transformer.Listener> transformerListeners;
private final HandlerWrapper transformerListenerHandler;
private TransformationRequest fallbackTransformationRequest; private TransformationRequest fallbackTransformationRequest;
private int trackCount; private int trackCount;
@ -40,16 +42,20 @@ import com.google.android.exoplayer2.util.Util;
* Creates a new instance. * Creates a new instance.
* *
* @param mediaItem The {@link MediaItem} to transform. * @param mediaItem The {@link MediaItem} to transform.
* @param transformerListeners The {@linkplain Transformer.Listener listeners} to forward events * @param transformerListeners The {@linkplain Transformer.Listener listeners} to call {@link
* to. * Transformer.Listener#onFallbackApplied} on.
* @param transformerListenerHandler The {@link HandlerWrapper} to call {@link
* Transformer.Listener#onFallbackApplied} events on.
* @param originalTransformationRequest The original {@link TransformationRequest}. * @param originalTransformationRequest The original {@link TransformationRequest}.
*/ */
public FallbackListener( public FallbackListener(
MediaItem mediaItem, MediaItem mediaItem,
ListenerSet<Transformer.Listener> transformerListeners, ListenerSet<Transformer.Listener> transformerListeners,
HandlerWrapper transformerListenerHandler,
TransformationRequest originalTransformationRequest) { TransformationRequest originalTransformationRequest) {
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.transformerListeners = transformerListeners; this.transformerListeners = transformerListeners;
this.transformerListenerHandler = transformerListenerHandler;
this.originalTransformationRequest = originalTransformationRequest; this.originalTransformationRequest = originalTransformationRequest;
this.fallbackTransformationRequest = originalTransformationRequest; this.fallbackTransformationRequest = originalTransformationRequest;
} }
@ -98,15 +104,19 @@ import com.google.android.exoplayer2.util.Util;
if (transformationRequest.hdrMode != originalTransformationRequest.hdrMode) { if (transformationRequest.hdrMode != originalTransformationRequest.hdrMode) {
fallbackRequestBuilder.setHdrMode(transformationRequest.hdrMode); fallbackRequestBuilder.setHdrMode(transformationRequest.hdrMode);
} }
fallbackTransformationRequest = fallbackRequestBuilder.build(); TransformationRequest newFallbackTransformationRequest = fallbackRequestBuilder.build();
fallbackTransformationRequest = newFallbackTransformationRequest;
if (trackCount == 0 && !originalTransformationRequest.equals(fallbackTransformationRequest)) { if (trackCount == 0 && !originalTransformationRequest.equals(fallbackTransformationRequest)) {
transformerListeners.queueEvent( transformerListenerHandler.post(
/* eventFlag= */ C.INDEX_UNSET, () ->
listener -> transformerListeners.sendEvent(
listener.onFallbackApplied( /* eventFlag= */ C.INDEX_UNSET,
mediaItem, originalTransformationRequest, fallbackTransformationRequest)); listener ->
transformerListeners.flushEvents(); listener.onFallbackApplied(
mediaItem,
originalTransformationRequest,
newFallbackTransformationRequest)));
} }
} }
} }

View File

@ -752,7 +752,11 @@ public final class Transformer {
TransformerInternalListener transformerInternalListener = TransformerInternalListener transformerInternalListener =
new TransformerInternalListener(mediaItem); new TransformerInternalListener(mediaItem);
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener(mediaItem, listeners, transformationRequest); new FallbackListener(
mediaItem,
listeners,
clock.createHandler(looper, /* callback= */ null),
transformationRequest);
transformerInternal = transformerInternal =
new TransformerInternal( new TransformerInternal(
context, context,

View File

@ -26,10 +26,12 @@ import android.os.Looper;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.shadows.ShadowLooper;
/** Unit tests for {@link FallbackListener}. */ /** Unit tests for {@link FallbackListener}. */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@ -41,7 +43,8 @@ public class FallbackListenerTest {
public void onTransformationRequestFinalized_withoutTrackRegistration_throwsException() { public void onTransformationRequestFinalized_withoutTrackRegistration_throwsException() {
TransformationRequest transformationRequest = new TransformationRequest.Builder().build(); TransformationRequest transformationRequest = new TransformationRequest.Builder().build();
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener(PLACEHOLDER_MEDIA_ITEM, createListenerSet(), transformationRequest); new FallbackListener(
PLACEHOLDER_MEDIA_ITEM, createListenerSet(), createHandler(), transformationRequest);
assertThrows( assertThrows(
IllegalStateException.class, IllegalStateException.class,
@ -52,10 +55,12 @@ public class FallbackListenerTest {
public void onTransformationRequestFinalized_afterTrackRegistration_completesSuccessfully() { public void onTransformationRequestFinalized_afterTrackRegistration_completesSuccessfully() {
TransformationRequest transformationRequest = new TransformationRequest.Builder().build(); TransformationRequest transformationRequest = new TransformationRequest.Builder().build();
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener(PLACEHOLDER_MEDIA_ITEM, createListenerSet(), transformationRequest); new FallbackListener(
PLACEHOLDER_MEDIA_ITEM, createListenerSet(), createHandler(), transformationRequest);
fallbackListener.registerTrack(); fallbackListener.registerTrack();
fallbackListener.onTransformationRequestFinalized(transformationRequest); fallbackListener.onTransformationRequestFinalized(transformationRequest);
ShadowLooper.idleMainLooper();
} }
@Test @Test
@ -66,10 +71,14 @@ public class FallbackListenerTest {
Transformer.Listener mockListener = mock(Transformer.Listener.class); Transformer.Listener mockListener = mock(Transformer.Listener.class);
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener( new FallbackListener(
PLACEHOLDER_MEDIA_ITEM, createListenerSet(mockListener), originalRequest); PLACEHOLDER_MEDIA_ITEM,
createListenerSet(mockListener),
createHandler(),
originalRequest);
fallbackListener.registerTrack(); fallbackListener.registerTrack();
fallbackListener.onTransformationRequestFinalized(unchangedRequest); fallbackListener.onTransformationRequestFinalized(unchangedRequest);
ShadowLooper.idleMainLooper();
verify(mockListener, never()).onFallbackApplied(any(), any(), any()); verify(mockListener, never()).onFallbackApplied(any(), any(), any());
} }
@ -83,10 +92,14 @@ public class FallbackListenerTest {
Transformer.Listener mockListener = mock(Transformer.Listener.class); Transformer.Listener mockListener = mock(Transformer.Listener.class);
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener( new FallbackListener(
PLACEHOLDER_MEDIA_ITEM, createListenerSet(mockListener), originalRequest); PLACEHOLDER_MEDIA_ITEM,
createListenerSet(mockListener),
createHandler(),
originalRequest);
fallbackListener.registerTrack(); fallbackListener.registerTrack();
fallbackListener.onTransformationRequestFinalized(audioFallbackRequest); fallbackListener.onTransformationRequestFinalized(audioFallbackRequest);
ShadowLooper.idleMainLooper();
verify(mockListener) verify(mockListener)
.onFallbackApplied(PLACEHOLDER_MEDIA_ITEM, originalRequest, audioFallbackRequest); .onFallbackApplied(PLACEHOLDER_MEDIA_ITEM, originalRequest, audioFallbackRequest);
@ -109,12 +122,16 @@ public class FallbackListenerTest {
Transformer.Listener mockListener = mock(Transformer.Listener.class); Transformer.Listener mockListener = mock(Transformer.Listener.class);
FallbackListener fallbackListener = FallbackListener fallbackListener =
new FallbackListener( new FallbackListener(
PLACEHOLDER_MEDIA_ITEM, createListenerSet(mockListener), originalRequest); PLACEHOLDER_MEDIA_ITEM,
createListenerSet(mockListener),
createHandler(),
originalRequest);
fallbackListener.registerTrack(); fallbackListener.registerTrack();
fallbackListener.registerTrack(); fallbackListener.registerTrack();
fallbackListener.onTransformationRequestFinalized(audioFallbackRequest); fallbackListener.onTransformationRequestFinalized(audioFallbackRequest);
fallbackListener.onTransformationRequestFinalized(videoFallbackRequest); fallbackListener.onTransformationRequestFinalized(videoFallbackRequest);
ShadowLooper.idleMainLooper();
verify(mockListener) verify(mockListener)
.onFallbackApplied(PLACEHOLDER_MEDIA_ITEM, originalRequest, mergedFallbackRequest); .onFallbackApplied(PLACEHOLDER_MEDIA_ITEM, originalRequest, mergedFallbackRequest);
@ -130,4 +147,8 @@ public class FallbackListenerTest {
private static ListenerSet<Transformer.Listener> createListenerSet() { private static ListenerSet<Transformer.Listener> createListenerSet() {
return new ListenerSet<>(Looper.myLooper(), Clock.DEFAULT, (listener, flags) -> {}); return new ListenerSet<>(Looper.myLooper(), Clock.DEFAULT, (listener, flags) -> {});
} }
private static HandlerWrapper createHandler() {
return Clock.DEFAULT.createHandler(Looper.myLooper(), /* callback= */ null);
}
} }

View File

@ -50,6 +50,7 @@ public final class VideoEncoderWrapperTest {
new FallbackListener( new FallbackListener(
MediaItem.fromUri(Uri.EMPTY), MediaItem.fromUri(Uri.EMPTY),
new ListenerSet<>(Looper.myLooper(), Clock.DEFAULT, (listener, flags) -> {}), new ListenerSet<>(Looper.myLooper(), Clock.DEFAULT, (listener, flags) -> {}),
Clock.DEFAULT.createHandler(Looper.myLooper(), /* callback= */ null),
emptyTransformationRequest); emptyTransformationRequest);
private final VideoTranscodingSamplePipeline.EncoderWrapper encoderWrapper = private final VideoTranscodingSamplePipeline.EncoderWrapper encoderWrapper =
new VideoTranscodingSamplePipeline.EncoderWrapper( new VideoTranscodingSamplePipeline.EncoderWrapper(