Plumb SubtitleParser.Factory into AviExtractor

AviExtractor supports text tracks (`AviExtractor.FOURCC_txts` -> `C.TRACK_TYPE_TEXT`) with subtitles.

AviExtractor will no longer be wrapped in SubtitleTranscodingExtractor, but instead use SubtitleTranscodingExtractorOutput under the hood.

FLAG_EMIT_RAW_SUBTITLE_DATA flag will be used to toggle between subtitle parsing during extraction (before the sample queue) or during decoding (after the sample queue).

PiperOrigin-RevId: 598594981
This commit is contained in:
jbibik 2024-01-15 06:19:06 -08:00 committed by Copybara-Service
parent 4a07d13838
commit a88d8a415a
4 changed files with 54 additions and 4 deletions

View File

@ -441,6 +441,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
&& !(extractor.getUnderlyingImplementation() instanceof FragmentedMp4Extractor)
&& !(extractor.getUnderlyingImplementation() instanceof Mp4Extractor)
&& !(extractor.getUnderlyingImplementation() instanceof TsExtractor)
&& !(extractor.getUnderlyingImplementation() instanceof AviExtractor)
? new SubtitleTranscodingExtractor(extractor, subtitleParserFactory)
: extractor;
}
@ -550,7 +551,10 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
break;
case FileTypes.AVI:
extractors.add(new AviExtractor());
extractors.add(
new AviExtractor(
(textTrackTranscodingEnabled ? 0 : AviExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA),
subtitleParserFactory));
break;
case FileTypes.PNG:
extractors.add(new PngExtractor());

View File

@ -34,6 +34,8 @@ import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.PositionHolder;
import androidx.media3.extractor.SeekMap;
import androidx.media3.extractor.TrackOutput;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleTranscodingExtractorOutput;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -116,6 +118,24 @@ public final class AviExtractor implements Extractor {
private static final int AVIIF_KEYFRAME = 16;
/**
* Flags controlling the behavior of the extractor. Possible flag value is {@link
* #FLAG_EMIT_RAW_SUBTITLE_DATA}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef(
flag = true,
value = {FLAG_EMIT_RAW_SUBTITLE_DATA})
public @interface Flags {}
/**
* Flag to use the source subtitle formats without modification. If unset, subtitles will be
* transcoded to {@link MimeTypes#APPLICATION_MEDIA3_CUES} during extraction.
*/
public static final int FLAG_EMIT_RAW_SUBTITLE_DATA = 1;
/**
* Maximum size to skip using {@link ExtractorInput#skip}. Boxes larger than this size are skipped
* using {@link #RESULT_SEEK}.
@ -124,6 +144,8 @@ public final class AviExtractor implements Extractor {
private final ParsableByteArray scratch;
private final ChunkHeaderHolder chunkHeaderHolder;
private final boolean parseSubtitlesDuringExtraction;
private final SubtitleParser.Factory subtitleParserFactory;
private @State int state;
private ExtractorOutput extractorOutput;
@ -139,7 +161,24 @@ public final class AviExtractor implements Extractor {
private int idx1BodySize;
private boolean seekMapHasBeenOutput;
/**
* @deprecated Use {@link #AviExtractor(int, SubtitleParser.Factory)} instead.
*/
@Deprecated
public AviExtractor() {
this(FLAG_EMIT_RAW_SUBTITLE_DATA, SubtitleParser.Factory.UNSUPPORTED);
}
/**
* Constructs an instance.
*
* @param extractorFlags Flags that control the extractor's behavior.
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
* extraction.
*/
public AviExtractor(@Flags int extractorFlags, SubtitleParser.Factory subtitleParserFactory) {
this.subtitleParserFactory = subtitleParserFactory;
parseSubtitlesDuringExtraction = (extractorFlags & FLAG_EMIT_RAW_SUBTITLE_DATA) == 0;
scratch = new ParsableByteArray(/* limit= */ 12);
chunkHeaderHolder = new ChunkHeaderHolder();
extractorOutput = new DummyExtractorOutput();
@ -155,7 +194,10 @@ public final class AviExtractor implements Extractor {
@Override
public void init(ExtractorOutput output) {
this.state = STATE_SKIPPING_TO_HDRL;
this.extractorOutput = output;
this.extractorOutput =
parseSubtitlesDuringExtraction
? new SubtitleTranscodingExtractorOutput(output, subtitleParserFactory)
: output;
pendingReposition = C.INDEX_UNSET;
}

View File

@ -175,7 +175,7 @@ public final class DefaultExtractorsFactoryTest {
tsExtractor = extractor;
}
}
assertThat(aviExtractor).isInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(aviExtractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(matroskaExtractor).isInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(mp4Extractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(fragmentedMp4Extractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.extractor.avi;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.test.utils.ExtractorAsserts;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
@ -36,6 +37,9 @@ public final class AviExtractorTest {
@Test
public void aviSample() throws Exception {
ExtractorAsserts.assertBehavior(AviExtractor::new, "media/avi/sample.avi", simulationConfig);
ExtractorAsserts.assertBehavior(
() -> new AviExtractor(/* extractorFlags= */ 0, SubtitleParser.Factory.UNSUPPORTED),
"media/avi/sample.avi",
simulationConfig);
}
}