From 8017d7bfa3fb8a5895838d1bc243a0cd938a495e Mon Sep 17 00:00:00 2001 From: tadepall_adobe Date: Tue, 7 Jul 2026 19:20:34 -0700 Subject: [PATCH] Update default image duration to 4s and add export progress logging Set DEFAULT_IMAGE_DURATION_US to 4 seconds and add a progress polling coroutine that periodically logs Transformer export progress. Co-authored-by: Cursor --- .../CompositionPreviewViewModel.kt | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/demos/composition/src/main/java/androidx/media3/demo/composition/CompositionPreviewViewModel.kt b/demos/composition/src/main/java/androidx/media3/demo/composition/CompositionPreviewViewModel.kt index a784c9bf937..881099ccfad 100644 --- a/demos/composition/src/main/java/androidx/media3/demo/composition/CompositionPreviewViewModel.kt +++ b/demos/composition/src/main/java/androidx/media3/demo/composition/CompositionPreviewViewModel.kt @@ -75,6 +75,7 @@ import androidx.media3.transformer.ExportResult import androidx.media3.transformer.InAppFragmentedMp4Muxer import androidx.media3.transformer.InAppMp4Muxer import androidx.media3.transformer.JsonUtil +import androidx.media3.transformer.ProgressHolder import androidx.media3.transformer.Transformer import com.google.common.base.Stopwatch import com.google.common.base.Ticker @@ -86,6 +87,8 @@ import kotlin.math.cos import kotlin.math.roundToInt import kotlin.math.sin import kotlin.math.sqrt +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -114,6 +117,7 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a internal var frameConsumerEnabled: Boolean = false internal var surfaceView: SurfaceView? = null private var transformer: Transformer? = null + private var progressPollingJob: Job? = null private var outputFile: File? = null private var preparedComposition: Composition? = null private var playerPrepared: Boolean = false @@ -657,6 +661,7 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a .addListener( object : Transformer.Listener { override fun onCompleted(composition: Composition, exportResult: ExportResult) { + progressPollingJob?.cancel() exportStopwatch.stop() val elapsedTimeMs = exportStopwatch.elapsed(TimeUnit.MILLISECONDS) val details = @@ -695,6 +700,7 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a exportResult: ExportResult, exportException: ExportException, ) { + progressPollingJob?.cancel() exportStopwatch.stop() _uiState.update { it.copy( @@ -731,10 +737,35 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a exportStopwatch.start() transformer!!.start(composition, filePath) Log.i(TAG, "Export started") + startProgressPolling() + } + + private fun startProgressPolling() { + progressPollingJob?.cancel() + progressPollingJob = + viewModelScope.launch(Dispatchers.Main) { + val progressHolder = ProgressHolder() + while (true) { + val currentTransformer = transformer ?: break + val progressState = currentTransformer.getProgress(progressHolder) + when (progressState) { + Transformer.PROGRESS_STATE_AVAILABLE -> + Log.i(TAG, "Export progress: ${progressHolder.progress}%") + Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY -> + Log.d(TAG, "Export progress: waiting for availability") + Transformer.PROGRESS_STATE_UNAVAILABLE -> + Log.d(TAG, "Export progress: unavailable") + Transformer.PROGRESS_STATE_NOT_STARTED -> break + } + delay(PROGRESS_POLLING_INTERVAL_MS) + } + } } /** Cancels any ongoing export operation, and deletes output file contents. */ fun cancelExport() { + progressPollingJob?.cancel() + progressPollingJob = null transformer?.cancel() transformer = null outputFile?.delete() @@ -1017,7 +1048,8 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a // A 3-second moving average window (rather than 1s) is used to smooth out micro-stutters // and provide a stable, readable real-time playback FPS value in the UI. private const val FPS_TRACKING_DURATION_MS = 3000L - private const val DEFAULT_IMAGE_DURATION_US = 1_000_000L + private const val DEFAULT_IMAGE_DURATION_US = 4_000_000L + private const val PROGRESS_POLLING_INTERVAL_MS = 100L val MEDIA_TYPES = arrayOf("video/*", "image/*", "audio/*") val HDR_MODE_DESCRIPTIONS = mapOf(