Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -491,21 +491,50 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a
return@launch
}

val newItem =
Media(
durationUs = durationUs,
title = displayName,
uri = uri.toString(),
selectedEffects = emptySet(),
val uriString = uri.toString()
val newItems = mutableListOf<Media>()
if (durationUs > SPLIT_THRESHOLD_US) {
newItems.add(
Media(
durationUs = SPLIT_THRESHOLD_US,
title = "$displayName (part 1)",
uri = uriString,
selectedEffects = emptySet(),
clipStartUs = 0L,
clipEndUs = SPLIT_THRESHOLD_US,
originalDurationUs = durationUs,
)
)
newItems.add(
Media(
durationUs = durationUs - SPLIT_THRESHOLD_US,
title = "$displayName (part 2)",
uri = uriString,
selectedEffects = emptySet(),
clipStartUs = SPLIT_THRESHOLD_US,
clipEndUs = durationUs,
originalDurationUs = durationUs,
)
)
Log.i(TAG, "Split media \"$displayName\" at ${SPLIT_THRESHOLD_US / 1_000_000}s into 2 parts")
} else {
newItems.add(
Media(
durationUs = durationUs,
title = displayName,
uri = uriString,
selectedEffects = emptySet(),
)
)
}

withContext(Dispatchers.Main) {
_uiState.update { currentState ->
val currentItemsBySequence =
currentState.mediaState.selectedItemsBySequence.toMutableList()
if (sequenceIndex < currentItemsBySequence.size) {
val updatedItems = currentItemsBySequence[sequenceIndex].toMutableList()
updatedItems.add(newItem)
updatedItems.addAll(newItems)
currentItemsBySequence[sequenceIndex] = updatedItems
}
currentState.copy(
Expand Down Expand Up @@ -776,11 +805,19 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a
sequenceBuilder.addGap(item.durationUs)
}
is Media -> {
val mediaItem =
val mediaItemBuilder =
MediaItem.Builder()
.setUri(item.uri)
.setImageDurationMs(usToMs(item.durationUs)) // Ignored for audio/video
.build()
if (item.clipStartUs > 0L || item.clipEndUs < Long.MAX_VALUE) {
val clippingConfig =
MediaItem.ClippingConfiguration.Builder()
.setStartPositionUs(item.clipStartUs)
.setEndPositionUs(item.clipEndUs)
.build()
mediaItemBuilder.setClippingConfiguration(clippingConfig)
}
val mediaItem = mediaItemBuilder.build()
val effectsForItem = mutableListOf<Effect>()
for (effectName in item.selectedEffects) {
// TODO(b/433484977): Order of applied effects should be more clear in the UI
Expand All @@ -804,8 +841,9 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a
// Required for image inputs. For video inputs, it sets the target FPS.
.setFrameRate(DEFAULT_FRAME_RATE_FPS)
// Setting duration explicitly is only required for preview with CompositionPlayer,
// and is not needed for export with Transformer.
.setDurationUs(item.durationUs)
// and is not needed for export with Transformer. When clipping is used, this must
// be the original media duration so getClippedDuration can compute correctly.
.setDurationUs(item.originalDurationUs)
sequenceBuilder.addItem(itemBuilder.build())
}
}
Expand Down Expand Up @@ -1018,6 +1056,7 @@ class CompositionPreviewViewModel(application: Application) : AndroidViewModel(a
// 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 SPLIT_THRESHOLD_US = 3_000_000L
val MEDIA_TYPES = arrayOf("video/*", "image/*", "audio/*")
val HDR_MODE_DESCRIPTIONS =
mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ data class Media(
val uri: String,
val selectedEffects: Set<String> = emptySet(),
val speed: Float = 1.0f,
val clipStartUs: Long = 0L,
val clipEndUs: Long = Long.MAX_VALUE,
val originalDurationUs: Long = durationUs,
) : Item {
override fun copy(): Item =
copy(
Expand All @@ -97,6 +100,9 @@ data class Media(
uri = uri,
selectedEffects = selectedEffects,
speed = speed,
clipStartUs = clipStartUs,
clipEndUs = clipEndUs,
originalDurationUs = originalDurationUs,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import androidx.media3.common.VideoFrameProcessingException
import androidx.media3.common.util.ExperimentalApi
import androidx.media3.common.util.GlProgram
import androidx.media3.common.util.GlUtil
import androidx.media3.common.util.Log
import androidx.media3.common.util.Size
import androidx.media3.common.util.Util
import androidx.media3.common.video.AsyncFrame
Expand Down Expand Up @@ -115,6 +116,7 @@ private constructor(
private val overlayMatrixProvider = OverlayMatrixProvider()
private var targetFormat: Format? = null
private var presentationEffect: Presentation? = null
private var lastItemIndex: Int = -1

private val lock = Any()
@GuardedBy("lock") private var needsWakeup = false
Expand Down Expand Up @@ -226,6 +228,14 @@ private constructor(
inputReleaseFences = result.readCompleteFences

// Step 4: Update the output frame with the new content time and metadata.
val itemIndex = firstInputFrame.metadata[KEY_ITEM_INDEX] as? Int ?: -1
if (itemIndex != lastItemIndex) {
Log.d(TAG, "Packet changed: itemIndex=$lastItemIndex -> $itemIndex" +
" at contentTimeUs=${firstInputFrame.contentTimeUs}")
lastItemIndex = itemIndex
}
Log.d(TAG, "Processing frame: contentTimeUs=${firstInputFrame.contentTimeUs}," +
" itemIndex=$itemIndex")
val updatedOutputFrame =
(outputFrame.frame as HardwareBufferFrame)
.buildUpon()
Expand Down Expand Up @@ -606,6 +616,8 @@ private constructor(
}

companion object {
private const val TAG = "SimpleGlFrameProcessor"
private const val KEY_ITEM_INDEX = "KEY_COMPOSITION_ITEM_INDEX"
private val FENCE_TIMEOUT = Duration.ofMillis(500)
private val USAGE = Frame.USAGE_GPU_COLOR_OUTPUT or Frame.USAGE_GPU_SAMPLED_IMAGE
}
Expand Down