Only init SpeedChangingAudioProcessor if Format.Metadata not null.

If the Metadata passed to SegmentSpeedProvider is null, then the
SegmentSpeedProvider will always return 1f from getSpeed.

Initializing a SpeedChangingAudioProcessor requires a SpeedProvider.
Once configured,this audioProcessor is always active, so buffers are
passed through it. Because getSpeed is always 1, the processor performs
a no-op, but still has to do a buffer copy for each buffer.

By not initializing the audio processor when metadata is null, this
copy can be skipped and the audio pipeline is more performant.

Note: This change does not affect the multiple media-item case, which
is not supported with speed changes, as per Transformer API
documentation.
PiperOrigin-RevId: 513261811
This commit is contained in:
samrobinson 2023-03-01 17:42:35 +00:00 committed by tonihei
parent 9f51182bb8
commit 65afd40622
3 changed files with 18 additions and 33 deletions

View File

@ -84,10 +84,12 @@ import org.checkerframework.dataflow.qual.Pure;
encoderInputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED); encoderInputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
encoderOutputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED); encoderOutputBuffer = new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
if (flattenForSlowMotion) { if (flattenForSlowMotion && firstInputFormat.metadata != null) {
audioProcessors = audioProcessors =
new ImmutableList.Builder<AudioProcessor>() new ImmutableList.Builder<AudioProcessor>()
.add(new SpeedChangingAudioProcessor(new SegmentSpeedProvider(firstInputFormat))) .add(
new SpeedChangingAudioProcessor(
new SegmentSpeedProvider(firstInputFormat.metadata)))
.addAll(audioProcessors) .addAll(audioProcessors)
.build(); .build();
} }

View File

