use of androidx.media3.extractor.text.SubtitleDecoderException in project media by androidx.
the class TtmlDecoder method parseFontSize.
private static void parseFontSize(String expression, TtmlStyle out) throws SubtitleDecoderException {
String[] expressions = Util.split(expression, "\\s+");
Matcher matcher;
if (expressions.length == 1) {
matcher = FONT_SIZE.matcher(expression);
} else if (expressions.length == 2) {
matcher = FONT_SIZE.matcher(expressions[1]);
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font" + " size and ignoring the first.");
} else {
throw new SubtitleDecoderException("Invalid number of entries for fontSize: " + expressions.length + ".");
}
if (matcher.matches()) {
String unit = Assertions.checkNotNull(matcher.group(3));
switch(unit) {
case "px":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
break;
case "em":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_EM);
break;
case "%":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break;
default:
throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
}
out.setFontSize(Float.parseFloat(Assertions.checkNotNull(matcher.group(1))));
} else {
throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
}
}
use of androidx.media3.extractor.text.SubtitleDecoderException in project media by androidx.
the class Tx3gDecoder method decode.
@Override
protected Subtitle decode(byte[] bytes, int length, boolean reset) throws SubtitleDecoderException {
parsableByteArray.reset(bytes, length);
String cueTextString = readSubtitleText(parsableByteArray);
if (cueTextString.isEmpty()) {
return Tx3gSubtitle.EMPTY;
}
// Attach default styles.
SpannableStringBuilder cueText = new SpannableStringBuilder(cueTextString);
attachFontFace(cueText, defaultFontFace, DEFAULT_FONT_FACE, 0, cueText.length(), SPAN_PRIORITY_LOW);
attachColor(cueText, defaultColorRgba, DEFAULT_COLOR, 0, cueText.length(), SPAN_PRIORITY_LOW);
attachFontFamily(cueText, defaultFontFamily, 0, cueText.length());
float verticalPlacement = defaultVerticalPlacement;
// Find and attach additional styles.
while (parsableByteArray.bytesLeft() >= SIZE_ATOM_HEADER) {
int position = parsableByteArray.getPosition();
int atomSize = parsableByteArray.readInt();
int atomType = parsableByteArray.readInt();
if (atomType == TYPE_STYL) {
assertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
int styleRecordCount = parsableByteArray.readUnsignedShort();
for (int i = 0; i < styleRecordCount; i++) {
applyStyleRecord(parsableByteArray, cueText);
}
} else if (atomType == TYPE_TBOX && customVerticalPlacement) {
assertTrue(parsableByteArray.bytesLeft() >= SIZE_SHORT);
int requestedVerticalPlacement = parsableByteArray.readUnsignedShort();
verticalPlacement = (float) requestedVerticalPlacement / calculatedVideoTrackHeight;
verticalPlacement = Util.constrainValue(verticalPlacement, 0.0f, 0.95f);
}
parsableByteArray.setPosition(position + atomSize);
}
return new Tx3gSubtitle(new Cue.Builder().setText(cueText).setLine(verticalPlacement, LINE_TYPE_FRACTION).setLineAnchor(ANCHOR_TYPE_START).build());
}
use of androidx.media3.extractor.text.SubtitleDecoderException in project media by androidx.
the class SubtitleExtractor method decode.
/**
* Decodes the subtitle data and stores the samples in the memory of the extractor.
*/
private void decode() throws IOException {
try {
@Nullable SubtitleInputBuffer inputBuffer = subtitleDecoder.dequeueInputBuffer();
while (inputBuffer == null) {
Thread.sleep(5);
inputBuffer = subtitleDecoder.dequeueInputBuffer();
}
inputBuffer.ensureSpaceForWrite(bytesRead);
inputBuffer.data.put(subtitleData.getData(), /* offset= */
0, bytesRead);
inputBuffer.data.limit(bytesRead);
subtitleDecoder.queueInputBuffer(inputBuffer);
@Nullable SubtitleOutputBuffer outputBuffer = subtitleDecoder.dequeueOutputBuffer();
while (outputBuffer == null) {
Thread.sleep(5);
outputBuffer = subtitleDecoder.dequeueOutputBuffer();
}
for (int i = 0; i < outputBuffer.getEventTimeCount(); i++) {
List<Cue> cues = outputBuffer.getCues(outputBuffer.getEventTime(i));
byte[] cuesSample = cueEncoder.encode(cues);
timestamps.add(outputBuffer.getEventTime(i));
samples.add(new ParsableByteArray(cuesSample));
}
outputBuffer.release();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedIOException();
} catch (SubtitleDecoderException e) {
throw ParserException.createForMalformedContainer("SubtitleDecoder failed.", e);
}
}
use of androidx.media3.extractor.text.SubtitleDecoderException in project media by androidx.
the class Cea608Decoder method dequeueOutputBuffer.
@Nullable
@Override
public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException {
SubtitleOutputBuffer outputBuffer = super.dequeueOutputBuffer();
if (outputBuffer != null) {
return outputBuffer;
}
if (shouldClearStuckCaptions()) {
outputBuffer = getAvailableOutputBuffer();
if (outputBuffer != null) {
cues = Collections.emptyList();
lastCueUpdateUs = C.TIME_UNSET;
Subtitle subtitle = createSubtitle();
outputBuffer.setContent(getPositionUs(), subtitle, Format.OFFSET_SAMPLE_RELATIVE);
return outputBuffer;
}
}
return null;
}
use of androidx.media3.extractor.text.SubtitleDecoderException in project media by androidx.
the class TtmlDecoder method parseTimeExpression.
/**
* Parses a time expression, returning the parsed timestamp.
*
* <p>For the format of a time expression, see: <a
* href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
*
* @param time A string that includes the time expression.
* @param frameAndTickRate The effective frame and tick rates of the stream.
* @return The parsed timestamp in microseconds.
* @throws SubtitleDecoderException If the given string does not contain a valid time expression.
*/
private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate) throws SubtitleDecoderException {
Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) {
String hours = Assertions.checkNotNull(matcher.group(1));
double durationSeconds = Long.parseLong(hours) * 3600;
String minutes = Assertions.checkNotNull(matcher.group(2));
durationSeconds += Long.parseLong(minutes) * 60;
String seconds = Assertions.checkNotNull(matcher.group(3));
durationSeconds += Long.parseLong(seconds);
@Nullable String fraction = matcher.group(4);
durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0;
@Nullable String frames = matcher.group(5);
durationSeconds += (frames != null) ? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0;
@Nullable String subframes = matcher.group(6);
durationSeconds += (subframes != null) ? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate / frameAndTickRate.effectiveFrameRate : 0;
return (long) (durationSeconds * C.MICROS_PER_SECOND);
}
matcher = OFFSET_TIME.matcher(time);
if (matcher.matches()) {
String timeValue = Assertions.checkNotNull(matcher.group(1));
double offsetSeconds = Double.parseDouble(timeValue);
String unit = Assertions.checkNotNull(matcher.group(2));
switch(unit) {
case "h":
offsetSeconds *= 3600;
break;
case "m":
offsetSeconds *= 60;
break;
case "s":
// Do nothing.
break;
case "ms":
offsetSeconds /= 1000;
break;
case "f":
offsetSeconds /= frameAndTickRate.effectiveFrameRate;
break;
case "t":
offsetSeconds /= frameAndTickRate.tickRate;
break;
}
return (long) (offsetSeconds * C.MICROS_PER_SECOND);
}
throw new SubtitleDecoderException("Malformed time expression: " + time);
}
Aggregations