Implement isNoOp for LanczosResample

This allows for transmuxing whenever resolutions match,
and LanczosResample is in the video effects chain.

PiperOrigin-RevId: 645433104
This commit is contained in:
dancho 2024-06-21 10:34:33 -07:00 committed by Copybara-Service
parent d16004781e
commit 476ec607f2
2 changed files with 59 additions and 10 deletions

View File

@ -23,6 +23,7 @@ import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
import static androidx.media3.test.utils.TestUtil.PSNR_THRESHOLD;
import static androidx.media3.test.utils.TestUtil.assertBitmapsAreSimilar;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.graphics.Bitmap;
@ -129,6 +130,27 @@ public class LanczosResampleTest {
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, PSNR_THRESHOLD);
}
@Test
public void isNoOp_whenSizeDoesntChange_returnsTrue() {
LanczosResample lanczosResample = LanczosResample.scaleToFit(720, 1280);
assertThat(lanczosResample.isNoOp(720, 1280)).isTrue();
}
@Test
public void isNoOp_forSmallScalingFactors_returnsTrue() {
LanczosResample lanczosResample = LanczosResample.scaleToFit(1920, 1072);
assertThat(lanczosResample.isNoOp(1920, 1080)).isTrue();
}
@Test
public void isNoOp_forLargeScalingFactors_returnsTrue() {
LanczosResample lanczosResample = LanczosResample.scaleToFit(1920, 1068);
assertThat(lanczosResample.isNoOp(1920, 1080)).isFalse();
}
private static GlTextureInfo setupInputTexture(String path) throws Exception {
Bitmap inputBitmap = readBitmap(path);
return new GlTextureInfo(

View File

@ -16,6 +16,7 @@
package androidx.media3.effect;
import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.Math.abs;
import static java.lang.Math.min;
import static java.lang.Math.round;
@ -37,6 +38,9 @@ public final class LanczosResample implements GlEffect {
// Default value for the radius, or alpha parameter used by Lanczos filter. A value of 3 is
// used by ffmpeg (https://ffmpeg.org/ffmpeg-scaler.html), libplacebo, or Apple's vImage library.
private static final float DEFAULT_RADIUS = 3f;
// For scaling factors near 1, this algorithm won't bring a big improvement over bilinear or
// nearest sampling using the GPU texture units
private static final float NO_OP_THRESHOLD = 0.01f;
private final float radius;
private final int width;
@ -68,6 +72,37 @@ public final class LanczosResample implements GlEffect {
context, useHdr, new LanczosResampleScaledFunctionProvider(radius, width, height));
}
/**
* {@inheritDoc}
*
* <p>For scaling factors near 1, this effect applies no change because the Lanczos algorithm
* won't bring a big improvement over bilinear or nearest sampling using the GPU texture units.
*/
@Override
public boolean isNoOp(int inputWidth, int inputHeight) {
return abs(scalingFactorToFit(inputWidth, inputHeight, width, height) - 1f) < NO_OP_THRESHOLD;
}
/**
* Returns the scaling factor required to fit an input image into a target rectangle.
*
* @param inputWidth The width of the input image.
* @param inputHeight The height of the input image.
* @param targetWidth The width of the target rectangle.
* @param targetHeight The height of the target rectangle.
*/
private static float scalingFactorToFit(
int inputWidth, int inputHeight, int targetWidth, int targetHeight) {
checkArgument(inputWidth > 0);
checkArgument(inputHeight > 0);
// Scale to fit, preserving aspect ratio.
if (inputHeight * targetWidth <= targetHeight * inputWidth) {
return (float) targetWidth / inputWidth;
} else {
return (float) targetHeight / inputHeight;
}
}
private static class LanczosResampleScaledFunctionProvider
implements ConvolutionFunction1D.Provider {
// Note: We deliberately don't use Float.MIN_VALUE because it's positive & very close to zero.
@ -101,16 +136,8 @@ public final class LanczosResample implements GlEffect {
@Override
public Size configure(Size inputSize) {
checkArgument(inputSize.getWidth() > 0);
checkArgument(inputSize.getHeight() > 0);
// Scale to fit, preserving aspect ratio.
if (inputSize.getHeight() * width <= height * inputSize.getWidth()) {
scale = (float) width / inputSize.getWidth();
return new Size(width, round(inputSize.getHeight() * scale));
} else {
scale = (float) height / inputSize.getHeight();
return new Size(round(inputSize.getWidth() * scale), height);
}
scale = scalingFactorToFit(inputSize.getWidth(), inputSize.getHeight(), width, height);
return new Size(round(inputSize.getWidth() * scale), round(inputSize.getHeight() * scale));
}
}
}