Export: Add methods to determine the conversion process of a track

PiperOrigin-RevId: 604754827
This commit is contained in:
tofunmi 2024-02-06 13:55:52 -08:00 committed by Copybara-Service
parent 138532e3fd
commit 2cb9d9b383
5 changed files with 124 additions and 8 deletions

View File

@ -11,6 +11,9 @@
key names by modifying it to only check for hyphen
([#1028](https://github.com/androidx/media/issues/1028)).
* Transformer:
* Add `audioConversionProcess` and `videoConversionProcess` to
`ExportResult` indicating how the respective track in the output file
was made.
* Track Selection:
* Extractors:
* Audio:

View File

@ -30,6 +30,10 @@ import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING;
import static androidx.media3.transformer.AndroidTestUtil.createOpenGlObjects;
import static androidx.media3.transformer.AndroidTestUtil.generateTextureFromBitmap;
import static androidx.media3.transformer.AndroidTestUtil.recordTestSkipped;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_NA;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_TRANSCODED;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_TRANSMUXED;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED;
import static androidx.media3.transformer.ExportResult.OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM;
import static androidx.media3.transformer.ExportResult.OPTIMIZATION_ABANDONED_TRIM_AND_TRANSCODING_TRANSFORMATION_REQUESTED;
import static androidx.media3.transformer.ExportResult.OPTIMIZATION_FAILED_FORMAT_MISMATCH;
@ -530,6 +534,8 @@ public class TransformerEndToEndTest {
assertThat(result.exportResult.optimizationResult)
.isEqualTo(OPTIMIZATION_FAILED_FORMAT_MISMATCH);
assertThat(result.exportResult.durationMs).isAtMost(clippingEndMs - clippingStartMs);
assertThat(result.exportResult.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -617,6 +623,8 @@ public class TransformerEndToEndTest {
assertThat(result.exportResult.optimizationResult)
.isEqualTo(OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
assertThat(result.exportResult.durationMs).isAtMost(clippingEndMs - clippingStartMs);
assertThat(result.exportResult.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -653,6 +661,8 @@ public class TransformerEndToEndTest {
.isEqualTo(OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
// The asset is 15 s 537 ms long.
assertThat(result.exportResult.durationMs).isAtMost(1_017);
assertThat(result.exportResult.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -685,6 +695,9 @@ public class TransformerEndToEndTest {
assertThat(result.exportResult.optimizationResult).isEqualTo(OPTIMIZATION_SUCCEEDED);
assertThat(result.exportResult.durationMs).isAtMost(2000);
assertThat(result.exportResult.videoConversionProcess)
.isEqualTo(CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -726,6 +739,9 @@ public class TransformerEndToEndTest {
assertThat(result.exportResult.optimizationResult).isEqualTo(OPTIMIZATION_SUCCEEDED);
assertThat(result.exportResult.durationMs).isAtMost(2000);
assertThat(result.exportResult.videoConversionProcess)
.isEqualTo(CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_NA);
Format format = FileUtil.retrieveTrackFormat(context, result.filePath, C.TRACK_TYPE_VIDEO);
// The video is trim-optimized, so the rotation is performed in MuxerWrapper.
@ -768,6 +784,8 @@ public class TransformerEndToEndTest {
assertThat(result.exportResult.optimizationResult)
.isEqualTo(OPTIMIZATION_ABANDONED_TRIM_AND_TRANSCODING_TRANSFORMATION_REQUESTED);
assertThat(result.exportResult.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSCODED);
assertThat(result.exportResult.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test

View File

@ -378,6 +378,39 @@ public final class ExportResult {
*/
public static final int OPTIMIZATION_FAILED_FORMAT_MISMATCH = 6;
/**
* Specifies what conversion process was used to make a track in the output file. One of:
*
* <ul>
* <li>{@link #CONVERSION_PROCESS_NA}
* <li>{@link #CONVERSION_PROCESS_TRANSCODED}
* <li>{@link #CONVERSION_PROCESS_TRANSMUXED}
* <li>{@link #CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED}
* </ul>
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({
CONVERSION_PROCESS_NA,
CONVERSION_PROCESS_TRANSCODED,
CONVERSION_PROCESS_TRANSMUXED,
CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED
})
@interface ConversionProcess {}
/** The output file doesn't contain this track type. */
public static final int CONVERSION_PROCESS_NA = 0;
/** The track was transcoded. */
public static final int CONVERSION_PROCESS_TRANSCODED = 1;
/** The track was transmuxed. */
public static final int CONVERSION_PROCESS_TRANSMUXED = 2;
/** The track was both transcoded and transmuxed. */
public static final int CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED = 3;
/** The list of {@linkplain ProcessedInput processed inputs}. */
public final ImmutableList<ProcessedInput> processedInputs;
@ -436,6 +469,12 @@ public final class ExportResult {
*/
@Nullable public final ExportException exportException;
/** Returns the {@link ConversionProcess} taken to create the video track in the output file. */
public final @ConversionProcess int videoConversionProcess;
/** Returns the {@link ConversionProcess} taken to create the audio track in the output file. */
public final @ConversionProcess int audioConversionProcess;
private ExportResult(
ImmutableList<ProcessedInput> processedInputs,
long durationMs,
@ -471,6 +510,12 @@ public final class ExportResult {
this.videoMimeType = videoMimeType;
this.optimizationResult = optimizationResult;
this.exportException = exportException;
audioConversionProcess =
getConversionProcess(
audioMimeType, optimizationResult, processedInputs, C.TRACK_TYPE_AUDIO);
videoConversionProcess =
getConversionProcess(
videoMimeType, optimizationResult, processedInputs, C.TRACK_TYPE_VIDEO);
}
public Builder buildUpon() {
@ -543,4 +588,41 @@ public final class ExportResult {
result = 31 * result + Objects.hashCode(exportException);
return result;
}
// Nullness test incorrectly throws monotonic type error when assigning the @Nullable decoderName.
@SuppressWarnings("monotonic.type.incompatible")
private static @ConversionProcess int getConversionProcess(
@Nullable String mimeType,
@OptimizationResult int optimizationResult,
List<ProcessedInput> processedInputs,
@C.TrackType int trackType) {
if (mimeType == null) {
return CONVERSION_PROCESS_NA;
}
if (optimizationResult == OPTIMIZATION_SUCCEEDED) { // Trim optimization occurred.
return trackType == C.TRACK_TYPE_AUDIO
? CONVERSION_PROCESS_TRANSMUXED
: CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED;
}
@ConversionProcess int conversionProcess = CONVERSION_PROCESS_NA;
for (ProcessedInput processedInput : processedInputs) {
@Nullable
String decoderName =
trackType == C.TRACK_TYPE_AUDIO
? processedInput.audioDecoderName
: processedInput.videoDecoderName;
if (decoderName == null) {
if (conversionProcess == CONVERSION_PROCESS_TRANSCODED) {
return CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED;
}
conversionProcess = CONVERSION_PROCESS_TRANSMUXED;
} else {
if (conversionProcess == CONVERSION_PROCESS_TRANSMUXED) {
return CONVERSION_PROCESS_TRANSMUXED_AND_TRANSCODED;
}
conversionProcess = CONVERSION_PROCESS_TRANSCODED;
}
}
return conversionProcess;
}
}

View File

@ -1475,6 +1475,7 @@ public final class Transformer {
if (!doesFormatsMatch(mediaItemInfo, firstEditedMediaItem)) {
remuxingMuxerWrapper = null;
transformerInternal = null;
exportResultBuilder.reset();
exportResultBuilder.setOptimizationResult(OPTIMIZATION_FAILED_FORMAT_MISMATCH);
processFullInput();
return;

View File

@ -19,6 +19,8 @@ package androidx.media3.transformer;
import static androidx.media3.test.utils.robolectric.RobolectricUtil.runLooperUntil;
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_DECODED;
import static androidx.media3.transformer.AssetLoader.SUPPORTED_OUTPUT_TYPE_ENCODED;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_NA;
import static androidx.media3.transformer.ExportResult.CONVERSION_PROCESS_TRANSMUXED;
import static androidx.media3.transformer.ExportResult.OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM;
import static androidx.media3.transformer.ExportResult.OPTIMIZATION_FAILED_EXTRACTION_FAILED;
import static androidx.media3.transformer.TestUtil.ASSET_URI_PREFIX;
@ -168,15 +170,17 @@ public final class MediaItemExportTest {
.build();
transformer.start(mediaItem, outputDir.newFile().getPath());
ExportResult exportResult = TransformerTestRunner.runLooper(transformer);
ExportResult result = TransformerTestRunner.runLooper(transformer);
assertThat(exportResult.optimizationResult)
assertThat(result.optimizationResult)
.isEqualTo(OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
// Asserts against file generated when experimentalSetTrimOptimizationEnabled is set to false.
DumpFileAsserts.assertOutput(
context,
muxerFactory.getCreatedMuxer(),
getDumpFileName(/* originalFileName= */ FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S));
assertThat(result.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
assertThat(result.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -230,9 +234,9 @@ public final class MediaItemExportTest {
.build();
transformer.start(mediaItem, outputDir.newFile().getPath());
ExportResult exportResult = TransformerTestRunner.runLooper(transformer);
ExportResult result = TransformerTestRunner.runLooper(transformer);
assertThat(exportResult.optimizationResult)
assertThat(result.optimizationResult)
.isEqualTo(OPTIMIZATION_ABANDONED_KEYFRAME_PLACEMENT_OPTIMAL_FOR_TRIM);
DumpFileAsserts.assertOutput(
context,
@ -240,6 +244,8 @@ public final class MediaItemExportTest {
getDumpFileName(
/* originalFileName= */ FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S,
/* modifications...= */ "clipped"));
assertThat(result.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
assertThat(result.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -259,13 +265,15 @@ public final class MediaItemExportTest {
.build();
transformer.start(mediaItem, outputDir.newFile().getPath());
ExportResult exportResult = TransformerTestRunner.runLooper(transformer);
ExportResult result = TransformerTestRunner.runLooper(transformer);
assertThat(exportResult.optimizationResult).isEqualTo(OPTIMIZATION_FAILED_EXTRACTION_FAILED);
assertThat(result.optimizationResult).isEqualTo(OPTIMIZATION_FAILED_EXTRACTION_FAILED);
DumpFileAsserts.assertOutput(
context,
muxerFactory.getCreatedMuxer(),
getDumpFileName(/* originalFileName= */ FILE_AUDIO_RAW, /* modifications...= */ "clipped"));
assertThat(result.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_NA);
assertThat(result.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test
@ -1069,11 +1077,13 @@ public final class MediaItemExportTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
transformer.start(editedMediaItem, outputDir.newFile().getPath());
TransformerTestRunner.runLooper(transformer);
ExportResult result = TransformerTestRunner.runLooper(transformer);
// Video transcoding in unit tests is not supported.
DumpFileAsserts.assertOutput(
context, muxerFactory.getCreatedMuxer(), getDumpFileName(FILE_VIDEO_ONLY));
assertThat(result.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
assertThat(result.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_NA);
}
@Test
@ -1089,7 +1099,7 @@ public final class MediaItemExportTest {
new EditedMediaItem.Builder(mediaItem).setEffects(effects).build();
transformer.start(editedMediaItem, outputDir.newFile().getPath());
TransformerTestRunner.runLooper(transformer);
ExportResult result = TransformerTestRunner.runLooper(transformer);
// Video transcoding in unit tests is not supported.
DumpFileAsserts.assertOutput(
@ -1097,6 +1107,8 @@ public final class MediaItemExportTest {
muxerFactory.getCreatedMuxer(),
getDumpFileName(
/* originalFileName= */ FILE_AUDIO_VIDEO, /* modifications...= */ "rotated"));
assertThat(result.videoConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
assertThat(result.audioConversionProcess).isEqualTo(CONVERSION_PROCESS_TRANSMUXED);
}
@Test