Allow multiple Transformer listeners to be registered.
Multiple listeners can be added to Transformer and its builder. All or specific listeners can also be removed. PiperOrigin-RevId: 421047650
This commit is contained in:
parent
b0ae7c04d5
commit
725b861f54
@ -118,6 +118,21 @@ public final class ListenerSet<T extends @NonNull Object> {
|
||||
*/
|
||||
@CheckResult
|
||||
public ListenerSet<T> copy(Looper looper, IterationFinishedEvent<T> iterationFinishedEvent) {
|
||||
return copy(looper, clock, iterationFinishedEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the listener set.
|
||||
*
|
||||
* @param looper The new {@link Looper} for the copied listener set.
|
||||
* @param clock The new {@link Clock} for the copied listener set.
|
||||
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
|
||||
* sent during one {@link Looper} message queue iteration were handled by the listeners.
|
||||
* @return The copied listener set.
|
||||
*/
|
||||
@CheckResult
|
||||
public ListenerSet<T> copy(
|
||||
Looper looper, Clock clock, IterationFinishedEvent<T> iterationFinishedEvent) {
|
||||
return new ListenerSet<>(listeners, looper, clock, iterationFinishedEvent);
|
||||
}
|
||||
|
||||
@ -152,6 +167,11 @@ public final class ListenerSet<T extends @NonNull Object> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes all listeners from the set. */
|
||||
public void clear() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
/** Returns the number of added listeners. */
|
||||
public int size() {
|
||||
return listeners.size();
|
||||
|
@ -72,7 +72,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
Transformer testTransformer =
|
||||
transformer
|
||||
.buildUpon()
|
||||
.setListener(
|
||||
.addListener(
|
||||
new Transformer.Listener() {
|
||||
@Override
|
||||
public void onTransformationCompleted(MediaItem inputMediaItem) {
|
||||
|
@ -43,6 +43,7 @@ import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.TracksInfo;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ListenerSet;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.DefaultLoadControl;
|
||||
@ -100,7 +101,7 @@ public final class Transformer {
|
||||
private boolean removeVideo;
|
||||
private String containerMimeType;
|
||||
private TransformationRequest transformationRequest;
|
||||
private Transformer.Listener listener;
|
||||
private ListenerSet<Transformer.Listener> listeners;
|
||||
private DebugViewProvider debugViewProvider;
|
||||
private Looper looper;
|
||||
private Clock clock;
|
||||
@ -110,9 +111,9 @@ public final class Transformer {
|
||||
@Deprecated
|
||||
public Builder() {
|
||||
muxerFactory = new FrameworkMuxer.Factory();
|
||||
listener = new Listener() {};
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
clock = Clock.DEFAULT;
|
||||
listeners = new ListenerSet<>(looper, clock, (listener, flags) -> {});
|
||||
encoderFactory = Codec.EncoderFactory.DEFAULT;
|
||||
debugViewProvider = DebugViewProvider.NONE;
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
@ -127,9 +128,9 @@ public final class Transformer {
|
||||
public Builder(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
muxerFactory = new FrameworkMuxer.Factory();
|
||||
listener = new Listener() {};
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
clock = Clock.DEFAULT;
|
||||
listeners = new ListenerSet<>(looper, clock, (listener, flags) -> {});
|
||||
encoderFactory = Codec.EncoderFactory.DEFAULT;
|
||||
debugViewProvider = DebugViewProvider.NONE;
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
@ -145,7 +146,7 @@ public final class Transformer {
|
||||
this.removeVideo = transformer.removeVideo;
|
||||
this.containerMimeType = transformer.containerMimeType;
|
||||
this.transformationRequest = transformer.transformationRequest;
|
||||
this.listener = transformer.listener;
|
||||
this.listeners = transformer.listeners;
|
||||
this.looper = transformer.looper;
|
||||
this.encoderFactory = transformer.encoderFactory;
|
||||
this.debugViewProvider = transformer.debugViewProvider;
|
||||
@ -267,15 +268,51 @@ public final class Transformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Transformer.Listener} to listen to the transformation events.
|
||||
* @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link
|
||||
* #removeAllListeners()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setListener(Transformer.Listener listener) {
|
||||
this.listeners.clear();
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Transformer.Listener} to listen to the transformation events.
|
||||
*
|
||||
* <p>This is equivalent to {@link Transformer#setListener(Listener)}.
|
||||
* <p>This is equivalent to {@link Transformer#addListener(Listener)}.
|
||||
*
|
||||
* @param listener A {@link Transformer.Listener}.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setListener(Transformer.Listener listener) {
|
||||
this.listener = listener;
|
||||
public Builder addListener(Transformer.Listener listener) {
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link Transformer.Listener}.
|
||||
*
|
||||
* <p>This is equivalent to {@link Transformer#removeListener(Listener)}.
|
||||
*
|
||||
* @param listener A {@link Transformer.Listener}.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder removeListener(Transformer.Listener listener) {
|
||||
this.listeners.remove(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all {@link Transformer.Listener listeners}.
|
||||
*
|
||||
* <p>This is equivalent to {@link Transformer#removeAllListeners()}.
|
||||
*
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder removeAllListeners() {
|
||||
this.listeners.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -290,6 +327,7 @@ public final class Transformer {
|
||||
*/
|
||||
public Builder setLooper(Looper looper) {
|
||||
this.looper = looper;
|
||||
this.listeners = listeners.copy(looper, (listener, flags) -> {});
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -330,6 +368,7 @@ public final class Transformer {
|
||||
@VisibleForTesting
|
||||
/* package */ Builder setClock(Clock clock) {
|
||||
this.clock = clock;
|
||||
this.listeners = listeners.copy(looper, clock, (listener, flags) -> {});
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -383,7 +422,7 @@ public final class Transformer {
|
||||
removeVideo,
|
||||
containerMimeType,
|
||||
transformationRequest,
|
||||
listener,
|
||||
listeners,
|
||||
looper,
|
||||
clock,
|
||||
encoderFactory,
|
||||
@ -482,8 +521,8 @@ public final class Transformer {
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final Transformer.DebugViewProvider debugViewProvider;
|
||||
private final ListenerSet<Transformer.Listener> listeners;
|
||||
|
||||
private Transformer.Listener listener;
|
||||
@Nullable private MuxerWrapper muxerWrapper;
|
||||
@Nullable private ExoPlayer player;
|
||||
@ProgressState private int progressState;
|
||||
@ -496,7 +535,7 @@ public final class Transformer {
|
||||
boolean removeVideo,
|
||||
String containerMimeType,
|
||||
TransformationRequest transformationRequest,
|
||||
Transformer.Listener listener,
|
||||
ListenerSet<Transformer.Listener> listeners,
|
||||
Looper looper,
|
||||
Clock clock,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
@ -510,7 +549,7 @@ public final class Transformer {
|
||||
this.removeVideo = removeVideo;
|
||||
this.containerMimeType = containerMimeType;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.listener = listener;
|
||||
this.listeners = listeners;
|
||||
this.looper = looper;
|
||||
this.clock = clock;
|
||||
this.encoderFactory = encoderFactory;
|
||||
@ -525,20 +564,52 @@ public final class Transformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Transformer.Listener} to listen to the transformation events.
|
||||
* @deprecated Use {@link #addListener(Listener)}, {@link #removeListener(Listener)} or {@link
|
||||
* #removeAllListeners()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setListener(Transformer.Listener listener) {
|
||||
verifyApplicationThread();
|
||||
this.listeners.clear();
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link Transformer.Listener} to listen to the transformation events.
|
||||
*
|
||||
* @param listener A {@link Transformer.Listener}.
|
||||
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||
*/
|
||||
public void setListener(Transformer.Listener listener) {
|
||||
public void addListener(Transformer.Listener listener) {
|
||||
verifyApplicationThread();
|
||||
this.listener = listener;
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link Transformer.Listener}.
|
||||
*
|
||||
* @param listener A {@link Transformer.Listener}.
|
||||
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||
*/
|
||||
public void removeListener(Transformer.Listener listener) {
|
||||
verifyApplicationThread();
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all {@link Transformer.Listener listeners}.
|
||||
*
|
||||
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||
*/
|
||||
public void removeAllListeners() {
|
||||
verifyApplicationThread();
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an asynchronous operation to transform the given {@link MediaItem}.
|
||||
*
|
||||
* <p>The transformation state is notified through the {@link Builder#setListener(Listener)
|
||||
* <p>The transformation state is notified through the {@link Builder#addListener(Listener)
|
||||
* listener}.
|
||||
*
|
||||
* <p>Concurrent transformations on the same Transformer object are not allowed.
|
||||
@ -561,7 +632,7 @@ public final class Transformer {
|
||||
/**
|
||||
* Starts an asynchronous operation to transform the given {@link MediaItem}.
|
||||
*
|
||||
* <p>The transformation state is notified through the {@link Builder#setListener(Listener)
|
||||
* <p>The transformation state is notified through the {@link Builder#addListener(Listener)
|
||||
* listener}.
|
||||
*
|
||||
* <p>Concurrent transformations on the same Transformer object are not allowed.
|
||||
@ -842,16 +913,26 @@ public final class Transformer {
|
||||
}
|
||||
|
||||
if (exception == null && resourceReleaseException == null) {
|
||||
listener.onTransformationCompleted(mediaItem);
|
||||
// TODO(b/213341814): Add event flags for Transformer events.
|
||||
listeners.queueEvent(
|
||||
/* eventFlag= */ C.INDEX_UNSET,
|
||||
listener -> listener.onTransformationCompleted(mediaItem));
|
||||
listeners.flushEvents();
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
listener.onTransformationError(mediaItem, exception);
|
||||
listeners.queueEvent(
|
||||
/* eventFlag= */ C.INDEX_UNSET,
|
||||
listener -> listener.onTransformationError(mediaItem, exception));
|
||||
}
|
||||
if (resourceReleaseException != null) {
|
||||
listener.onTransformationError(mediaItem, resourceReleaseException);
|
||||
TransformationException finalResourceReleaseException = resourceReleaseException;
|
||||
listeners.queueEvent(
|
||||
/* eventFlag= */ C.INDEX_UNSET,
|
||||
listener -> listener.onTransformationError(mediaItem, finalResourceReleaseException));
|
||||
}
|
||||
listeners.flushEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE
|
||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaCrypto;
|
||||
@ -248,6 +251,77 @@ public final class TransformerTest {
|
||||
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startTransformation_withMultipleListeners_callsEachOnCompletion() throws Exception {
|
||||
Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.addListener(mockListener1)
|
||||
.addListener(mockListener2)
|
||||
.addListener(mockListener3)
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||
|
||||
transformer.startTransformation(mediaItem, outputPath);
|
||||
TransformerTestRunner.runUntilCompleted(transformer);
|
||||
|
||||
verify(mockListener1, times(1)).onTransformationCompleted(mediaItem);
|
||||
verify(mockListener2, times(1)).onTransformationCompleted(mediaItem);
|
||||
verify(mockListener3, times(1)).onTransformationCompleted(mediaItem);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startTransformation_withMultipleListeners_callsEachOnError() throws Exception {
|
||||
Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.addListener(mockListener1)
|
||||
.addListener(mockListener2)
|
||||
.addListener(mockListener3)
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER);
|
||||
|
||||
transformer.startTransformation(mediaItem, outputPath);
|
||||
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
|
||||
|
||||
verify(mockListener1, times(1)).onTransformationError(mediaItem, exception);
|
||||
verify(mockListener2, times(1)).onTransformationError(mediaItem, exception);
|
||||
verify(mockListener3, times(1)).onTransformationError(mediaItem, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startTransformation_afterBuildUponWithListenerRemoved_onlyCallsRemainingListeners()
|
||||
throws Exception {
|
||||
Transformer.Listener mockListener1 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
|
||||
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
|
||||
Transformer transformer1 =
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.addListener(mockListener1)
|
||||
.addListener(mockListener2)
|
||||
.addListener(mockListener3)
|
||||
.build();
|
||||
Transformer transformer2 = transformer1.buildUpon().removeListener(mockListener2).build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||
|
||||
transformer2.startTransformation(mediaItem, outputPath);
|
||||
TransformerTestRunner.runUntilCompleted(transformer2);
|
||||
|
||||
verify(mockListener1, times(1)).onTransformationCompleted(mediaItem);
|
||||
verify(mockListener2, times(0)).onTransformationCompleted(mediaItem);
|
||||
verify(mockListener3, times(1)).onTransformationCompleted(mediaItem);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception {
|
||||
Transformer transformer =
|
||||
|
@ -69,7 +69,7 @@ public final class TransformerTestRunner {
|
||||
private static TransformationException runUntilListenerCalled(Transformer transformer)
|
||||
throws TimeoutException {
|
||||
TransformationResult transformationResult = new TransformationResult();
|
||||
Transformer.Listener listener =
|
||||
transformer.addListener(
|
||||
new Transformer.Listener() {
|
||||
@Override
|
||||
public void onTransformationCompleted(MediaItem inputMediaItem) {
|
||||
@ -81,8 +81,7 @@ public final class TransformerTestRunner {
|
||||
MediaItem inputMediaItem, TransformationException exception) {
|
||||
transformationResult.exception = exception;
|
||||
}
|
||||
};
|
||||
transformer.setListener(listener);
|
||||
});
|
||||
runLooperUntil(
|
||||
transformer.getApplicationLooper(),
|
||||
() -> transformationResult.isCompleted || transformationResult.exception != null);
|
||||
|
Loading…
x
Reference in New Issue
Block a user