@ -20,7 +20,6 @@ import static androidx.media3.extractor.metadata.mp4.SlowMotionData.Segment.BY_S
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Metadata; import androidx.media3.common.Metadata;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.extractor.metadata.mp4.SlowMotionData; import androidx.media3.extractor.metadata.mp4.SlowMotionData;
@ -45,11 +44,11 @@ import java.util.TreeMap;
private final ImmutableSortedMap<Long, Float> speedsByStartTimeUs; private final ImmutableSortedMap<Long, Float> speedsByStartTimeUs;
private final float baseSpeedMultiplier; private final float baseSpeedMultiplier;
public SegmentSpeedProvider(Format format) { public SegmentSpeedProvider(Metadata metadata) {
float captureFrameRate = getCaptureFrameRate(format); float captureFrameRate = getCaptureFrameRate(metadata);
this.baseSpeedMultiplier = this.baseSpeedMultiplier =
captureFrameRate == C.RATE_UNSET ? 1 : captureFrameRate / INPUT_FRAME_RATE; captureFrameRate == C.RATE_UNSET ? 1 : captureFrameRate / INPUT_FRAME_RATE;
this.speedsByStartTimeUs = buildSpeedByStartTimeUsMap(format, baseSpeedMultiplier); this.speedsByStartTimeUs = buildSpeedByStartTimeUsMap(metadata, baseSpeedMultiplier);
} }
@Override @Override
@ -67,8 +66,8 @@ import java.util.TreeMap;
} }
private static ImmutableSortedMap<Long, Float> buildSpeedByStartTimeUsMap( private static ImmutableSortedMap<Long, Float> buildSpeedByStartTimeUsMap(
Format format, float baseSpeed) { Metadata metadata, float baseSpeed) {
List<Segment> segments = extractSlowMotionSegments(format); ImmutableList<Segment> segments = extractSlowMotionSegments(metadata);
if (segments.isEmpty()) { if (segments.isEmpty()) {
return ImmutableSortedMap.of(); return ImmutableSortedMap.of();
@ -96,11 +95,7 @@ import java.util.TreeMap;
return ImmutableSortedMap.copyOf(speedsByStartTimeUs); return ImmutableSortedMap.copyOf(speedsByStartTimeUs);
} }
private static float getCaptureFrameRate(Format format) { private static float getCaptureFrameRate(Metadata metadata) {
@Nullable Metadata metadata = format.metadata;
if (metadata == null) {
return C.RATE_UNSET;
}
for (int i = 0; i < metadata.length(); i++) { for (int i = 0; i < metadata.length(); i++) {
Metadata.Entry entry = metadata.get(i); Metadata.Entry entry = metadata.get(i);
if (entry instanceof SmtaMetadataEntry) { if (entry instanceof SmtaMetadataEntry) {
@ -111,17 +106,14 @@ import java.util.TreeMap;
return C.RATE_UNSET; return C.RATE_UNSET;
} }
private static ImmutableList<Segment> extractSlowMotionSegments(Format format) { private static ImmutableList<Segment> extractSlowMotionSegments(Metadata metadata) {
List<Segment> segments = new ArrayList<>(); List<Segment> segments = new ArrayList<>();
@Nullable Metadata metadata = format.metadata;
if (metadata != null) {
for (int i = 0; i < metadata.length(); i++) { for (int i = 0; i < metadata.length(); i++) {
Metadata.Entry entry = metadata.get(i); Metadata.Entry entry = metadata.get(i);
if (entry instanceof SlowMotionData) { if (entry instanceof SlowMotionData) {
segments.addAll(((SlowMotionData) entry).segments); segments.addAll(((SlowMotionData) entry).segments);
} }
} }
}
return ImmutableList.sortedCopyOf(BY_START_THEN_END_THEN_DIVISOR, segments); return ImmutableList.sortedCopyOf(BY_START_THEN_END_THEN_DIVISOR, segments);
} }
} }

View File

@ -18,7 +18,6 @@ package androidx.media3.transformer;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import androidx.media3.common.Format;
import androidx.media3.common.Metadata; import androidx.media3.common.Metadata;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.extractor.metadata.mp4.SlowMotionData; import androidx.media3.extractor.metadata.mp4.SlowMotionData;
@ -39,9 +38,7 @@ public class SegmentSpeedProviderTest {
@Test @Test
public void getSpeed_noSegments_returnsBaseSpeed() { public void getSpeed_noSegments_returnsBaseSpeed() {
SegmentSpeedProvider provider = SegmentSpeedProvider provider = new SegmentSpeedProvider(new Metadata(SMTA_SPEED_8));
new SegmentSpeedProvider(
new Format.Builder().setMetadata(new Metadata(SMTA_SPEED_8)).build());
assertThat(provider.getSpeed(0)).isEqualTo(8); assertThat(provider.getSpeed(0)).isEqualTo(8);
assertThat(provider.getSpeed(1_000_000)).isEqualTo(8); assertThat(provider.getSpeed(1_000_000)).isEqualTo(8);
} }
@ -55,10 +52,7 @@ public class SegmentSpeedProviderTest {
new Segment(/* startTimeMs= */ 2000, /* endTimeMs= */ 2500, /* speedDivisor= */ 2)); new Segment(/* startTimeMs= */ 2000, /* endTimeMs= */ 2500, /* speedDivisor= */ 2));
SegmentSpeedProvider provider = SegmentSpeedProvider provider =
new SegmentSpeedProvider( new SegmentSpeedProvider(new Metadata(new SlowMotionData(segments), SMTA_SPEED_8));
new Format.Builder()
.setMetadata(new Metadata(new SlowMotionData(segments), SMTA_SPEED_8))
.build());
assertThat(provider.getSpeed(Util.msToUs(0))).isEqualTo(8); assertThat(provider.getSpeed(Util.msToUs(0))).isEqualTo(8);
assertThat(provider.getSpeed(Util.msToUs(500))).isEqualTo(1); assertThat(provider.getSpeed(Util.msToUs(500))).isEqualTo(1);
@ -77,9 +71,6 @@ public class SegmentSpeedProviderTest {
public void getSpeed_withNegativeTimestamp_throwsException() { public void getSpeed_withNegativeTimestamp_throwsException() {
assertThrows( assertThrows(
IllegalArgumentException.class, IllegalArgumentException.class,
() -> () -> new SegmentSpeedProvider(new Metadata(SMTA_SPEED_8)).getSpeed(-1));
new SegmentSpeedProvider(
new Format.Builder().setMetadata(new Metadata(SMTA_SPEED_8)).build())
.getSpeed(-1));
} }
} }