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:
cdrolle 2016-05-04 14:23:51 -07:00 committed by Oliver Woodman
parent 6e86cafce0
commit d978398faf
5 changed files with 99 additions and 45 deletions

View File

@ -22,13 +22,19 @@ import com.google.android.exoplayer.util.extensions.SimpleDecoder;
* Base class for subtitle parsers that use their own decode thread.
*/
public abstract class SimpleSubtitleParser extends
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
SimpleDecoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> implements
SubtitleParser {
protected SimpleSubtitleParser() {
super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]);
setInitialInputBufferSize(1024);
}
@Override
public void setPositionUs(long timeUs) {
// Do nothing
}
@Override
protected final SubtitleInputBuffer createInputBuffer() {
return new SubtitleInputBuffer();

View File

@ -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);
}

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer.text;
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.extensions.Decoder;
@ -41,7 +40,7 @@ public interface SubtitleParserFactory {
* @return A new {@link Decoder}.
* @throws IllegalArgumentException If the {@link Format} is not supported.
*/
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> createParser(Format format);
SubtitleParser createParser(Format format);
/**
* Default {@link SubtitleParserFactory} implementation.
@ -63,16 +62,14 @@ public interface SubtitleParserFactory {
return getParserClass(format.sampleMimeType) != null;
}
@SuppressWarnings("unchecked")
@Override
public Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> createParser(
Format format) {
public SubtitleParser createParser(Format format) {
try {
Class<?> clazz = getParserClass(format.sampleMimeType);
if (clazz == null) {
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) {
throw new IllegalStateException("Unexpected error instantiating parser", e);
}

View File

@ -55,7 +55,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
private boolean inputStreamEnded;
private boolean outputStreamEnded;
private Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> parser;
private SubtitleParser parser;
private SubtitleInputBuffer nextInputBuffer;
private SubtitleOutputBuffer subtitle;
private SubtitleOutputBuffer nextSubtitle;
@ -133,6 +133,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback {
}
if (nextSubtitle == null) {
parser.setPositionUs(positionUs);
try {
nextSubtitle = parser.dequeueOutputBuffer();
} catch (IOException e) {

View File

@ -19,10 +19,12 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.text.SubtitleInputBuffer;
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.ParsableBitArray;
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.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")
* Closed Captions from the SEI data block from H.264.
*/
public final class Eia608Parser implements
Decoder<SubtitleInputBuffer, SubtitleOutputBuffer, ParserException> {
public final class Eia608Parser implements SubtitleParser {
private static final int NUM_INPUT_BUFFERS = 10;
private static final int NUM_OUTPUT_BUFFERS = 2;
@ -170,6 +171,8 @@ public final class Eia608Parser implements
private final StringBuilder captionStringBuilder;
private long playbackPositionUs;
private SubtitleInputBuffer dequeuedInputBuffer;
private int captionMode;
@ -201,6 +204,11 @@ public final class Eia608Parser implements
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
}
@Override
public void setPositionUs(long positionUs) {
playbackPositionUs = positionUs;
}
@Override
public SubtitleInputBuffer dequeueInputBuffer() throws ParserException {
Assertions.checkState(dequeuedInputBuffer == null);
@ -221,34 +229,43 @@ public final class Eia608Parser implements
@Override
public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException {
if (queuedInputBuffers.isEmpty() || availableOutputBuffers.isEmpty()) {
if (availableOutputBuffers.isEmpty()) {
return null;
}
SubtitleOutputBuffer outputBuffer = null;
// iterate through all available input buffers whose timestamps are less than or equal
// 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
// SubtitleOutputBuffer; it isn't as simple as just iterating through all of the queued input
// buffers until there is a change because for pop-on captions this will result in the input
// buffers being processed almost sequentially as they are queued, eliminating the re-ordering,
// resulting in the content having characters out of order
// If the input buffer indicates we've reached the end of the stream, we can
// return immediately with an output buffer propagating that
if (inputBuffer.isEndOfStream()) {
outputBuffer = availableOutputBuffers.pollFirst();
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
} else if (decode(inputBuffer)
&& ((captionString == null && lastCaptionString != null)
|| (captionString != null && !captionString.equals((lastCaptionString))))) {
releaseInputBuffer(inputBuffer);
return outputBuffer;
}
decode(inputBuffer);
// check if we have any caption updates to report
if (!TextUtils.equals(captionString, lastCaptionString)) {
lastCaptionString = captionString;
outputBuffer = availableOutputBuffers.pollFirst();
if (!inputBuffer.isDecodeOnly()) {
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
outputBuffer.setOutput(inputBuffer.timeUs, new Eia608Subtitle(captionString), 0);
if (inputBuffer.isDecodeOnly()) {
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
releaseInputBuffer(inputBuffer);
return outputBuffer;
}
}
releaseInputBuffer(inputBuffer);
return outputBuffer;
}
return null;
}
private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) {
@ -265,6 +282,7 @@ public final class Eia608Parser implements
public void flush() {
setCaptionMode(CC_MODE_UNKNOWN);
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
playbackPositionUs = 0;
captionStringBuilder.setLength(0);
captionString = null;
lastCaptionString = null;
@ -285,9 +303,9 @@ public final class Eia608Parser implements
// Do nothing
}
private boolean decode(SubtitleInputBuffer inputBuffer) {
private void decode(SubtitleInputBuffer inputBuffer) {
if (inputBuffer.size < 10) {
return false;
return;
}
seiBuffer.reset(inputBuffer.data.array());
@ -363,18 +381,14 @@ public final class Eia608Parser implements
}
}
if (!captionDataProcessed) {
return false;
}
if (captionDataProcessed) {
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) {