mirror of
https://github.com/samsonjs/media.git
synced 2026-04-04 11:05:47 +00:00
Move shouldTrancodeAudio/Video to static helper methods
PiperOrigin-RevId: 578617730
This commit is contained in:
parent
a671f4aede
commit
de82aa93f0
2 changed files with 147 additions and 113 deletions
|
|
@ -23,14 +23,13 @@ import static androidx.media3.common.util.Assertions.checkState;
|
|||
import static androidx.media3.common.util.Util.contains;
|
||||
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.Composition.HDR_MODE_KEEP_HDR;
|
||||
import static androidx.media3.transformer.ExportException.ERROR_CODE_FAILED_RUNTIME_CHECK;
|
||||
import static androidx.media3.transformer.ExportException.ERROR_CODE_MUXING_FAILED;
|
||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
|
||||
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NOT_STARTED;
|
||||
import static androidx.media3.transformer.TransformerUtil.areVideoEffectsAllNoOp;
|
||||
import static androidx.media3.transformer.TransformerUtil.containsSlowMotionData;
|
||||
import static androidx.media3.transformer.TransformerUtil.getProcessedTrackType;
|
||||
import static androidx.media3.transformer.TransformerUtil.shouldTranscodeAudio;
|
||||
import static androidx.media3.transformer.TransformerUtil.shouldTranscodeVideo;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
|
||||
|
|
@ -45,14 +44,12 @@ import androidx.annotation.IntDef;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -445,7 +442,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private final class SequenceAssetLoaderListener implements AssetLoader.Listener {
|
||||
|
||||
private final int sequenceIndex;
|
||||
private final ImmutableList<EditedMediaItem> editedMediaItems;
|
||||
private final EditedMediaItem firstEditedMediaItem;
|
||||
private final Composition composition;
|
||||
private final TransformationRequest transformationRequest;
|
||||
private final AudioMixer.Factory audioMixerFactory;
|
||||
|
|
@ -463,7 +460,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
FallbackListener fallbackListener,
|
||||
DebugViewProvider debugViewProvider) {
|
||||
this.sequenceIndex = sequenceIndex;
|
||||
this.editedMediaItems = composition.sequences.get(sequenceIndex).editedMediaItems;
|
||||
this.firstEditedMediaItem = composition.sequences.get(sequenceIndex).editedMediaItems.get(0);
|
||||
this.composition = composition;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.audioMixerFactory = audioMixerFactory;
|
||||
|
|
@ -535,7 +532,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
}
|
||||
|
||||
GraphInput sampleExporterInput =
|
||||
sampleExporter.getInput(editedMediaItems.get(0), assetLoaderOutputFormat);
|
||||
sampleExporter.getInput(firstEditedMediaItem, assetLoaderOutputFormat);
|
||||
OnMediaItemChangedListener onMediaItemChangedListener =
|
||||
(editedMediaItem, durationUs, trackFormat, isLast) -> {
|
||||
onMediaItemChanged(trackType, durationUs, isLast);
|
||||
|
|
@ -581,7 +578,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
firstAssetLoaderInputFormat,
|
||||
/* firstInputFormat= */ assetLoaderOutputFormat,
|
||||
transformationRequest,
|
||||
editedMediaItems.get(0),
|
||||
firstEditedMediaItem,
|
||||
audioMixerFactory,
|
||||
encoderFactory,
|
||||
muxerWrapper,
|
||||
|
|
@ -680,95 +677,29 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
if (!assetLoaderCanOutputEncoded) {
|
||||
shouldTranscode = true;
|
||||
} else if (trackType == TRACK_TYPE_AUDIO) {
|
||||
shouldTranscode = shouldTranscodeAudio(inputFormat);
|
||||
shouldTranscode =
|
||||
shouldTranscodeAudio(
|
||||
inputFormat,
|
||||
composition,
|
||||
sequenceIndex,
|
||||
transformationRequest,
|
||||
encoderFactory,
|
||||
muxerWrapper);
|
||||
} else if (trackType == C.TRACK_TYPE_VIDEO) {
|
||||
shouldTranscode = shouldTranscodeVideo(inputFormat);
|
||||
shouldTranscode =
|
||||
shouldTranscodeVideo(
|
||||
inputFormat,
|
||||
composition,
|
||||
sequenceIndex,
|
||||
transformationRequest,
|
||||
encoderFactory,
|
||||
muxerWrapper);
|
||||
}
|
||||
|
||||
checkState(!shouldTranscode || assetLoaderCanOutputDecoded);
|
||||
|
||||
return shouldTranscode;
|
||||
}
|
||||
|
||||
private boolean shouldTranscodeAudio(Format inputFormat) {
|
||||
if (composition.sequences.size() > 1 || editedMediaItems.size() > 1) {
|
||||
return !composition.transmuxAudio;
|
||||
}
|
||||
if (encoderFactory.audioNeedsEncoding()) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.audioMimeType != null
|
||||
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.audioMimeType == null
|
||||
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
EditedMediaItem firstEditedMediaItem = editedMediaItems.get(0);
|
||||
if (firstEditedMediaItem.flattenForSlowMotion && containsSlowMotionData(inputFormat)) {
|
||||
return true;
|
||||
}
|
||||
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldTranscodeVideo(Format inputFormat) {
|
||||
if (composition.sequences.size() > 1 || editedMediaItems.size() > 1) {
|
||||
return !composition.transmuxVideo;
|
||||
}
|
||||
EditedMediaItem firstEditedMediaItem = editedMediaItems.get(0);
|
||||
if (firstEditedMediaItem.mediaItem.clippingConfiguration.startPositionMs > 0
|
||||
&& !firstEditedMediaItem.mediaItem.clippingConfiguration.startsAtKeyFrame) {
|
||||
return true;
|
||||
}
|
||||
if (encoderFactory.videoNeedsEncoding()) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.hdrMode != HDR_MODE_KEEP_HDR) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.videoMimeType != null
|
||||
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.videoMimeType == null
|
||||
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (inputFormat.pixelWidthHeightRatio != 1f) {
|
||||
return true;
|
||||
}
|
||||
ImmutableList<Effect> videoEffects = firstEditedMediaItem.effects.videoEffects;
|
||||
return !videoEffects.isEmpty()
|
||||
&& !areVideoEffectsAllNoOp(videoEffects, inputFormat)
|
||||
&& !hasOnlyRegularRotationEffect(videoEffects);
|
||||
}
|
||||
|
||||
private boolean hasOnlyRegularRotationEffect(ImmutableList<Effect> videoEffects) {
|
||||
if (videoEffects.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
Effect videoEffect = videoEffects.get(0);
|
||||
if (!(videoEffect instanceof ScaleAndRotateTransformation)) {
|
||||
return false;
|
||||
}
|
||||
ScaleAndRotateTransformation scaleAndRotateTransformation =
|
||||
(ScaleAndRotateTransformation) videoEffect;
|
||||
if (scaleAndRotateTransformation.scaleX != 1f || scaleAndRotateTransformation.scaleY != 1f) {
|
||||
return false;
|
||||
}
|
||||
float rotationDegrees = scaleAndRotateTransformation.rotationDegrees;
|
||||
if (rotationDegrees == 90f || rotationDegrees == 180f || rotationDegrees == 270f) {
|
||||
// The MuxerWrapper rotation is clockwise while the ScaleAndRotateTransformation rotation
|
||||
// is counterclockwise.
|
||||
muxerWrapper.setAdditionalRotationDegrees(360 - Math.round(rotationDegrees));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Tracks the inputs and outputs of {@link AssetLoader AssetLoaders}. */
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import static androidx.media3.transformer.Composition.HDR_MODE_KEEP_HDR;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
|
|
@ -24,6 +26,7 @@ import androidx.media3.common.Format;
|
|||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.effect.GlEffect;
|
||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
|
|
@ -45,6 +48,110 @@ import com.google.common.collect.ImmutableList;
|
|||
return trackType == C.TRACK_TYPE_IMAGE ? C.TRACK_TYPE_VIDEO : trackType;
|
||||
}
|
||||
|
||||
/** Returns {@link MediaCodec} flags corresponding to {@link C.BufferFlags}. */
|
||||
public static int getMediaCodecFlags(@C.BufferFlags int flags) {
|
||||
int mediaCodecFlags = 0;
|
||||
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME) {
|
||||
mediaCodecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
|
||||
}
|
||||
if ((flags & C.BUFFER_FLAG_END_OF_STREAM) == C.BUFFER_FLAG_END_OF_STREAM) {
|
||||
mediaCodecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
|
||||
}
|
||||
return mediaCodecFlags;
|
||||
}
|
||||
|
||||
/** Returns whether the audio track should be transcoded. */
|
||||
public static boolean shouldTranscodeAudio(
|
||||
Format inputFormat,
|
||||
Composition composition,
|
||||
int sequenceIndex,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
MuxerWrapper muxerWrapper) {
|
||||
if (composition.sequences.size() > 1
|
||||
|| composition.sequences.get(sequenceIndex).editedMediaItems.size() > 1) {
|
||||
return !composition.transmuxAudio;
|
||||
}
|
||||
if (encoderFactory.audioNeedsEncoding()) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.audioMimeType != null
|
||||
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.audioMimeType == null
|
||||
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
EditedMediaItem firstEditedMediaItem =
|
||||
composition.sequences.get(sequenceIndex).editedMediaItems.get(0);
|
||||
if (firstEditedMediaItem.flattenForSlowMotion && containsSlowMotionData(inputFormat)) {
|
||||
return true;
|
||||
}
|
||||
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Format} contains {@linkplain SlowMotionData slow motion metadata}.
|
||||
*/
|
||||
private static boolean containsSlowMotionData(Format format) {
|
||||
@Nullable Metadata metadata = format.metadata;
|
||||
if (metadata == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < metadata.length(); i++) {
|
||||
if (metadata.get(i) instanceof SlowMotionData) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns whether the video track should be transcoded. */
|
||||
public static boolean shouldTranscodeVideo(
|
||||
Format inputFormat,
|
||||
Composition composition,
|
||||
int sequenceIndex,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
MuxerWrapper muxerWrapper) {
|
||||
|
||||
if (composition.sequences.size() > 1
|
||||
|| composition.sequences.get(sequenceIndex).editedMediaItems.size() > 1) {
|
||||
return !composition.transmuxVideo;
|
||||
}
|
||||
EditedMediaItem firstEditedMediaItem =
|
||||
composition.sequences.get(sequenceIndex).editedMediaItems.get(0);
|
||||
if (firstEditedMediaItem.mediaItem.clippingConfiguration.startPositionMs > 0
|
||||
&& !firstEditedMediaItem.mediaItem.clippingConfiguration.startsAtKeyFrame) {
|
||||
return true;
|
||||
}
|
||||
if (encoderFactory.videoNeedsEncoding()) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.hdrMode != HDR_MODE_KEEP_HDR) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.videoMimeType != null
|
||||
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformationRequest.videoMimeType == null
|
||||
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (inputFormat.pixelWidthHeightRatio != 1f) {
|
||||
return true;
|
||||
}
|
||||
ImmutableList<Effect> videoEffects = firstEditedMediaItem.effects.videoEffects;
|
||||
return !videoEffects.isEmpty()
|
||||
&& !areVideoEffectsAllNoOp(videoEffects, inputFormat)
|
||||
&& !hasOnlyRegularRotationEffect(videoEffects, muxerWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the collection of {@code videoEffects} would be a {@linkplain
|
||||
* GlEffect#isNoOp(int, int) no-op}, if queued samples of this {@link Format}.
|
||||
|
|
@ -70,31 +177,27 @@ import com.google.common.collect.ImmutableList;
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Format} contains {@linkplain SlowMotionData slow motion metadata}.
|
||||
*/
|
||||
public static boolean containsSlowMotionData(Format format) {
|
||||
@Nullable Metadata metadata = format.metadata;
|
||||
if (metadata == null) {
|
||||
private static boolean hasOnlyRegularRotationEffect(
|
||||
ImmutableList<Effect> videoEffects, MuxerWrapper muxerWrapper) {
|
||||
if (videoEffects.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < metadata.length(); i++) {
|
||||
if (metadata.get(i) instanceof SlowMotionData) {
|
||||
return true;
|
||||
}
|
||||
Effect videoEffect = videoEffects.get(0);
|
||||
if (!(videoEffect instanceof ScaleAndRotateTransformation)) {
|
||||
return false;
|
||||
}
|
||||
ScaleAndRotateTransformation scaleAndRotateTransformation =
|
||||
(ScaleAndRotateTransformation) videoEffect;
|
||||
if (scaleAndRotateTransformation.scaleX != 1f || scaleAndRotateTransformation.scaleY != 1f) {
|
||||
return false;
|
||||
}
|
||||
float rotationDegrees = scaleAndRotateTransformation.rotationDegrees;
|
||||
if (rotationDegrees == 90f || rotationDegrees == 180f || rotationDegrees == 270f) {
|
||||
// The MuxerWrapper rotation is clockwise while the ScaleAndRotateTransformation rotation
|
||||
// is counterclockwise.
|
||||
muxerWrapper.setAdditionalRotationDegrees(360 - Math.round(rotationDegrees));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns {@link MediaCodec} flags corresponding to {@link C.BufferFlags}. */
|
||||
public static int getMediaCodecFlags(@C.BufferFlags int flags) {
|
||||
int mediaCodecFlags = 0;
|
||||
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME) {
|
||||
mediaCodecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
|
||||
}
|
||||
if ((flags & C.BUFFER_FLAG_END_OF_STREAM) == C.BUFFER_FLAG_END_OF_STREAM) {
|
||||
mediaCodecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
|
||||
}
|
||||
return mediaCodecFlags;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue