Commit e46b4c0f by samrobinson Committed by Ian Baker

Create and return a TransformationResult regardless of success.

The TransformationResult has some useful values that are set in error
cases, such as the codecs used.

PiperOrigin-RevId: 495568259
parent c9e644b4
......@@ -311,13 +311,15 @@ public final class TransformerActivity extends AppCompatActivity {
new Transformer.Listener() {
@Override
public void onTransformationCompleted(
MediaItem mediaItem, TransformationResult transformationResult) {
MediaItem mediaItem, TransformationResult result) {
TransformerActivity.this.onTransformationCompleted(filePath, mediaItem);
}
@Override
public void onTransformationError(
MediaItem mediaItem, TransformationException exception) {
MediaItem mediaItem,
TransformationResult result,
TransformationException exception) {
TransformerActivity.this.onTransformationError(exception);
}
})
......
......@@ -65,12 +65,12 @@ app is notified of events via the listener passed to the `Transformer` builder.
Transformer.Listener transformerListener =
new Transformer.Listener() {
@Override
public void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult transformationResult) {
public void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult result) {
playOutput();
}
@Override
public void onTransformationError(MediaItem inputMediaItem, TransformationException e) {
public void onTransformationError(MediaItem inputMediaItem, TransformationResult result, TransformationException exception) {
displayError(e);
}
};
......
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.transformer;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import org.json.JSONException;
import org.json.JSONObject;
......@@ -28,21 +29,34 @@ public class TransformationTestResult {
/** A builder for {@link TransformationTestResult}. */
public static class Builder {
private final TransformationResult transformationResult;
@Nullable private TransformationResult transformationResult;
@Nullable private String filePath;
@Nullable private Exception analysisException;
private long elapsedTimeMs;
private double ssim;
@Nullable private Exception testException;
@Nullable private Exception analysisException;
/** Creates a new {@link Builder}. */
public Builder(TransformationResult transformationResult) {
this.transformationResult = transformationResult;
public Builder() {
this.elapsedTimeMs = C.TIME_UNSET;
this.ssim = SSIM_UNSET;
}
/**
* Sets the {@link TransformationResult} of the transformation.
*
* <p>This field must be set.
*
* @param transformationResult The {@link TransformationResult}.
* @return This {@link Builder}
*/
@CanIgnoreReturnValue
public Builder setTransformationResult(TransformationResult transformationResult) {
this.transformationResult = transformationResult;
return this;
}
/**
* Sets the file path of the output file.
*
* <p>{@code null} represents an unset or unknown value.
......@@ -86,6 +100,20 @@ public class TransformationTestResult {
}
/**
* Sets an {@link Exception} that occurred during the test.
*
* <p>{@code null} represents an unset or unknown value.
*
* @param testException The {@link Exception} thrown during the test.
* @return This {@link Builder}.
*/
@CanIgnoreReturnValue
public Builder setTestException(@Nullable Exception testException) {
this.testException = testException;
return this;
}
/**
* Sets an {@link Exception} that occurred during post-transformation analysis.
*
* <p>{@code null} represents an unset or unknown value.
......@@ -102,7 +130,12 @@ public class TransformationTestResult {
/** Builds the {@link TransformationTestResult} instance. */
public TransformationTestResult build() {
return new TransformationTestResult(
transformationResult, filePath, elapsedTimeMs, ssim, analysisException);
Assertions.checkNotNull(transformationResult),
filePath,
elapsedTimeMs,
ssim,
testException,
analysisException);
}
}
......@@ -120,6 +153,12 @@ public class TransformationTestResult {
public final long elapsedTimeMs;
/** The SSIM score of the transformation, {@link #SSIM_UNSET} if unavailable. */
public final double ssim;
/**
* The {@link Exception} that was thrown during the test, or {@code null} if nothing was thrown.
*/
@Nullable public final Exception testException;
/**
* The {@link Exception} that was thrown during post-transformation analysis, or {@code null} if
* nothing was thrown.
......@@ -165,6 +204,9 @@ public class TransformationTestResult {
if (ssim != TransformationTestResult.SSIM_UNSET) {
jsonObject.put("ssim", ssim);
}
if (testException != null) {
jsonObject.put("testException", AndroidTestUtil.exceptionAsJsonObject(testException));
}
if (analysisException != null) {
jsonObject.put("analysisException", AndroidTestUtil.exceptionAsJsonObject(analysisException));
}
......@@ -176,11 +218,13 @@ public class TransformationTestResult {
@Nullable String filePath,
long elapsedTimeMs,
double ssim,
@Nullable Exception testException,
@Nullable Exception analysisException) {
this.transformationResult = transformationResult;
this.filePath = filePath;
this.elapsedTimeMs = elapsedTimeMs;
this.ssim = ssim;
this.testException = testException;
this.analysisException = analysisException;
this.throughputFps =
elapsedTimeMs != C.TIME_UNSET && transformationResult.videoFrameCount > 0
......
......@@ -36,6 +36,7 @@ public final class TransformationResult {
@Nullable private String audioEncoderName;
@Nullable private String videoDecoderName;
@Nullable private String videoEncoderName;
@Nullable private TransformationException transformationException;
public Builder() {
durationMs = C.TIME_UNSET;
......@@ -51,7 +52,7 @@ public final class TransformationResult {
*/
@CanIgnoreReturnValue
public Builder setDurationMs(long durationMs) {
checkArgument(durationMs > 0 || durationMs == C.TIME_UNSET);
checkArgument(durationMs >= 0 || durationMs == C.TIME_UNSET);
this.durationMs = durationMs;
return this;
}
......@@ -132,6 +133,14 @@ public final class TransformationResult {
return this;
}
/** Sets the {@link TransformationException} that caused the transformation to fail. */
@CanIgnoreReturnValue
public Builder setTransformationException(
@Nullable TransformationException transformationException) {
this.transformationException = transformationException;
return this;
}
public TransformationResult build() {
return new TransformationResult(
durationMs,
......@@ -142,7 +151,8 @@ public final class TransformationResult {
audioDecoderName,
audioEncoderName,
videoDecoderName,
videoEncoderName);
videoEncoderName,
transformationException);
}
}
......@@ -160,7 +170,6 @@ public final class TransformationResult {
public final int averageVideoBitrate;
/** The number of video frames. */
public final int videoFrameCount;
/** The name of the audio decoder used, or {@code null} if none were used. */
@Nullable public final String audioDecoderName;
/** The name of the audio encoder used, or {@code null} if none were used. */
......@@ -169,6 +178,11 @@ public final class TransformationResult {
@Nullable public final String videoDecoderName;
/** 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 transformation to fail, or {@code null} if
* the transformation was a success.
*/
@Nullable public final TransformationException transformationException;
private TransformationResult(
long durationMs,
......@@ -179,7 +193,8 @@ public final class TransformationResult {
@Nullable String audioDecoderName,
@Nullable String audioEncoderName,
@Nullable String videoDecoderName,
@Nullable String videoEncoderName) {
@Nullable String videoEncoderName,
@Nullable TransformationException transformationException) {
this.durationMs = durationMs;
this.fileSizeBytes = fileSizeBytes;
this.averageAudioBitrate = averageAudioBitrate;
......@@ -189,6 +204,7 @@ public final class TransformationResult {
this.audioEncoderName = audioEncoderName;
this.videoDecoderName = videoDecoderName;
this.videoEncoderName = videoEncoderName;
this.transformationException = transformationException;
}
public Builder buildUpon() {
......@@ -201,7 +217,8 @@ public final class TransformationResult {
.setAudioDecoderName(audioDecoderName)
.setAudioEncoderName(audioEncoderName)
.setVideoDecoderName(videoDecoderName)
.setVideoEncoderName(videoEncoderName);
.setVideoEncoderName(videoEncoderName)
.setTransformationException(transformationException);
}
@Override
......@@ -221,7 +238,8 @@ public final class TransformationResult {
&& Objects.equals(audioDecoderName, result.audioDecoderName)
&& Objects.equals(audioEncoderName, result.audioEncoderName)
&& Objects.equals(videoDecoderName, result.videoDecoderName)
&& Objects.equals(videoEncoderName, result.videoEncoderName);
&& Objects.equals(videoEncoderName, result.videoEncoderName)
&& Objects.equals(transformationException, result.transformationException);
}
@Override
......@@ -235,6 +253,7 @@ public final class TransformationResult {
result = 31 * result + Objects.hashCode(audioEncoderName);
result = 31 * result + Objects.hashCode(videoDecoderName);
result = 31 * result + Objects.hashCode(videoEncoderName);
result = 31 * result + Objects.hashCode(transformationException);
return result;
}
}
......@@ -533,7 +533,8 @@ public final class Transformer {
}
/**
* @deprecated Use {@link #onTransformationError(MediaItem, TransformationException)}.
* @deprecated Use {@link #onTransformationError(MediaItem, TransformationResult,
* TransformationException)}.
*/
@Deprecated
default void onTransformationError(MediaItem inputMediaItem, Exception exception) {
......@@ -541,13 +542,24 @@ public final class Transformer {
}
/**
* @deprecated Use {@link #onTransformationError(MediaItem, TransformationResult,
* TransformationException)}.
*/
@Deprecated
default void onTransformationError(
MediaItem inputMediaItem, TransformationException exception) {
onTransformationError(inputMediaItem, new TransformationResult.Builder().build(), exception);
}
/**
* Called if an exception occurs during the transformation.
*
* @param inputMediaItem The {@link MediaItem} for which the exception occurs.
* @param result The {@link TransformationResult} of the transformation.
* @param exception The {@link TransformationException} describing the exception.
*/
default void onTransformationError(
MediaItem inputMediaItem, TransformationException exception) {}
MediaItem inputMediaItem, TransformationResult result, TransformationException exception) {}
/**
* Called when fallback to an alternative {@link TransformationRequest} is necessary to comply
......@@ -878,13 +890,14 @@ public final class Transformer {
}
@Override
public void onTransformationError(TransformationException exception) {
public void onTransformationError(
TransformationResult result, TransformationException exception) {
handler.post(
() -> {
transformerInternal = null;
listeners.queueEvent(
/* eventFlag= */ C.INDEX_UNSET,
listener -> listener.onTransformationError(mediaItem, exception));
listener -> listener.onTransformationError(mediaItem, result, exception));
listeners.flushEvents();
});
}
......
......@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.transformer.TransformationException.ERROR_CODE_MUXING_FAILED;
import static com.google.android.exoplayer2.transformer.Transformer.PROGRESS_STATE_NO_TRANSFORMATION;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
......@@ -57,9 +56,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public interface Listener {
void onTransformationCompleted(TransformationResult transformationResult);
void onTransformationCompleted(TransformationResult result);
void onTransformationError(TransformationException exception);
void onTransformationError(TransformationResult result, TransformationException exception);
}
/**
......@@ -300,7 +299,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private void endInternal(
@EndReason int endReason, @Nullable TransformationException transformationException) {
@Nullable TransformationResult transformationResult = null;
TransformationResult.Builder transformationResultBuilder =
new TransformationResult.Builder()
.setAudioDecoderName(decoderFactory.getAudioDecoderName())
.setVideoDecoderName(decoderFactory.getVideoDecoderName())
.setAudioEncoderName(encoderFactory.getAudioEncoderName())
.setVideoEncoderName(encoderFactory.getVideoEncoderName());
boolean forCancellation = endReason == END_REASON_CANCELLED;
@Nullable TransformationException releaseTransformationException = null;
if (!released) {
......@@ -315,26 +320,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
assetLoader.release();
} finally {
try {
for (int i = 0; i < samplePipelines.size(); i++) {
samplePipelines.get(i).release();
if (endReason == END_REASON_COMPLETED) {
transformationResultBuilder
.setDurationMs(muxerWrapper.getDurationMs())
.setFileSizeBytes(muxerWrapper.getCurrentOutputSizeBytes())
.setAverageAudioBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO))
.setAverageVideoBitrate(muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO))
.setVideoFrameCount(muxerWrapper.getTrackSampleCount(C.TRACK_TYPE_VIDEO));
}
// TODO(b/250564186): Create TransformationResult on END_REASON_ERROR as well.
if (endReason == END_REASON_COMPLETED) {
transformationResult =
new TransformationResult.Builder()
.setDurationMs(checkNotNull(muxerWrapper).getDurationMs())
.setAverageAudioBitrate(
muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_AUDIO))
.setAverageVideoBitrate(
muxerWrapper.getTrackAverageBitrate(C.TRACK_TYPE_VIDEO))
.setVideoFrameCount(muxerWrapper.getTrackSampleCount(C.TRACK_TYPE_VIDEO))
.setFileSizeBytes(muxerWrapper.getCurrentOutputSizeBytes())
.setAudioDecoderName(decoderFactory.getAudioDecoderName())
.setAudioEncoderName(encoderFactory.getAudioEncoderName())
.setVideoDecoderName(decoderFactory.getVideoDecoderName())
.setVideoEncoderName(encoderFactory.getVideoEncoderName())
.build();
for (int i = 0; i < samplePipelines.size(); i++) {
samplePipelines.get(i).release();
}
} finally {
muxerWrapper.release(forCancellation);
......@@ -369,9 +365,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
if (exception != null) {
listener.onTransformationError(exception);
listener.onTransformationError(
transformationResultBuilder.setTransformationException(exception).build(), exception);
} else {
listener.onTransformationCompleted(checkNotNull(transformationResult));
listener.onTransformationCompleted(transformationResultBuilder.build());
}
}
......
......@@ -337,9 +337,9 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
verify(mockListener1).onTransformationError(mediaItem, exception);
verify(mockListener2).onTransformationError(mediaItem, exception);
verify(mockListener3).onTransformationError(mediaItem, exception);
verify(mockListener1).onTransformationError(eq(mediaItem), any(), eq(exception));
verify(mockListener2).onTransformationError(eq(mediaItem), any(), eq(exception));
verify(mockListener3).onTransformationError(eq(mediaItem), any(), eq(exception));
}
@Test
......
......@@ -85,7 +85,9 @@ public final class TransformerTestRunner {
@Override
public void onTransformationError(
MediaItem inputMediaItem, TransformationException exception) {
MediaItem inputMediaItem,
TransformationResult result,
TransformationException exception) {
transformationException.set(exception);
}
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment