Add TransformationRequest.
PiperOrigin-RevId: 417786661
This commit is contained in:
parent
7166e091cd
commit
0e61f44d4d
@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.transformer.TransformationRequest;
|
||||
import androidx.media3.transformer.Transformer;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -43,9 +44,12 @@ public final class RepeatedTranscodeTransformationTest {
|
||||
transformationMatrix.postTranslate((float) 0.1, (float) 0.1);
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H265)
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_NB)
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H265)
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_NB)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
Set<Long> differentOutputSizesBytes = new HashSet<>();
|
||||
@ -74,9 +78,12 @@ public final class RepeatedTranscodeTransformationTest {
|
||||
transformationMatrix.postTranslate((float) 0.1, (float) 0.1);
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H265)
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.setRemoveAudio(true)
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H265)
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
Set<Long> differentOutputSizesBytes = new HashSet<>();
|
||||
@ -103,8 +110,11 @@ public final class RepeatedTranscodeTransformationTest {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
Transformer transcodingTransformer =
|
||||
new Transformer.Builder(context)
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_NB)
|
||||
.setRemoveVideo(true)
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_NB)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
Set<Long> differentOutputSizesBytes = new HashSet<>();
|
||||
|
@ -19,6 +19,7 @@ import static androidx.media3.transformer.mh.AndroidTestUtil.SEF_ASSET_URI_STRIN
|
||||
import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.transformer.TransformationRequest;
|
||||
import androidx.media3.transformer.Transformer;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -32,7 +33,10 @@ public class SefTransformationTest {
|
||||
public void sefTransform() throws Exception {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context).setFlattenForSlowMotion(true).build();
|
||||
new Transformer.Builder(context)
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder().setFlattenForSlowMotion(true).build())
|
||||
.build();
|
||||
runTransformer(
|
||||
context,
|
||||
/* testId = */ "sefTransform",
|
||||
|
@ -20,6 +20,7 @@ import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import androidx.media3.transformer.TransformationRequest;
|
||||
import androidx.media3.transformer.Transformer;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -35,7 +36,12 @@ public class SetTransformationMatrixTransformationTest {
|
||||
Matrix transformationMatrix = new Matrix();
|
||||
transformationMatrix.postTranslate(/* dx= */ .2f, /* dy= */ .1f);
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context).setTransformationMatrix(transformationMatrix).build();
|
||||
new Transformer.Builder(context)
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
runTransformer(
|
||||
context,
|
||||
|
@ -43,7 +43,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
private static final int DEFAULT_ENCODER_BITRATE = 128 * 1024;
|
||||
|
||||
private final Format inputFormat;
|
||||
private final Transformation transformation;
|
||||
private final TransformationRequest transformationRequest;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
|
||||
private final Codec decoder;
|
||||
@ -66,12 +66,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
public AudioSamplePipeline(
|
||||
Format inputFormat,
|
||||
Transformation transformation,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory)
|
||||
throws TransformationException {
|
||||
this.inputFormat = inputFormat;
|
||||
this.transformation = transformation;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.encoderFactory = encoderFactory;
|
||||
decoderInputBuffer =
|
||||
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||
@ -294,7 +294,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
decoderOutputFormat.sampleRate,
|
||||
decoderOutputFormat.channelCount,
|
||||
decoderOutputFormat.pcmEncoding);
|
||||
if (transformation.flattenForSlowMotion) {
|
||||
if (transformationRequest.flattenForSlowMotion) {
|
||||
try {
|
||||
outputAudioFormat = sonicAudioProcessor.configure(outputAudioFormat);
|
||||
flushSonicAndSetSpeed(currentSpeed);
|
||||
@ -310,9 +310,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
encoderFactory.createForAudioEncoding(
|
||||
new Format.Builder()
|
||||
.setSampleMimeType(
|
||||
transformation.audioMimeType == null
|
||||
transformationRequest.audioMimeType == null
|
||||
? inputFormat.sampleMimeType
|
||||
: transformation.audioMimeType)
|
||||
: transformationRequest.audioMimeType)
|
||||
.setSampleRate(outputAudioFormat.sampleRate)
|
||||
.setChannelCount(outputAudioFormat.channelCount)
|
||||
.setAverageBitrate(DEFAULT_ENCODER_BITRATE)
|
||||
@ -322,7 +322,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
|
||||
private boolean isSpeedChanging(BufferInfo bufferInfo) {
|
||||
if (!transformation.flattenForSlowMotion) {
|
||||
if (!transformationRequest.flattenForSlowMotion) {
|
||||
return false;
|
||||
}
|
||||
float newSpeed = speedProvider.getSpeed(bufferInfo.presentationTimeUs);
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 android.graphics.Matrix;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/** A media transformation configuration. */
|
||||
/* package */ final class Transformation {
|
||||
|
||||
public final boolean removeAudio;
|
||||
public final boolean removeVideo;
|
||||
public final boolean flattenForSlowMotion;
|
||||
public final int outputHeight;
|
||||
public final Matrix transformationMatrix;
|
||||
public final String containerMimeType;
|
||||
@Nullable public final String audioMimeType;
|
||||
@Nullable public final String videoMimeType;
|
||||
|
||||
public Transformation(
|
||||
boolean removeAudio,
|
||||
boolean removeVideo,
|
||||
boolean flattenForSlowMotion,
|
||||
int outputHeight,
|
||||
Matrix transformationMatrix,
|
||||
String containerMimeType,
|
||||
@Nullable String audioMimeType,
|
||||
@Nullable String videoMimeType) {
|
||||
this.removeAudio = removeAudio;
|
||||
this.removeVideo = removeVideo;
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
this.outputHeight = outputHeight;
|
||||
this.transformationMatrix = transformationMatrix;
|
||||
this.containerMimeType = containerMimeType;
|
||||
this.audioMimeType = audioMimeType;
|
||||
this.videoMimeType = videoMimeType;
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2020 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 android.graphics.Matrix;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.source.MediaSourceFactory;
|
||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||
|
||||
/** A media transformation request. */
|
||||
@UnstableApi
|
||||
public final class TransformationRequest {
|
||||
|
||||
/** A builder for {@link TransformationRequest} instances. */
|
||||
public static final class Builder {
|
||||
|
||||
private Matrix transformationMatrix;
|
||||
private boolean flattenForSlowMotion;
|
||||
private int outputHeight;
|
||||
@Nullable private String audioMimeType;
|
||||
@Nullable private String videoMimeType;
|
||||
|
||||
/**
|
||||
* Creates a new instance with default values.
|
||||
*
|
||||
* <p>Use {@link TransformationRequest#buildUpon()} to obtain a builder representing an existing
|
||||
* {@link TransformationRequest}.
|
||||
*/
|
||||
public Builder() {
|
||||
transformationMatrix = new Matrix();
|
||||
outputHeight = C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
private Builder(TransformationRequest transformationRequest) {
|
||||
this.transformationMatrix = transformationRequest.transformationMatrix;
|
||||
this.flattenForSlowMotion = transformationRequest.flattenForSlowMotion;
|
||||
this.outputHeight = transformationRequest.outputHeight;
|
||||
this.audioMimeType = transformationRequest.audioMimeType;
|
||||
this.videoMimeType = transformationRequest.videoMimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transformation matrix. The default value is to apply no change.
|
||||
*
|
||||
* <p>This can be used to perform operations supported by {@link Matrix}, like scaling and
|
||||
* rotating the video.
|
||||
*
|
||||
* <p>For now, resolution will not be affected by this method.
|
||||
*
|
||||
* @param transformationMatrix The transformation to apply to video frames.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setTransformationMatrix(Matrix transformationMatrix) {
|
||||
// TODO(b/201293185): After {@link #setResolution} supports arbitrary resolutions,
|
||||
// allow transformations to change the resolution, by scaling to the appropriate min/max
|
||||
// values. This will also be required to create the VertexTransformation class, in order to
|
||||
// have aspect ratio helper methods (which require resolution to change).
|
||||
this.transformationMatrix = transformationMatrix;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the input should be flattened for media containing slow motion markers. The
|
||||
* transformed output is obtained by removing the slow motion metadata and by actually slowing
|
||||
* down the parts of the video and audio streams defined in this metadata. The default value for
|
||||
* {@code flattenForSlowMotion} is {@code false}.
|
||||
*
|
||||
* <p>Only Samsung Extension Format (SEF) slow motion metadata type is supported. The
|
||||
* transformation has no effect if the input does not contain this metadata type.
|
||||
*
|
||||
* <p>For SEF slow motion media, the following assumptions are made on the input:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The input container format is (unfragmented) MP4.
|
||||
* <li>The input contains an AVC video elementary stream with temporal SVC.
|
||||
* <li>The recording frame rate of the video is 120 or 240 fps.
|
||||
* </ul>
|
||||
*
|
||||
* <p>If specifying a {@link MediaSourceFactory} using {@link
|
||||
* Transformer.Builder#setMediaSourceFactory(MediaSourceFactory)}, make sure that {@link
|
||||
* Mp4Extractor#FLAG_READ_SEF_DATA} is set on the {@link Mp4Extractor} used. Otherwise, the slow
|
||||
* motion metadata will be ignored and the input won't be flattened.
|
||||
*
|
||||
* @param flattenForSlowMotion Whether to flatten for slow motion.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setFlattenForSlowMotion(boolean flattenForSlowMotion) {
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output resolution using the output height. The default value is the same height as
|
||||
* the input. Output width will scale to preserve the input video's aspect ratio.
|
||||
*
|
||||
* <p>For now, only "popular" heights like 144, 240, 360, 480, 720, 1080, 1440, or 2160 are
|
||||
* supported, to ensure compatibility on different devices.
|
||||
*
|
||||
* <p>For example, a 1920x1440 video can be scaled to 640x480 by calling setResolution(480).
|
||||
*
|
||||
* @param outputHeight The output height in pixels.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setResolution(int outputHeight) {
|
||||
// TODO(b/201293185): Restructure to input a Presentation class.
|
||||
// TODO(b/201293185): Check encoder codec capabilities in order to allow arbitrary
|
||||
// resolutions and reasonable fallbacks.
|
||||
if (outputHeight != 144
|
||||
&& outputHeight != 240
|
||||
&& outputHeight != 360
|
||||
&& outputHeight != 480
|
||||
&& outputHeight != 720
|
||||
&& outputHeight != 1080
|
||||
&& outputHeight != 1440
|
||||
&& outputHeight != 2160) {
|
||||
throw new IllegalArgumentException(
|
||||
"Please use a height of 144, 240, 360, 480, 720, 1080, 1440, or 2160.");
|
||||
}
|
||||
this.outputHeight = outputHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the video MIME type of the output. The default value is to use the same MIME type as the
|
||||
* input. Supported values are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link MimeTypes#VIDEO_H263}
|
||||
* <li>{@link MimeTypes#VIDEO_H264}
|
||||
* <li>{@link MimeTypes#VIDEO_H265} from API level 24
|
||||
* <li>{@link MimeTypes#VIDEO_MP4V}
|
||||
* </ul>
|
||||
*
|
||||
* @param videoMimeType The MIME type of the video samples in the output.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setVideoMimeType(String videoMimeType) {
|
||||
// TODO(b/209469847): Validate videoMimeType here once deprecated
|
||||
// Transformer.Builder#setOuputMimeType(String) has been removed.
|
||||
this.videoMimeType = videoMimeType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the audio MIME type of the output. The default value is to use the same MIME type as the
|
||||
* input. Supported values are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link MimeTypes#AUDIO_AAC}
|
||||
* <li>{@link MimeTypes#AUDIO_AMR_NB}
|
||||
* <li>{@link MimeTypes#AUDIO_AMR_WB}
|
||||
* </ul>
|
||||
*
|
||||
* @param audioMimeType The MIME type of the audio samples in the output.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setAudioMimeType(String audioMimeType) {
|
||||
// TODO(b/209469847): Validate audioMimeType here once deprecated
|
||||
// Transformer.Builder#setOuputMimeType(String) has been removed.
|
||||
this.audioMimeType = audioMimeType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds a {@link TransformationRequest} instance. */
|
||||
public TransformationRequest build() {
|
||||
return new TransformationRequest(
|
||||
transformationMatrix, flattenForSlowMotion, outputHeight, audioMimeType, videoMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
public final Matrix transformationMatrix;
|
||||
public final boolean flattenForSlowMotion;
|
||||
public final int outputHeight;
|
||||
@Nullable public final String audioMimeType;
|
||||
@Nullable public final String videoMimeType;
|
||||
|
||||
private TransformationRequest(
|
||||
Matrix transformationMatrix,
|
||||
boolean flattenForSlowMotion,
|
||||
int outputHeight,
|
||||
@Nullable String audioMimeType,
|
||||
@Nullable String videoMimeType) {
|
||||
this.transformationMatrix = transformationMatrix;
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
this.outputHeight = outputHeight;
|
||||
this.audioMimeType = audioMimeType;
|
||||
this.videoMimeType = videoMimeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TransformationRequest)) {
|
||||
return false;
|
||||
}
|
||||
TransformationRequest that = (TransformationRequest) o;
|
||||
return transformationMatrix.equals(that.transformationMatrix)
|
||||
&& flattenForSlowMotion == that.flattenForSlowMotion
|
||||
&& outputHeight == that.outputHeight
|
||||
&& Util.areEqual(audioMimeType, that.audioMimeType)
|
||||
&& Util.areEqual(videoMimeType, that.videoMimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = transformationMatrix.hashCode();
|
||||
result = 31 * result + (flattenForSlowMotion ? 1 : 0);
|
||||
result = 31 * result + outputHeight;
|
||||
result = 31 * result + (audioMimeType != null ? audioMimeType.hashCode() : 0);
|
||||
result = 31 * result + (videoMimeType != null ? videoMimeType.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link TransformationRequest.Builder} initialized with the values of this
|
||||
* instance.
|
||||
*/
|
||||
public Builder buildUpon() {
|
||||
return new Builder(this);
|
||||
}
|
||||
}
|
@ -99,12 +99,9 @@ public final class Transformer {
|
||||
private Muxer.Factory muxerFactory;
|
||||
private boolean removeAudio;
|
||||
private boolean removeVideo;
|
||||
private boolean flattenForSlowMotion;
|
||||
private int outputHeight;
|
||||
private Matrix transformationMatrix;
|
||||
private String containerMimeType;
|
||||
@Nullable private String audioMimeType;
|
||||
@Nullable private String videoMimeType;
|
||||
// TODO(b/204869912): Make final once deprecated setters are removed.
|
||||
private TransformationRequest transformationRequest;
|
||||
private Transformer.Listener listener;
|
||||
private DebugViewProvider debugViewProvider;
|
||||
private Looper looper;
|
||||
@ -115,14 +112,13 @@ public final class Transformer {
|
||||
@Deprecated
|
||||
public Builder() {
|
||||
muxerFactory = new FrameworkMuxer.Factory();
|
||||
outputHeight = C.LENGTH_UNSET;
|
||||
transformationMatrix = new Matrix();
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
listener = new Listener() {};
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
clock = Clock.DEFAULT;
|
||||
encoderFactory = Codec.EncoderFactory.DEFAULT;
|
||||
debugViewProvider = DebugViewProvider.NONE;
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
transformationRequest = new TransformationRequest.Builder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,14 +129,13 @@ public final class Transformer {
|
||||
public Builder(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
muxerFactory = new FrameworkMuxer.Factory();
|
||||
outputHeight = C.LENGTH_UNSET;
|
||||
transformationMatrix = new Matrix();
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
listener = new Listener() {};
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
clock = Clock.DEFAULT;
|
||||
encoderFactory = Codec.EncoderFactory.DEFAULT;
|
||||
debugViewProvider = DebugViewProvider.NONE;
|
||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||
this.transformationRequest = new TransformationRequest.Builder().build();
|
||||
}
|
||||
|
||||
/** Creates a builder with the values of the provided {@link Transformer}. */
|
||||
@ -148,14 +143,10 @@ public final class Transformer {
|
||||
this.context = transformer.context;
|
||||
this.mediaSourceFactory = transformer.mediaSourceFactory;
|
||||
this.muxerFactory = transformer.muxerFactory;
|
||||
this.removeAudio = transformer.transformation.removeAudio;
|
||||
this.removeVideo = transformer.transformation.removeVideo;
|
||||
this.flattenForSlowMotion = transformer.transformation.flattenForSlowMotion;
|
||||
this.outputHeight = transformer.transformation.outputHeight;
|
||||
this.transformationMatrix = transformer.transformation.transformationMatrix;
|
||||
this.containerMimeType = transformer.transformation.containerMimeType;
|
||||
this.audioMimeType = transformer.transformation.audioMimeType;
|
||||
this.videoMimeType = transformer.transformation.videoMimeType;
|
||||
this.removeAudio = transformer.removeAudio;
|
||||
this.removeVideo = transformer.removeVideo;
|
||||
this.containerMimeType = transformer.containerMimeType;
|
||||
this.transformationRequest = transformer.transformationRequest;
|
||||
this.listener = transformer.listener;
|
||||
this.looper = transformer.looper;
|
||||
this.encoderFactory = transformer.encoderFactory;
|
||||
@ -170,6 +161,17 @@ public final class Transformer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link TransformationRequest} which configures the editing and transcoding options.
|
||||
*
|
||||
* @param transformationRequest The {@link TransformationRequest}.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setTransformationRequest(TransformationRequest transformationRequest) {
|
||||
this.transformationRequest = transformationRequest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MediaSourceFactory} to be used to retrieve the inputs to transform. The
|
||||
* default value is a {@link DefaultMediaSourceFactory} built with the context provided in
|
||||
@ -212,83 +214,31 @@ public final class Transformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the input should be flattened for media containing slow motion markers. The
|
||||
* transformed output is obtained by removing the slow motion metadata and by actually slowing
|
||||
* down the parts of the video and audio streams defined in this metadata. The default value for
|
||||
* {@code flattenForSlowMotion} is {@code false}.
|
||||
*
|
||||
* <p>Only Samsung Extension Format (SEF) slow motion metadata type is supported. The
|
||||
* transformation has no effect if the input does not contain this metadata type.
|
||||
*
|
||||
* <p>For SEF slow motion media, the following assumptions are made on the input:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The input container format is (unfragmented) MP4.
|
||||
* <li>The input contains an AVC video elementary stream with temporal SVC.
|
||||
* <li>The recording frame rate of the video is 120 or 240 fps.
|
||||
* </ul>
|
||||
*
|
||||
* <p>If specifying a {@link MediaSourceFactory} using {@link
|
||||
* #setMediaSourceFactory(MediaSourceFactory)}, make sure that {@link
|
||||
* Mp4Extractor#FLAG_READ_SEF_DATA} is set on the {@link Mp4Extractor} used. Otherwise, the slow
|
||||
* motion metadata will be ignored and the input won't be flattened.
|
||||
*
|
||||
* @param flattenForSlowMotion Whether to flatten for slow motion.
|
||||
* @return This builder.
|
||||
* @deprecated Use {@link TransformationRequest.Builder#setFlattenForSlowMotion(boolean)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setFlattenForSlowMotion(boolean flattenForSlowMotion) {
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
transformationRequest =
|
||||
transformationRequest.buildUpon().setFlattenForSlowMotion(flattenForSlowMotion).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output resolution using the output height. The default value is the same height as
|
||||
* the input. Output width will scale to preserve the input video's aspect ratio.
|
||||
*
|
||||
* <p>For now, only "popular" heights like 144, 240, 360, 480, 720, 1080, 1440, or 2160 are
|
||||
* supported, to ensure compatibility on different devices.
|
||||
*
|
||||
* <p>For example, a 1920x1440 video can be scaled to 640x480 by calling setResolution(480).
|
||||
*
|
||||
* @param outputHeight The output height in pixels.
|
||||
* @return This builder.
|
||||
*/
|
||||
/** @deprecated Use {@link TransformationRequest.Builder#setResolution(int)} instead. */
|
||||
@Deprecated
|
||||
public Builder setResolution(int outputHeight) {
|
||||
// TODO(Internal b/201293185): Restructure to input a Presentation class.
|
||||
// TODO(Internal b/201293185): Check encoder codec capabilities in order to allow arbitrary
|
||||
// resolutions and reasonable fallbacks.
|
||||
if (outputHeight != 144
|
||||
&& outputHeight != 240
|
||||
&& outputHeight != 360
|
||||
&& outputHeight != 480
|
||||
&& outputHeight != 720
|
||||
&& outputHeight != 1080
|
||||
&& outputHeight != 1440
|
||||
&& outputHeight != 2160) {
|
||||
throw new IllegalArgumentException(
|
||||
"Please use a height of 144, 240, 360, 480, 720, 1080, 1440, or 2160.");
|
||||
}
|
||||
this.outputHeight = outputHeight;
|
||||
transformationRequest = transformationRequest.buildUpon().setResolution(outputHeight).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transformation matrix. The default value is to apply no change.
|
||||
*
|
||||
* <p>This can be used to perform operations supported by {@link Matrix}, like scaling and
|
||||
* rotating the video.
|
||||
*
|
||||
* <p>For now, resolution will not be affected by this method.
|
||||
*
|
||||
* @param transformationMatrix The transformation to apply to video frames.
|
||||
* @return This builder.
|
||||
* @deprecated Use {@link TransformationRequest.Builder#setTransformationMatrix(Matrix)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setTransformationMatrix(Matrix transformationMatrix) {
|
||||
// TODO(Internal b/201293185): After {@link #setResolution} supports arbitrary resolutions,
|
||||
// allow transformations to change the resolution, by scaling to the appropriate min/max
|
||||
// values. This will also be required to create the VertexTransformation class, in order to
|
||||
// have aspect ratio helper methods (which require resolution to change).
|
||||
this.transformationMatrix = transformationMatrix;
|
||||
transformationRequest =
|
||||
transformationRequest.buildUpon().setTransformationMatrix(transformationMatrix).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -302,40 +252,19 @@ public final class Transformer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the video MIME type of the output. The default value is to use the same MIME type as the
|
||||
* input. Supported values are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link MimeTypes#VIDEO_H263}
|
||||
* <li>{@link MimeTypes#VIDEO_H264}
|
||||
* <li>{@link MimeTypes#VIDEO_H265} from API level 24
|
||||
* <li>{@link MimeTypes#VIDEO_MP4V}
|
||||
* </ul>
|
||||
*
|
||||
* @param videoMimeType The MIME type of the video samples in the output.
|
||||
* @return This builder.
|
||||
*/
|
||||
/** @deprecated Use {@link TransformationRequest.Builder#setVideoMimeType(String)} instead. */
|
||||
@Deprecated
|
||||
public Builder setVideoMimeType(String videoMimeType) {
|
||||
this.videoMimeType = videoMimeType;
|
||||
transformationRequest =
|
||||
transformationRequest.buildUpon().setVideoMimeType(videoMimeType).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the audio MIME type of the output. The default value is to use the same MIME type as the
|
||||
* input. Supported values are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link MimeTypes#AUDIO_AAC}
|
||||
* <li>{@link MimeTypes#AUDIO_AMR_NB}
|
||||
* <li>{@link MimeTypes#AUDIO_AMR_WB}
|
||||
* </ul>
|
||||
*
|
||||
* @param audioMimeType The MIME type of the audio samples in the output.
|
||||
* @return This builder.
|
||||
*/
|
||||
/** @deprecated Use {@link TransformationRequest.Builder#setAudioMimeType(String)} instead. */
|
||||
@Deprecated
|
||||
public Builder setAudioMimeType(String audioMimeType) {
|
||||
this.audioMimeType = audioMimeType;
|
||||
transformationRequest =
|
||||
transformationRequest.buildUpon().setAudioMimeType(audioMimeType).build();
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -434,7 +363,7 @@ public final class Transformer {
|
||||
checkNotNull(context);
|
||||
if (mediaSourceFactory == null) {
|
||||
DefaultExtractorsFactory defaultExtractorsFactory = new DefaultExtractorsFactory();
|
||||
if (flattenForSlowMotion) {
|
||||
if (transformationRequest.flattenForSlowMotion) {
|
||||
defaultExtractorsFactory.setMp4ExtractorFlags(Mp4Extractor.FLAG_READ_SEF_DATA);
|
||||
}
|
||||
mediaSourceFactory = new DefaultMediaSourceFactory(context, defaultExtractorsFactory);
|
||||
@ -442,27 +371,20 @@ public final class Transformer {
|
||||
checkState(
|
||||
muxerFactory.supportsOutputMimeType(containerMimeType),
|
||||
"Unsupported container MIME type: " + containerMimeType);
|
||||
if (audioMimeType != null) {
|
||||
checkSampleMimeType(audioMimeType);
|
||||
if (transformationRequest.audioMimeType != null) {
|
||||
checkSampleMimeType(transformationRequest.audioMimeType);
|
||||
}
|
||||
if (videoMimeType != null) {
|
||||
checkSampleMimeType(videoMimeType);
|
||||
if (transformationRequest.videoMimeType != null) {
|
||||
checkSampleMimeType(transformationRequest.videoMimeType);
|
||||
}
|
||||
Transformation transformation =
|
||||
new Transformation(
|
||||
removeAudio,
|
||||
removeVideo,
|
||||
flattenForSlowMotion,
|
||||
outputHeight,
|
||||
transformationMatrix,
|
||||
containerMimeType,
|
||||
audioMimeType,
|
||||
videoMimeType);
|
||||
return new Transformer(
|
||||
context,
|
||||
mediaSourceFactory,
|
||||
muxerFactory,
|
||||
transformation,
|
||||
removeAudio,
|
||||
removeVideo,
|
||||
containerMimeType,
|
||||
transformationRequest,
|
||||
listener,
|
||||
looper,
|
||||
clock,
|
||||
@ -553,7 +475,10 @@ public final class Transformer {
|
||||
private final Context context;
|
||||
private final MediaSourceFactory mediaSourceFactory;
|
||||
private final Muxer.Factory muxerFactory;
|
||||
private final Transformation transformation;
|
||||
private final boolean removeAudio;
|
||||
private final boolean removeVideo;
|
||||
private final String containerMimeType;
|
||||
private final TransformationRequest transformationRequest;
|
||||
private final Looper looper;
|
||||
private final Clock clock;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
@ -569,20 +494,24 @@ public final class Transformer {
|
||||
Context context,
|
||||
MediaSourceFactory mediaSourceFactory,
|
||||
Muxer.Factory muxerFactory,
|
||||
Transformation transformation,
|
||||
boolean removeAudio,
|
||||
boolean removeVideo,
|
||||
String containerMimeType,
|
||||
TransformationRequest transformationRequest,
|
||||
Transformer.Listener listener,
|
||||
Looper looper,
|
||||
Clock clock,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
Transformer.DebugViewProvider debugViewProvider) {
|
||||
checkState(
|
||||
!transformation.removeAudio || !transformation.removeVideo,
|
||||
"Audio and video cannot both be removed.");
|
||||
checkState(!removeAudio || !removeVideo, "Audio and video cannot both be removed.");
|
||||
this.context = context;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
this.muxerFactory = muxerFactory;
|
||||
this.transformation = transformation;
|
||||
this.removeAudio = removeAudio;
|
||||
this.removeVideo = removeVideo;
|
||||
this.containerMimeType = containerMimeType;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.listener = listener;
|
||||
this.looper = looper;
|
||||
this.clock = clock;
|
||||
@ -628,7 +557,7 @@ public final class Transformer {
|
||||
* @throws IOException If an error occurs opening the output file for writing.
|
||||
*/
|
||||
public void startTransformation(MediaItem mediaItem, String path) throws IOException {
|
||||
startTransformation(mediaItem, muxerFactory.create(path, transformation.containerMimeType));
|
||||
startTransformation(mediaItem, muxerFactory.create(path, containerMimeType));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -656,8 +585,7 @@ public final class Transformer {
|
||||
@RequiresApi(26)
|
||||
public void startTransformation(MediaItem mediaItem, ParcelFileDescriptor parcelFileDescriptor)
|
||||
throws IOException {
|
||||
startTransformation(
|
||||
mediaItem, muxerFactory.create(parcelFileDescriptor, transformation.containerMimeType));
|
||||
startTransformation(mediaItem, muxerFactory.create(parcelFileDescriptor, containerMimeType));
|
||||
}
|
||||
|
||||
private void startTransformation(MediaItem mediaItem, Muxer muxer) {
|
||||
@ -666,8 +594,7 @@ public final class Transformer {
|
||||
throw new IllegalStateException("There is already a transformation in progress.");
|
||||
}
|
||||
|
||||
MuxerWrapper muxerWrapper =
|
||||
new MuxerWrapper(muxer, muxerFactory, transformation.containerMimeType);
|
||||
MuxerWrapper muxerWrapper = new MuxerWrapper(muxer, muxerFactory, containerMimeType);
|
||||
this.muxerWrapper = muxerWrapper;
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
|
||||
trackSelector.setParameters(
|
||||
@ -690,7 +617,9 @@ public final class Transformer {
|
||||
new TransformerRenderersFactory(
|
||||
context,
|
||||
muxerWrapper,
|
||||
transformation,
|
||||
removeAudio,
|
||||
removeVideo,
|
||||
transformationRequest,
|
||||
encoderFactory,
|
||||
decoderFactory,
|
||||
debugViewProvider))
|
||||
@ -781,7 +710,9 @@ public final class Transformer {
|
||||
private final Context context;
|
||||
private final MuxerWrapper muxerWrapper;
|
||||
private final TransformerMediaClock mediaClock;
|
||||
private final Transformation transformation;
|
||||
private final boolean removeAudio;
|
||||
private final boolean removeVideo;
|
||||
private final TransformationRequest transformationRequest;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
private final Codec.DecoderFactory decoderFactory;
|
||||
private final Transformer.DebugViewProvider debugViewProvider;
|
||||
@ -789,13 +720,17 @@ public final class Transformer {
|
||||
public TransformerRenderersFactory(
|
||||
Context context,
|
||||
MuxerWrapper muxerWrapper,
|
||||
Transformation transformation,
|
||||
boolean removeAudio,
|
||||
boolean removeVideo,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
Transformer.DebugViewProvider debugViewProvider) {
|
||||
this.context = context;
|
||||
this.muxerWrapper = muxerWrapper;
|
||||
this.transformation = transformation;
|
||||
this.removeAudio = removeAudio;
|
||||
this.removeVideo = removeVideo;
|
||||
this.transformationRequest = transformationRequest;
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.decoderFactory = decoderFactory;
|
||||
this.debugViewProvider = debugViewProvider;
|
||||
@ -809,22 +744,22 @@ public final class Transformer {
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
int rendererCount = transformation.removeAudio || transformation.removeVideo ? 1 : 2;
|
||||
int rendererCount = removeAudio || removeVideo ? 1 : 2;
|
||||
Renderer[] renderers = new Renderer[rendererCount];
|
||||
int index = 0;
|
||||
if (!transformation.removeAudio) {
|
||||
if (!removeAudio) {
|
||||
renderers[index] =
|
||||
new TransformerAudioRenderer(
|
||||
muxerWrapper, mediaClock, transformation, encoderFactory, decoderFactory);
|
||||
muxerWrapper, mediaClock, transformationRequest, encoderFactory, decoderFactory);
|
||||
index++;
|
||||
}
|
||||
if (!transformation.removeVideo) {
|
||||
if (!removeVideo) {
|
||||
renderers[index] =
|
||||
new TransformerVideoRenderer(
|
||||
context,
|
||||
muxerWrapper,
|
||||
mediaClock,
|
||||
transformation,
|
||||
transformationRequest,
|
||||
encoderFactory,
|
||||
decoderFactory,
|
||||
debugViewProvider);
|
||||
|
@ -39,10 +39,10 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
public TransformerAudioRenderer(
|
||||
MuxerWrapper muxerWrapper,
|
||||
TransformerMediaClock mediaClock,
|
||||
Transformation transformation,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory) {
|
||||
super(C.TRACK_TYPE_AUDIO, muxerWrapper, mediaClock, transformation);
|
||||
super(C.TRACK_TYPE_AUDIO, muxerWrapper, mediaClock, transformationRequest);
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.decoderFactory = decoderFactory;
|
||||
decoderInputBuffer =
|
||||
@ -69,7 +69,8 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
Format inputFormat = checkNotNull(formatHolder.format);
|
||||
if (shouldTranscode(inputFormat)) {
|
||||
samplePipeline =
|
||||
new AudioSamplePipeline(inputFormat, transformation, encoderFactory, decoderFactory);
|
||||
new AudioSamplePipeline(
|
||||
inputFormat, transformationRequest, encoderFactory, decoderFactory);
|
||||
} else {
|
||||
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
||||
}
|
||||
@ -77,11 +78,11 @@ import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
}
|
||||
|
||||
private boolean shouldTranscode(Format inputFormat) {
|
||||
if (transformation.audioMimeType != null
|
||||
&& !transformation.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
if (transformationRequest.audioMimeType != null
|
||||
&& !transformationRequest.audioMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformation.flattenForSlowMotion && isSlowMotion(inputFormat)) {
|
||||
if (transformationRequest.flattenForSlowMotion && isSlowMotion(inputFormat)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -38,7 +38,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
|
||||
protected final MuxerWrapper muxerWrapper;
|
||||
protected final TransformerMediaClock mediaClock;
|
||||
protected final Transformation transformation;
|
||||
protected final TransformationRequest transformationRequest;
|
||||
|
||||
protected boolean isRendererStarted;
|
||||
protected boolean muxerWrapperTrackAdded;
|
||||
@ -50,11 +50,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
int trackType,
|
||||
MuxerWrapper muxerWrapper,
|
||||
TransformerMediaClock mediaClock,
|
||||
Transformation transformation) {
|
||||
TransformationRequest transformationRequest) {
|
||||
super(trackType);
|
||||
this.muxerWrapper = muxerWrapper;
|
||||
this.mediaClock = mediaClock;
|
||||
this.transformation = transformation;
|
||||
this.transformationRequest = transformationRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,14 +65,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
|
||||
} else if ((MimeTypes.isAudio(sampleMimeType)
|
||||
&& muxerWrapper.supportsSampleMimeType(
|
||||
transformation.audioMimeType == null
|
||||
transformationRequest.audioMimeType == null
|
||||
? sampleMimeType
|
||||
: transformation.audioMimeType))
|
||||
: transformationRequest.audioMimeType))
|
||||
|| (MimeTypes.isVideo(sampleMimeType)
|
||||
&& muxerWrapper.supportsSampleMimeType(
|
||||
transformation.videoMimeType == null
|
||||
transformationRequest.videoMimeType == null
|
||||
? sampleMimeType
|
||||
: transformation.videoMimeType))) {
|
||||
: transformationRequest.videoMimeType))) {
|
||||
return RendererCapabilities.create(C.FORMAT_HANDLED);
|
||||
} else {
|
||||
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
|
||||
|
@ -45,11 +45,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
Context context,
|
||||
MuxerWrapper muxerWrapper,
|
||||
TransformerMediaClock mediaClock,
|
||||
Transformation transformation,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
Transformer.DebugViewProvider debugViewProvider) {
|
||||
super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformation);
|
||||
super(C.TRACK_TYPE_VIDEO, muxerWrapper, mediaClock, transformationRequest);
|
||||
this.context = context;
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.decoderFactory = decoderFactory;
|
||||
@ -81,29 +81,29 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
new VideoSamplePipeline(
|
||||
context,
|
||||
inputFormat,
|
||||
transformation,
|
||||
transformationRequest,
|
||||
encoderFactory,
|
||||
decoderFactory,
|
||||
debugViewProvider);
|
||||
} else {
|
||||
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
||||
}
|
||||
if (transformation.flattenForSlowMotion) {
|
||||
if (transformationRequest.flattenForSlowMotion) {
|
||||
sefSlowMotionFlattener = new SefSlowMotionFlattener(inputFormat);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldTranscode(Format inputFormat) {
|
||||
if (transformation.videoMimeType != null
|
||||
&& !transformation.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
if (transformationRequest.videoMimeType != null
|
||||
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||
return true;
|
||||
}
|
||||
if (transformation.outputHeight != C.LENGTH_UNSET
|
||||
&& transformation.outputHeight != inputFormat.height) {
|
||||
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
||||
&& transformationRequest.outputHeight != inputFormat.height) {
|
||||
return true;
|
||||
}
|
||||
if (!transformation.transformationMatrix.isIdentity()) {
|
||||
if (!transformationRequest.transformationMatrix.isIdentity()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -50,7 +50,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
public VideoSamplePipeline(
|
||||
Context context,
|
||||
Format inputFormat,
|
||||
Transformation transformation,
|
||||
TransformationRequest transformationRequest,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Codec.DecoderFactory decoderFactory,
|
||||
Transformer.DebugViewProvider debugViewProvider)
|
||||
@ -63,10 +63,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
// TODO(internal b/209781577): Think about which edge length should be set for portrait videos.
|
||||
int outputWidth = inputFormat.width;
|
||||
int outputHeight = inputFormat.height;
|
||||
if (transformation.outputHeight != C.LENGTH_UNSET
|
||||
&& transformation.outputHeight != inputFormat.height) {
|
||||
outputWidth = inputFormat.width * transformation.outputHeight / inputFormat.height;
|
||||
outputHeight = transformation.outputHeight;
|
||||
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
||||
&& transformationRequest.outputHeight != inputFormat.height) {
|
||||
outputWidth = inputFormat.width * transformationRequest.outputHeight / inputFormat.height;
|
||||
outputHeight = transformationRequest.outputHeight;
|
||||
}
|
||||
|
||||
if (inputFormat.height > inputFormat.width) {
|
||||
@ -83,7 +83,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
// them back for improved encoder compatibility.
|
||||
// TODO(internal b/201293185): After fragment shader transformations are implemented, put
|
||||
// postrotation in a later vertex shader.
|
||||
transformation.transformationMatrix.postRotate(outputRotationDegrees);
|
||||
transformationRequest.transformationMatrix.postRotate(outputRotationDegrees);
|
||||
|
||||
encoder =
|
||||
encoderFactory.createForVideoEncoding(
|
||||
@ -92,19 +92,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
.setHeight(outputHeight)
|
||||
.setRotationDegrees(0)
|
||||
.setSampleMimeType(
|
||||
transformation.videoMimeType != null
|
||||
? transformation.videoMimeType
|
||||
transformationRequest.videoMimeType != null
|
||||
? transformationRequest.videoMimeType
|
||||
: inputFormat.sampleMimeType)
|
||||
.build());
|
||||
if (inputFormat.height != outputHeight
|
||||
|| inputFormat.width != outputWidth
|
||||
|| !transformation.transformationMatrix.isIdentity()) {
|
||||
|| !transformationRequest.transformationMatrix.isIdentity()) {
|
||||
frameEditor =
|
||||
FrameEditor.create(
|
||||
context,
|
||||
outputWidth,
|
||||
outputHeight,
|
||||
transformation.transformationMatrix,
|
||||
transformationRequest.transformationMatrix,
|
||||
/* outputSurface= */ checkNotNull(encoder.getInputSurface()),
|
||||
debugViewProvider);
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021 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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link TransformationRequest}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TransformationRequestTest {
|
||||
|
||||
@Test
|
||||
public void buildUponTransformationRequest_createsEqualTransformationRequest() {
|
||||
TransformationRequest request = createTestTransformationRequest();
|
||||
assertThat(request.buildUpon().build()).isEqualTo(request);
|
||||
}
|
||||
|
||||
private static TransformationRequest createTestTransformationRequest() {
|
||||
Matrix transformationMatrix = new Matrix();
|
||||
transformationMatrix.preRotate(36);
|
||||
transformationMatrix.postTranslate((float) 0.5, (float) -0.2);
|
||||
|
||||
return new TransformationRequest.Builder()
|
||||
.setFlattenForSlowMotion(true)
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AAC)
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H264)
|
||||
.setTransformationMatrix(transformationMatrix)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -51,4 +51,36 @@ public class TransformerBuilderTest {
|
||||
IllegalStateException.class,
|
||||
() -> new Transformer.Builder(context).setRemoveAudio(true).setRemoveVideo(true).build());
|
||||
}
|
||||
|
||||
// TODO(b/209469847): Move this test to TransformationRequestBuilderTest once deprecated
|
||||
// Transformer.Builder#setOuputMimeType(String) has been removed.
|
||||
@Test
|
||||
public void build_withUnsupportedAudioMimeType_throws() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
TransformationRequest transformationRequest =
|
||||
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_UNKNOWN).build();
|
||||
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
new Transformer.Builder(context)
|
||||
.setTransformationRequest(transformationRequest)
|
||||
.build());
|
||||
}
|
||||
|
||||
// TODO(b/209469847): Move this test to TransformationRequestBuilderTest once deprecated
|
||||
// Transformer.Builder#setOuputMimeType(String) has been removed.
|
||||
@Test
|
||||
public void build_withUnsupportedVideoMimeType_throws() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
TransformationRequest transformationRequest =
|
||||
new TransformationRequest.Builder().setVideoMimeType(MimeTypes.VIDEO_UNKNOWN).build();
|
||||
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
new Transformer.Builder(context)
|
||||
.setTransformationRequest(transformationRequest)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
@ -224,9 +224,10 @@ public final class TransformerTest {
|
||||
public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception {
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setFlattenForSlowMotion(true)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder().setFlattenForSlowMotion(true).build())
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_SEF_SLOW_MOTION);
|
||||
|
||||
@ -243,7 +244,10 @@ public final class TransformerTest {
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_WB) // unsupported encoder MIME type
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AMR_WB) // unsupported encoder MIME type
|
||||
.build())
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_AUDIO_ONLY);
|
||||
|
||||
@ -262,7 +266,10 @@ public final class TransformerTest {
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported encoder MIME type
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported encoder MIME type
|
||||
.build())
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_WITH_ALL_SAMPLE_FORMATS_UNSUPPORTED);
|
||||
|
||||
@ -281,7 +288,10 @@ public final class TransformerTest {
|
||||
new Transformer.Builder(context)
|
||||
.setClock(clock)
|
||||
.setMuxerFactory(new TestMuxerFactory())
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H263) // unsupported encoder MIME type
|
||||
.setTransformationRequest(
|
||||
new TransformationRequest.Builder()
|
||||
.setVideoMimeType(MimeTypes.VIDEO_H263) // unsupported encoder MIME type
|
||||
.build())
|
||||
.build();
|
||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user