mirror of
https://github.com/androidx/media.git
synced 2025-05-08 08:00:49 +08:00
Export: Add methods to determine the conversion process of a track
PiperOrigin-RevId: 604754827
This commit is contained in:
parent
138532e3fd
commit
2cb9d9b383
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user