mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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 final Matrix transformationMatrix;
|
||||||
private @MonotonicNonNull Matrix adjustedTransformationMatrix;
|
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.
|
* @param rotationDegrees How much to rotate the frame counterclockwise, in degrees.
|
||||||
*/
|
*/
|
||||||
private ScaleToFitTransformation(float scaleX, float scaleY, float rotationDegrees) {
|
private ScaleToFitTransformation(float scaleX, float scaleY, float rotationDegrees) {
|
||||||
|
this.scaleX = scaleX;
|
||||||
|
this.scaleY = scaleY;
|
||||||
|
this.rotationDegrees = rotationDegrees;
|
||||||
transformationMatrix = new Matrix();
|
transformationMatrix = new Matrix();
|
||||||
transformationMatrix.postScale(scaleX, scaleY);
|
transformationMatrix.postScale(scaleX, scaleY);
|
||||||
transformationMatrix.postRotate(rotationDegrees);
|
transformationMatrix.postRotate(rotationDegrees);
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.common.util.HandlerWrapper;
|
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 androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
@ -577,16 +580,34 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
if (inputFormat.pixelWidthHeightRatio != 1f) {
|
if (inputFormat.pixelWidthHeightRatio != 1f) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// The decoder rotates encoded frames for display by inputFormat.rotationDegrees.
|
|
||||||
int decodedHeight =
|
// TODO(b/265927935): consider generalizing this logic.
|
||||||
(inputFormat.rotationDegrees % 180 == 0) ? inputFormat.height : inputFormat.width;
|
for (int i = 0; i < videoEffects.size(); i++) {
|
||||||
if (transformationRequest.outputHeight != C.LENGTH_UNSET
|
Effect videoEffect = videoEffects.get(i);
|
||||||
&& transformationRequest.outputHeight != decodedHeight) {
|
if (videoEffect instanceof Presentation) {
|
||||||
return true;
|
Presentation presentation = (Presentation) videoEffect;
|
||||||
}
|
// The decoder rotates encoded frames for display by inputFormat.rotationDegrees.
|
||||||
if (!videoEffects.isEmpty()) {
|
int decodedWidth =
|
||||||
return true;
|
(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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,14 @@ import android.os.ParcelFileDescriptor;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||||
import androidx.media3.common.util.Util;
|
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.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||||
|
|
@ -728,6 +731,23 @@ public final class TransformerEndToEndTest {
|
||||||
assertThat(transformationException).hasCauseThat().isInstanceOf(IllegalStateException.class);
|
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
|
@Test
|
||||||
public void getProgress_knownDuration_returnsConsistentStates() throws Exception {
|
public void getProgress_knownDuration_returnsConsistentStates() throws Exception {
|
||||||
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue