mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +00:00
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:
parent
69cfba7c53
commit
d4db33a535
3 changed files with 60 additions and 9 deletions
|
|
@ -92,6 +92,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;
|
||||
|
||||
|
|
@ -103,6 +110,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);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ import androidx.media3.common.audio.AudioProcessor;
|
|||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.effect.Presentation;
|
||||
import androidx.media3.effect.ScaleToFitTransformation;
|
||||
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
|
@ -577,16 +580,34 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
if (inputFormat.pixelWidthHeightRatio != 1f) {
|
||||
return true;
|
||||
}
|
||||
// The decoder rotates encoded frames for display by inputFormat.rotationDegrees.
|
||||
int decodedHeight =
|
||||
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width;
|
||||
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
||||
&& transformationRequest.outputHeight != decodedHeight) {
|
||||
return true;
|
||||
}
|
||||
if (!videoEffects.isEmpty()) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,14 @@ import android.os.ParcelFileDescriptor;
|
|||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.Presentation;
|
||||
import androidx.media3.effect.ScaleToFitTransformation;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue