Avoid re-encoding if video effects are no-op

This is to avoid regressions introduced by removing the convenience
methods from TransformationRequest.

PiperOrigin-RevId: 502864512
This commit is contained in:
kimvde 2023-01-18 14:54:00 +00:00 committed by christosts
parent 50f066d634
commit a043c8c204
3 changed files with 60 additions and 9 deletions

View File

@ -90,6 +90,13 @@ public final class ScaleToFitTransformation implements MatrixTransformation {
}
}
/** The multiplier by which the frame will scale horizontally, along the x-axis. */
public final float scaleX;
/** The multiplier by which the frame will scale vertically, along the y-axis. */
public final float scaleY;
/** How much to rotate the frame counterclockwise, in degrees. */
public final float rotationDegrees;
private final Matrix transformationMatrix;
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
@ -101,6 +108,9 @@ public final class ScaleToFitTransformation implements MatrixTransformation {
* @param rotationDegrees How much to rotate the frame counterclockwise, in degrees.
*/
private ScaleToFitTransformation(float scaleX, float scaleY, float rotationDegrees) {
this.scaleX = scaleX;
this.scaleY = scaleY;
this.rotationDegrees = rotationDegrees;
transformationMatrix = new Matrix();
transformationMatrix.postScale(scaleX, scaleY);
transformationMatrix.postRotate(rotationDegrees);

View File

@ -35,6 +35,8 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.effect.Presentation;
import com.google.android.exoplayer2.effect.ScaleToFitTransformation;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.mp4.SlowMotionData;
import com.google.android.exoplayer2.util.Clock;
@ -44,6 +46,7 @@ import com.google.android.exoplayer2.util.Effect;
import com.google.android.exoplayer2.util.FrameProcessor;
import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Size;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Documented;
@ -577,16 +580,34 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (inputFormat.pixelWidthHeightRatio != 1f) {
return true;
}
// TODO(b/265927935): consider generalizing this logic.
for (int i = 0; i < videoEffects.size(); i++) {
Effect videoEffect = videoEffects.get(i);
if (videoEffect instanceof Presentation) {
Presentation presentation = (Presentation) videoEffect;
// The decoder rotates encoded frames for display by inputFormat.rotationDegrees.
int decodedWidth =
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.width : inputFormat.height;
int decodedHeight =
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width;
if (transformationRequest.outputHeight != C.LENGTH_UNSET
&& transformationRequest.outputHeight != decodedHeight) {
Size outputSize = presentation.configure(decodedWidth, decodedHeight);
if (outputSize.getWidth() != decodedWidth || outputSize.getHeight() != decodedHeight) {
return true;
}
if (!videoEffects.isEmpty()) {
} else if (videoEffect instanceof ScaleToFitTransformation) {
ScaleToFitTransformation scaleToFitTransformation =
(ScaleToFitTransformation) videoEffect;
if (scaleToFitTransformation.scaleX != 1f
|| scaleToFitTransformation.scaleY != 1f
|| scaleToFitTransformation.rotationDegrees != 0f) {
return true;
}
} else {
return true;
}
}
return false;
}
}

View File

@ -48,6 +48,8 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.audio.SonicAudioProcessor;
import com.google.android.exoplayer2.effect.Presentation;
import com.google.android.exoplayer2.effect.ScaleToFitTransformation;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
@ -58,6 +60,7 @@ import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.testutil.DumpFileAsserts;
import com.google.android.exoplayer2.testutil.FakeClock;
import com.google.android.exoplayer2.util.Effect;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
@ -728,6 +731,23 @@ public final class TransformerEndToEndTest {
assertThat(transformationException).hasCauseThat().isInstanceOf(IllegalStateException.class);
}
@Test
public void startTransformation_withNoOpEffects_transmuxes() throws Exception {
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
int mediaItemHeightPixels = 720;
List<Effect> videoEffects = new ArrayList<>();
videoEffects.add(Presentation.createForHeight(mediaItemHeightPixels));
videoEffects.add(new ScaleToFitTransformation.Builder().build());
Transformer transformer =
createTransformerBuilder(/* enableFallback= */ false).setVideoEffects(videoEffects).build();
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runLooper(transformer);
// Video transcoding in unit tests is not supported.
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY));
}
@Test
public void getProgress_knownDuration_returnsConsistentStates() throws Exception {
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();