Commit fff60235 by Zsolt Matyas

CEA608: Limiting duplicated command checks to immediate frames

Reported in https://github.com/google/ExoPlayer/issues/3860
For failing examples see the github link above.

[Problem]
We drop matching control codes even if they are not received on
consecutive frames.

The specification says
"(4) If the first transmission of a control code pair passes parity,
it is acted upon within one video frame. If the NEXT frame contains
a perfect repeat of the same pair, the redundant code is ignored."

Keyword is the NEXT. The frames must arrive immediately after
each other.

See https://www.law.cornell.edu/cfr/text/47/79.101

[Solution]
Set an additional flag when any data is processed. Control code
duplication checks should be limited only for the first control
byte pairs processed after any control code.

[Test]
Sarnoff tests have equivalent CEA708 and CEA608 Streams.
parent e0c6f538
...@@ -292,7 +292,6 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -292,7 +292,6 @@ public final class Cea608Decoder extends CeaDecoder {
protected void decode(SubtitleInputBuffer inputBuffer) { protected void decode(SubtitleInputBuffer inputBuffer) {
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
boolean captionDataProcessed = false; boolean captionDataProcessed = false;
boolean isRepeatableControl = false;
while (ccData.bytesLeft() >= packetLength) { while (ccData.bytesLeft() >= packetLength) {
byte ccHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER byte ccHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER
: (byte) ccData.readUnsignedByte(); : (byte) ccData.readUnsignedByte();
...@@ -337,6 +336,9 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -337,6 +336,9 @@ public final class Cea608Decoder extends CeaDecoder {
// If we've reached this point then there is data to process; flag that work has been done. // If we've reached this point then there is data to process; flag that work has been done.
captionDataProcessed = true; captionDataProcessed = true;
boolean repeatedControlPossible = repeatableControlSet;
repeatableControlSet = false;
if (!ODD_PARITY_BYTE_TABLE[ccByte1] || !ODD_PARITY_BYTE_TABLE[ccByte2]) { if (!ODD_PARITY_BYTE_TABLE[ccByte1] || !ODD_PARITY_BYTE_TABLE[ccByte2]) {
// The data is invalid. // The data is invalid.
resetCueBuilders(); resetCueBuilders();
...@@ -372,7 +374,7 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -372,7 +374,7 @@ public final class Cea608Decoder extends CeaDecoder {
// Control character. // Control character.
// ccData1 - 0|0|0|X|X|X|X|X // ccData1 - 0|0|0|X|X|X|X|X
if ((ccData1 & 0xE0) == 0x00) { if ((ccData1 & 0xE0) == 0x00) {
isRepeatableControl = handleCtrl(ccData1, ccData2); handleCtrl(ccData1, ccData2, repeatedControlPossible);
continue; continue;
} }
...@@ -384,30 +386,26 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -384,30 +386,26 @@ public final class Cea608Decoder extends CeaDecoder {
} }
if (captionDataProcessed) { if (captionDataProcessed) {
if (!isRepeatableControl) {
repeatableControlSet = false;
}
if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) {
cues = getDisplayCues(); cues = getDisplayCues();
} }
} }
} }
private boolean handleCtrl(byte cc1, byte cc2) { private void handleCtrl(byte cc1, byte cc2, boolean repeatedControlPossible) {
boolean isRepeatableControl = isRepeatable(cc1); boolean isRepeatableControl = isRepeatable(cc1);
// Most control commands are sent twice in succession to ensure they are received properly. // Most control commands are sent twice in succession to ensure they are received properly.
// We don't want to process duplicate commands, so if we see the same repeatable command twice // We don't want to process duplicate commands, so if we see the same repeatable command twice
// in a row, ignore the second one. // in a row, ignore the second one.
if (isRepeatableControl) { if (isRepeatableControl) {
if (repeatableControlSet if (repeatedControlPossible
&& repeatableControlCc1 == cc1 && repeatableControlCc1 == cc1
&& repeatableControlCc2 == cc2) { && repeatableControlCc2 == cc2) {
// This is a duplicate. Clear the repeatable control flag and return. // This is a duplicate. Repeatable control flag should be already cleared, let's return.
repeatableControlSet = false; return;
return true;
} else { } else {
// This is a repeatable command, but we haven't see it yet, so set the repeatable control // This is a repeatable command, but we haven't seen it yet, so set the repeatable control
// flag (to ensure we ignore the next one should it be a duplicate) and continue processing // flag (to ensure we ignore the next one should it be a duplicate) and continue processing
// the command. // the command.
repeatableControlSet = true; repeatableControlSet = true;
...@@ -425,8 +423,6 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -425,8 +423,6 @@ public final class Cea608Decoder extends CeaDecoder {
} else if (isMiscCode(cc1, cc2)) { } else if (isMiscCode(cc1, cc2)) {
handleMiscCode(cc2); handleMiscCode(cc2);
} }
return isRepeatableControl;
} }
private void handleMidrowCtrl(byte cc2) { private void handleMidrowCtrl(byte cc2) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment