Migrate SubtitleParser
implementations to incremental parse()
All production and test callers of the non-incremental methods are already migrated, so we can remove them in this change too. #minor-release PiperOrigin-RevId: 573207318
This commit is contained in:
parent
a19f577976
commit
ecd24646cb
@ -16,21 +16,18 @@
|
|||||||
|
|
||||||
package androidx.media3.extractor.text;
|
package androidx.media3.extractor.text;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.util.Consumer;
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses subtitle data into timed {@linkplain CuesWithTiming} instances.
|
* Parses subtitle data into timed {@linkplain CuesWithTiming} instances.
|
||||||
*
|
*
|
||||||
* <p>Instances are stateful, so samples can be fed in repeated calls to {@link #parse(byte[])}, and
|
* <p>Instances are stateful, so samples can be fed in repeated calls to {@link #parse}, and one or
|
||||||
* one or more complete {@link CuesWithTiming} instances will be returned when enough data has been
|
* more complete {@link CuesWithTiming} instances will be returned when enough data has been
|
||||||
* received. Due to this stateful-ness, {@link #reset()} must be called after a seek or similar
|
* received. Due to this stateful-ness, {@link #reset()} must be called after a seek or similar
|
||||||
* discontinuity in the source data.
|
* discontinuity in the source data.
|
||||||
*/
|
*/
|
||||||
@ -124,17 +121,6 @@ public interface SubtitleParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses {@code data} (and any data stored from previous invocations) and returns any resulting
|
|
||||||
* complete {@link CuesWithTiming} instances.
|
|
||||||
*
|
|
||||||
* <p>Equivalent to {@link #parse(byte[], int, int) parse(data, 0, data.length)}.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
default List<CuesWithTiming> parse(byte[] data) {
|
|
||||||
return parse(data, /* offset= */ 0, data.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses {@code data} (and any data stored from previous invocations) and emits resulting {@link
|
* Parses {@code data} (and any data stored from previous invocations) and emits resulting {@link
|
||||||
* CuesWithTiming} instances.
|
* CuesWithTiming} instances.
|
||||||
@ -146,33 +132,6 @@ public interface SubtitleParser {
|
|||||||
parse(data, /* offset= */ 0, data.length, outputOptions, output);
|
parse(data, /* offset= */ 0, data.length, outputOptions, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses {@code data} (and any data stored from previous invocations) and returns any resulting
|
|
||||||
* complete {@link CuesWithTiming} instances.
|
|
||||||
*
|
|
||||||
* <p>Any samples not used from {@code data} will be persisted and used during subsequent calls to
|
|
||||||
* this method.
|
|
||||||
*
|
|
||||||
* <p>{@link CuesWithTiming#startTimeUs} in the returned instance is derived only from the
|
|
||||||
* provided sample data, so has to be considered together with any relevant {@link
|
|
||||||
* Format#subsampleOffsetUs}. If the provided sample doesn't contain any timing information then
|
|
||||||
* at most one {@link CuesWithTiming} instance will be returned, with {@link
|
|
||||||
* CuesWithTiming#startTimeUs} set to {@link C#TIME_UNSET}, in which case {@link
|
|
||||||
* Format#subsampleOffsetUs} <b>must</b> be {@link Format#OFFSET_SAMPLE_RELATIVE}.
|
|
||||||
*
|
|
||||||
* @param data The subtitle data to parse. This must contain only complete samples. For subtitles
|
|
||||||
* muxed inside a media container, a sample is usually defined by the container. For subtitles
|
|
||||||
* read from a text file, a sample is usually the entire contents of the text file.
|
|
||||||
* @param offset The index in {@code data} to start reading from (inclusive).
|
|
||||||
* @param length The number of bytes to read from {@code data}.
|
|
||||||
* @return The {@linkplain CuesWithTiming} instances parsed from {@code data} (and possibly
|
|
||||||
* previous provided samples too), sorted in ascending order by {@link
|
|
||||||
* CuesWithTiming#startTimeUs}. Otherwise null if there is insufficient data to generate a
|
|
||||||
* complete {@link CuesWithTiming}.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
List<CuesWithTiming> parse(byte[] data, int offset, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses {@code data} (and any data stored from previous invocations) and emits any resulting
|
* Parses {@code data} (and any data stored from previous invocations) and emits any resulting
|
||||||
* complete {@link CuesWithTiming} instances via {@code output}.
|
* complete {@link CuesWithTiming} instances via {@code output}.
|
||||||
@ -197,35 +156,12 @@ public interface SubtitleParser {
|
|||||||
* will be made on the thread that called this method, and will be completed before this
|
* will be made on the thread that called this method, and will be completed before this
|
||||||
* method returns.
|
* method returns.
|
||||||
*/
|
*/
|
||||||
default void parse(
|
void parse(
|
||||||
byte[] data,
|
byte[] data,
|
||||||
int offset,
|
int offset,
|
||||||
int length,
|
int length,
|
||||||
OutputOptions outputOptions,
|
OutputOptions outputOptions,
|
||||||
Consumer<CuesWithTiming> output) {
|
Consumer<CuesWithTiming> output);
|
||||||
List<CuesWithTiming> cuesWithTimingList = parse(data, offset, length);
|
|
||||||
if (cuesWithTimingList == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@Nullable
|
|
||||||
List<CuesWithTiming> cuesWithTimingBeforeRequestedStartTimeUs =
|
|
||||||
outputOptions.startTimeUs != C.TIME_UNSET && outputOptions.outputAllCues
|
|
||||||
? new ArrayList<>()
|
|
||||||
: null;
|
|
||||||
for (CuesWithTiming cuesWithTiming : cuesWithTimingList) {
|
|
||||||
if (outputOptions.startTimeUs == C.TIME_UNSET
|
|
||||||
|| cuesWithTiming.startTimeUs >= outputOptions.startTimeUs) {
|
|
||||||
output.accept(cuesWithTiming);
|
|
||||||
} else if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
|
||||||
cuesWithTimingBeforeRequestedStartTimeUs.add(cuesWithTiming);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
|
||||||
for (CuesWithTiming cuesWithTiming : cuesWithTimingBeforeRequestedStartTimeUs) {
|
|
||||||
output.accept(cuesWithTiming);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses {@code data} to a legacy {@link Subtitle} instance.
|
* Parses {@code data} to a legacy {@link Subtitle} instance.
|
||||||
@ -250,7 +186,7 @@ public interface SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears any data stored inside this parser from previous {@link #parse(byte[])} calls.
|
* Clears any data stored inside this parser from previous {@link #parse} calls.
|
||||||
*
|
*
|
||||||
* <p>This must be called after a seek or other similar discontinuity in the source data.
|
* <p>This must be called after a seek or other similar discontinuity in the source data.
|
||||||
*
|
*
|
||||||
|
@ -29,6 +29,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableBitArray;
|
import androidx.media3.common.util.ParsableBitArray;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
@ -143,9 +144,19 @@ public final class DvbParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
ParsableBitArray dataBitArray = new ParsableBitArray(data, /* limit= */ offset + length);
|
ParsableBitArray dataBitArray = new ParsableBitArray(data, /* limit= */ offset + length);
|
||||||
dataBitArray.setPosition(offset);
|
dataBitArray.setPosition(offset);
|
||||||
|
output.accept(parse(dataBitArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CuesWithTiming parse(ParsableBitArray dataBitArray) {
|
||||||
|
|
||||||
while (dataBitArray.bitsLeft() >= 48 // sync_byte (8) + segment header (40)
|
while (dataBitArray.bitsLeft() >= 48 // sync_byte (8) + segment header (40)
|
||||||
&& dataBitArray.readBits(8) == 0x0F) {
|
&& dataBitArray.readBits(8) == 0x0F) {
|
||||||
parseSubtitlingSegment(dataBitArray, subtitleService);
|
parseSubtitlingSegment(dataBitArray, subtitleService);
|
||||||
@ -153,9 +164,8 @@ public final class DvbParser implements SubtitleParser {
|
|||||||
|
|
||||||
@Nullable PageComposition pageComposition = subtitleService.pageComposition;
|
@Nullable PageComposition pageComposition = subtitleService.pageComposition;
|
||||||
if (pageComposition == null) {
|
if (pageComposition == null) {
|
||||||
return ImmutableList.of(
|
return new CuesWithTiming(
|
||||||
new CuesWithTiming(
|
ImmutableList.of(), /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET);
|
||||||
ImmutableList.of(), /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the canvas bitmap if necessary.
|
// Update the canvas bitmap if necessary.
|
||||||
@ -266,8 +276,8 @@ public final class DvbParser implements SubtitleParser {
|
|||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImmutableList.of(
|
return new CuesWithTiming(
|
||||||
new CuesWithTiming(cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static parsing.
|
// Static parsing.
|
||||||
|
@ -23,12 +23,12 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.extractor.text.CuesWithTiming;
|
import androidx.media3.extractor.text.CuesWithTiming;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
@ -68,7 +68,12 @@ public final class PgsParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
buffer.reset(data, /* limit= */ offset + length);
|
buffer.reset(data, /* limit= */ offset + length);
|
||||||
buffer.setPosition(offset);
|
buffer.setPosition(offset);
|
||||||
maybeInflateData(buffer);
|
maybeInflateData(buffer);
|
||||||
@ -80,7 +85,7 @@ public final class PgsParser implements SubtitleParser {
|
|||||||
cues.add(cue);
|
cues.add(cue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ImmutableList.of(
|
output.accept(
|
||||||
new CuesWithTiming(cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
new CuesWithTiming(cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -40,7 +41,6 @@ import androidx.media3.extractor.text.CuesWithTiming;
|
|||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.base.Ascii;
|
import com.google.common.base.Ascii;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -131,9 +131,13 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
return CUE_REPLACEMENT_BEHAVIOR;
|
return CUE_REPLACEMENT_BEHAVIOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
List<List<Cue>> cues = new ArrayList<>();
|
List<List<Cue>> cues = new ArrayList<>();
|
||||||
List<Long> startTimesUs = new ArrayList<>();
|
List<Long> startTimesUs = new ArrayList<>();
|
||||||
|
|
||||||
@ -146,7 +150,11 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
parseEventBody(parsableByteArray, cues, startTimesUs, charset);
|
parseEventBody(parsableByteArray, cues, startTimesUs, charset);
|
||||||
|
|
||||||
ImmutableList.Builder<CuesWithTiming> cuesWithStartTimeAndDuration = ImmutableList.builder();
|
@Nullable
|
||||||
|
List<CuesWithTiming> cuesWithTimingBeforeRequestedStartTimeUs =
|
||||||
|
outputOptions.startTimeUs != C.TIME_UNSET && outputOptions.outputAllCues
|
||||||
|
? new ArrayList<>()
|
||||||
|
: null;
|
||||||
for (int i = 0; i < cues.size(); i++) {
|
for (int i = 0; i < cues.size(); i++) {
|
||||||
List<Cue> cuesForThisStartTime = cues.get(i);
|
List<Cue> cuesForThisStartTime = cues.get(i);
|
||||||
if (cuesForThisStartTime.isEmpty() && i != 0) {
|
if (cuesForThisStartTime.isEmpty() && i != 0) {
|
||||||
@ -160,10 +168,19 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
long startTimeUs = startTimesUs.get(i);
|
long startTimeUs = startTimesUs.get(i);
|
||||||
// It's safe to inspect element i+1, because we already exited the loop above if i=size()-1.
|
// It's safe to inspect element i+1, because we already exited the loop above if i=size()-1.
|
||||||
long durationUs = startTimesUs.get(i + 1) - startTimesUs.get(i);
|
long durationUs = startTimesUs.get(i + 1) - startTimesUs.get(i);
|
||||||
cuesWithStartTimeAndDuration.add(
|
if (outputOptions.startTimeUs == C.TIME_UNSET || startTimeUs >= outputOptions.startTimeUs) {
|
||||||
new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
|
output.accept(new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
|
||||||
|
|
||||||
|
} else if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
||||||
|
cuesWithTimingBeforeRequestedStartTimeUs.add(
|
||||||
|
new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
||||||
|
for (CuesWithTiming cuesWithTiming : cuesWithTimingBeforeRequestedStartTimeUs) {
|
||||||
|
output.accept(cuesWithTiming);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cuesWithStartTimeAndDuration.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,10 +22,12 @@ import android.text.Spanned;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -35,6 +37,7 @@ import com.google.common.base.Charsets;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -91,15 +94,22 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
return CUE_REPLACEMENT_BEHAVIOR;
|
return CUE_REPLACEMENT_BEHAVIOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
ImmutableList.Builder<CuesWithTiming> cues = new ImmutableList.Builder<>();
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
parsableByteArray.reset(data, /* limit= */ offset + length);
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
parsableByteArray.setPosition(offset);
|
parsableByteArray.setPosition(offset);
|
||||||
Charset charset = detectUtfCharset(parsableByteArray);
|
Charset charset = detectUtfCharset(parsableByteArray);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
List<CuesWithTiming> cuesWithTimingBeforeRequestedStartTimeUs =
|
||||||
|
outputOptions.startTimeUs != C.TIME_UNSET && outputOptions.outputAllCues
|
||||||
|
? new ArrayList<>()
|
||||||
|
: null;
|
||||||
@Nullable String currentLine;
|
@Nullable String currentLine;
|
||||||
while ((currentLine = parsableByteArray.readLine(charset)) != null) {
|
while ((currentLine = parsableByteArray.readLine(charset)) != null) {
|
||||||
if (currentLine.length() == 0) {
|
if (currentLine.length() == 0) {
|
||||||
@ -156,13 +166,25 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cues.add(
|
if (outputOptions.startTimeUs == C.TIME_UNSET || startTimeUs >= outputOptions.startTimeUs) {
|
||||||
new CuesWithTiming(
|
output.accept(
|
||||||
ImmutableList.of(buildCue(text, alignmentTag)),
|
new CuesWithTiming(
|
||||||
startTimeUs,
|
ImmutableList.of(buildCue(text, alignmentTag)),
|
||||||
/* durationUs= */ endTimeUs - startTimeUs));
|
startTimeUs,
|
||||||
|
/* durationUs= */ endTimeUs - startTimeUs));
|
||||||
|
} else if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
||||||
|
cuesWithTimingBeforeRequestedStartTimeUs.add(
|
||||||
|
new CuesWithTiming(
|
||||||
|
ImmutableList.of(buildCue(text, alignmentTag)),
|
||||||
|
startTimeUs,
|
||||||
|
/* durationUs= */ endTimeUs - startTimeUs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cuesWithTimingBeforeRequestedStartTimeUs != null) {
|
||||||
|
for (CuesWithTiming cuesWithTiming : cuesWithTimingBeforeRequestedStartTimeUs) {
|
||||||
|
output.accept(cuesWithTiming);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cues.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +41,6 @@ import androidx.media3.extractor.text.Subtitle;
|
|||||||
import androidx.media3.extractor.text.SubtitleDecoderException;
|
import androidx.media3.extractor.text.SubtitleDecoderException;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.base.Ascii;
|
import com.google.common.base.Ascii;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -133,13 +132,6 @@ public final class TtmlParser implements SubtitleParser {
|
|||||||
return CUE_REPLACEMENT_BEHAVIOR;
|
return CUE_REPLACEMENT_BEHAVIOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
|
||||||
ImmutableList.Builder<CuesWithTiming> cues = ImmutableList.builder();
|
|
||||||
parse(data, offset, length, OutputOptions.allCues(), cues::add);
|
|
||||||
return cues.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(
|
public void parse(
|
||||||
byte[] data,
|
byte[] data,
|
||||||
|
@ -32,6 +32,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -137,16 +138,22 @@ public final class Tx3gParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
parsableByteArray.reset(data, /* limit= */ offset + length);
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
parsableByteArray.setPosition(offset);
|
parsableByteArray.setPosition(offset);
|
||||||
String cueTextString = readSubtitleText(parsableByteArray);
|
String cueTextString = readSubtitleText(parsableByteArray);
|
||||||
if (cueTextString.isEmpty()) {
|
if (cueTextString.isEmpty()) {
|
||||||
return ImmutableList.of(
|
output.accept(
|
||||||
new CuesWithTiming(
|
new CuesWithTiming(
|
||||||
/* cues= */ ImmutableList.of(),
|
/* cues= */ ImmutableList.of(),
|
||||||
/* startTimeUs= */ C.TIME_UNSET,
|
/* startTimeUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ C.TIME_UNSET));
|
/* durationUs= */ C.TIME_UNSET));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Attach default styles.
|
// Attach default styles.
|
||||||
SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
|
SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
|
||||||
@ -180,7 +187,7 @@ public final class Tx3gParser implements SubtitleParser {
|
|||||||
.setLine(verticalPlacement, LINE_TYPE_FRACTION)
|
.setLine(verticalPlacement, LINE_TYPE_FRACTION)
|
||||||
.setLineAnchor(ANCHOR_TYPE_START)
|
.setLineAnchor(ANCHOR_TYPE_START)
|
||||||
.build();
|
.build();
|
||||||
return ImmutableList.of(
|
output.accept(
|
||||||
new CuesWithTiming(
|
new CuesWithTiming(
|
||||||
ImmutableList.of(cue),
|
ImmutableList.of(cue),
|
||||||
/* startTimeUs= */ C.TIME_UNSET,
|
/* startTimeUs= */ C.TIME_UNSET,
|
||||||
|
@ -22,12 +22,12 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Format.CueReplacementBehavior;
|
import androidx.media3.common.Format.CueReplacementBehavior;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
|
import androidx.media3.common.util.Consumer;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.extractor.text.CuesWithTiming;
|
import androidx.media3.extractor.text.CuesWithTiming;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -67,7 +67,12 @@ public final class Mp4WebvttParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public void parse(
|
||||||
|
byte[] data,
|
||||||
|
int offset,
|
||||||
|
int length,
|
||||||
|
OutputOptions outputOptions,
|
||||||
|
Consumer<CuesWithTiming> output) {
|
||||||
parsableByteArray.reset(data, /* limit= */ offset + length);
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
parsableByteArray.setPosition(offset);
|
parsableByteArray.setPosition(offset);
|
||||||
List<Cue> cues = new ArrayList<>();
|
List<Cue> cues = new ArrayList<>();
|
||||||
@ -86,7 +91,7 @@ public final class Mp4WebvttParser implements SubtitleParser {
|
|||||||
parsableByteArray.skipBytes(boxSize - BOX_HEADER_SIZE);
|
parsableByteArray.skipBytes(boxSize - BOX_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ImmutableList.of(
|
output.accept(
|
||||||
new CuesWithTiming(cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
new CuesWithTiming(cues, /* startTimeUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
import androidx.media3.extractor.text.CuesWithTiming;
|
import androidx.media3.extractor.text.CuesWithTiming;
|
||||||
import androidx.media3.extractor.text.LegacySubtitleUtil;
|
import androidx.media3.extractor.text.LegacySubtitleUtil;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -67,13 +66,6 @@ public final class WebvttParser implements SubtitleParser {
|
|||||||
return CUE_REPLACEMENT_BEHAVIOR;
|
return CUE_REPLACEMENT_BEHAVIOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
|
||||||
ImmutableList.Builder<CuesWithTiming> result = ImmutableList.builder();
|
|
||||||
parse(data, offset, length, OutputOptions.allCues(), result::add);
|
|
||||||
return result.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(
|
public void parse(
|
||||||
byte[] data,
|
byte[] data,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user