mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Refactored Eia608Parser so that it manipulates the caption string builder directly, avoiding unnecessary object allocation.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=120926283
This commit is contained in:
parent
ab3489efc1
commit
3d14c7242d
5 changed files with 140 additions and 294 deletions
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.eia608;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Closed Caption that contains textual data associated with time indices.
|
|
||||||
*/
|
|
||||||
/* package */ abstract class ClosedCaption {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Identifies closed captions with control characters.
|
|
||||||
*/
|
|
||||||
public static final int TYPE_CTRL = 0;
|
|
||||||
/**
|
|
||||||
* Identifies closed captions with textual information.
|
|
||||||
*/
|
|
||||||
public static final int TYPE_TEXT = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the closed caption data.
|
|
||||||
*/
|
|
||||||
public final int type;
|
|
||||||
|
|
||||||
protected ClosedCaption(int type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.eia608;
|
|
||||||
|
|
||||||
/* package */ final class ClosedCaptionCtrl extends ClosedCaption {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command initiating pop-on style captioning. Subsequent data should be loaded into a
|
|
||||||
* non-displayed memory and held there until the {@link #END_OF_CAPTION} command is received, at
|
|
||||||
* which point the non-displayed memory becomes the displayed memory (and vice versa).
|
|
||||||
*/
|
|
||||||
public static final byte RESUME_CAPTION_LOADING = 0x20;
|
|
||||||
/**
|
|
||||||
* Command initiating roll-up style captioning, with the maximum of 2 rows displayed
|
|
||||||
* simultaneously.
|
|
||||||
*/
|
|
||||||
public static final byte ROLL_UP_CAPTIONS_2_ROWS = 0x25;
|
|
||||||
/**
|
|
||||||
* Command initiating roll-up style captioning, with the maximum of 3 rows displayed
|
|
||||||
* simultaneously.
|
|
||||||
*/
|
|
||||||
public static final byte ROLL_UP_CAPTIONS_3_ROWS = 0x26;
|
|
||||||
/**
|
|
||||||
* Command initiating roll-up style captioning, with the maximum of 4 rows displayed
|
|
||||||
* simultaneously.
|
|
||||||
*/
|
|
||||||
public static final byte ROLL_UP_CAPTIONS_4_ROWS = 0x27;
|
|
||||||
/**
|
|
||||||
* Command initiating paint-on style captioning. Subsequent data should be addressed immediately
|
|
||||||
* to displayed memory without need for the {@link #RESUME_CAPTION_LOADING} command.
|
|
||||||
*/
|
|
||||||
public static final byte RESUME_DIRECT_CAPTIONING = 0x29;
|
|
||||||
/**
|
|
||||||
* Command indicating the end of a pop-on style caption. At this point the caption loaded in
|
|
||||||
* non-displayed memory should be swapped with the one in displayed memory. If no
|
|
||||||
* {@link #RESUME_CAPTION_LOADING} command has been received, this command forces the receiver
|
|
||||||
* into pop-on style.
|
|
||||||
*/
|
|
||||||
public static final byte END_OF_CAPTION = 0x2F;
|
|
||||||
|
|
||||||
public static final byte ERASE_DISPLAYED_MEMORY = 0x2C;
|
|
||||||
public static final byte CARRIAGE_RETURN = 0x2D;
|
|
||||||
public static final byte ERASE_NON_DISPLAYED_MEMORY = 0x2E;
|
|
||||||
|
|
||||||
public static final byte BACKSPACE = 0x21;
|
|
||||||
|
|
||||||
|
|
||||||
public static final byte MID_ROW_CHAN_1 = 0x11;
|
|
||||||
public static final byte MID_ROW_CHAN_2 = 0x19;
|
|
||||||
|
|
||||||
public static final byte MISC_CHAN_1 = 0x14;
|
|
||||||
public static final byte MISC_CHAN_2 = 0x1C;
|
|
||||||
|
|
||||||
public static final byte TAB_OFFSET_CHAN_1 = 0x17;
|
|
||||||
public static final byte TAB_OFFSET_CHAN_2 = 0x1F;
|
|
||||||
|
|
||||||
public final byte cc1;
|
|
||||||
public final byte cc2;
|
|
||||||
|
|
||||||
protected ClosedCaptionCtrl(byte cc1, byte cc2) {
|
|
||||||
super(ClosedCaption.TYPE_CTRL);
|
|
||||||
this.cc1 = cc1;
|
|
||||||
this.cc2 = cc2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMidRowCode() {
|
|
||||||
return (cc1 == MID_ROW_CHAN_1 || cc1 == MID_ROW_CHAN_2) && (cc2 >= 0x20 && cc2 <= 0x2F);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMiscCode() {
|
|
||||||
return (cc1 == MISC_CHAN_1 || cc1 == MISC_CHAN_2) && (cc2 >= 0x20 && cc2 <= 0x2F);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTabOffsetCode() {
|
|
||||||
return (cc1 == TAB_OFFSET_CHAN_1 || cc1 == TAB_OFFSET_CHAN_2) && (cc2 >= 0x21 && cc2 <= 0x23);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPreambleAddressCode() {
|
|
||||||
return (cc1 >= 0x10 && cc1 <= 0x1F) && (cc2 >= 0x40 && cc2 <= 0x7F);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRepeatable() {
|
|
||||||
return cc1 >= 0x10 && cc1 <= 0x1F;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.eia608;
|
|
||||||
|
|
||||||
/* package */ final class ClosedCaptionList {
|
|
||||||
|
|
||||||
public final long timeUs;
|
|
||||||
public final ClosedCaption[] captions;
|
|
||||||
|
|
||||||
public ClosedCaptionList(long timeUs, ClosedCaption[] captions) {
|
|
||||||
this.timeUs = timeUs;
|
|
||||||
this.captions = captions;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.eia608;
|
|
||||||
|
|
||||||
/* package */ final class ClosedCaptionText extends ClosedCaption {
|
|
||||||
|
|
||||||
public final String text;
|
|
||||||
|
|
||||||
public ClosedCaptionText(String text) {
|
|
||||||
super(ClosedCaption.TYPE_TEXT);
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,6 @@ 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 com.google.android.exoplayer.util.extensions.Decoder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
|
@ -52,6 +51,55 @@ public final class Eia608Parser implements
|
||||||
// The default number of rows to display in roll-up captions mode.
|
// The default number of rows to display in roll-up captions mode.
|
||||||
private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4;
|
private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command initiating pop-on style captioning. Subsequent data should be loaded into a
|
||||||
|
* non-displayed memory and held there until the {@link #CTRL_END_OF_CAPTION} command is received,
|
||||||
|
* at which point the non-displayed memory becomes the displayed memory (and vice versa).
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_RESUME_CAPTION_LOADING = 0x20;
|
||||||
|
/**
|
||||||
|
* Command initiating roll-up style captioning, with the maximum of 2 rows displayed
|
||||||
|
* simultaneously.
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_ROLL_UP_CAPTIONS_2_ROWS = 0x25;
|
||||||
|
/**
|
||||||
|
* Command initiating roll-up style captioning, with the maximum of 3 rows displayed
|
||||||
|
* simultaneously.
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_ROLL_UP_CAPTIONS_3_ROWS = 0x26;
|
||||||
|
/**
|
||||||
|
* Command initiating roll-up style captioning, with the maximum of 4 rows displayed
|
||||||
|
* simultaneously.
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_ROLL_UP_CAPTIONS_4_ROWS = 0x27;
|
||||||
|
/**
|
||||||
|
* Command initiating paint-on style captioning. Subsequent data should be addressed immediately
|
||||||
|
* to displayed memory without need for the {@link #CTRL_RESUME_CAPTION_LOADING} command.
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_RESUME_DIRECT_CAPTIONING = 0x29;
|
||||||
|
/**
|
||||||
|
* Command indicating the end of a pop-on style caption. At this point the caption loaded in
|
||||||
|
* non-displayed memory should be swapped with the one in displayed memory. If no
|
||||||
|
* {@link #CTRL_RESUME_CAPTION_LOADING} command has been received, this command forces the
|
||||||
|
* receiver into pop-on style.
|
||||||
|
*/
|
||||||
|
private static final byte CTRL_END_OF_CAPTION = 0x2F;
|
||||||
|
|
||||||
|
private static final byte CTRL_ERASE_DISPLAYED_MEMORY = 0x2C;
|
||||||
|
private static final byte CTRL_CARRIAGE_RETURN = 0x2D;
|
||||||
|
private static final byte CTRL_ERASE_NON_DISPLAYED_MEMORY = 0x2E;
|
||||||
|
|
||||||
|
private static final byte CTRL_BACKSPACE = 0x21;
|
||||||
|
|
||||||
|
private static final byte CTRL_MID_ROW_CHAN_1 = 0x11;
|
||||||
|
private static final byte CTRL_MID_ROW_CHAN_2 = 0x19;
|
||||||
|
|
||||||
|
private static final byte CTRL_MISC_CHAN_1 = 0x14;
|
||||||
|
private static final byte CTRL_MISC_CHAN_2 = 0x1C;
|
||||||
|
|
||||||
|
private static final byte CTRL_TAB_OFFSET_CHAN_1 = 0x17;
|
||||||
|
private static final byte CTRL_TAB_OFFSET_CHAN_2 = 0x1F;
|
||||||
|
|
||||||
// Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20).
|
// Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20).
|
||||||
private static final int[] BASIC_CHARACTER_SET = new int[] {
|
private static final int[] BASIC_CHARACTER_SET = new int[] {
|
||||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // ! " # $ % & '
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // ! " # $ % & '
|
||||||
|
|
@ -125,8 +173,6 @@ public final class Eia608Parser implements
|
||||||
private final TreeSet<SubtitleInputBuffer> queuedInputBuffers;
|
private final TreeSet<SubtitleInputBuffer> queuedInputBuffers;
|
||||||
|
|
||||||
private final ParsableBitArray seiBuffer;
|
private final ParsableBitArray seiBuffer;
|
||||||
private final StringBuilder textStringBuilder;
|
|
||||||
private final ArrayList<ClosedCaption> captions;
|
|
||||||
|
|
||||||
private final StringBuilder captionStringBuilder;
|
private final StringBuilder captionStringBuilder;
|
||||||
|
|
||||||
|
|
@ -135,7 +181,12 @@ public final class Eia608Parser implements
|
||||||
private int captionMode;
|
private int captionMode;
|
||||||
private int captionRowCount;
|
private int captionRowCount;
|
||||||
private String captionString;
|
private String captionString;
|
||||||
private ClosedCaptionCtrl repeatableControl;
|
|
||||||
|
private String lastCaptionString;
|
||||||
|
|
||||||
|
private boolean repeatableControlSet;
|
||||||
|
private byte repeatableControlCc1;
|
||||||
|
private byte repeatableControlCc2;
|
||||||
|
|
||||||
public Eia608Parser() {
|
public Eia608Parser() {
|
||||||
availableInputBuffers = new LinkedList<>();
|
availableInputBuffers = new LinkedList<>();
|
||||||
|
|
@ -149,8 +200,6 @@ public final class Eia608Parser implements
|
||||||
queuedInputBuffers = new TreeSet<>();
|
queuedInputBuffers = new TreeSet<>();
|
||||||
|
|
||||||
seiBuffer = new ParsableBitArray();
|
seiBuffer = new ParsableBitArray();
|
||||||
textStringBuilder = new StringBuilder();
|
|
||||||
captions = new ArrayList<>();
|
|
||||||
|
|
||||||
captionStringBuilder = new StringBuilder();
|
captionStringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
|
@ -182,21 +231,28 @@ public final class Eia608Parser implements
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst();
|
SubtitleOutputBuffer outputBuffer = null;
|
||||||
SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst();
|
SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst();
|
||||||
|
|
||||||
// TODO: investigate ways of batching multiple SubtitleInputBuffers into a single
|
// TODO: investigate ways of batching multiple SubtitleInputBuffers into a single
|
||||||
// SubtitleOutputBuffer
|
// 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 (inputBuffer.isEndOfStream()) {
|
if (inputBuffer.isEndOfStream()) {
|
||||||
|
outputBuffer = availableOutputBuffers.pollFirst();
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
return outputBuffer;
|
} else if (decode(inputBuffer)
|
||||||
}
|
&& ((captionString == null && lastCaptionString != null)
|
||||||
ClosedCaptionList captionList = decode(inputBuffer);
|
|| (captionString != null && !captionString.equals((lastCaptionString))))) {
|
||||||
Eia608Subtitle subtitle = generateSubtitle(captionList);
|
lastCaptionString = captionString;
|
||||||
outputBuffer.setOutput(inputBuffer.timeUs, subtitle, 0);
|
outputBuffer = availableOutputBuffers.pollFirst();
|
||||||
if (inputBuffer.isDecodeOnly()) {
|
outputBuffer.setOutput(inputBuffer.timeUs, new Eia608Subtitle(captionString), 0);
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
if (inputBuffer.isDecodeOnly()) {
|
||||||
|
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseInputBuffer(inputBuffer);
|
releaseInputBuffer(inputBuffer);
|
||||||
return outputBuffer;
|
return outputBuffer;
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +273,10 @@ public final class Eia608Parser implements
|
||||||
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
|
||||||
captionStringBuilder.setLength(0);
|
captionStringBuilder.setLength(0);
|
||||||
captionString = null;
|
captionString = null;
|
||||||
repeatableControl = null;
|
lastCaptionString = null;
|
||||||
|
repeatableControlSet = false;
|
||||||
|
repeatableControlCc1 = 0;
|
||||||
|
repeatableControlCc2 = 0;
|
||||||
while (!queuedInputBuffers.isEmpty()) {
|
while (!queuedInputBuffers.isEmpty()) {
|
||||||
releaseInputBuffer(queuedInputBuffers.pollFirst());
|
releaseInputBuffer(queuedInputBuffers.pollFirst());
|
||||||
}
|
}
|
||||||
|
|
@ -232,13 +291,10 @@ public final class Eia608Parser implements
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClosedCaptionList decode(SubtitleInputBuffer inputBuffer) {
|
private boolean decode(SubtitleInputBuffer inputBuffer) {
|
||||||
if (inputBuffer.size < 10) {
|
if (inputBuffer.size < 10) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
captions.clear();
|
|
||||||
textStringBuilder.setLength(0);
|
|
||||||
seiBuffer.reset(inputBuffer.data.array());
|
seiBuffer.reset(inputBuffer.data.array());
|
||||||
|
|
||||||
// country_code (8) + provider_code (16) + user_identifier (32) + user_data_type_code (8) +
|
// country_code (8) + provider_code (16) + user_identifier (32) + user_data_type_code (8) +
|
||||||
|
|
@ -247,6 +303,8 @@ public final class Eia608Parser implements
|
||||||
int ccCount = seiBuffer.readBits(5);
|
int ccCount = seiBuffer.readBits(5);
|
||||||
seiBuffer.skipBits(8);
|
seiBuffer.skipBits(8);
|
||||||
|
|
||||||
|
boolean captionDataProcessed = false;
|
||||||
|
boolean isRepeatableControl = false;
|
||||||
for (int i = 0; i < ccCount; i++) {
|
for (int i = 0; i < ccCount; i++) {
|
||||||
seiBuffer.skipBits(5); // one_bit + reserved
|
seiBuffer.skipBits(5); // one_bit + reserved
|
||||||
boolean ccValid = seiBuffer.readBit();
|
boolean ccValid = seiBuffer.readBit();
|
||||||
|
|
@ -269,11 +327,14 @@ public final class Eia608Parser implements
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we've reached this point then there is data to process; flag that work has been done.
|
||||||
|
captionDataProcessed = true;
|
||||||
|
|
||||||
// Special North American character set.
|
// Special North American character set.
|
||||||
// ccData2 - P|0|1|1|X|X|X|X
|
// ccData2 - P|0|1|1|X|X|X|X
|
||||||
if ((ccData1 == 0x11 || ccData1 == 0x19)
|
if ((ccData1 == 0x11 || ccData1 == 0x19)
|
||||||
&& ((ccData2 & 0x70) == 0x30)) {
|
&& ((ccData2 & 0x70) == 0x30)) {
|
||||||
textStringBuilder.append(getSpecialChar(ccData2));
|
captionStringBuilder.append(getSpecialChar(ccData2));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +343,7 @@ public final class Eia608Parser implements
|
||||||
if ((ccData1 == 0x12 || ccData1 == 0x1A)
|
if ((ccData1 == 0x12 || ccData1 == 0x1A)
|
||||||
&& ((ccData2 & 0x60) == 0x20)) {
|
&& ((ccData2 & 0x60) == 0x20)) {
|
||||||
backspace(); // Remove standard equivalent of the special extended char.
|
backspace(); // Remove standard equivalent of the special extended char.
|
||||||
textStringBuilder.append(getExtendedEsFrChar(ccData2));
|
captionStringBuilder.append(getExtendedEsFrChar(ccData2));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,89 +352,76 @@ public final class Eia608Parser implements
|
||||||
if ((ccData1 == 0x13 || ccData1 == 0x1B)
|
if ((ccData1 == 0x13 || ccData1 == 0x1B)
|
||||||
&& ((ccData2 & 0x60) == 0x20)) {
|
&& ((ccData2 & 0x60) == 0x20)) {
|
||||||
backspace(); // Remove standard equivalent of the special extended char.
|
backspace(); // Remove standard equivalent of the special extended char.
|
||||||
textStringBuilder.append(getExtendedPtDeChar(ccData2));
|
captionStringBuilder.append(getExtendedPtDeChar(ccData2));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control character.
|
// Control character.
|
||||||
if (ccData1 < 0x20) {
|
if (ccData1 < 0x20) {
|
||||||
addCtrl(ccData1, ccData2);
|
isRepeatableControl = handleCtrl(ccData1, ccData2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic North American character set.
|
// Basic North American character set.
|
||||||
textStringBuilder.append(getChar(ccData1));
|
captionStringBuilder.append(getChar(ccData1));
|
||||||
if (ccData2 >= 0x20) {
|
if (ccData2 >= 0x20) {
|
||||||
textStringBuilder.append(getChar(ccData2));
|
captionStringBuilder.append(getChar(ccData2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addBufferedText();
|
if (!captionDataProcessed) {
|
||||||
|
return false;
|
||||||
if (captions.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosedCaption[] captionArray = new ClosedCaption[captions.size()];
|
if (!isRepeatableControl) {
|
||||||
captions.toArray(captionArray);
|
repeatableControlSet = false;
|
||||||
return new ClosedCaptionList(inputBuffer.timeUs, captionArray);
|
}
|
||||||
|
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
||||||
|
captionString = getDisplayCaption();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Eia608Subtitle generateSubtitle(ClosedCaptionList captionList) {
|
private boolean handleCtrl(byte cc1, byte cc2) {
|
||||||
int captionBufferSize = (captionList == null) ? 0 : captionList.captions.length;
|
boolean isRepeatableControl = isRepeatable(cc1, cc2);
|
||||||
if (captionBufferSize != 0) {
|
if (isRepeatableControl && repeatableControlSet
|
||||||
boolean isRepeatableControl = false;
|
&& repeatableControlCc1 == cc1
|
||||||
for (ClosedCaption caption : captionList.captions) {
|
&& repeatableControlCc2 == cc2) {
|
||||||
if (caption.type == ClosedCaption.TYPE_CTRL) {
|
repeatableControlSet = false;
|
||||||
ClosedCaptionCtrl captionCtrl = (ClosedCaptionCtrl) caption;
|
return true;
|
||||||
isRepeatableControl = captionBufferSize == 1 && captionCtrl.isRepeatable();
|
} else if (isRepeatableControl) {
|
||||||
if (isRepeatableControl && repeatableControl != null
|
repeatableControlSet = true;
|
||||||
&& repeatableControl.cc1 == captionCtrl.cc1
|
repeatableControlCc1 = cc1;
|
||||||
&& repeatableControl.cc2 == captionCtrl.cc2) {
|
repeatableControlCc2 = cc2;
|
||||||
repeatableControl = null;
|
|
||||||
continue;
|
|
||||||
} else if (isRepeatableControl) {
|
|
||||||
repeatableControl = captionCtrl;
|
|
||||||
}
|
|
||||||
if (captionCtrl.isMiscCode()) {
|
|
||||||
handleMiscCode(captionCtrl);
|
|
||||||
} else if (captionCtrl.isPreambleAddressCode()) {
|
|
||||||
handlePreambleAddressCode();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleText((ClosedCaptionText) caption);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isRepeatableControl) {
|
|
||||||
repeatableControl = null;
|
|
||||||
}
|
|
||||||
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
|
||||||
captionString = getDisplayCaption();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (isMiscCode(cc1, cc2)) {
|
||||||
return new Eia608Subtitle(captionString);
|
handleMiscCode(cc2);
|
||||||
|
} else if (isPreambleAddressCode(cc1, cc2)) {
|
||||||
|
// TODO: Add better handling of this with specific positioning.
|
||||||
|
maybeAppendNewline();
|
||||||
|
}
|
||||||
|
return isRepeatableControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMiscCode(ClosedCaptionCtrl caption) {
|
private void handleMiscCode(byte cc2) {
|
||||||
switch (caption.cc2) {
|
switch (cc2) {
|
||||||
case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_2_ROWS:
|
case CTRL_ROLL_UP_CAPTIONS_2_ROWS:
|
||||||
captionRowCount = 2;
|
captionRowCount = 2;
|
||||||
setCaptionMode(CC_MODE_ROLL_UP);
|
setCaptionMode(CC_MODE_ROLL_UP);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_3_ROWS:
|
case CTRL_ROLL_UP_CAPTIONS_3_ROWS:
|
||||||
captionRowCount = 3;
|
captionRowCount = 3;
|
||||||
setCaptionMode(CC_MODE_ROLL_UP);
|
setCaptionMode(CC_MODE_ROLL_UP);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_4_ROWS:
|
case CTRL_ROLL_UP_CAPTIONS_4_ROWS:
|
||||||
captionRowCount = 4;
|
captionRowCount = 4;
|
||||||
setCaptionMode(CC_MODE_ROLL_UP);
|
setCaptionMode(CC_MODE_ROLL_UP);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.RESUME_CAPTION_LOADING:
|
case CTRL_RESUME_CAPTION_LOADING:
|
||||||
setCaptionMode(CC_MODE_POP_ON);
|
setCaptionMode(CC_MODE_POP_ON);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.RESUME_DIRECT_CAPTIONING:
|
case CTRL_RESUME_DIRECT_CAPTIONING:
|
||||||
setCaptionMode(CC_MODE_PAINT_ON);
|
setCaptionMode(CC_MODE_PAINT_ON);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -382,24 +430,24 @@ public final class Eia608Parser implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (caption.cc2) {
|
switch (cc2) {
|
||||||
case ClosedCaptionCtrl.ERASE_DISPLAYED_MEMORY:
|
case CTRL_ERASE_DISPLAYED_MEMORY:
|
||||||
captionString = null;
|
captionString = null;
|
||||||
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
|
||||||
captionStringBuilder.setLength(0);
|
captionStringBuilder.setLength(0);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.ERASE_NON_DISPLAYED_MEMORY:
|
case CTRL_ERASE_NON_DISPLAYED_MEMORY:
|
||||||
captionStringBuilder.setLength(0);
|
captionStringBuilder.setLength(0);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.END_OF_CAPTION:
|
case CTRL_END_OF_CAPTION:
|
||||||
captionString = getDisplayCaption();
|
captionString = getDisplayCaption();
|
||||||
captionStringBuilder.setLength(0);
|
captionStringBuilder.setLength(0);
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.CARRIAGE_RETURN:
|
case CTRL_CARRIAGE_RETURN:
|
||||||
maybeAppendNewline();
|
maybeAppendNewline();
|
||||||
return;
|
return;
|
||||||
case ClosedCaptionCtrl.BACKSPACE:
|
case CTRL_BACKSPACE:
|
||||||
if (captionStringBuilder.length() > 0) {
|
if (captionStringBuilder.length() > 0) {
|
||||||
captionStringBuilder.setLength(captionStringBuilder.length() - 1);
|
captionStringBuilder.setLength(captionStringBuilder.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
@ -407,13 +455,10 @@ public final class Eia608Parser implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePreambleAddressCode() {
|
private void backspace() {
|
||||||
// TODO: Add better handling of this with specific positioning.
|
if (captionStringBuilder.length() > 0) {
|
||||||
maybeAppendNewline();
|
captionStringBuilder.setLength(captionStringBuilder.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleText(ClosedCaptionText caption) {
|
|
||||||
captionStringBuilder.append(caption.text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeAppendNewline() {
|
private void maybeAppendNewline() {
|
||||||
|
|
@ -485,21 +530,17 @@ public final class Eia608Parser implements
|
||||||
return (char) SPECIAL_PT_DE_CHARACTER_SET[index];
|
return (char) SPECIAL_PT_DE_CHARACTER_SET[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBufferedText() {
|
private static boolean isMiscCode(byte cc1, byte cc2) {
|
||||||
if (textStringBuilder.length() > 0) {
|
return (cc1 == CTRL_MISC_CHAN_1 || cc1 == CTRL_MISC_CHAN_2)
|
||||||
String textSnippet = textStringBuilder.toString();
|
&& (cc2 >= 0x20 && cc2 <= 0x2F);
|
||||||
captions.add(new ClosedCaptionText(textSnippet));
|
|
||||||
textStringBuilder.setLength(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCtrl(byte ccData1, byte ccData2) {
|
private static boolean isPreambleAddressCode(byte cc1, byte cc2) {
|
||||||
addBufferedText();
|
return (cc1 >= 0x10 && cc1 <= 0x1F) && (cc2 >= 0x40 && cc2 <= 0x7F);
|
||||||
captions.add(new ClosedCaptionCtrl(ccData1, ccData2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backspace() {
|
private static boolean isRepeatable(byte cc1, byte cc2) {
|
||||||
addCtrl((byte) 0x14, ClosedCaptionCtrl.BACKSPACE);
|
return cc1 >= 0x10 && cc1 <= 0x1F;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue