diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java
index 7088cab431..baa8be2faf 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java
@@ -16,7 +16,6 @@
package androidx.media3.transformer;
-import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.usToMs;
import android.content.Context;
@@ -37,15 +36,64 @@ import androidx.media3.common.util.SystemClock;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
-import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
- * A metrics collector that collects editing events and forwards them to an {@link EditingSession}
- * created by {@link MediaMetricsManager}.
+ * A metrics collector that collects editing events and forwards them to {@link MetricsReporter}.
*/
@RequiresApi(35)
/* package */ final class EditingMetricsCollector {
+ /** Reports the collected metrics. */
+ public interface MetricsReporter extends AutoCloseable {
+
+ /**
+ * Reports the given {@link EditingEndedEvent}.
+ *
+ *
The method should be called at most once.
+ */
+ void reportMetrics(EditingEndedEvent editingEndedEvent);
+ }
+
+ /**
+ * A default implementation of {@link MetricsReporter} that reports metrics to an {@link
+ * EditingSession}.
+ */
+ static final class DefaultMetricsReporter implements MetricsReporter {
+
+ /** The {@link EditingSession} to report collected metrics to. */
+ @Nullable private EditingSession editingSession;
+
+ /**
+ * Creates an instance.
+ *
+ * @param context A {@link Context}.
+ */
+ public DefaultMetricsReporter(Context context) {
+ @Nullable
+ MediaMetricsManager mediaMetricsManager =
+ (MediaMetricsManager) context.getSystemService(Context.MEDIA_METRICS_SERVICE);
+ if (mediaMetricsManager != null) {
+ editingSession = mediaMetricsManager.createEditingSession();
+ }
+ }
+
+ @Override
+ public void reportMetrics(EditingEndedEvent editingEndedEvent) {
+ if (editingSession != null) {
+ editingSession.reportEditingEndedEvent(editingEndedEvent);
+ close();
+ }
+ }
+
+ @Override
+ public void close() {
+ if (editingSession != null) {
+ editingSession.close();
+ editingSession = null;
+ }
+ }
+ }
+
// TODO: b/386328723 - Add missing error codes to EditingEndedEvent.ErrorCode.
private static final SparseIntArray ERROR_CODE_CONVERSION_MAP = new SparseIntArray();
private static final SparseIntArray DATA_SPACE_STANDARD_CONVERSION_MAP = new SparseIntArray();
@@ -128,10 +176,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
private static final int SUCCESS_PROGRESS_PERCENTAGE = 100;
+ private final long startTimeMs;
private final String exporterName;
@Nullable private final String muxerName;
- private @MonotonicNonNull EditingSession editingSession;
- private long startTimeMs;
+ private final MetricsReporter metricsReporter;
/**
* Creates an instance.
@@ -141,22 +189,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*
Both {@code exporterName} and {@code muxerName} should follow the format
* ":".
*
- * @param context The {@link Context}.
+ * @param metricsReporter The {@link MetricsReporter} to report metrics.
* @param exporterName Java package name and version of the library or application implementing
* the editing operation.
* @param muxerName Java package name and version of the library or application that writes to the
* output file.
*/
- public EditingMetricsCollector(Context context, String exporterName, @Nullable String muxerName) {
- @Nullable
- MediaMetricsManager mediaMetricsManager =
- (MediaMetricsManager) context.getSystemService(Context.MEDIA_METRICS_SERVICE);
- if (mediaMetricsManager != null) {
- editingSession = checkNotNull(mediaMetricsManager.createEditingSession());
- startTimeMs = SystemClock.DEFAULT.elapsedRealtime();
- }
+ public EditingMetricsCollector(
+ MetricsReporter metricsReporter, String exporterName, @Nullable String muxerName) {
+ this.metricsReporter = metricsReporter;
this.exporterName = exporterName;
this.muxerName = muxerName;
+ startTimeMs = SystemClock.DEFAULT.elapsedRealtime();
}
/**
@@ -165,9 +209,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* @param exportResult The {@link ExportResult} of the export.
*/
public void onExportSuccess(ExportResult exportResult) {
- if (editingSession == null) {
- return;
- }
EditingEndedEvent.Builder editingEndedEventBuilder =
createEditingEndedEventBuilder(EditingEndedEvent.FINAL_STATE_SUCCEEDED)
.setFinalProgressPercent(SUCCESS_PROGRESS_PERCENTAGE);
@@ -180,8 +221,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
editingEndedEventBuilder.setOutputMediaItemInfo(getOutputMediaItemInfo(exportResult));
- editingSession.reportEditingEndedEvent(editingEndedEventBuilder.build());
- editingSession.close();
+ metricsReporter.reportMetrics(editingEndedEventBuilder.build());
}
/**
@@ -194,9 +234,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
public void onExportError(
int progressPercentage, ExportException exportException, ExportResult exportResult) {
- if (editingSession == null) {
- return;
- }
EditingEndedEvent.Builder editingEndedEventBuilder =
createEditingEndedEventBuilder(EditingEndedEvent.FINAL_STATE_ERROR)
.setErrorCode(getEditingEndedEventErrorCode(exportException.errorCode));
@@ -212,8 +249,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
editingEndedEventBuilder.setOutputMediaItemInfo(getOutputMediaItemInfo(exportResult));
- editingSession.reportEditingEndedEvent(editingEndedEventBuilder.build());
- editingSession.close();
+ metricsReporter.reportMetrics(editingEndedEventBuilder.build());
}
/**
@@ -223,16 +259,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* C#PERCENTAGE_UNSET} if unknown or between 0 and 100 inclusive.
*/
public void onExportCancelled(int progressPercentage) {
- if (editingSession == null) {
- return;
- }
EditingEndedEvent.Builder editingEndedEventBuilder =
createEditingEndedEventBuilder(EditingEndedEvent.FINAL_STATE_CANCELED);
if (progressPercentage != C.PERCENTAGE_UNSET) {
editingEndedEventBuilder.setFinalProgressPercent(progressPercentage);
}
- editingSession.reportEditingEndedEvent(editingEndedEventBuilder.build());
- editingSession.close();
+
+ metricsReporter.reportMetrics(editingEndedEventBuilder.build());
}
private EditingEndedEvent.Builder createEditingEndedEventBuilder(int finalState) {
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
index 551c5fe20b..e7871e38cf 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
@@ -1588,7 +1588,11 @@ public final class Transformer {
} else if (muxerFactory instanceof DefaultMuxer.Factory) {
muxerName = DefaultMuxer.MUXER_NAME;
}
- editingMetricsCollector = new EditingMetricsCollector(context, EXPORTER_NAME, muxerName);
+ editingMetricsCollector =
+ new EditingMetricsCollector(
+ new EditingMetricsCollector.DefaultMetricsReporter(context),
+ EXPORTER_NAME,
+ muxerName);
}
transformerInternal =
new TransformerInternal(
diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/EditingMetricsCollectorTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/EditingMetricsCollectorTest.java
new file mode 100644
index 0000000000..7b30cfc1c9
--- /dev/null
+++ b/libraries/transformer/src/test/java/androidx/media3/transformer/EditingMetricsCollectorTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2025 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.Util.usToMs;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.DataSpace;
+import android.media.metrics.EditingEndedEvent;
+import android.media.metrics.MediaItemInfo;
+import android.util.Size;
+import androidx.media3.common.C;
+import androidx.media3.common.ColorInfo;
+import androidx.media3.common.Format;
+import androidx.media3.common.MediaItem;
+import androidx.media3.common.MediaLibraryInfo;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link EditingMetricsCollector}. */
+@Config(minSdk = 35)
+@RunWith(AndroidJUnit4.class)
+public final class EditingMetricsCollectorTest {
+
+ private static final int MEDIA_DURATION_US = 1_000_000_000;
+ private static final String EXPORTER_NAME =
+ "androidx.media3.media3-transformer:" + MediaLibraryInfo.VERSION;
+ private static final String MUXER_NAME =
+ "androidx.media3.media3-muxer:" + MediaLibraryInfo.VERSION;
+ private static final String VIDEO_MIME_TYPE = "video/hevc";
+ private static final String AUDIO_MIME_TYPE = "audio/mp4a-latm";
+ private static final String VIDEO_ENCODER_NAME = "c2.android.hevc.encoder";
+ private static final String AUDIO_ENCODER_NAME = "c2.android.aac.encoder";
+ private static final String VIDEO_DECODER_NAME = "c2.android.hevc.decoder";
+ private static final String AUDIO_DECODER_NAME = "c2.android.aac.decoder";
+ private static final float VIDEO_FRAME_RATE = 30.0f;
+ private static final Size VIDEO_SIZE = new Size(/* width= */ 1920, /* height= */ 1080);
+ private static final ColorInfo VIDEO_COLOR_INFO =
+ new ColorInfo.Builder()
+ .setColorSpace(C.COLOR_SPACE_BT2020)
+ .setColorTransfer(C.COLOR_TRANSFER_ST2084)
+ .setColorRange(C.COLOR_RANGE_LIMITED)
+ .build();
+ private static final int AUDIO_SAMPLE_RATE = 48_000;
+ private static final int AUDIO_CHANNEL_COUNT = 2;
+ private static final Format FAKE_AUDIO_FORMAT =
+ new Format.Builder()
+ .setSampleMimeType(AUDIO_MIME_TYPE)
+ .setSampleRate(AUDIO_SAMPLE_RATE)
+ .setChannelCount(AUDIO_CHANNEL_COUNT)
+ .setPcmEncoding(C.ENCODING_PCM_16BIT)
+ .build();
+ private static final Format FAKE_VIDEO_FORMAT =
+ new Format.Builder()
+ .setContainerMimeType(VIDEO_MIME_TYPE)
+ .setSampleMimeType(VIDEO_MIME_TYPE)
+ .setFrameRate(VIDEO_FRAME_RATE)
+ .setWidth(VIDEO_SIZE.getWidth())
+ .setHeight(VIDEO_SIZE.getHeight())
+ .setColorInfo(VIDEO_COLOR_INFO)
+ .build();
+
+ @Test
+ public void onExportSuccess_populatesEditingEndedEvent() {
+ List processedInputs = new ArrayList<>();
+ processedInputs.add(
+ new ExportResult.ProcessedInput(
+ MediaItem.EMPTY,
+ MEDIA_DURATION_US,
+ FAKE_AUDIO_FORMAT,
+ FAKE_VIDEO_FORMAT,
+ AUDIO_DECODER_NAME,
+ VIDEO_DECODER_NAME));
+ ExportResult exportResult =
+ new ExportResult.Builder()
+ .setDurationMs(usToMs(MEDIA_DURATION_US))
+ .setAudioMimeType(AUDIO_MIME_TYPE)
+ .setVideoMimeType(VIDEO_MIME_TYPE)
+ .setChannelCount(AUDIO_CHANNEL_COUNT)
+ .setSampleRate(AUDIO_SAMPLE_RATE)
+ .setAudioEncoderName(AUDIO_ENCODER_NAME)
+ .setVideoEncoderName(VIDEO_ENCODER_NAME)
+ .setVideoFrameCount(2400)
+ .setWidth(VIDEO_SIZE.getWidth())
+ .setHeight(VIDEO_SIZE.getHeight())
+ .setColorInfo(VIDEO_COLOR_INFO)
+ .addProcessedInputs(processedInputs)
+ .build();
+ AtomicReference editingEndedEventAtomicReference = new AtomicReference<>();
+ EditingMetricsCollector editingMetricsCollector =
+ new EditingMetricsCollector(
+ new EditingMetricsCollector.MetricsReporter() {
+ @Override
+ public void reportMetrics(EditingEndedEvent editingEndedEvent) {
+ editingEndedEventAtomicReference.set(editingEndedEvent);
+ }
+
+ @Override
+ public void close() {}
+ },
+ EXPORTER_NAME,
+ MUXER_NAME);
+
+ editingMetricsCollector.onExportSuccess(exportResult);
+
+ EditingEndedEvent editingEndedEvent = editingEndedEventAtomicReference.get();
+ assertThat(editingEndedEvent.getFinalState())
+ .isEqualTo(EditingEndedEvent.FINAL_STATE_SUCCEEDED);
+ assertThat(editingEndedEvent.getTimeSinceCreatedMillis()).isAtLeast(0);
+ assertThat(editingEndedEvent.getExporterName()).isEqualTo(EXPORTER_NAME);
+ assertThat(editingEndedEvent.getMuxerName()).isEqualTo(MUXER_NAME);
+ assertThat(editingEndedEvent.getFinalProgressPercent()).isEqualTo(100);
+ assertThat(editingEndedEvent.getInputMediaItemInfos()).hasSize(1);
+ // Assert input media items information
+ MediaItemInfo inputMediaItemInfo = editingEndedEvent.getInputMediaItemInfos().get(0);
+ assertThat(inputMediaItemInfo.getClipDurationMillis())
+ .isEqualTo(usToMs(processedInputs.get(0).durationUs));
+ assertThat(inputMediaItemInfo.getCodecNames().get(0)).isEqualTo(VIDEO_DECODER_NAME);
+ assertThat(inputMediaItemInfo.getCodecNames().get(1)).isEqualTo(AUDIO_DECODER_NAME);
+ assertThat(inputMediaItemInfo.getContainerMimeType()).isEqualTo(VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes()).hasSize(2);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes().get(0))
+ .isAnyOf(AUDIO_MIME_TYPE, VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes().get(1))
+ .isAnyOf(AUDIO_MIME_TYPE, VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getDataTypes())
+ .isEqualTo(MediaItemInfo.DATA_TYPE_VIDEO | MediaItemInfo.DATA_TYPE_AUDIO);
+ assertThat(inputMediaItemInfo.getVideoFrameRate()).isEqualTo(VIDEO_FRAME_RATE);
+ assertThat(inputMediaItemInfo.getVideoSize()).isEqualTo(VIDEO_SIZE);
+ assertThat(inputMediaItemInfo.getVideoDataSpace())
+ .isEqualTo(
+ DataSpace.pack(
+ DataSpace.STANDARD_BT2020, DataSpace.TRANSFER_ST2084, DataSpace.RANGE_LIMITED));
+ assertThat(inputMediaItemInfo.getAudioChannelCount()).isEqualTo(AUDIO_CHANNEL_COUNT);
+ assertThat(inputMediaItemInfo.getAudioSampleRateHz()).isEqualTo(AUDIO_SAMPLE_RATE);
+ // Assert output media item information
+ MediaItemInfo outputMediaItemInfo = editingEndedEvent.getOutputMediaItemInfo();
+ assertThat(outputMediaItemInfo).isNotNull();
+ assertThat(outputMediaItemInfo.getDurationMillis()).isEqualTo(exportResult.durationMs);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes()).hasSize(2);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes().get(0))
+ .isAnyOf(exportResult.audioMimeType, exportResult.videoMimeType);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes().get(1))
+ .isAnyOf(exportResult.audioMimeType, exportResult.videoMimeType);
+ assertThat(outputMediaItemInfo.getAudioChannelCount()).isEqualTo(exportResult.channelCount);
+ assertThat(outputMediaItemInfo.getAudioSampleRateHz()).isEqualTo(exportResult.sampleRate);
+ assertThat(outputMediaItemInfo.getCodecNames()).hasSize(2);
+ assertThat(outputMediaItemInfo.getCodecNames().get(0))
+ .isAnyOf(exportResult.audioEncoderName, exportResult.videoEncoderName);
+ assertThat(outputMediaItemInfo.getCodecNames().get(1))
+ .isAnyOf(exportResult.audioEncoderName, exportResult.videoEncoderName);
+ assertThat(outputMediaItemInfo.getVideoSampleCount()).isEqualTo(exportResult.videoFrameCount);
+ assertThat(outputMediaItemInfo.getVideoSize())
+ .isEqualTo(new Size(exportResult.width, exportResult.height));
+ assertThat(inputMediaItemInfo.getVideoDataSpace())
+ .isEqualTo(
+ DataSpace.pack(
+ DataSpace.STANDARD_BT2020, DataSpace.TRANSFER_ST2084, DataSpace.RANGE_LIMITED));
+ }
+
+ @Test
+ public void onExportError_populatesEditingEndedEvent() {
+ List processedInputs = new ArrayList<>();
+ processedInputs.add(
+ new ExportResult.ProcessedInput(
+ MediaItem.EMPTY,
+ MEDIA_DURATION_US,
+ FAKE_AUDIO_FORMAT,
+ FAKE_VIDEO_FORMAT,
+ AUDIO_DECODER_NAME,
+ VIDEO_DECODER_NAME));
+ ExportResult exportResult =
+ new ExportResult.Builder()
+ .setDurationMs(usToMs(MEDIA_DURATION_US))
+ .setAudioMimeType(AUDIO_MIME_TYPE)
+ .setVideoMimeType(VIDEO_MIME_TYPE)
+ .setChannelCount(AUDIO_CHANNEL_COUNT)
+ .setSampleRate(AUDIO_SAMPLE_RATE)
+ .setAudioEncoderName(AUDIO_ENCODER_NAME)
+ .setVideoEncoderName(VIDEO_ENCODER_NAME)
+ .setVideoFrameCount(2400)
+ .setWidth(VIDEO_SIZE.getWidth())
+ .setHeight(VIDEO_SIZE.getHeight())
+ .setColorInfo(VIDEO_COLOR_INFO)
+ .addProcessedInputs(processedInputs)
+ .build();
+ ExportException exception =
+ ExportException.createForMuxer(
+ new RuntimeException(), ExportException.ERROR_CODE_MUXING_FAILED);
+ AtomicReference editingEndedEventAtomicReference = new AtomicReference<>();
+ EditingMetricsCollector editingMetricsCollector =
+ new EditingMetricsCollector(
+ new EditingMetricsCollector.MetricsReporter() {
+ @Override
+ public void reportMetrics(EditingEndedEvent editingEndedEvent) {
+ editingEndedEventAtomicReference.set(editingEndedEvent);
+ }
+
+ @Override
+ public void close() {}
+ },
+ EXPORTER_NAME,
+ MUXER_NAME);
+ int progressPercentage = 10;
+
+ editingMetricsCollector.onExportError(progressPercentage, exception, exportResult);
+
+ EditingEndedEvent editingEndedEvent = editingEndedEventAtomicReference.get();
+ assertThat(editingEndedEvent.getFinalState()).isEqualTo(EditingEndedEvent.FINAL_STATE_ERROR);
+ assertThat(editingEndedEvent.getTimeSinceCreatedMillis()).isAtLeast(0);
+ assertThat(editingEndedEvent.getExporterName()).isEqualTo(EXPORTER_NAME);
+ assertThat(editingEndedEvent.getMuxerName()).isEqualTo(MUXER_NAME);
+ assertThat(editingEndedEvent.getFinalProgressPercent()).isEqualTo(progressPercentage);
+ assertThat(editingEndedEvent.getErrorCode())
+ .isEqualTo(EditingEndedEvent.ERROR_CODE_MUXING_FAILED);
+ // Assert input media items information
+ assertThat(editingEndedEvent.getInputMediaItemInfos()).hasSize(1);
+ MediaItemInfo inputMediaItemInfo = editingEndedEvent.getInputMediaItemInfos().get(0);
+ assertThat(inputMediaItemInfo.getClipDurationMillis())
+ .isEqualTo(usToMs(processedInputs.get(0).durationUs));
+ assertThat(inputMediaItemInfo.getCodecNames().get(0)).isEqualTo(VIDEO_DECODER_NAME);
+ assertThat(inputMediaItemInfo.getCodecNames().get(1)).isEqualTo(AUDIO_DECODER_NAME);
+ assertThat(inputMediaItemInfo.getContainerMimeType()).isEqualTo(VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes()).hasSize(2);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes().get(0))
+ .isAnyOf(AUDIO_MIME_TYPE, VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getSampleMimeTypes().get(1))
+ .isAnyOf(AUDIO_MIME_TYPE, VIDEO_MIME_TYPE);
+ assertThat(inputMediaItemInfo.getDataTypes())
+ .isEqualTo(MediaItemInfo.DATA_TYPE_VIDEO | MediaItemInfo.DATA_TYPE_AUDIO);
+ assertThat(inputMediaItemInfo.getVideoFrameRate()).isEqualTo(VIDEO_FRAME_RATE);
+ assertThat(inputMediaItemInfo.getVideoSize()).isEqualTo(VIDEO_SIZE);
+ assertThat(inputMediaItemInfo.getVideoDataSpace())
+ .isEqualTo(
+ DataSpace.pack(
+ DataSpace.STANDARD_BT2020, DataSpace.TRANSFER_ST2084, DataSpace.RANGE_LIMITED));
+ assertThat(inputMediaItemInfo.getAudioChannelCount()).isEqualTo(AUDIO_CHANNEL_COUNT);
+ assertThat(inputMediaItemInfo.getAudioSampleRateHz()).isEqualTo(AUDIO_SAMPLE_RATE);
+ // Assert output media item information
+ MediaItemInfo outputMediaItemInfo = editingEndedEvent.getOutputMediaItemInfo();
+ assertThat(outputMediaItemInfo).isNotNull();
+ assertThat(outputMediaItemInfo.getDurationMillis()).isEqualTo(exportResult.durationMs);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes()).hasSize(2);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes().get(0))
+ .isAnyOf(exportResult.audioMimeType, exportResult.videoMimeType);
+ assertThat(outputMediaItemInfo.getSampleMimeTypes().get(1))
+ .isAnyOf(exportResult.audioMimeType, exportResult.videoMimeType);
+ assertThat(outputMediaItemInfo.getAudioChannelCount()).isEqualTo(exportResult.channelCount);
+ assertThat(outputMediaItemInfo.getAudioSampleRateHz()).isEqualTo(exportResult.sampleRate);
+ assertThat(outputMediaItemInfo.getCodecNames()).hasSize(2);
+ assertThat(outputMediaItemInfo.getCodecNames().get(0))
+ .isAnyOf(exportResult.audioEncoderName, exportResult.videoEncoderName);
+ assertThat(outputMediaItemInfo.getCodecNames().get(1))
+ .isAnyOf(exportResult.audioEncoderName, exportResult.videoEncoderName);
+ assertThat(outputMediaItemInfo.getVideoSampleCount()).isEqualTo(exportResult.videoFrameCount);
+ assertThat(outputMediaItemInfo.getVideoSize())
+ .isEqualTo(new Size(exportResult.width, exportResult.height));
+ assertThat(inputMediaItemInfo.getVideoDataSpace())
+ .isEqualTo(
+ DataSpace.pack(
+ DataSpace.STANDARD_BT2020, DataSpace.TRANSFER_ST2084, DataSpace.RANGE_LIMITED));
+ }
+
+ @Test
+ public void onExportCancelled_populatesEditingEndedEvent() {
+ AtomicReference editingEndedEventAtomicReference = new AtomicReference<>();
+ EditingMetricsCollector editingMetricsCollector =
+ new EditingMetricsCollector(
+ new EditingMetricsCollector.MetricsReporter() {
+ @Override
+ public void reportMetrics(EditingEndedEvent editingEndedEvent) {
+ editingEndedEventAtomicReference.set(editingEndedEvent);
+ }
+
+ @Override
+ public void close() {}
+ },
+ EXPORTER_NAME,
+ MUXER_NAME);
+ int progressPercentage = 70;
+
+ editingMetricsCollector.onExportCancelled(progressPercentage);
+
+ EditingEndedEvent editingEndedEvent = editingEndedEventAtomicReference.get();
+ assertThat(editingEndedEvent.getFinalState()).isEqualTo(EditingEndedEvent.FINAL_STATE_CANCELED);
+ assertThat(editingEndedEvent.getTimeSinceCreatedMillis()).isAtLeast(0);
+ assertThat(editingEndedEvent.getExporterName()).isEqualTo(EXPORTER_NAME);
+ assertThat(editingEndedEvent.getMuxerName()).isEqualTo(MUXER_NAME);
+ assertThat(editingEndedEvent.getFinalProgressPercent()).isEqualTo(progressPercentage);
+ }
+}