From cd5d5bde27d3421ef6d2d032f9aeb057c76603ac Mon Sep 17 00:00:00 2001 From: shahddaghash Date: Fri, 3 Jan 2025 05:24:35 -0800 Subject: [PATCH] Implement basic Metrics Collector in Transformer Added a new `EditingMediaMetrics` class that interacts with the platform's `MediaMetricsManager` through an `EditingSession` created when export starts. Currently, only the `finalState` of the export event is reported to the `EditingSession`. Future changes will collect additional metrics based on the export operation's output. PiperOrigin-RevId: 711721801 --- .../transformer/EditingMetricsCollector.java | 83 +++++++++++++++++++ .../media3/transformer/Transformer.java | 18 ++++ 2 files changed, 101 insertions(+) create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java new file mode 100644 index 0000000000..0d2743f5fd --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EditingMetricsCollector.java @@ -0,0 +1,83 @@ +/* + * Copyright 2024 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.checkNotNull; + +import android.content.Context; +import android.media.metrics.EditingEndedEvent; +import android.media.metrics.EditingSession; +import android.media.metrics.MediaMetricsManager; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +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}. + */ +@RequiresApi(35) +/* package */ final class EditingMetricsCollector { + + private @MonotonicNonNull EditingSession editingSession; + + /** + * Creates an instance. + * + *

A new instance must be created before starting a new export. + * + * @param context The {@link Context}. + */ + public EditingMetricsCollector(Context context) { + @Nullable + MediaMetricsManager mediaMetricsManager = + (MediaMetricsManager) context.getSystemService(Context.MEDIA_METRICS_SERVICE); + if (mediaMetricsManager != null) { + editingSession = checkNotNull(mediaMetricsManager.createEditingSession()); + } + } + + /** Called when export completes with success. */ + public void onExportSuccess() { + if (editingSession == null) { + return; + } + editingSession.reportEditingEndedEvent( + new EditingEndedEvent.Builder(EditingEndedEvent.FINAL_STATE_SUCCEEDED).build()); + editingSession.close(); + } + + /** Called when export completes with an error. */ + public void onExportError() { + if (editingSession == null) { + return; + } + editingSession.reportEditingEndedEvent( + new EditingEndedEvent.Builder(EditingEndedEvent.FINAL_STATE_ERROR).build()); + editingSession.close(); + } + + /** Called when export is cancelled. */ + public void onExportCancelled() { + if (editingSession == null) { + return; + } + editingSession.reportEditingEndedEvent( + new EditingEndedEvent.Builder(EditingEndedEvent.FINAL_STATE_CANCELED).build()); + editingSession.close(); + } +} 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 9af1fce83b..79852ef184 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -19,6 +19,7 @@ package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; +import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.common.util.Util.isRunningOnEmulator; import static androidx.media3.extractor.AacUtil.AAC_LC_AUDIO_SAMPLE_COUNT; import static androidx.media3.transformer.ExportException.ERROR_CODE_MUXING_APPEND; @@ -771,6 +772,7 @@ public final class Transformer { private final HandlerWrapper applicationHandler; private final ComponentListener componentListener; private final ExportResult.Builder exportResultBuilder; + private @MonotonicNonNull EditingMetricsCollector editingMetricsCollector; @Nullable private TransformerInternal transformerInternal; @Nullable private MuxerWrapper remuxingMuxerWrapper; @@ -1168,6 +1170,9 @@ public final class Transformer { } finally { transformerInternal = null; } + if (canCollectEditingMetrics()) { + checkNotNull(editingMetricsCollector).onExportCancelled(); + } if (getResumeMetadataFuture != null && !getResumeMetadataFuture.isDone()) { getResumeMetadataFuture.cancel(/* mayInterruptIfRunning= */ false); @@ -1542,6 +1547,10 @@ public final class Transformer { } } + private boolean canCollectEditingMetrics() { + return SDK_INT >= 35 && usePlatformDiagnostics; + } + private void startInternal( Composition composition, MuxerWrapper muxerWrapper, @@ -1563,6 +1572,9 @@ public final class Transformer { context, new DefaultDecoderFactory.Builder(context).build(), clock); } DebugTraceUtil.reset(); + if (canCollectEditingMetrics()) { + editingMetricsCollector = new EditingMetricsCollector(context); + } transformerInternal = new TransformerInternal( context, @@ -1590,6 +1602,9 @@ public final class Transformer { /* eventFlag= */ C.INDEX_UNSET, listener -> listener.onCompleted(checkNotNull(composition), exportResultBuilder.build())); listeners.flushEvents(); + if (canCollectEditingMetrics()) { + checkNotNull(editingMetricsCollector).onExportSuccess(); + } transformerState = TRANSFORMER_STATE_PROCESS_FULL_INPUT; } @@ -1600,6 +1615,9 @@ public final class Transformer { listener -> listener.onError(checkNotNull(composition), exportResultBuilder.build(), exception)); listeners.flushEvents(); + if (canCollectEditingMetrics()) { + checkNotNull(editingMetricsCollector).onExportError(); + } transformerState = TRANSFORMER_STATE_PROCESS_FULL_INPUT; }