diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/Crop.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/Crop.java index b302b07989..4cd1d3af55 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/Crop.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/Crop.java @@ -91,4 +91,12 @@ public final class Crop implements MatrixTransformation { public Matrix getMatrix(long presentationTimeUs) { return checkStateNotNull(transformationMatrix, "configure must be called first"); } + + @Override + public boolean isNoOp(int inputWidth, int inputHeight) { + Size outputSize = configure(inputWidth, inputHeight); + return checkStateNotNull(transformationMatrix).isIdentity() + && inputWidth == outputSize.getWidth() + && inputHeight == outputSize.getHeight(); + } } diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffect.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffect.java index 2e273e7ebc..282f3d9686 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffect.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffect.java @@ -38,4 +38,19 @@ public interface GlEffect extends Effect { */ GlShaderProgram toGlShaderProgram(Context context, boolean useHdr) throws FrameProcessingException; + + /** + * Returns whether a {@link GlEffect} applies no change at every timestamp. + * + *
This can be used as a hint to skip this instance.
+ *
+ * @param inputWidth The input frame width, in pixels.
+ * @param inputHeight The input frame height, in pixels.
+ */
+ default boolean isNoOp(int inputWidth, int inputHeight) {
+ // TODO(b/265927935): Generalize this logic by implementing this method on all
+ // subclasses, and deleting the default implementation here. Otherwise, some no-op effects may
+ // not be properly detected or handled.
+ return false;
+ }
}
diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/Presentation.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/Presentation.java
index 9c82e156e6..9c1b352b0c 100644
--- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/Presentation.java
+++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/Presentation.java
@@ -212,6 +212,14 @@ public final class Presentation implements MatrixTransformation {
return checkStateNotNull(transformationMatrix, "configure must be called first");
}
+ @Override
+ public boolean isNoOp(int inputWidth, int inputHeight) {
+ configure(inputWidth, inputHeight);
+ return checkStateNotNull(transformationMatrix).isIdentity()
+ && inputWidth == Math.round(outputWidth)
+ && inputHeight == Math.round(outputHeight);
+ }
+
@RequiresNonNull("transformationMatrix")
private void applyAspectRatio() {
float inputAspectRatio = outputWidth / outputHeight;
diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/ScaleToFitTransformation.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/ScaleToFitTransformation.java
index f7a34f89b3..ae270963b9 100644
--- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/ScaleToFitTransformation.java
+++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/ScaleToFitTransformation.java
@@ -90,13 +90,6 @@ 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;
@@ -108,9 +101,6 @@ 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);
@@ -160,4 +150,12 @@ public final class ScaleToFitTransformation implements MatrixTransformation {
public Matrix getMatrix(long presentationTimeUs) {
return checkStateNotNull(adjustedTransformationMatrix, "configure must be called first");
}
+
+ @Override
+ public boolean isNoOp(int inputWidth, int inputHeight) {
+ Size outputSize = configure(inputWidth, inputHeight);
+ return checkStateNotNull(adjustedTransformationMatrix).isIdentity()
+ && inputWidth == outputSize.getWidth()
+ && inputHeight == outputSize.getHeight();
+ }
}
diff --git a/library/effect/src/test/java/com/google/android/exoplayer2/effect/CropTest.java b/library/effect/src/test/java/com/google/android/exoplayer2/effect/CropTest.java
index eba38629cf..ac43d10567 100644
--- a/library/effect/src/test/java/com/google/android/exoplayer2/effect/CropTest.java
+++ b/library/effect/src/test/java/com/google/android/exoplayer2/effect/CropTest.java
@@ -37,7 +37,9 @@ public final class CropTest {
Crop crop = new Crop(/* left= */ -1, /* right= */ 1, /* bottom= */ -1, /* top= */ 1);
Size outputSize = crop.configure(inputWidth, inputHeight);
+ boolean isNoOp = crop.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isTrue();
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -53,7 +55,9 @@ public final class CropTest {
Crop crop = new Crop(left, right, bottom, top);
Size outputSize = crop.configure(inputWidth, inputHeight);
+ boolean isNoOp = crop.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
int expectedPostCropWidth = Math.round(inputWidth * (right - left) / GlUtil.LENGTH_NDC);
int expectedPostCropHeight = Math.round(inputHeight * (top - bottom) / GlUtil.LENGTH_NDC);
assertThat(outputSize.getWidth()).isEqualTo(expectedPostCropWidth);
diff --git a/library/effect/src/test/java/com/google/android/exoplayer2/effect/PresentationTest.java b/library/effect/src/test/java/com/google/android/exoplayer2/effect/PresentationTest.java
index d3a6894f73..38164b75cf 100644
--- a/library/effect/src/test/java/com/google/android/exoplayer2/effect/PresentationTest.java
+++ b/library/effect/src/test/java/com/google/android/exoplayer2/effect/PresentationTest.java
@@ -37,7 +37,9 @@ public final class PresentationTest {
Presentation presentation = Presentation.createForHeight(C.LENGTH_UNSET);
Size outputSize = presentation.configure(inputWidth, inputHeight);
+ boolean isNoOp = presentation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isTrue();
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -50,7 +52,9 @@ public final class PresentationTest {
Presentation presentation = Presentation.createForHeight(requestedHeight);
Size outputSize = presentation.configure(inputWidth, inputHeight);
+ boolean isNoOp = presentation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(requestedHeight * inputWidth / inputHeight);
assertThat(outputSize.getHeight()).isEqualTo(requestedHeight);
}
@@ -64,7 +68,9 @@ public final class PresentationTest {
Presentation.createForAspectRatio(aspectRatio, Presentation.LAYOUT_SCALE_TO_FIT);
Size outputSize = presentation.configure(inputWidth, inputHeight);
+ boolean isNoOp = presentation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(Math.round(aspectRatio * inputHeight));
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -80,7 +86,9 @@ public final class PresentationTest {
requestedWidth, requestedHeight, Presentation.LAYOUT_SCALE_TO_FIT);
Size outputSize = presentation.configure(inputWidth, inputHeight);
+ boolean isNoOp = presentation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(requestedWidth);
assertThat(outputSize.getHeight()).isEqualTo(requestedHeight);
}
diff --git a/library/effect/src/test/java/com/google/android/exoplayer2/effect/ScaleToFitTransformationTest.java b/library/effect/src/test/java/com/google/android/exoplayer2/effect/ScaleToFitTransformationTest.java
index 93daefb096..7cd23bafe7 100644
--- a/library/effect/src/test/java/com/google/android/exoplayer2/effect/ScaleToFitTransformationTest.java
+++ b/library/effect/src/test/java/com/google/android/exoplayer2/effect/ScaleToFitTransformationTest.java
@@ -39,7 +39,9 @@ public final class ScaleToFitTransformationTest {
new ScaleToFitTransformation.Builder().build();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isTrue();
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -54,7 +56,9 @@ public final class ScaleToFitTransformationTest {
.build();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(Math.round(inputWidth * .5f));
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -67,7 +71,9 @@ public final class ScaleToFitTransformationTest {
new ScaleToFitTransformation.Builder().setScale(/* scaleX= */ 2f, /* scaleY= */ 1f).build();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(inputWidth * 2);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight);
}
@@ -80,7 +86,9 @@ public final class ScaleToFitTransformationTest {
new ScaleToFitTransformation.Builder().setScale(/* scaleX= */ 1f, /* scaleY= */ 2f).build();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(inputWidth);
assertThat(outputSize.getHeight()).isEqualTo(inputHeight * 2);
}
@@ -93,7 +101,9 @@ public final class ScaleToFitTransformationTest {
new ScaleToFitTransformation.Builder().setRotationDegrees(90).build();
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(inputHeight);
assertThat(outputSize.getHeight()).isEqualTo(inputWidth);
}
@@ -107,7 +117,9 @@ public final class ScaleToFitTransformationTest {
long expectedOutputWidthHeight = 247;
Size outputSize = scaleToFitTransformation.configure(inputWidth, inputHeight);
+ boolean isNoOp = scaleToFitTransformation.isNoOp(inputWidth, inputHeight);
+ assertThat(isNoOp).isFalse();
assertThat(outputSize.getWidth()).isEqualTo(expectedOutputWidthHeight);
assertThat(outputSize.getHeight()).isEqualTo(expectedOutputWidthHeight);
}
diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
index 72c31b6db9..254161fa6c 100644
--- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
+++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java
@@ -32,8 +32,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
-import com.google.android.exoplayer2.effect.Presentation;
-import com.google.android.exoplayer2.effect.ScaleToFitTransformation;
+import com.google.android.exoplayer2.effect.GlEffect;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.mp4.SlowMotionData;
import com.google.android.exoplayer2.util.Clock;
@@ -42,7 +41,6 @@ import com.google.android.exoplayer2.util.DebugViewProvider;
import com.google.android.exoplayer2.util.Effect;
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 java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -565,35 +563,34 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (inputFormat.pixelWidthHeightRatio != 1f) {
return true;
}
+ if (!areVideoEffectsAllNoOp(firstEditedMediaItem.effects.videoEffects, inputFormat)) {
+ return true;
+ }
+ return false;
+ }
- // TODO(b/265927935): consider generalizing this logic.
- for (int i = 0; i < firstEditedMediaItem.effects.videoEffects.size(); i++) {
- Effect videoEffect = firstEditedMediaItem.effects.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;
- Size outputSize = presentation.configure(decodedWidth, decodedHeight);
- if (outputSize.getWidth() != decodedWidth || outputSize.getHeight() != decodedHeight) {
- return true;
- }
- } else if (videoEffect instanceof ScaleToFitTransformation) {
- ScaleToFitTransformation scaleToFitTransformation =
- (ScaleToFitTransformation) videoEffect;
- if (scaleToFitTransformation.scaleX != 1f
- || scaleToFitTransformation.scaleY != 1f
- || scaleToFitTransformation.rotationDegrees != 0f) {
- return true;
- }
- } else {
- return true;
+ /**
+ * Returns whether all {@code videoEffects} are {@linkplain GlEffect#isNoOp(int, int) no-ops},
+ * given an input {@link Format}.
+ */
+ private boolean areVideoEffectsAllNoOp(ImmutableList