diff --git a/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java b/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java index 67605237c0..070bcbd7c2 100644 --- a/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java +++ b/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java @@ -39,6 +39,7 @@ import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.transformer.ProgressHolder; import com.google.android.exoplayer2.transformer.TransformationException; import com.google.android.exoplayer2.transformer.TransformationRequest; +import com.google.android.exoplayer2.transformer.TransformationResult; import com.google.android.exoplayer2.transformer.Transformer; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.StyledPlayerView; @@ -223,7 +224,8 @@ public final class TransformerActivity extends AppCompatActivity { .addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem mediaItem) { + public void onTransformationCompleted( + MediaItem mediaItem, TransformationResult transformationResult) { TransformerActivity.this.onTransformationCompleted(filePath); } diff --git a/docs/transforming-media.md b/docs/transforming-media.md index 81f54a8656..4ad82abeb4 100644 --- a/docs/transforming-media.md +++ b/docs/transforming-media.md @@ -65,7 +65,7 @@ app is notified of events via the listener passed to the `Transformer` builder. Transformer.Listener transformerListener = new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem inputMediaItem) { + public void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult transformationResult) { playOutput(); } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/C.java b/library/common/src/main/java/com/google/android/exoplayer2/C.java index c41273d57d..a8d37d0912 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/C.java @@ -69,6 +69,9 @@ public final class C { /** Represents an unset or unknown rate. */ public static final float RATE_UNSET = -Float.MAX_VALUE; + /** Represents an unset or unknown integer rate. */ + public static final int RATE_UNSET_INT = Integer.MIN_VALUE + 1; + /** Represents an unset or unknown length. */ public static final int LENGTH_UNSET = -1; diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java index 286d959d1c..56172633bc 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Build; import androidx.annotation.Nullable; import androidx.test.platform.app.InstrumentationRegistry; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; import java.io.File; import java.io.FileWriter; @@ -39,50 +40,6 @@ public final class AndroidTestUtil { public static final String REMOTE_MP4_10_SECONDS_URI_STRING = "https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4"; - /** Information about the result of successfully running a transformer. */ - public static final class TransformationResult { - - /** A builder for {@link TransformationResult} instances. */ - public static final class Builder { - private final String testId; - @Nullable private Long fileSizeBytes; - - public Builder(String testId) { - this.testId = testId; - } - - public Builder setFileSizeBytes(long fileSizeBytes) { - this.fileSizeBytes = fileSizeBytes; - return this; - } - - public TransformationResult build() { - return new TransformationResult(testId, fileSizeBytes); - } - } - - public final String testId; - @Nullable public final Long fileSizeBytes; - - private TransformationResult(String testId, @Nullable Long fileSizeBytes) { - this.testId = testId; - this.fileSizeBytes = fileSizeBytes; - } - - /** - * Returns all the analysis data from the test. - * - *

If a value was not generated, it will not be part of the return value. - */ - public String getFormattedAnalysis() { - String analysis = "test=" + testId; - if (fileSizeBytes != null) { - analysis += ", fileSizeBytes=" + fileSizeBytes; - } - return analysis; - } - } - /** * Transforms the {@code uriString} with the {@link Transformer}. * @@ -98,6 +55,7 @@ public final class AndroidTestUtil { Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) throws Exception { AtomicReference<@NullableType Exception> exceptionReference = new AtomicReference<>(); + AtomicReference resultReference = new AtomicReference<>(); CountDownLatch countDownLatch = new CountDownLatch(1); Transformer testTransformer = @@ -106,7 +64,9 @@ public final class AndroidTestUtil { .addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem inputMediaItem) { + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult result) { + resultReference.set(result); countDownLatch.countDown(); } @@ -141,19 +101,21 @@ public final class AndroidTestUtil { } TransformationResult result = - new TransformationResult.Builder(testId).setFileSizeBytes(outputVideoFile.length()).build(); + resultReference.get().buildUpon().setFileSizeBytes(outputVideoFile.length()).build(); - writeTransformationResultToFile(context, result); + writeResultToFile(context, testId, result); return result; } - private static void writeTransformationResultToFile(Context context, TransformationResult result) + private static void writeResultToFile(Context context, String testId, TransformationResult result) throws IOException { - File analysisFile = - createExternalCacheFile(context, /* fileName= */ result.testId + "-result.txt"); + File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt"); try (FileWriter fileWriter = new FileWriter(analysisFile)) { String fileContents = - result.getFormattedAnalysis() + "test=" + + testId + + ", " + + getFormattedResult(result) + ", deviceFingerprint=" + Build.FINGERPRINT + ", deviceBrand=" @@ -166,6 +128,22 @@ public final class AndroidTestUtil { } } + /** Formats a {@link TransformationResult} into a comma separated String. */ + public static String getFormattedResult(TransformationResult result) { + String analysis = ""; + if (result.fileSizeBytes != C.LENGTH_UNSET) { + analysis += "fileSizeBytes=" + result.fileSizeBytes; + } + if (result.averageAudioBitrate != C.RATE_UNSET_INT) { + analysis += ", averageAudioBitrate=" + result.averageAudioBitrate; + } + if (result.averageVideoBitrate != C.RATE_UNSET_INT) { + analysis += ", averageVideoBitrate=" + result.averageVideoBitrate; + } + + return analysis; + } + private static File createExternalCacheFile(Context context, String fileName) throws IOException { File file = new File(context.getExternalCacheDir(), fileName); checkState(!file.exists() || file.delete(), "Could not delete file: " + file.getAbsolutePath()); diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/RepeatedTranscodeTransformationTest.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/RepeatedTranscodeTransformationTest.java index 189cde0ae8..d49b8d00de 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -24,6 +24,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.transformer.AndroidTestUtil; import com.google.android.exoplayer2.transformer.TransformationRequest; +import com.google.android.exoplayer2.transformer.TransformationResult; import com.google.android.exoplayer2.transformer.Transformer; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; @@ -57,7 +58,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, @@ -91,7 +92,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, @@ -122,7 +123,7 @@ public final class RepeatedTranscodeTransformationTest { Set differentOutputSizesBytes = new HashSet<>(); for (int i = 0; i < TRANSCODE_COUNT; i++) { // Use a long video in case an error occurs a while after the start of the video. - AndroidTestUtil.TransformationResult result = + TransformationResult result = runTransformer( context, /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i, diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java index 67478587c5..3fc3b3b5de 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MuxerWrapper.java @@ -48,6 +48,7 @@ import java.nio.ByteBuffer; private final Muxer.Factory muxerFactory; private final SparseIntArray trackTypeToIndex; private final SparseLongArray trackTypeToTimeUs; + private final SparseLongArray trackTypeToBytesWritten; private final String containerMimeType; private int trackCount; @@ -62,6 +63,7 @@ import java.nio.ByteBuffer; this.containerMimeType = containerMimeType; trackTypeToIndex = new SparseIntArray(); trackTypeToTimeUs = new SparseLongArray(); + trackTypeToBytesWritten = new SparseLongArray(); previousTrackType = C.TRACK_TYPE_NONE; } @@ -121,6 +123,7 @@ import java.nio.ByteBuffer; int trackIndex = muxer.addTrack(format); trackTypeToIndex.put(trackType, trackIndex); trackTypeToTimeUs.put(trackType, 0L); + trackTypeToBytesWritten.put(trackType, 0L); trackFormatCount++; if (trackFormatCount == trackCount) { isReady = true; @@ -154,8 +157,11 @@ import java.nio.ByteBuffer; return false; } - muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); + trackTypeToBytesWritten.put( + trackType, trackTypeToBytesWritten.get(trackType) + data.remaining()); trackTypeToTimeUs.put(trackType, presentationTimeUs); + + muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); previousTrackType = trackType; return true; } @@ -168,7 +174,6 @@ import java.nio.ByteBuffer; */ public void endTrack(@C.TrackType int trackType) { trackTypeToIndex.delete(trackType); - trackTypeToTimeUs.delete(trackType); } /** @@ -191,6 +196,25 @@ import java.nio.ByteBuffer; return trackCount; } + /** + * Returns the average bitrate of data written to the track of the provided {@code trackType}, or + * {@link C#RATE_UNSET_INT} if there is no track data. + */ + public int getTrackAverageBitrate(@C.TrackType int trackType) { + long trackDurationUs = trackTypeToTimeUs.get(trackType, /* valueIfKeyNotFound= */ -1); + long trackBytes = trackTypeToBytesWritten.get(trackType, /* valueIfKeyNotFound= */ -1); + if (trackDurationUs <= 0 || trackBytes <= 0) { + return C.RATE_UNSET_INT; + } + // The number of bytes written is not a timestamp, however this utility method provides + // overflow-safe multiplication & division. + return (int) + Util.scaleLargeTimestamp( + /* timestamp= */ trackBytes, + /* multiplier= */ C.BITS_PER_BYTE * C.MICROS_PER_SECOND, + /* divisor= */ trackDurationUs); + } + /** * Returns whether the muxer can write a sample of the given track type. * @@ -208,7 +232,7 @@ import java.nio.ByteBuffer; if (!isReady) { return false; } - if (trackTypeToTimeUs.size() == 1) { + if (trackTypeToIndex.size() == 1) { return true; } if (trackType != previousTrackType) { diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformationResult.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformationResult.java new file mode 100644 index 0000000000..09eda6c2c5 --- /dev/null +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformationResult.java @@ -0,0 +1,122 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.transformer; + +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.C; + +/** Information about the result of a successful transformation. */ +public final class TransformationResult { + + /** A builder for {@link TransformationResult} instances. */ + public static final class Builder { + private long fileSizeBytes; + private int averageAudioBitrate; + private int averageVideoBitrate; + + public Builder() { + fileSizeBytes = C.LENGTH_UNSET; + averageAudioBitrate = C.RATE_UNSET_INT; + averageVideoBitrate = C.RATE_UNSET_INT; + } + + /** + * Sets the file size in bytes. + * + *

Input must be positive or {@link C#LENGTH_UNSET}. + */ + public Builder setFileSizeBytes(long fileSizeBytes) { + checkArgument(fileSizeBytes > 0 || fileSizeBytes == C.LENGTH_UNSET); + this.fileSizeBytes = fileSizeBytes; + return this; + } + + /** + * Sets the average audio bitrate. + * + *

Input must be positive or {@link C#RATE_UNSET_INT}. + */ + public Builder setAverageAudioBitrate(int averageAudioBitrate) { + checkArgument(averageAudioBitrate > 0 || averageAudioBitrate == C.RATE_UNSET_INT); + this.averageAudioBitrate = averageAudioBitrate; + return this; + } + + /** + * Sets the average video bitrate. + * + *

Input must be positive or {@link C#RATE_UNSET_INT}. + */ + public Builder setAverageVideoBitrate(int averageVideoBitrate) { + checkArgument(averageVideoBitrate > 0 || averageVideoBitrate == C.RATE_UNSET_INT); + this.averageVideoBitrate = averageVideoBitrate; + return this; + } + + public TransformationResult build() { + return new TransformationResult(fileSizeBytes, averageAudioBitrate, averageVideoBitrate); + } + } + + /** The size of the file in bytes, or {@link C#LENGTH_UNSET} if unset or unknown. */ + public final long fileSizeBytes; + /** + * The average bitrate of the audio track data, or {@link C#RATE_UNSET_INT} if unset or unknown. + */ + public final int averageAudioBitrate; + /** + * The average bitrate of the video track data, or {@link C#RATE_UNSET_INT} if unset or unknown. + */ + public final int averageVideoBitrate; + + private TransformationResult( + long fileSizeBytes, int averageAudioBitrate, int averageVideoBitrate) { + this.fileSizeBytes = fileSizeBytes; + this.averageAudioBitrate = averageAudioBitrate; + this.averageVideoBitrate = averageVideoBitrate; + } + + public Builder buildUpon() { + return new Builder() + .setFileSizeBytes(fileSizeBytes) + .setAverageAudioBitrate(averageAudioBitrate) + .setAverageVideoBitrate(averageVideoBitrate); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TransformationResult)) { + return false; + } + TransformationResult result = (TransformationResult) o; + return fileSizeBytes == result.fileSizeBytes + && averageAudioBitrate == result.averageAudioBitrate + && averageVideoBitrate == result.averageVideoBitrate; + } + + @Override + public int hashCode() { + int result = (int) fileSizeBytes; + result = 31 * result + averageAudioBitrate; + result = 31 * result + averageVideoBitrate; + return result; + } +} diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 826d6b8226..2a3055bcf2 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -443,12 +443,22 @@ public final class Transformer { /** A listener for the transformation events. */ public interface Listener { + /** + * @deprecated Use {@link #onTransformationCompleted(MediaItem, TransformationResult)} instead. + */ + @Deprecated + default void onTransformationCompleted(MediaItem inputMediaItem) {} + /** * Called when the transformation is completed successfully. * * @param inputMediaItem The {@link MediaItem} for which the transformation is completed. + * @param transformationResult The {@link TransformationResult} of the transformation. */ - default void onTransformationCompleted(MediaItem inputMediaItem) {} + default void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { + onTransformationCompleted(inputMediaItem); + } /** @deprecated Use {@link #onTransformationError(MediaItem, TransformationException)}. */ @Deprecated @@ -733,8 +743,9 @@ public final class Transformer { * Returns the current {@link ProgressState} and updates {@code progressHolder} with the current * progress if it is {@link #PROGRESS_STATE_AVAILABLE available}. * - *

After a transformation {@link Listener#onTransformationCompleted(MediaItem) completes}, this - * method returns {@link #PROGRESS_STATE_NO_TRANSFORMATION}. + *

After a transformation {@link Listener#onTransformationCompleted(MediaItem, + * TransformationResult) completes}, this method returns {@link + * #PROGRESS_STATE_NO_TRANSFORMATION}. * * @param progressHolder A {@link ProgressHolder}, updated to hold the percentage progress if * {@link #PROGRESS_STATE_AVAILABLE available}. @@ -950,9 +961,14 @@ public final class Transformer { /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onTransformationError(mediaItem, finalException)); } else { + TransformationResult result = + new TransformationResult.Builder() + .setAverageAudioBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO)) + .setAverageVideoBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO)) + .build(); listeners.queueEvent( /* eventFlag= */ C.INDEX_UNSET, - listener -> listener.onTransformationCompleted(mediaItem)); + listener -> listener.onTransformationCompleted(mediaItem, result)); } listeners.flushEvents(); } diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java index 8b9ad9b4e3..d1148f28dc 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java @@ -22,6 +22,8 @@ import static com.google.android.exoplayer2.transformer.Transformer.PROGRESS_STA import static com.google.android.exoplayer2.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.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -57,6 +59,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import org.checkerframework.checker.nullness.compatqual.NullableType; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -239,9 +242,9 @@ public final class TransformerEndToEndTest { 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); + verify(mockListener1, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener2, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener3, times(1)).onTransformationCompleted(eq(mediaItem), any()); } @Test @@ -313,9 +316,9 @@ public final class TransformerEndToEndTest { transformer2.startTransformation(mediaItem, outputPath); TransformerTestRunner.runUntilCompleted(transformer2); - verify(mockListener1, times(1)).onTransformationCompleted(mediaItem); - verify(mockListener2, never()).onTransformationCompleted(mediaItem); - verify(mockListener3, times(1)).onTransformationCompleted(mediaItem); + verify(mockListener1, times(1)).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener2, never()).onTransformationCompleted(eq(mediaItem), any()); + verify(mockListener3, times(1)).onTransformationCompleted(eq(mediaItem), any()); } @Test @@ -333,6 +336,30 @@ public final class TransformerEndToEndTest { DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SEF_SLOW_MOTION)); } + @Test + public void startTransformation_completesWithValidBitrate() throws Exception { + AtomicReference<@NullableType TransformationResult> resultReference = new AtomicReference<>(); + Transformer.Listener listener = + new Transformer.Listener() { + @Override + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { + resultReference.set(transformationResult); + } + }; + Transformer transformer = + createTransformerBuilder(/* disableFallback= */ true).addListener(listener).build(); + MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_VIDEO); + + transformer.startTransformation(mediaItem, outputPath); + TransformerTestRunner.runUntilCompleted(transformer); + + @Nullable TransformationResult result = resultReference.get(); + assertThat(result).isNotNull(); + assertThat(result.averageAudioBitrate).isGreaterThan(0); + assertThat(result.averageVideoBitrate).isGreaterThan(0); + } + @Test public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError() throws Exception { diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTestRunner.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTestRunner.java index 1e837be970..86e2853c8d 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTestRunner.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerTestRunner.java @@ -78,7 +78,8 @@ public final class TransformerTestRunner { transformer.addListener( new Transformer.Listener() { @Override - public void onTransformationCompleted(MediaItem inputMediaItem) { + public void onTransformationCompleted( + MediaItem inputMediaItem, TransformationResult transformationResult) { transformationCompleted.set(true); }