mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +00:00
Adding support for overlapping subtitles
This commit is contained in:
parent
579167743b
commit
0391e73a0b
2 changed files with 56 additions and 36 deletions
|
|
@ -24,7 +24,6 @@ import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
|||
import com.google.android.exoplayer2.text.Subtitle;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.LongArray;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -82,19 +81,15 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
|
||||
@Override
|
||||
protected Subtitle decode(byte[] bytes, int length, boolean reset) {
|
||||
ArrayList<Cue> cues = new ArrayList<>();
|
||||
LongArray cueTimesUs = new LongArray();
|
||||
ArrayList<List<Cue>> cues = new ArrayList<>();
|
||||
List<Long> cueTimesUs = new ArrayList<>();
|
||||
|
||||
ParsableByteArray data = new ParsableByteArray(bytes, length);
|
||||
if (!haveInitializationData) {
|
||||
parseHeader(data);
|
||||
}
|
||||
parseEventBody(data, cues, cueTimesUs);
|
||||
|
||||
Cue[] cuesArray = new Cue[cues.size()];
|
||||
cues.toArray(cuesArray);
|
||||
long[] cueTimesUsArray = cueTimesUs.toArray();
|
||||
return new SsaSubtitle(cuesArray, cueTimesUsArray);
|
||||
return new SsaSubtitle(cues, cueTimesUs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,7 +121,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
* @param cues A list to which parsed cues will be added.
|
||||
* @param cueTimesUs An array to which parsed cue timestamps will be added.
|
||||
*/
|
||||
private void parseEventBody(ParsableByteArray data, List<Cue> cues, LongArray cueTimesUs) {
|
||||
private void parseEventBody(ParsableByteArray data, List<List<Cue>> cues, List<Long> cueTimesUs) {
|
||||
String currentLine;
|
||||
while ((currentLine = data.readLine()) != null) {
|
||||
if (!haveInitializationData && currentLine.startsWith(FORMAT_LINE_PREFIX)) {
|
||||
|
|
@ -180,7 +175,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
* @param cues A list to which parsed cues will be added.
|
||||
* @param cueTimesUs An array to which parsed cue timestamps will be added.
|
||||
*/
|
||||
private void parseDialogueLine(String dialogueLine, List<Cue> cues, LongArray cueTimesUs) {
|
||||
private void parseDialogueLine(String dialogueLine, List<List<Cue>> cues, List<Long> cueTimesUs) {
|
||||
if (formatKeyCount == 0) {
|
||||
Log.w(TAG, "Skipping dialogue line before complete format: " + dialogueLine);
|
||||
return;
|
||||
|
|
@ -222,7 +217,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
cue = new Cue(
|
||||
text,
|
||||
/* textAlignment */ null,
|
||||
1 - position.second / playResY,
|
||||
position.second / playResY,
|
||||
Cue.LINE_TYPE_FRACTION,
|
||||
Cue.ANCHOR_TYPE_START,
|
||||
position.first / playResX,
|
||||
|
|
@ -232,12 +227,44 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
cue = new Cue(text);
|
||||
}
|
||||
|
||||
cues.add(cue);
|
||||
cueTimesUs.add(startTimeUs);
|
||||
if (endTimeUs != C.TIME_UNSET) {
|
||||
cues.add(Cue.EMPTY);
|
||||
cueTimesUs.add(endTimeUs);
|
||||
int startTimeIndex = insertToCueTimes(cueTimesUs, startTimeUs);
|
||||
|
||||
List<Cue> startCueList = new ArrayList<>();
|
||||
if (startTimeIndex != 0) {
|
||||
startCueList.addAll(cues.get(startTimeIndex - 1));
|
||||
}
|
||||
cues.add(startTimeIndex, startCueList);
|
||||
|
||||
if (endTimeUs != C.TIME_UNSET) {
|
||||
int endTimeIndex = insertToCueTimes(cueTimesUs, endTimeUs);
|
||||
List<Cue> endList = new ArrayList<>(cues.get(endTimeIndex - 1));
|
||||
cues.add(endTimeIndex, endList);
|
||||
|
||||
int i = startTimeIndex;
|
||||
do {
|
||||
cues.get(i).add(cue);
|
||||
i++;
|
||||
} while (i != endTimeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given cue time into the given array keeping the array sorted.
|
||||
*
|
||||
* @param cueTimes The array with sorted cue times
|
||||
* @param timeUs The cue time to be inserted
|
||||
* @return The index where the cue time was inserted
|
||||
*/
|
||||
private static int insertToCueTimes(List<Long> cueTimes, long timeUs) {
|
||||
for (int i = cueTimes.size() - 1; i >= 0; i--) {
|
||||
if (cueTimes.get(i) <= timeUs) {
|
||||
cueTimes.add(i + 1, timeUs);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
cueTimes.add(0, timeUs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,7 +273,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
* @param timeString The string to parse.
|
||||
* @return The parsed timestamp in microseconds.
|
||||
*/
|
||||
public static long parseTimecodeUs(String timeString) {
|
||||
private static long parseTimecodeUs(String timeString) {
|
||||
Matcher matcher = SSA_TIMECODE_PATTERN.matcher(timeString);
|
||||
if (!matcher.matches()) {
|
||||
return C.TIME_UNSET;
|
||||
|
|
@ -258,21 +285,15 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
|
|||
return timestampUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an SSA position attribute.
|
||||
*
|
||||
* @param line The string to parse.
|
||||
* @return The parsed position in a pair (x,y).
|
||||
*/
|
||||
@Nullable
|
||||
public static Pair<Float, Float> parsePosition(String line) {
|
||||
public static Pair<Float, Float> parsePosition(String line){
|
||||
Matcher matcher = SSA_POSITION_PATTERN.matcher(line);
|
||||
if (!matcher.find()) {
|
||||
if(!matcher.find()){
|
||||
return null;
|
||||
}
|
||||
float x = Float.parseFloat(matcher.group(1));
|
||||
float y = Float.parseFloat(matcher.group(3));
|
||||
return new Pair<>(x, y);
|
||||
return new Pair<>(x,y);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ import java.util.List;
|
|||
*/
|
||||
/* package */ final class SsaSubtitle implements Subtitle {
|
||||
|
||||
private final Cue[] cues;
|
||||
private final long[] cueTimesUs;
|
||||
private final List<List<Cue>> cues;
|
||||
private final List<Long> cueTimesUs;
|
||||
|
||||
/**
|
||||
* @param cues The cues in the subtitle.
|
||||
* @param cueTimesUs The cue times, in microseconds.
|
||||
*/
|
||||
public SsaSubtitle(Cue[] cues, long[] cueTimesUs) {
|
||||
public SsaSubtitle(List<List<Cue>> cues, List<Long> cueTimesUs) {
|
||||
this.cues = cues;
|
||||
this.cueTimesUs = cueTimesUs;
|
||||
}
|
||||
|
|
@ -43,30 +43,29 @@ import java.util.List;
|
|||
@Override
|
||||
public int getNextEventTimeIndex(long timeUs) {
|
||||
int index = Util.binarySearchCeil(cueTimesUs, timeUs, false, false);
|
||||
return index < cueTimesUs.length ? index : C.INDEX_UNSET;
|
||||
return index < cueTimesUs.size() ? index : C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEventTimeCount() {
|
||||
return cueTimesUs.length;
|
||||
return cueTimesUs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEventTime(int index) {
|
||||
Assertions.checkArgument(index >= 0);
|
||||
Assertions.checkArgument(index < cueTimesUs.length);
|
||||
return cueTimesUs[index];
|
||||
Assertions.checkArgument(index < cueTimesUs.size());
|
||||
return cueTimesUs.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cue> getCues(long timeUs) {
|
||||
int index = Util.binarySearchFloor(cueTimesUs, timeUs, true, false);
|
||||
if (index == -1 || cues[index] == Cue.EMPTY) {
|
||||
if (index == -1 || cues.get(index).isEmpty()) {
|
||||
// timeUs is earlier than the start of the first cue, or we have an empty cue.
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Collections.singletonList(cues[index]);
|
||||
return cues.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue