Rename TransformationResult to ExportResult

Also replace some usages of deprecated Transformer listeners with the new
ones.

PiperOrigin-RevId: 507743860
This commit is contained in:
kimvde 2023-02-07 12:50:12 +00:00 committed by microkatz
parent 01d7bc7279
commit c434cc0c9f
20 changed files with 671 additions and 242 deletions

View File

@ -74,14 +74,15 @@ import androidx.media3.effect.TextureOverlay;
import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor; import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor;
import androidx.media3.exoplayer.util.DebugTextViewHelper; import androidx.media3.exoplayer.util.DebugTextViewHelper;
import androidx.media3.transformer.Composition;
import androidx.media3.transformer.DefaultEncoderFactory; import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.DefaultMuxer; import androidx.media3.transformer.DefaultMuxer;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.Effects; import androidx.media3.transformer.Effects;
import androidx.media3.transformer.ExportResult;
import androidx.media3.transformer.ProgressHolder; import androidx.media3.transformer.ProgressHolder;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.ui.AspectRatioFrameLayout; import androidx.media3.ui.AspectRatioFrameLayout;
import androidx.media3.ui.PlayerView; import androidx.media3.ui.PlayerView;
@ -307,15 +308,16 @@ public final class TransformerActivity extends AppCompatActivity {
.addListener( .addListener(
new Transformer.Listener() { new Transformer.Listener() {
@Override @Override
public void onTransformationCompleted( public void onCompleted(Composition composition, ExportResult exportResult) {
MediaItem mediaItem, TransformationResult result) { MediaItem mediaItem =
composition.sequences.get(0).editedMediaItems.get(0).mediaItem;
TransformerActivity.this.onTransformationCompleted(filePath, mediaItem); TransformerActivity.this.onTransformationCompleted(filePath, mediaItem);
} }
@Override @Override
public void onTransformationError( public void onError(
MediaItem mediaItem, Composition composition,
TransformationResult result, ExportResult exportResult,
TransformationException exception) { TransformationException exception) {
TransformerActivity.this.onTransformationError(exception); TransformerActivity.this.onTransformationError(exception);
} }

View File

@ -504,17 +504,17 @@ public final class AndroidTestUtil {
} }
/** /**
* Creates a {@link JSONArray} from {@link TransformationResult.ProcessedInput processed inputs}. * Creates a {@link JSONArray} from {@link ExportResult.ProcessedInput processed inputs}.
* *
* @param processedInputs The list of {@link TransformationResult.ProcessedInput} instances. * @param processedInputs The list of {@link ExportResult.ProcessedInput} instances.
* @return A {@link JSONArray} containing {@link JSONObject} instances representing the {@link * @return A {@link JSONArray} containing {@link JSONObject} instances representing the {@link
* TransformationResult.ProcessedInput} instances. * ExportResult.ProcessedInput} instances.
*/ */
public static JSONArray processedInputsAsJsonArray( public static JSONArray processedInputsAsJsonArray(
ImmutableList<TransformationResult.ProcessedInput> processedInputs) throws JSONException { ImmutableList<ExportResult.ProcessedInput> processedInputs) throws JSONException {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
for (int i = 0; i < processedInputs.size(); i++) { for (int i = 0; i < processedInputs.size(); i++) {
TransformationResult.ProcessedInput processedInput = processedInputs.get(i); ExportResult.ProcessedInput processedInput = processedInputs.get(i);
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
@Nullable @Nullable
MediaItem.LocalConfiguration localConfiguration = processedInput.mediaItem.localConfiguration; MediaItem.LocalConfiguration localConfiguration = processedInput.mediaItem.localConfiguration;

View File

@ -25,14 +25,14 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
/** A test only class for holding the details of a test transformation. */ /** A test only class for holding the details of a test export. */
public class TransformationTestResult { public class ExportTestResult {
/** Represents an unset or unknown SSIM score. */ /** Represents an unset or unknown SSIM score. */
public static final double SSIM_UNSET = -1.0d; public static final double SSIM_UNSET = -1.0d;
/** A builder for {@link TransformationTestResult}. */ /** A builder for {@link ExportTestResult}. */
public static class Builder { public static class Builder {
private final TransformationResult transformationResult; private final ExportResult exportResult;
@Nullable private String filePath; @Nullable private String filePath;
private long elapsedTimeMs; private long elapsedTimeMs;
@ -41,8 +41,8 @@ public class TransformationTestResult {
@Nullable private Exception analysisException; @Nullable private Exception analysisException;
/** Creates a new {@link Builder}. */ /** Creates a new {@link Builder}. */
public Builder(TransformationResult transformationResult) { public Builder(ExportResult exportResult) {
this.transformationResult = transformationResult; this.exportResult = exportResult;
this.elapsedTimeMs = C.TIME_UNSET; this.elapsedTimeMs = C.TIME_UNSET;
this.ssim = SSIM_UNSET; this.ssim = SSIM_UNSET;
} }
@ -62,8 +62,8 @@ public class TransformationTestResult {
} }
/** /**
* Sets the amount of time taken to perform the transformation in milliseconds. {@link * Sets the amount of time taken to perform the export in milliseconds. {@link C#TIME_UNSET} if
* C#TIME_UNSET} if unset. * unset.
* *
* <p>{@link C#TIME_UNSET} represents an unset or unknown value. * <p>{@link C#TIME_UNSET} represents an unset or unknown value.
* *
@ -92,7 +92,7 @@ public class TransformationTestResult {
/** /**
* Sets an {@link FallbackDetails} object that describes the fallbacks that occurred during * Sets an {@link FallbackDetails} object that describes the fallbacks that occurred during
* post-transformation analysis. * post-export analysis.
* *
* <p>{@code null} represents no fallback was applied. * <p>{@code null} represents no fallback was applied.
* *
@ -106,7 +106,7 @@ public class TransformationTestResult {
} }
/** /**
* Sets an {@link Exception} that occurred during post-transformation analysis. * Sets an {@link Exception} that occurred during post-export analysis.
* *
* <p>{@code null} represents an unset or unknown value. * <p>{@code null} represents an unset or unknown value.
* *
@ -119,20 +119,20 @@ public class TransformationTestResult {
return this; return this;
} }
/** Builds the {@link TransformationTestResult} instance. */ /** Builds the {@link ExportTestResult} instance. */
public TransformationTestResult build() { public ExportTestResult build() {
return new TransformationTestResult( return new ExportTestResult(
transformationResult, filePath, elapsedTimeMs, ssim, fallbackDetails, analysisException); exportResult, filePath, elapsedTimeMs, ssim, fallbackDetails, analysisException);
} }
} }
/** The {@link TransformationResult} of the transformation. */ /** The {@link ExportResult} of the export. */
public final TransformationResult transformationResult; public final ExportResult exportResult;
/** The path to the file created in the transformation, or {@code null} if unset. */ /** The path to the file created in the export, or {@code null} if unset. */
@Nullable public final String filePath; @Nullable public final String filePath;
/** /**
* The amount of time taken to perform the transformation in milliseconds, or {@link C#TIME_UNSET} * The amount of time taken to perform the export in milliseconds, or {@link C#TIME_UNSET} if
* if unset. * unset.
*/ */
public final long elapsedTimeMs; public final long elapsedTimeMs;
/** /**
@ -140,16 +140,16 @@ public class TransformationTestResult {
* C#RATE_UNSET} if unset. * C#RATE_UNSET} if unset.
*/ */
public final float throughputFps; public final float throughputFps;
/** The SSIM score of the transformation, or {@link #SSIM_UNSET} if unset. */ /** The SSIM score of the export, or {@link #SSIM_UNSET} if unset. */
public final double ssim; public final double ssim;
/** /**
* The {@link FallbackDetails} describing the fallbacks that occurred doing transformation, or * The {@link FallbackDetails} describing the fallbacks that occurred doing export, or {@code
* {@code null} if no fallback occurred. * null} if no fallback occurred.
*/ */
@Nullable public final FallbackDetails fallbackDetails; @Nullable public final FallbackDetails fallbackDetails;
/** /**
* The {@link Exception} thrown during post-transformation analysis, or {@code null} if nothing * The {@link Exception} thrown during post-export analysis, or {@code null} if nothing was
* was thrown. * thrown.
*/ */
@Nullable public final Exception analysisException; @Nullable public final Exception analysisException;
@ -157,80 +157,77 @@ public class TransformationTestResult {
public JSONObject asJsonObject() throws JSONException { public JSONObject asJsonObject() throws JSONException {
JSONObject jsonObject = JSONObject jsonObject =
new JSONObject() new JSONObject()
.putOpt("audioEncoderName", transformationResult.audioEncoderName) .putOpt("audioEncoderName", exportResult.audioEncoderName)
.putOpt( .putOpt(
"fallbackDetails", fallbackDetails != null ? fallbackDetails.asJsonObject() : null) "fallbackDetails", fallbackDetails != null ? fallbackDetails.asJsonObject() : null)
.putOpt("filePath", filePath) .putOpt("filePath", filePath)
.putOpt("colorInfo", transformationResult.colorInfo) .putOpt("colorInfo", exportResult.colorInfo)
.putOpt("videoEncoderName", transformationResult.videoEncoderName) .putOpt("videoEncoderName", exportResult.videoEncoderName)
.putOpt( .putOpt("testException", exceptionAsJsonObject(exportResult.transformationException))
"testException",
exceptionAsJsonObject(transformationResult.transformationException))
.putOpt("analysisException", exceptionAsJsonObject(analysisException)); .putOpt("analysisException", exceptionAsJsonObject(analysisException));
if (!transformationResult.processedInputs.isEmpty()) { if (!exportResult.processedInputs.isEmpty()) {
jsonObject.put( jsonObject.put("processedInputs", processedInputsAsJsonArray(exportResult.processedInputs));
"processedInputs", processedInputsAsJsonArray(transformationResult.processedInputs));
} }
if (transformationResult.averageAudioBitrate != C.RATE_UNSET_INT) { if (exportResult.averageAudioBitrate != C.RATE_UNSET_INT) {
jsonObject.put("averageAudioBitrate", transformationResult.averageAudioBitrate); jsonObject.put("averageAudioBitrate", exportResult.averageAudioBitrate);
} }
if (transformationResult.averageVideoBitrate != C.RATE_UNSET_INT) { if (exportResult.averageVideoBitrate != C.RATE_UNSET_INT) {
jsonObject.put("averageVideoBitrate", transformationResult.averageVideoBitrate); jsonObject.put("averageVideoBitrate", exportResult.averageVideoBitrate);
} }
if (transformationResult.channelCount != C.LENGTH_UNSET) { if (exportResult.channelCount != C.LENGTH_UNSET) {
jsonObject.put("channelCount", transformationResult.channelCount); jsonObject.put("channelCount", exportResult.channelCount);
} }
if (transformationResult.durationMs != C.TIME_UNSET) { if (exportResult.durationMs != C.TIME_UNSET) {
jsonObject.put("durationMs", transformationResult.durationMs); jsonObject.put("durationMs", exportResult.durationMs);
} }
if (elapsedTimeMs != C.TIME_UNSET) { if (elapsedTimeMs != C.TIME_UNSET) {
jsonObject.put("elapsedTimeMs", elapsedTimeMs); jsonObject.put("elapsedTimeMs", elapsedTimeMs);
} }
if (transformationResult.fileSizeBytes != C.LENGTH_UNSET) { if (exportResult.fileSizeBytes != C.LENGTH_UNSET) {
jsonObject.put("fileSizeBytes", transformationResult.fileSizeBytes); jsonObject.put("fileSizeBytes", exportResult.fileSizeBytes);
} }
if (transformationResult.height != C.LENGTH_UNSET) { if (exportResult.height != C.LENGTH_UNSET) {
jsonObject.put("height", transformationResult.height); jsonObject.put("height", exportResult.height);
} }
if (transformationResult.pcmEncoding != Format.NO_VALUE) { if (exportResult.pcmEncoding != Format.NO_VALUE) {
jsonObject.put("pcmEncoding", transformationResult.pcmEncoding); jsonObject.put("pcmEncoding", exportResult.pcmEncoding);
} }
if (transformationResult.sampleRate != C.RATE_UNSET_INT) { if (exportResult.sampleRate != C.RATE_UNSET_INT) {
jsonObject.put("sampleRate", transformationResult.sampleRate); jsonObject.put("sampleRate", exportResult.sampleRate);
} }
if (ssim != TransformationTestResult.SSIM_UNSET) { if (ssim != ExportTestResult.SSIM_UNSET) {
jsonObject.put("ssim", ssim); jsonObject.put("ssim", ssim);
} }
if (throughputFps != C.RATE_UNSET) { if (throughputFps != C.RATE_UNSET) {
jsonObject.put("throughputFps", throughputFps); jsonObject.put("throughputFps", throughputFps);
} }
if (transformationResult.videoFrameCount > 0) { if (exportResult.videoFrameCount > 0) {
jsonObject.put("videoFrameCount", transformationResult.videoFrameCount); jsonObject.put("videoFrameCount", exportResult.videoFrameCount);
} }
if (transformationResult.width != C.LENGTH_UNSET) { if (exportResult.width != C.LENGTH_UNSET) {
jsonObject.put("width", transformationResult.width); jsonObject.put("width", exportResult.width);
} }
return jsonObject; return jsonObject;
} }
private TransformationTestResult( private ExportTestResult(
TransformationResult transformationResult, ExportResult exportResult,
@Nullable String filePath, @Nullable String filePath,
long elapsedTimeMs, long elapsedTimeMs,
double ssim, double ssim,
@Nullable FallbackDetails fallbackDetails, @Nullable FallbackDetails fallbackDetails,
@Nullable Exception analysisException) { @Nullable Exception analysisException) {
this.transformationResult = transformationResult; this.exportResult = exportResult;
this.filePath = filePath; this.filePath = filePath;
this.elapsedTimeMs = elapsedTimeMs; this.elapsedTimeMs = elapsedTimeMs;
this.ssim = ssim; this.ssim = ssim;
this.fallbackDetails = fallbackDetails; this.fallbackDetails = fallbackDetails;
this.analysisException = analysisException; this.analysisException = analysisException;
this.throughputFps = this.throughputFps =
elapsedTimeMs != C.TIME_UNSET && transformationResult.videoFrameCount > 0 elapsedTimeMs != C.TIME_UNSET && exportResult.videoFrameCount > 0
? 1000f * transformationResult.videoFrameCount / elapsedTimeMs ? 1000f * exportResult.videoFrameCount / elapsedTimeMs
: C.RATE_UNSET; : C.RATE_UNSET;
} }
} }

View File

@ -174,36 +174,34 @@ public class TransformerAndroidTestRunner {
} }
/** /**
* Transforms the {@link EditedMediaItem}, saving a summary of the transformation to the * Exports the {@link EditedMediaItem}, saving a summary of the export to the application cache.
* application cache.
* *
* @param testId A unique identifier for the transformer test run. * @param testId A unique identifier for the transformer test run.
* @param editedMediaItem The {@link EditedMediaItem} to transform. * @param editedMediaItem The {@link EditedMediaItem} to export.
* @return The {@link TransformationTestResult}. * @return The {@link ExportTestResult}.
* @throws Exception The cause of the transformation not completing. * @throws Exception The cause of the export not completing.
*/ */
public TransformationTestResult run(String testId, EditedMediaItem editedMediaItem) public ExportTestResult run(String testId, EditedMediaItem editedMediaItem) throws Exception {
throws Exception {
JSONObject resultJson = new JSONObject(); JSONObject resultJson = new JSONObject();
if (inputValues != null) { if (inputValues != null) {
resultJson.put("inputValues", JSONObject.wrap(inputValues)); resultJson.put("inputValues", JSONObject.wrap(inputValues));
} }
try { try {
TransformationTestResult transformationTestResult = runInternal(testId, editedMediaItem); ExportTestResult exportTestResult = runInternal(testId, editedMediaItem);
resultJson.put("transformationResult", transformationTestResult.asJsonObject()); resultJson.put("exportResult", exportTestResult.asJsonObject());
if (transformationTestResult.transformationResult.transformationException != null) { if (exportTestResult.exportResult.transformationException != null) {
throw transformationTestResult.transformationResult.transformationException; throw exportTestResult.exportResult.transformationException;
} }
if (!suppressAnalysisExceptions && transformationTestResult.analysisException != null) { if (!suppressAnalysisExceptions && exportTestResult.analysisException != null) {
throw transformationTestResult.analysisException; throw exportTestResult.analysisException;
} }
return transformationTestResult; return exportTestResult;
} catch (InterruptedException } catch (InterruptedException
| IOException | IOException
| TimeoutException | TimeoutException
| UnsupportedOperationException e) { | UnsupportedOperationException e) {
resultJson.put( resultJson.put(
"transformationResult", "exportResult",
new JSONObject().put("testException", AndroidTestUtil.exceptionAsJsonObject(e))); new JSONObject().put("testException", AndroidTestUtil.exceptionAsJsonObject(e)));
throw e; throw e;
} finally { } finally {
@ -212,33 +210,32 @@ public class TransformerAndroidTestRunner {
} }
/** /**
* Transforms the {@link MediaItem}, saving a summary of the transformation to the application * Exports the {@link MediaItem}, saving a summary of the export to the application cache.
* cache.
* *
* @param testId A unique identifier for the transformer test run. * @param testId A unique identifier for the transformer test run.
* @param mediaItem The {@link MediaItem} to transform. * @param mediaItem The {@link MediaItem} to export.
* @return The {@link TransformationTestResult}. * @return The {@link ExportTestResult}.
* @throws Exception The cause of the transformation not completing. * @throws Exception The cause of the export not completing.
*/ */
public TransformationTestResult run(String testId, MediaItem mediaItem) throws Exception { public ExportTestResult run(String testId, MediaItem mediaItem) throws Exception {
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build(); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
return run(testId, editedMediaItem); return run(testId, editedMediaItem);
} }
/** /**
* Transforms the {@link EditedMediaItem}. * Exports the {@link EditedMediaItem}.
* *
* @param testId An identifier for the test. * @param testId An identifier for the test.
* @param editedMediaItem The {@link EditedMediaItem} to transform. * @param editedMediaItem The {@link EditedMediaItem} to export.
* @return The {@link TransformationTestResult}. * @return The {@link ExportTestResult}.
* @throws IllegalStateException See {@link Transformer#start(EditedMediaItem, String)}. * @throws IllegalStateException See {@link Transformer#start(EditedMediaItem, String)}.
* @throws InterruptedException If the thread is interrupted whilst waiting for transformer to * @throws InterruptedException If the thread is interrupted whilst waiting for transformer to
* complete. * complete.
* @throws IOException If an error occurs opening the output file for writing. * @throws IOException If an error occurs opening the output file for writing.
* @throws TimeoutException If the transformation has not completed after {@linkplain * @throws TimeoutException If the export has not completed after {@linkplain
* Builder#setTimeoutSeconds(int) the given timeout}. * Builder#setTimeoutSeconds(int) the given timeout}.
*/ */
private TransformationTestResult runInternal(String testId, EditedMediaItem editedMediaItem) private ExportTestResult runInternal(String testId, EditedMediaItem editedMediaItem)
throws InterruptedException, IOException, TimeoutException { throws InterruptedException, IOException, TimeoutException {
MediaItem mediaItem = editedMediaItem.mediaItem; MediaItem mediaItem = editedMediaItem.mediaItem;
if (!mediaItem.clippingConfiguration.equals(MediaItem.ClippingConfiguration.UNSET) if (!mediaItem.clippingConfiguration.equals(MediaItem.ClippingConfiguration.UNSET)
@ -258,8 +255,7 @@ public class TransformerAndroidTestRunner {
AtomicReference<@NullableType FallbackDetails> fallbackDetailsReference = AtomicReference<@NullableType FallbackDetails> fallbackDetailsReference =
new AtomicReference<>(); new AtomicReference<>();
AtomicReference<@NullableType Exception> unexpectedExceptionReference = new AtomicReference<>(); AtomicReference<@NullableType Exception> unexpectedExceptionReference = new AtomicReference<>();
AtomicReference<@NullableType TransformationResult> transformationResultReference = AtomicReference<@NullableType ExportResult> exportResultReference = new AtomicReference<>();
new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch countDownLatch = new CountDownLatch(1);
long startTimeMs = SystemClock.DEFAULT.elapsedRealtime(); long startTimeMs = SystemClock.DEFAULT.elapsedRealtime();
@ -269,18 +265,17 @@ public class TransformerAndroidTestRunner {
.addListener( .addListener(
new Transformer.Listener() { new Transformer.Listener() {
@Override @Override
public void onTransformationCompleted( public void onCompleted(Composition composition, ExportResult exportResult) {
MediaItem inputMediaItem, TransformationResult result) { exportResultReference.set(exportResult);
transformationResultReference.set(result);
countDownLatch.countDown(); countDownLatch.countDown();
} }
@Override @Override
public void onTransformationError( public void onError(
MediaItem inputMediaItem, Composition composition,
TransformationResult result, ExportResult exportResult,
TransformationException exception) { TransformationException exception) {
transformationResultReference.set(result); exportResultReference.set(exportResult);
countDownLatch.countDown(); countDownLatch.countDown();
} }
@ -333,19 +328,19 @@ public class TransformerAndroidTestRunner {
long elapsedTimeMs = SystemClock.DEFAULT.elapsedRealtime() - startTimeMs; long elapsedTimeMs = SystemClock.DEFAULT.elapsedRealtime() - startTimeMs;
@Nullable FallbackDetails fallbackDetails = fallbackDetailsReference.get(); @Nullable FallbackDetails fallbackDetails = fallbackDetailsReference.get();
TransformationResult transformationResult = checkNotNull(transformationResultReference.get()); ExportResult exportResult = checkNotNull(exportResultReference.get());
if (transformationResult.transformationException != null) { if (exportResult.transformationException != null) {
return new TransformationTestResult.Builder(transformationResult) return new ExportTestResult.Builder(exportResult)
.setElapsedTimeMs(elapsedTimeMs) .setElapsedTimeMs(elapsedTimeMs)
.setFallbackDetails(fallbackDetails) .setFallbackDetails(fallbackDetails)
.build(); .build();
} }
// No exceptions raised, transformation has succeeded. // No exceptions raised, transformation has succeeded.
TransformationTestResult.Builder testResultBuilder = ExportTestResult.Builder testResultBuilder =
new TransformationTestResult.Builder( new ExportTestResult.Builder(
checkNotNull(transformationResultReference.get()) checkNotNull(exportResultReference.get())
.buildUpon() .buildUpon()
.setFileSizeBytes(outputVideoFile.length()) .setFileSizeBytes(outputVideoFile.length())
.build()) .build())
@ -373,7 +368,7 @@ public class TransformerAndroidTestRunner {
} catch (InterruptedException interruptedException) { } catch (InterruptedException interruptedException) {
// InterruptedException is a special unexpected case because it is not related to Ssim // InterruptedException is a special unexpected case because it is not related to Ssim
// calculation, so it should be thrown, rather than processed as part of the // calculation, so it should be thrown, rather than processed as part of the
// TransformationTestResult. // ExportTestResult.
throw interruptedException; throw interruptedException;
} catch (Throwable analysisFailure) { } catch (Throwable analysisFailure) {
if (Util.SDK_INT == 21 && Ascii.toLowerCase(Util.MODEL).contains("nexus")) { if (Util.SDK_INT == 21 && Ascii.toLowerCase(Util.MODEL).contains("nexus")) {
@ -381,7 +376,7 @@ public class TransformerAndroidTestRunner {
Log.i(TAG, testId + ": Skipping SSIM calculation due to known device-specific issue"); Log.i(TAG, testId + ": Skipping SSIM calculation due to known device-specific issue");
} else { } else {
// Catch all (checked and unchecked) failures thrown by the SsimHelper and process them as // Catch all (checked and unchecked) failures thrown by the SsimHelper and process them as
// part of the TransformationTestResult. // part of the ExportTestResult.
Exception analysisException = Exception analysisException =
analysisFailure instanceof Exception analysisFailure instanceof Exception
? (Exception) analysisFailure ? (Exception) analysisFailure

View File

@ -53,12 +53,12 @@ public class TransformerAudioEndToEndTest {
.setEffects(effects) .setEffects(effects)
.build(); .build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build()) new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
assertThat(result.transformationResult.channelCount).isEqualTo(2); assertThat(result.exportResult.channelCount).isEqualTo(2);
} }
@Test @Test
@ -76,12 +76,12 @@ public class TransformerAudioEndToEndTest {
.setEffects(effects) .setEffects(effects)
.build(); .build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build()) new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
assertThat(result.transformationResult.pcmEncoding).isEqualTo(C.ENCODING_PCM_FLOAT); assertThat(result.exportResult.pcmEncoding).isEqualTo(C.ENCODING_PCM_FLOAT);
} }
@Test @Test
@ -100,12 +100,12 @@ public class TransformerAudioEndToEndTest {
.setEffects(effects) .setEffects(effects)
.build(); .build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build()) new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
assertThat(result.transformationResult.pcmEncoding).isEqualTo(C.ENCODING_PCM_16BIT); assertThat(result.exportResult.pcmEncoding).isEqualTo(C.ENCODING_PCM_16BIT);
} }
private static Effects createForAudioProcessors(AudioProcessor... audioProcessors) { private static Effects createForAudioProcessors(AudioProcessor... audioProcessors) {

View File

@ -57,12 +57,12 @@ public class TransformerEndToEndTest {
// ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4 // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4
int expectedFrameCount = 30; int expectedFrameCount = 30;
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(/* testId= */ "videoEditing_completesWithConsistentFrameCount", editedMediaItem); .run(/* testId= */ "videoEditing_completesWithConsistentFrameCount", editedMediaItem);
assertThat(result.transformationResult.videoFrameCount).isEqualTo(expectedFrameCount); assertThat(result.exportResult.videoFrameCount).isEqualTo(expectedFrameCount);
} }
@Test @Test
@ -79,12 +79,12 @@ public class TransformerEndToEndTest {
new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).setEffects(effects).build();
long expectedDurationMs = 967; long expectedDurationMs = 967;
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(/* testId= */ "videoOnly_completesWithConsistentDuration", editedMediaItem); .run(/* testId= */ "videoOnly_completesWithConsistentDuration", editedMediaItem);
assertThat(result.transformationResult.durationMs).isEqualTo(expectedDurationMs); assertThat(result.exportResult.durationMs).isEqualTo(expectedDurationMs);
} }
@Test @Test
@ -102,12 +102,12 @@ public class TransformerEndToEndTest {
.build()) .build())
.build(); .build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(/* testId= */ "clippedMedia_completesWithClippedDuration", mediaItem); .run(/* testId= */ "clippedMedia_completesWithClippedDuration", mediaItem);
assertThat(result.transformationResult.durationMs).isAtMost(clippingEndMs - clippingStartMs); assertThat(result.exportResult.durationMs).isAtMost(clippingEndMs - clippingStartMs);
} }
@Test @Test

View File

@ -27,9 +27,9 @@ import androidx.media3.common.C;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -69,11 +69,11 @@ public class ForceInterpretHdrVideoAsSdrTest {
.build(); .build();
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
Log.i(TAG, "Transformed."); Log.i(TAG, "Transformed.");
} catch (TransformationException exception) { } catch (TransformationException exception) {
if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED
@ -106,11 +106,11 @@ public class ForceInterpretHdrVideoAsSdrTest {
.build(); .build();
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
Log.i(TAG, "Transformed."); Log.i(TAG, "Transformed.");
} catch (TransformationException exception) { } catch (TransformationException exception) {
if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED

View File

@ -36,9 +36,9 @@ import androidx.media3.effect.ScaleToFitTransformation;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.Effects; import androidx.media3.transformer.Effects;
import androidx.media3.transformer.EncoderUtil; import androidx.media3.transformer.EncoderUtil;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -77,12 +77,12 @@ public class HdrEditingTest {
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Transformed."); Log.i(TAG, "Transformed.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_ST2084); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_ST2084);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -102,12 +102,12 @@ public class HdrEditingTest {
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Transformed."); Log.i(TAG, "Transformed.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_HLG); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_HLG);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -136,11 +136,11 @@ public class HdrEditingTest {
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_ST2084); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_ST2084);
} }
@Test @Test
@ -161,11 +161,11 @@ public class HdrEditingTest {
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_HLG); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_HLG);
} }
@Test @Test
@ -206,13 +206,13 @@ public class HdrEditingTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertThat(isToneMappingFallbackApplied.get()).isTrue(); assertThat(isToneMappingFallbackApplied.get()).isTrue();
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -260,13 +260,13 @@ public class HdrEditingTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertThat(isToneMappingFallbackApplied.get()).isTrue(); assertThat(isToneMappingFallbackApplied.get()).isTrue();
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);

View File

@ -26,8 +26,8 @@ import androidx.media3.effect.ScaleToFitTransformation;
import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.Effects; import androidx.media3.transformer.Effects;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -65,10 +65,10 @@ public final class RepeatedTranscodeTest {
Set<Long> differentOutputSizesBytes = new HashSet<>(); Set<Long> differentOutputSizesBytes = new HashSet<>();
for (int i = 0; i < TRANSCODE_COUNT; i++) { 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. // Use a long video in case an error occurs a while after the start of the video.
TransformationTestResult testResult = ExportTestResult testResult =
transformerRunner.run( transformerRunner.run(
/* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, editedMediaItem); /* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i, editedMediaItem);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); differentOutputSizesBytes.add(checkNotNull(testResult.exportResult.fileSizeBytes));
} }
assertWithMessage( assertWithMessage(
@ -98,11 +98,11 @@ public final class RepeatedTranscodeTest {
Set<Long> differentOutputSizesBytes = new HashSet<>(); Set<Long> differentOutputSizesBytes = new HashSet<>();
for (int i = 0; i < TRANSCODE_COUNT; i++) { 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. // Use a long video in case an error occurs a while after the start of the video.
TransformationTestResult testResult = ExportTestResult testResult =
transformerRunner.run( transformerRunner.run(
/* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, /* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i,
editedMediaItem); editedMediaItem);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); differentOutputSizesBytes.add(checkNotNull(testResult.exportResult.fileSizeBytes));
} }
assertWithMessage( assertWithMessage(
@ -130,11 +130,11 @@ public final class RepeatedTranscodeTest {
Set<Long> differentOutputSizesBytes = new HashSet<>(); Set<Long> differentOutputSizesBytes = new HashSet<>();
for (int i = 0; i < TRANSCODE_COUNT; i++) { 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. // Use a long video in case an error occurs a while after the start of the video.
TransformationTestResult testResult = ExportTestResult testResult =
transformerRunner.run( transformerRunner.run(
/* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i, /* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i,
editedMediaItem); editedMediaItem);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes)); differentOutputSizesBytes.add(checkNotNull(testResult.exportResult.fileSizeBytes));
} }
assertWithMessage( assertWithMessage(

View File

@ -30,9 +30,9 @@ import androidx.media3.common.util.Log;
import androidx.media3.effect.ScaleToFitTransformation; import androidx.media3.effect.ScaleToFitTransformation;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.Effects; import androidx.media3.transformer.Effects;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -78,12 +78,12 @@ public class ToneMapHdrToSdrUsingMediaCodecTest {
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -120,12 +120,12 @@ public class ToneMapHdrToSdrUsingMediaCodecTest {
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -167,12 +167,12 @@ public class ToneMapHdrToSdrUsingMediaCodecTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
@ -214,12 +214,12 @@ public class ToneMapHdrToSdrUsingMediaCodecTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build(); new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);

View File

@ -32,9 +32,9 @@ import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationException; import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -87,12 +87,12 @@ public class ToneMapHdrToSdrUsingOpenGlTest {
.build(); .build();
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_5_SECOND_HLG10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED) { if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED) {
@ -138,12 +138,12 @@ public class ToneMapHdrToSdrUsingOpenGlTest {
.build(); .build();
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10));
try { try {
TransformationTestResult transformationTestResult = ExportTestResult exportTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.build() .build()
.run(testId, mediaItem); .run(testId, mediaItem);
Log.i(TAG, "Tone mapped."); Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) { } catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED) { if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED) {

View File

@ -25,8 +25,8 @@ import androidx.media3.common.MimeTypes;
import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.DefaultEncoderFactory; import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.EditedMediaItem;
import androidx.media3.transformer.ExportTestResult;
import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer; import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner; import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.media3.transformer.VideoEncoderSettings; import androidx.media3.transformer.VideoEncoderSettings;
@ -70,13 +70,13 @@ public final class TranscodeQualityTest {
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build(); new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.setRequestCalculateSsim(true) .setRequestCalculateSsim(true)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
if (result.ssim != TransformationTestResult.SSIM_UNSET) { if (result.ssim != ExportTestResult.SSIM_UNSET) {
assertThat(result.ssim).isGreaterThan(0.90); assertThat(result.ssim).isGreaterThan(0.90);
} }
} }
@ -108,13 +108,13 @@ public final class TranscodeQualityTest {
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build(); new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.setRequestCalculateSsim(true) .setRequestCalculateSsim(true)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
if (result.ssim != TransformationTestResult.SSIM_UNSET) { if (result.ssim != ExportTestResult.SSIM_UNSET) {
assertThat(result.ssim).isGreaterThan(0.90); assertThat(result.ssim).isGreaterThan(0.90);
} }
} }
@ -140,13 +140,13 @@ public final class TranscodeQualityTest {
EditedMediaItem editedMediaItem = EditedMediaItem editedMediaItem =
new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build(); new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build();
TransformationTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer) new TransformerAndroidTestRunner.Builder(context, transformer)
.setRequestCalculateSsim(true) .setRequestCalculateSsim(true)
.build() .build()
.run(testId, editedMediaItem); .run(testId, editedMediaItem);
if (result.ssim != TransformationTestResult.SSIM_UNSET) { if (result.ssim != ExportTestResult.SSIM_UNSET) {
assertThat(result.ssim).isGreaterThan(0.90); assertThat(result.ssim).isGreaterThan(0.90);
} }
} }

View File

@ -43,7 +43,7 @@ import static androidx.media3.transformer.AndroidTestUtil.MP4_REMOTE_854W_480H_3
import static androidx.media3.transformer.AndroidTestUtil.MP4_REMOTE_854W_480H_30_SECOND_ROOF_REDMINOTE9_DOWNSAMPLED; import static androidx.media3.transformer.AndroidTestUtil.MP4_REMOTE_854W_480H_30_SECOND_ROOF_REDMINOTE9_DOWNSAMPLED;
import static androidx.media3.transformer.AndroidTestUtil.getFormatForTestFile; import static androidx.media3.transformer.AndroidTestUtil.getFormatForTestFile;
import static androidx.media3.transformer.AndroidTestUtil.skipAndLogIfInsufficientCodecSupport; import static androidx.media3.transformer.AndroidTestUtil.skipAndLogIfInsufficientCodecSupport;
import static androidx.media3.transformer.TransformationTestResult.SSIM_UNSET; import static androidx.media3.transformer.ExportTestResult.SSIM_UNSET;
import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getLast;
import android.content.Context; import android.content.Context;

View File

@ -54,7 +54,7 @@ import java.util.concurrent.atomic.AtomicLong;
private final Listener compositeAssetLoaderListener; private final Listener compositeAssetLoaderListener;
private final Map<Integer, SampleConsumer> sampleConsumersByTrackType; private final Map<Integer, SampleConsumer> sampleConsumersByTrackType;
private final Map<Integer, OnMediaItemChangedListener> mediaItemChangedListenersByTrackType; private final Map<Integer, OnMediaItemChangedListener> mediaItemChangedListenersByTrackType;
private final ImmutableList.Builder<TransformationResult.ProcessedInput> processedInputsBuilder; private final ImmutableList.Builder<ExportResult.ProcessedInput> processedInputsBuilder;
private final AtomicLong totalDurationUs; private final AtomicLong totalDurationUs;
private final AtomicInteger nonEndedTracks; private final AtomicInteger nonEndedTracks;
@ -114,10 +114,9 @@ import java.util.concurrent.atomic.AtomicLong;
} }
/** /**
* Returns the partially or entirely {@linkplain TransformationResult.ProcessedInput processed * Returns the partially or entirely {@linkplain ExportResult.ProcessedInput processed inputs}.
* inputs}.
*/ */
public ImmutableList<TransformationResult.ProcessedInput> getProcessedInputs() { public ImmutableList<ExportResult.ProcessedInput> getProcessedInputs() {
addCurrentProcessedInput(); addCurrentProcessedInput();
return processedInputsBuilder.build(); return processedInputsBuilder.build();
} }
@ -212,7 +211,7 @@ import java.util.concurrent.atomic.AtomicLong;
MediaItem mediaItem = editedMediaItems.get(currentMediaItemIndex).mediaItem; MediaItem mediaItem = editedMediaItems.get(currentMediaItemIndex).mediaItem;
ImmutableMap<Integer, String> decoders = currentAssetLoader.getDecoderNames(); ImmutableMap<Integer, String> decoders = currentAssetLoader.getDecoderNames();
processedInputsBuilder.add( processedInputsBuilder.add(
new TransformationResult.ProcessedInput( new ExportResult.ProcessedInput(
mediaItem, decoders.get(C.TRACK_TYPE_AUDIO), decoders.get(C.TRACK_TYPE_VIDEO))); mediaItem, decoders.get(C.TRACK_TYPE_AUDIO), decoders.get(C.TRACK_TYPE_VIDEO)));
processedInputsSize++; processedInputsSize++;
} }

View File

@ -0,0 +1,401 @@
/*
* 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 androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.Format;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Information about the result of an export. */
@UnstableApi
public final class ExportResult {
/** A builder for {@link ExportResult} instances. */
public static final class Builder {
private ImmutableList<ProcessedInput> processedInputs;
private long durationMs;
private long fileSizeBytes;
private int averageAudioBitrate;
private int channelCount;
private @C.PcmEncoding int pcmEncoding;
private int sampleRate;
@Nullable private String audioEncoderName;
private int averageVideoBitrate;
@Nullable ColorInfo colorInfo;
private int height;
private int width;
private int videoFrameCount;
@Nullable private String videoEncoderName;
@Nullable private TransformationException transformationException;
/** Creates a builder. */
public Builder() {
processedInputs = ImmutableList.of();
durationMs = C.TIME_UNSET;
fileSizeBytes = C.LENGTH_UNSET;
averageAudioBitrate = C.RATE_UNSET_INT;
channelCount = C.LENGTH_UNSET;
pcmEncoding = Format.NO_VALUE;
sampleRate = C.RATE_UNSET_INT;
averageVideoBitrate = C.RATE_UNSET_INT;
height = C.LENGTH_UNSET;
width = C.LENGTH_UNSET;
}
/** Sets the {@linkplain ProcessedInput processed inputs}. */
@CanIgnoreReturnValue
public Builder setProcessedInputs(ImmutableList<ProcessedInput> processedInputs) {
this.processedInputs = processedInputs;
return this;
}
/**
* Sets the duration of the output in milliseconds.
*
* <p>Must be positive or {@link C#TIME_UNSET}.
*/
@CanIgnoreReturnValue
public Builder setDurationMs(long durationMs) {
checkArgument(durationMs >= 0 || durationMs == C.TIME_UNSET);
this.durationMs = durationMs;
return this;
}
/**
* Sets the file size in bytes.
*
* <p>Must be positive or {@link C#LENGTH_UNSET}.
*/
@CanIgnoreReturnValue
public Builder setFileSizeBytes(long fileSizeBytes) {
checkArgument(fileSizeBytes > 0 || fileSizeBytes == C.LENGTH_UNSET);
this.fileSizeBytes = fileSizeBytes;
return this;
}
/**
* Sets the average audio bitrate.
*
* <p>Must be positive or {@link C#RATE_UNSET_INT}.
*/
@CanIgnoreReturnValue
public Builder setAverageAudioBitrate(int averageAudioBitrate) {
checkArgument(averageAudioBitrate > 0 || averageAudioBitrate == C.RATE_UNSET_INT);
this.averageAudioBitrate = averageAudioBitrate;
return this;
}
/**
* Sets the channel count.
*
* <p>Must be positive or {@link C#LENGTH_UNSET}.
*/
@CanIgnoreReturnValue
public Builder setChannelCount(int channelCount) {
checkArgument(channelCount > 0 || channelCount == C.LENGTH_UNSET);
this.channelCount = channelCount;
return this;
}
/** Sets the {@link C.PcmEncoding}. */
@CanIgnoreReturnValue
public Builder setPcmEncoding(@C.PcmEncoding int pcmEncoding) {
this.pcmEncoding = pcmEncoding;
return this;
}
/**
* Sets the sample rate.
*
* <p>Must be positive or {@link C#RATE_UNSET_INT}.
*/
@CanIgnoreReturnValue
public Builder setSampleRate(int sampleRate) {
checkArgument(sampleRate > 0 || sampleRate == C.RATE_UNSET_INT);
this.sampleRate = sampleRate;
return this;
}
/** Sets the name of the audio encoder used. */
@CanIgnoreReturnValue
public Builder setAudioEncoderName(@Nullable String audioEncoderName) {
this.audioEncoderName = audioEncoderName;
return this;
}
/**
* Sets the average video bitrate.
*
* <p>Must be positive or {@link C#RATE_UNSET_INT}.
*/
@CanIgnoreReturnValue
public Builder setAverageVideoBitrate(int averageVideoBitrate) {
checkArgument(averageVideoBitrate > 0 || averageVideoBitrate == C.RATE_UNSET_INT);
this.averageVideoBitrate = averageVideoBitrate;
return this;
}
/** Sets the {@link ColorInfo}. */
@CanIgnoreReturnValue
public Builder setColorInfo(@Nullable ColorInfo colorInfo) {
this.colorInfo = colorInfo;
return this;
}
/**
* Sets the height.
*
* <p>Must be positive or {@link C#LENGTH_UNSET}.
*/
@CanIgnoreReturnValue
public Builder setHeight(int height) {
checkArgument(height > 0 || height == C.LENGTH_UNSET);
this.height = height;
return this;
}
/**
* Sets the width.
*
* <p>Must be positive or {@link C#LENGTH_UNSET}.
*/
@CanIgnoreReturnValue
public Builder setWidth(int width) {
checkArgument(width > 0 || width == C.LENGTH_UNSET);
this.width = width;
return this;
}
/**
* Sets the number of video frames.
*
* <p>Must be positive or {@code 0}.
*/
@CanIgnoreReturnValue
public Builder setVideoFrameCount(int videoFrameCount) {
checkArgument(videoFrameCount >= 0);
this.videoFrameCount = videoFrameCount;
return this;
}
/** Sets the name of the video encoder used. */
@CanIgnoreReturnValue
public Builder setVideoEncoderName(@Nullable String videoEncoderName) {
this.videoEncoderName = videoEncoderName;
return this;
}
/** Sets the {@link TransformationException} that caused the export to fail. */
@CanIgnoreReturnValue
public Builder setTransformationException(
@Nullable TransformationException transformationException) {
this.transformationException = transformationException;
return this;
}
/** Builds an {@link ExportResult} instance. */
public ExportResult build() {
return new ExportResult(
processedInputs,
durationMs,
fileSizeBytes,
averageAudioBitrate,
channelCount,
pcmEncoding,
sampleRate,
audioEncoderName,
averageVideoBitrate,
colorInfo,
height,
width,
videoFrameCount,
videoEncoderName,
transformationException);
}
}
/** An input entirely or partially processed. */
public static final class ProcessedInput {
/** The processed {@link MediaItem}. */
public final MediaItem mediaItem;
/**
* The name of the audio decoder used to process {@code mediaItem}. This field is {@code null}
* if no audio decoder was used.
*/
public final @MonotonicNonNull String audioDecoderName;
/**
* The name of the video decoder used to process {@code mediaItem}. This field is {@code null}
* if no video decoder was used.
*/
public final @MonotonicNonNull String videoDecoderName;
/** Creates an instance. */
public ProcessedInput(
MediaItem mediaItem, @Nullable String audioDecoderName, @Nullable String videoDecoderName) {
this.mediaItem = mediaItem;
this.audioDecoderName = audioDecoderName;
this.videoDecoderName = videoDecoderName;
}
}
/** The list of {@linkplain ProcessedInput processed inputs}. */
public final ImmutableList<ProcessedInput> processedInputs;
/** The duration of the file in milliseconds, or {@link C#TIME_UNSET} if unset or unknown. */
public final long durationMs;
/** 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 channel count of the audio, or {@link C#LENGTH_UNSET} if unset or unknown. */
public final int channelCount;
/** The {@link C.PcmEncoding} of the audio, or {@link Format#NO_VALUE} if unset or unknown. */
public final @C.PcmEncoding int pcmEncoding;
/** The sample rate of the audio, or {@link C#RATE_UNSET_INT} if unset or unknown. */
public final int sampleRate;
/** The name of the audio encoder used, or {@code null} if none were used. */
@Nullable public final String audioEncoderName;
/**
* The average bitrate of the video track data, or {@link C#RATE_UNSET_INT} if unset or unknown.
*/
public final int averageVideoBitrate;
/** The {@link ColorInfo} of the video, or {@code null} if unset or unknown. */
@Nullable public final ColorInfo colorInfo;
/** The height of the video, or {@link C#LENGTH_UNSET} if unset or unknown. */
public final int height;
/** The width of the video, or {@link C#LENGTH_UNSET} if unset or unknown. */
public final int width;
/** The number of video frames. */
public final int videoFrameCount;
/** The name of the video encoder used, or {@code null} if none were used. */
@Nullable public final String videoEncoderName;
/**
* The {@link TransformationException} that caused the export to fail, or {@code null} if the
* export was a success.
*/
@Nullable public final TransformationException transformationException;
private ExportResult(
ImmutableList<ProcessedInput> processedInputs,
long durationMs,
long fileSizeBytes,
int averageAudioBitrate,
int channelCount,
@C.PcmEncoding int pcmEncoding,
int sampleRate,
@Nullable String audioEncoderName,
int averageVideoBitrate,
@Nullable ColorInfo colorInfo,
int height,
int width,
int videoFrameCount,
@Nullable String videoEncoderName,
@Nullable TransformationException transformationException) {
this.processedInputs = processedInputs;
this.durationMs = durationMs;
this.fileSizeBytes = fileSizeBytes;
this.averageAudioBitrate = averageAudioBitrate;
this.channelCount = channelCount;
this.pcmEncoding = pcmEncoding;
this.sampleRate = sampleRate;
this.audioEncoderName = audioEncoderName;
this.averageVideoBitrate = averageVideoBitrate;
this.colorInfo = colorInfo;
this.height = height;
this.width = width;
this.videoFrameCount = videoFrameCount;
this.videoEncoderName = videoEncoderName;
this.transformationException = transformationException;
}
public Builder buildUpon() {
return new Builder()
.setProcessedInputs(processedInputs)
.setDurationMs(durationMs)
.setFileSizeBytes(fileSizeBytes)
.setAverageAudioBitrate(averageAudioBitrate)
.setChannelCount(channelCount)
.setPcmEncoding(pcmEncoding)
.setSampleRate(sampleRate)
.setAudioEncoderName(audioEncoderName)
.setAverageVideoBitrate(averageVideoBitrate)
.setColorInfo(colorInfo)
.setHeight(height)
.setWidth(width)
.setVideoFrameCount(videoFrameCount)
.setVideoEncoderName(videoEncoderName)
.setTransformationException(transformationException);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ExportResult)) {
return false;
}
ExportResult result = (ExportResult) o;
return Objects.equals(processedInputs, result.processedInputs)
&& durationMs == result.durationMs
&& fileSizeBytes == result.fileSizeBytes
&& averageAudioBitrate == result.averageAudioBitrate
&& channelCount == result.channelCount
&& pcmEncoding == result.pcmEncoding
&& sampleRate == result.sampleRate
&& Objects.equals(audioEncoderName, result.audioEncoderName)
&& averageVideoBitrate == result.averageVideoBitrate
&& Objects.equals(colorInfo, result.colorInfo)
&& height == result.height
&& width == result.width
&& videoFrameCount == result.videoFrameCount
&& Objects.equals(videoEncoderName, result.videoEncoderName)
&& Objects.equals(transformationException, result.transformationException);
}
@Override
public int hashCode() {
int result = Objects.hashCode(processedInputs);
result = 31 * result + (int) durationMs;
result = 31 * result + (int) fileSizeBytes;
result = 31 * result + averageAudioBitrate;
result = 31 * result + channelCount;
result = 31 * result + pcmEncoding;
result = 31 * result + sampleRate;
result = 31 * result + Objects.hashCode(audioEncoderName);
result = 31 * result + averageVideoBitrate;
result = 31 * result + Objects.hashCode(colorInfo);
result = 31 * result + height;
result = 31 * result + width;
result = 31 * result + videoFrameCount;
result = 31 * result + Objects.hashCode(videoEncoderName);
result = 31 * result + Objects.hashCode(transformationException);
return result;
}
}

View File

@ -28,10 +28,16 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Objects; import java.util.Objects;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Information about the result of a transformation. */ /**
* @deprecated Use {@link ExportResult} instead.
*/
@Deprecated
@UnstableApi @UnstableApi
public final class TransformationResult { public final class TransformationResult {
/** A builder for {@link TransformationResult} instances. */ /**
* @deprecated Use {@link ExportResult.Builder} instead.
*/
@Deprecated
public static final class Builder { public static final class Builder {
private ImmutableList<ProcessedInput> processedInputs; private ImmutableList<ProcessedInput> processedInputs;
private long durationMs; private long durationMs;
@ -63,6 +69,34 @@ public final class TransformationResult {
width = C.LENGTH_UNSET; width = C.LENGTH_UNSET;
} }
/** Creates a builder from an {@link ExportResult}. */
/* package */ Builder(ExportResult exportResult) {
ImmutableList.Builder<ProcessedInput> processedInputsBuilder = new ImmutableList.Builder<>();
for (int i = 0; i < exportResult.processedInputs.size(); i++) {
ExportResult.ProcessedInput processedInput = exportResult.processedInputs.get(i);
processedInputsBuilder.add(
new ProcessedInput(
processedInput.mediaItem,
processedInput.audioDecoderName,
processedInput.videoDecoderName));
}
processedInputs = processedInputsBuilder.build();
durationMs = exportResult.durationMs;
fileSizeBytes = exportResult.fileSizeBytes;
averageAudioBitrate = exportResult.averageAudioBitrate;
channelCount = exportResult.channelCount;
pcmEncoding = exportResult.pcmEncoding;
sampleRate = exportResult.sampleRate;
audioEncoderName = exportResult.audioEncoderName;
averageVideoBitrate = exportResult.averageVideoBitrate;
colorInfo = exportResult.colorInfo;
height = exportResult.height;
width = exportResult.width;
videoFrameCount = exportResult.videoFrameCount;
videoEncoderName = exportResult.videoEncoderName;
transformationException = exportResult.transformationException;
}
/** Sets the {@linkplain ProcessedInput processed inputs}. */ /** Sets the {@linkplain ProcessedInput processed inputs}. */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setProcessedInputs(ImmutableList<ProcessedInput> processedInputs) { public Builder setProcessedInputs(ImmutableList<ProcessedInput> processedInputs) {
@ -235,7 +269,10 @@ public final class TransformationResult {
} }
} }
/** An input entirely or partially processed. */ /**
* @deprecated Use {@link ExportResult.ProcessedInput} instead.
*/
@Deprecated
public static final class ProcessedInput { public static final class ProcessedInput {
/** The processed {@link MediaItem}. */ /** The processed {@link MediaItem}. */
public final MediaItem mediaItem; public final MediaItem mediaItem;

View File

@ -446,13 +446,13 @@ public final class Transformer {
public interface Listener { public interface Listener {
/** /**
* @deprecated Use {@link #onCompleted(Composition, TransformationResult)} instead. * @deprecated Use {@link #onCompleted(Composition, ExportResult)} instead.
*/ */
@Deprecated @Deprecated
default void onTransformationCompleted(MediaItem inputMediaItem) {} default void onTransformationCompleted(MediaItem inputMediaItem) {}
/** /**
* @deprecated Use {@link #onCompleted(Composition, TransformationResult)} instead. * @deprecated Use {@link #onCompleted(Composition, ExportResult)} instead.
*/ */
@Deprecated @Deprecated
default void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult result) { default void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult result) {
@ -463,23 +463,22 @@ public final class Transformer {
* Called when the export is completed successfully. * Called when the export is completed successfully.
* *
* @param composition The {@link Composition} for which the export is completed. * @param composition The {@link Composition} for which the export is completed.
* @param result The {@link TransformationResult} of the export. * @param exportResult The {@link ExportResult} of the export.
*/ */
default void onCompleted(Composition composition, TransformationResult result) { @SuppressWarnings("deprecation") // Calling deprecated listener method.
default void onCompleted(Composition composition, ExportResult exportResult) {
MediaItem mediaItem = composition.sequences.get(0).editedMediaItems.get(0).mediaItem; MediaItem mediaItem = composition.sequences.get(0).editedMediaItems.get(0).mediaItem;
onTransformationCompleted(mediaItem, result); onTransformationCompleted(mediaItem, new TransformationResult.Builder(exportResult).build());
} }
/** /**
* @deprecated Use {@link #onError(Composition, TransformationResult, TransformationException)} * @deprecated Use {@link #onError(Composition, ExportResult, TransformationException)} instead.
* instead.
*/ */
@Deprecated @Deprecated
default void onTransformationError(MediaItem inputMediaItem, Exception exception) {} default void onTransformationError(MediaItem inputMediaItem, Exception exception) {}
/** /**
* @deprecated Use {@link #onError(Composition, TransformationResult, TransformationException)} * @deprecated Use {@link #onError(Composition, ExportResult, TransformationException)} instead.
* instead.
*/ */
@Deprecated @Deprecated
default void onTransformationError( default void onTransformationError(
@ -488,8 +487,7 @@ public final class Transformer {
} }
/** /**
* @deprecated Use {@link #onError(Composition, TransformationResult, TransformationException)} * @deprecated Use {@link #onError(Composition, ExportResult, TransformationException)} instead.
* instead.
*/ */
@Deprecated @Deprecated
default void onTransformationError( default void onTransformationError(
@ -501,15 +499,17 @@ public final class Transformer {
* Called if an exception occurs during the export. * Called if an exception occurs during the export.
* *
* @param composition The {@link Composition} for which the exception occurs. * @param composition The {@link Composition} for which the exception occurs.
* @param result The {@link TransformationResult} of the export. * @param exportResult The {@link ExportResult} of the export.
* @param exception The {@link TransformationException} describing the exception. This is the * @param exception The {@link TransformationException} describing the exception. This is the
* same instance as the {@linkplain TransformationResult#transformationException exception} * same instance as the {@linkplain ExportResult#transformationException exception} in
* in {@code result}. * {@code result}.
*/ */
@SuppressWarnings("deprecation") // Calling deprecated listener method.
default void onError( default void onError(
Composition composition, TransformationResult result, TransformationException exception) { Composition composition, ExportResult exportResult, TransformationException exception) {
MediaItem mediaItem = composition.sequences.get(0).editedMediaItems.get(0).mediaItem; MediaItem mediaItem = composition.sequences.get(0).editedMediaItems.get(0).mediaItem;
onTransformationError(mediaItem, result, exception); onTransformationError(
mediaItem, new TransformationResult.Builder(exportResult).build(), exception);
} }
/** /**
@ -534,6 +534,7 @@ public final class Transformer {
* TransformationRequest#videoMimeType}, {@link TransformationRequest#outputHeight}, and * TransformationRequest#videoMimeType}, {@link TransformationRequest#outputHeight}, and
* {@link TransformationRequest#hdrMode} values set. * {@link TransformationRequest#hdrMode} values set.
*/ */
@SuppressWarnings("deprecation") // Calling deprecated listener method.
default void onFallbackApplied( default void onFallbackApplied(
Composition composition, Composition composition,
TransformationRequest originalTransformationRequest, TransformationRequest originalTransformationRequest,
@ -792,8 +793,8 @@ public final class Transformer {
* Returns the current {@link ProgressState} and updates {@code progressHolder} with the current * Returns the current {@link ProgressState} and updates {@code progressHolder} with the current
* progress if it is {@link #PROGRESS_STATE_AVAILABLE available}. * progress if it is {@link #PROGRESS_STATE_AVAILABLE available}.
* *
* <p>After an export {@linkplain Listener#onCompleted(Composition,TransformationResult) * <p>After an export {@linkplain Listener#onCompleted(Composition, ExportResult) completes}, this
* completes}, this method returns {@link #PROGRESS_STATE_NOT_STARTED}. * method returns {@link #PROGRESS_STATE_NOT_STARTED}.
* *
* @param progressHolder A {@link ProgressHolder}, updated to hold the percentage progress if * @param progressHolder A {@link ProgressHolder}, updated to hold the percentage progress if
* {@link #PROGRESS_STATE_AVAILABLE available}. * {@link #PROGRESS_STATE_AVAILABLE available}.
@ -839,21 +840,21 @@ public final class Transformer {
} }
@Override @Override
public void onCompleted(TransformationResult transformationResult) { public void onCompleted(ExportResult exportResult) {
// TODO(b/213341814): Add event flags for Transformer events. // TODO(b/213341814): Add event flags for Transformer events.
transformerInternal = null; transformerInternal = null;
listeners.queueEvent( listeners.queueEvent(
/* eventFlag= */ C.INDEX_UNSET, /* eventFlag= */ C.INDEX_UNSET,
listener -> listener.onCompleted(composition, transformationResult)); listener -> listener.onCompleted(composition, exportResult));
listeners.flushEvents(); listeners.flushEvents();
} }
@Override @Override
public void onError(TransformationResult result, TransformationException exception) { public void onError(ExportResult exportResult, TransformationException exception) {
transformerInternal = null; transformerInternal = null;
listeners.queueEvent( listeners.queueEvent(
/* eventFlag= */ C.INDEX_UNSET, /* eventFlag= */ C.INDEX_UNSET,
listener -> listener.onError(composition, result, exception)); listener -> listener.onError(composition, exportResult, exception));
listeners.flushEvents(); listeners.flushEvents();
} }
} }

View File

@ -57,9 +57,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public interface Listener { public interface Listener {
void onCompleted(TransformationResult result); void onCompleted(ExportResult exportResult);
void onError(TransformationResult result, TransformationException exception); void onError(ExportResult exportResult, TransformationException exception);
} }
/** /**
@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final List<SamplePipeline> samplePipelines; private final List<SamplePipeline> samplePipelines;
private final MuxerWrapper muxerWrapper; private final MuxerWrapper muxerWrapper;
private final ConditionVariable transformerConditionVariable; private final ConditionVariable transformerConditionVariable;
private final TransformationResult.Builder transformationResultBuilder; private final ExportResult.Builder exportResultBuilder;
private boolean generateSilentAudio; private boolean generateSilentAudio;
private boolean isDrainingPipelines; private boolean isDrainingPipelines;
@ -142,7 +142,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
samplePipelines = new ArrayList<>(); samplePipelines = new ArrayList<>();
muxerWrapper = new MuxerWrapper(outputPath, muxerFactory, componentListener); muxerWrapper = new MuxerWrapper(outputPath, muxerFactory, componentListener);
transformerConditionVariable = new ConditionVariable(); transformerConditionVariable = new ConditionVariable();
transformationResultBuilder = new TransformationResult.Builder(); exportResultBuilder = new ExportResult.Builder();
// It's safe to use "this" because we don't send a message before exiting the constructor. // It's safe to use "this" because we don't send a message before exiting the constructor.
@SuppressWarnings("nullness:methodref.receiver.bound") @SuppressWarnings("nullness:methodref.receiver.bound")
HandlerWrapper internalHandler = HandlerWrapper internalHandler =
@ -243,9 +243,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private void endInternal( private void endInternal(
@EndReason int endReason, @Nullable TransformationException transformationException) { @EndReason int endReason, @Nullable TransformationException transformationException) {
ImmutableList<TransformationResult.ProcessedInput> processedInputs = ImmutableList<ExportResult.ProcessedInput> processedInputs =
compositeAssetLoader.getProcessedInputs(); compositeAssetLoader.getProcessedInputs();
transformationResultBuilder exportResultBuilder
.setProcessedInputs(processedInputs) .setProcessedInputs(processedInputs)
.setAudioEncoderName(encoderFactory.getAudioEncoderName()) .setAudioEncoderName(encoderFactory.getAudioEncoderName())
.setVideoEncoderName(encoderFactory.getVideoEncoderName()); .setVideoEncoderName(encoderFactory.getVideoEncoderName());
@ -300,10 +300,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
applicationHandler.post( applicationHandler.post(
() -> () ->
listener.onError( listener.onError(
transformationResultBuilder.setTransformationException(finalException).build(), exportResultBuilder.setTransformationException(finalException).build(),
finalException)); finalException));
} else { } else {
applicationHandler.post(() -> listener.onCompleted(transformationResultBuilder.build())); applicationHandler.post(() -> listener.onCompleted(exportResultBuilder.build()));
} }
} }
@ -416,32 +416,32 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public void onTrackEnded( public void onTrackEnded(
@C.TrackType int trackType, Format format, int averageBitrate, int sampleCount) { @C.TrackType int trackType, Format format, int averageBitrate, int sampleCount) {
if (trackType == C.TRACK_TYPE_AUDIO) { if (trackType == C.TRACK_TYPE_AUDIO) {
transformationResultBuilder exportResultBuilder
.setAverageAudioBitrate(averageBitrate) .setAverageAudioBitrate(averageBitrate)
.setPcmEncoding(format.pcmEncoding); .setPcmEncoding(format.pcmEncoding);
if (format.channelCount != Format.NO_VALUE) { if (format.channelCount != Format.NO_VALUE) {
transformationResultBuilder.setChannelCount(format.channelCount); exportResultBuilder.setChannelCount(format.channelCount);
} }
if (format.sampleRate != Format.NO_VALUE) { if (format.sampleRate != Format.NO_VALUE) {
transformationResultBuilder.setSampleRate(format.sampleRate); exportResultBuilder.setSampleRate(format.sampleRate);
} }
} else if (trackType == C.TRACK_TYPE_VIDEO) { } else if (trackType == C.TRACK_TYPE_VIDEO) {
transformationResultBuilder exportResultBuilder
.setAverageVideoBitrate(averageBitrate) .setAverageVideoBitrate(averageBitrate)
.setColorInfo(format.colorInfo) .setColorInfo(format.colorInfo)
.setVideoFrameCount(sampleCount); .setVideoFrameCount(sampleCount);
if (format.height != Format.NO_VALUE) { if (format.height != Format.NO_VALUE) {
transformationResultBuilder.setHeight(format.height); exportResultBuilder.setHeight(format.height);
} }
if (format.width != Format.NO_VALUE) { if (format.width != Format.NO_VALUE) {
transformationResultBuilder.setWidth(format.width); exportResultBuilder.setWidth(format.width);
} }
} }
} }
@Override @Override
public void onEnded(long durationMs, long fileSizeBytes) { public void onEnded(long durationMs, long fileSizeBytes) {
transformationResultBuilder.setDurationMs(durationMs).setFileSizeBytes(fileSizeBytes); exportResultBuilder.setDurationMs(durationMs).setFileSizeBytes(fileSizeBytes);
internalHandler internalHandler
.obtainMessage( .obtainMessage(

View File

@ -602,10 +602,10 @@ public final class TransformerEndToEndTest {
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO); MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.start(mediaItem, outputPath); transformer.start(mediaItem, outputPath);
TransformationResult result = TransformerTestRunner.runLooper(transformer); ExportResult exportResult = TransformerTestRunner.runLooper(transformer);
assertThat(result.averageAudioBitrate).isGreaterThan(0); assertThat(exportResult.averageAudioBitrate).isGreaterThan(0);
assertThat(result.averageVideoBitrate).isGreaterThan(0); assertThat(exportResult.averageVideoBitrate).isGreaterThan(0);
} }
@Test @Test
@ -760,10 +760,10 @@ public final class TransformerEndToEndTest {
// This would throw if the previous transformation had not been cancelled. // This would throw if the previous transformation had not been cancelled.
transformer.start(mediaItem, outputPath); transformer.start(mediaItem, outputPath);
TransformationResult transformationResult = TransformerTestRunner.runLooper(transformer); ExportResult exportResult = TransformerTestRunner.runLooper(transformer);
// TODO(b/264974805): Make transformation output deterministic and check it against dump file. // TODO(b/264974805): Make transformation output deterministic and check it against dump file.
assertThat(transformationResult.transformationException).isNull(); assertThat(exportResult.transformationException).isNull();
} }
@Test @Test

View File

@ -19,7 +19,6 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.test.utils.robolectric.RobolectricUtil.runLooperUntil; import static androidx.media3.test.utils.robolectric.RobolectricUtil.runLooperUntil;
import androidx.media3.common.MediaItem;
import androidx.media3.test.utils.robolectric.RobolectricUtil; import androidx.media3.test.utils.robolectric.RobolectricUtil;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -33,46 +32,44 @@ public final class TransformerTestRunner {
/** /**
* Runs tasks of the {@linkplain Transformer#getApplicationLooper() transformer Looper} until the * Runs tasks of the {@linkplain Transformer#getApplicationLooper() transformer Looper} until the
* {@linkplain Transformer transformation} ends. * {@linkplain Transformer export} ends.
* *
* @param transformer The {@link Transformer}. * @param transformer The {@link Transformer}.
* @return The {@link TransformationResult}. * @return The {@link ExportResult}.
* @throws TransformationException If the transformation threw an exception. * @throws TransformationException If the transformation threw an exception.
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is * @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded. * exceeded.
* @throws IllegalStateException If the method is not called from the main thread. * @throws IllegalStateException If the method is not called from the main thread.
*/ */
public static TransformationResult runLooper(Transformer transformer) public static ExportResult runLooper(Transformer transformer)
throws TransformationException, TimeoutException { throws TransformationException, TimeoutException {
AtomicReference<@NullableType TransformationResult> transformationResultRef = AtomicReference<@NullableType ExportResult> exportResultRef = new AtomicReference<>();
new AtomicReference<>();
transformer.addListener( transformer.addListener(
new Transformer.Listener() { new Transformer.Listener() {
@Override @Override
public void onTransformationCompleted( public void onCompleted(Composition composition, ExportResult exportResult) {
MediaItem inputMediaItem, TransformationResult result) { exportResultRef.set(exportResult);
transformationResultRef.set(result);
} }
@Override @Override
public void onTransformationError( public void onError(
MediaItem inputMediaItem, Composition composition,
TransformationResult result, ExportResult exportResult,
TransformationException exception) { TransformationException exception) {
if (!Objects.equals(result.transformationException, exception)) { if (!Objects.equals(exportResult.transformationException, exception)) {
result = result.buildUpon().setTransformationException(exception).build(); exportResult = exportResult.buildUpon().setTransformationException(exception).build();
} }
transformationResultRef.set(result); exportResultRef.set(exportResult);
} }
}); });
runLooperUntil(transformer.getApplicationLooper(), () -> transformationResultRef.get() != null); runLooperUntil(transformer.getApplicationLooper(), () -> exportResultRef.get() != null);
TransformationResult result = checkNotNull(transformationResultRef.get()); ExportResult exportResult = checkNotNull(exportResultRef.get());
if (result.transformationException != null) { if (exportResult.transformationException != null) {
throw result.transformationException; throw exportResult.transformationException;
} }
return result; return exportResult;
} }
} }