mirror of
https://github.com/androidx/media.git
synced 2025-05-17 04:29:55 +08:00
Added SubtitleParser interface (extending Decoder)
SubtitleParser includes a method updating the current playback position for parsers that need to be aware of the position in order to determine how to batch captions (i.e. Eia608Parser). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=121519712
This commit is contained in:
parent
6e86cafce0
commit
d978398faf
@ -22,13 +22,19 @@ import com.google.android.exoplayer.util.extensions.SimpleDecoder;
|
|||||||
* Base class for subtitle parsers that use their own decode thread.
|
* Base class for subtitle parsers that use their own decode thread.
|
||||||
*/
|
*/
|
||||||
public abstract class SimpleSubtitleParser extends
|
public abstract class SimpleSubtitleParser extends
|
||||||
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
|
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> implements
|
||||||
|
SubtitleParser {
|
||||||
|
|
||||||
protected SimpleSubtitleParser() {
|
protected SimpleSubtitleParser() {
|
||||||
super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]);
|
super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]);
|
||||||
setInitialInputBufferSize(1024);
|
setInitialInputBufferSize(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPositionUs(long timeUs) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final SubtitleInputBuffer createInputBuffer() {
|
protected final SubtitleInputBuffer createInputBuffer() {
|
||||||
return new SubtitleInputBuffer();
|
return new SubtitleInputBuffer();
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer.text;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.ParserException;
|
||||||
|
import com.google.android.exoplayer.util.extensions.Decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses {@link Subtitle}s from {@link SubtitleInputBuffer}s.
|
||||||
|
*/
|
||||||
|
public interface SubtitleParser extends
|
||||||
|
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the parser of the current playback position.
|
||||||
|
* <p>
|
||||||
|
* Must be called prior to each attempt to dequeue output buffers from the parser.
|
||||||
|
*
|
||||||
|
* @param positionUs The current playback position in microseconds.
|
||||||
|
*/
|
||||||
|
public void setPositionUs(long positionUs);
|
||||||
|
|
||||||
|
}
|
@ -16,7 +16,6 @@
|
|||||||
package com.google.android.exoplayer.text;
|
package com.google.android.exoplayer.text;
|
||||||
|
|
||||||
import com.google.android.exoplayer.Format;
|
import com.google.android.exoplayer.Format;
|
||||||
import com.google.android.exoplayer.ParserException;
|
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.extensions.Decoder;
|
import com.google.android.exoplayer.util.extensions.Decoder;
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ public interface SubtitleParserFactory {
|
|||||||
* @return A new {@link Decoder}.
|
* @return A new {@link Decoder}.
|
||||||
* @throws IllegalArgumentException If the {@link Format} is not supported.
|
* @throws IllegalArgumentException If the {@link Format} is not supported.
|
||||||
*/
|
*/
|
||||||
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> createParser(Format format);
|
SubtitleParser createParser(Format format);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default {@link SubtitleParserFactory} implementation.
|
* Default {@link SubtitleParserFactory} implementation.
|
||||||
@ -63,16 +62,14 @@ public interface SubtitleParserFactory {
|
|||||||
return getParserClass(format.sampleMimeType) != null;
|
return getParserClass(format.sampleMimeType) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> createParser(
|
public SubtitleParser createParser(Format format) {
|
||||||
Format format) {
|
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = getParserClass(format.sampleMimeType);
|
Class<?> clazz = getParserClass(format.sampleMimeType);
|
||||||
if (clazz == null) {
|
if (clazz == null) {
|
||||||
throw new IllegalArgumentException("Attempted to create parser for unsupported format");
|
throw new IllegalArgumentException("Attempted to create parser for unsupported format");
|
||||||
}
|
}
|
||||||
return clazz.asSubclass(Decoder.class).newInstance();
|
return clazz.asSubclass(SubtitleParser.class).newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException("Unexpected error instantiating parser", e);
|
throw new IllegalStateException("Unexpected error instantiating parser", e);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
|
|||||||
|
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
private boolean outputStreamEnded;
|
private boolean outputStreamEnded;
|
||||||
private Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> parser;
|
private SubtitleParser parser;
|
||||||
private SubtitleInputBuffer nextInputBuffer;
|
private SubtitleInputBuffer nextInputBuffer;
|
||||||
private SubtitleOutputBuffer subtitle;
|
private SubtitleOutputBuffer subtitle;
|
||||||
private SubtitleOutputBuffer nextSubtitle;
|
private SubtitleOutputBuffer nextSubtitle;
|
||||||
@ -133,6 +133,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextSubtitle == null) {
|
if (nextSubtitle == null) {
|
||||||
|
parser.setPositionUs(positionUs);
|
||||||
try {
|
try {
|
||||||
nextSubtitle = parser.dequeueOutputBuffer();
|
nextSubtitle = parser.dequeueOutputBuffer();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -19,10 +19,12 @@ import com.google.android.exoplayer.C;
|
|||||||
import com.google.android.exoplayer.ParserException;
|
import com.google.android.exoplayer.ParserException;
|
||||||
import com.google.android.exoplayer.text.SubtitleInputBuffer;
|
import com.google.android.exoplayer.text.SubtitleInputBuffer;
|
||||||
import com.google.android.exoplayer.text.SubtitleOutputBuffer;
|
import com.google.android.exoplayer.text.SubtitleOutputBuffer;
|
||||||
|
import com.google.android.exoplayer.text.SubtitleParser;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.ParsableBitArray;
|
import com.google.android.exoplayer.util.ParsableBitArray;
|
||||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer.util.extensions.Decoder;
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@ -31,8 +33,7 @@ import java.util.TreeSet;
|
|||||||
* Facilitates the extraction and parsing of EIA-608 (a.k.a. "line 21 captions" and "CEA-608")
|
* Facilitates the extraction and parsing of EIA-608 (a.k.a. "line 21 captions" and "CEA-608")
|
||||||
* Closed Captions from the SEI data block from H.264.
|
* Closed Captions from the SEI data block from H.264.
|
||||||
*/
|
*/
|
||||||
public final class Eia608Parser implements
|
public final class Eia608Parser implements SubtitleParser {
|
||||||
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
|
|
||||||
|
|
||||||
private static final int NUM_INPUT_BUFFERS = 10;
|
private static final int NUM_INPUT_BUFFERS = 10;
|
||||||
private static final int NUM_OUTPUT_BUFFERS = 2;
|
private static final int NUM_OUTPUT_BUFFERS = 2;
|
||||||
@ -170,6 +171,8 @@ public final class Eia608Parser implements
|
|||||||
|
|
||||||
private final StringBuilder captionStringBuilder;
|
private final StringBuilder captionStringBuilder;
|
||||||
|
|
||||||
|
private long playbackPositionUs;
|
||||||
|
|
||||||
private SubtitleInputBuffer dequeuedInputBuffer;
|
private SubtitleInputBuffer dequeuedInputBuffer;
|
||||||
|
|
||||||
private int captionMode;
|
private int captionMode;
|
||||||
@ -201,6 +204,11 @@ public final class Eia608Parser implements
|
|||||||
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPositionUs(long positionUs) {
|
||||||
|
playbackPositionUs = positionUs;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleInputBuffer dequeueInputBuffer() throws ParserException {
|
public SubtitleInputBuffer dequeueInputBuffer() throws ParserException {
|
||||||
Assertions.checkState(dequeuedInputBuffer == null);
|
Assertions.checkState(dequeuedInputBuffer == null);
|
||||||
@ -221,34 +229,43 @@ public final class Eia608Parser implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException {
|
public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException {
|
||||||
if (queuedInputBuffers.isEmpty() || availableOutputBuffers.isEmpty()) {
|
if (availableOutputBuffers.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtitleOutputBuffer outputBuffer = null;
|
// iterate through all available input buffers whose timestamps are less than or equal
|
||||||
SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst();
|
// to the current playback position; processing input buffers for future content should
|
||||||
|
// be deferred until they would be applicable
|
||||||
|
while (!queuedInputBuffers.isEmpty()
|
||||||
|
&& queuedInputBuffers.first().timeUs <= playbackPositionUs) {
|
||||||
|
SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst();
|
||||||
|
|
||||||
// TODO: investigate ways of batching multiple SubtitleInputBuffers into a single
|
// If the input buffer indicates we've reached the end of the stream, we can
|
||||||
// SubtitleOutputBuffer; it isn't as simple as just iterating through all of the queued input
|
// return immediately with an output buffer propagating that
|
||||||
// buffers until there is a change because for pop-on captions this will result in the input
|
if (inputBuffer.isEndOfStream()) {
|
||||||
// buffers being processed almost sequentially as they are queued, eliminating the re-ordering,
|
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
|
||||||
// resulting in the content having characters out of order
|
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
if (inputBuffer.isEndOfStream()) {
|
releaseInputBuffer(inputBuffer);
|
||||||
outputBuffer = availableOutputBuffers.pollFirst();
|
return outputBuffer;
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
} else if (decode(inputBuffer)
|
|
||||||
&& ((captionString == null && lastCaptionString != null)
|
|
||||||
|| (captionString != null && !captionString.equals((lastCaptionString))))) {
|
|
||||||
lastCaptionString = captionString;
|
|
||||||
outputBuffer = availableOutputBuffers.pollFirst();
|
|
||||||
outputBuffer.setOutput(inputBuffer.timeUs, new Eia608Subtitle(captionString), 0);
|
|
||||||
if (inputBuffer.isDecodeOnly()) {
|
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decode(inputBuffer);
|
||||||
|
|
||||||
|
// check if we have any caption updates to report
|
||||||
|
if (!TextUtils.equals(captionString, lastCaptionString)) {
|
||||||
|
lastCaptionString = captionString;
|
||||||
|
if (!inputBuffer.isDecodeOnly()) {
|
||||||
|
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
|
||||||
|
outputBuffer.setOutput(inputBuffer.timeUs, new Eia608Subtitle(captionString), 0);
|
||||||
|
releaseInputBuffer(inputBuffer);
|
||||||
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseInputBuffer(inputBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseInputBuffer(inputBuffer);
|
return null;
|
||||||
return outputBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) {
|
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) {
|
||||||
@ -265,6 +282,7 @@ public final class Eia608Parser implements
|
|||||||
public void flush() {
|
public void flush() {
|
||||||
setCaptionMode(CC_MODE_UNKNOWN);
|
setCaptionMode(CC_MODE_UNKNOWN);
|
||||||
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
||||||
|
playbackPositionUs = 0;
|
||||||
captionStringBuilder.setLength(0);
|
captionStringBuilder.setLength(0);
|
||||||
captionString = null;
|
captionString = null;
|
||||||
lastCaptionString = null;
|
lastCaptionString = null;
|
||||||
@ -285,9 +303,9 @@ public final class Eia608Parser implements
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean decode(SubtitleInputBuffer inputBuffer) {
|
private void decode(SubtitleInputBuffer inputBuffer) {
|
||||||
if (inputBuffer.size < 10) {
|
if (inputBuffer.size < 10) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
seiBuffer.reset(inputBuffer.data.array());
|
seiBuffer.reset(inputBuffer.data.array());
|
||||||
|
|
||||||
@ -363,18 +381,14 @@ public final class Eia608Parser implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!captionDataProcessed) {
|
if (captionDataProcessed) {
|
||||||
return false;
|
if (!isRepeatableControl) {
|
||||||
|
repeatableControlSet = false;
|
||||||
|
}
|
||||||
|
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
||||||
|
captionString = getDisplayCaption();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isRepeatableControl) {
|
|
||||||
repeatableControlSet = false;
|
|
||||||
}
|
|
||||||
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
|
||||||
captionString = getDisplayCaption();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleCtrl(byte cc1, byte cc2) {
|
private boolean handleCtrl(byte cc1, byte cc2) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user