From fff602358fed751a5cf906338b20a56df1433522 Mon Sep 17 00:00:00 2001 From: Zsolt Matyas Date: Wed, 9 Jan 2019 12:18:02 -0800 Subject: [PATCH] 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. --- .../exoplayer2/text/cea/Cea608Decoder.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java index 13cd01e926..e4333478e3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java @@ -292,7 +292,6 @@ public final class Cea608Decoder extends CeaDecoder { protected void decode(SubtitleInputBuffer inputBuffer) { ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); boolean captionDataProcessed = false; - boolean isRepeatableControl = false; while (ccData.bytesLeft() >= packetLength) { byte ccHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER : (byte) ccData.readUnsignedByte(); @@ -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. captionDataProcessed = true; + boolean repeatedControlPossible = repeatableControlSet; + repeatableControlSet = false; + if (!ODD_PARITY_BYTE_TABLE[ccByte1] || !ODD_PARITY_BYTE_TABLE[ccByte2]) { // The data is invalid. resetCueBuilders(); @@ -372,7 +374,7 @@ public final class Cea608Decoder extends CeaDecoder { // Control character. // ccData1 - 0|0|0|X|X|X|X|X if ((ccData1 & 0xE0) == 0x00) { - isRepeatableControl = handleCtrl(ccData1, ccData2); + handleCtrl(ccData1, ccData2, repeatedControlPossible); continue; } @@ -384,30 +386,26 @@ public final class Cea608Decoder extends CeaDecoder { } if (captionDataProcessed) { - if (!isRepeatableControl) { - repeatableControlSet = false; - } if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { cues = getDisplayCues(); } } } - private boolean handleCtrl(byte cc1, byte cc2) { + private void handleCtrl(byte cc1, byte cc2, boolean repeatedControlPossible) { boolean isRepeatableControl = isRepeatable(cc1); // 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 // in a row, ignore the second one. if (isRepeatableControl) { - if (repeatableControlSet + if (repeatedControlPossible && repeatableControlCc1 == cc1 && repeatableControlCc2 == cc2) { - // This is a duplicate. Clear the repeatable control flag and return. - repeatableControlSet = false; - return true; + // This is a duplicate. Repeatable control flag should be already cleared, let's return. + return; } 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 // the command. repeatableControlSet = true; @@ -425,8 +423,6 @@ public final class Cea608Decoder extends CeaDecoder { } else if (isMiscCode(cc1, cc2)) { handleMiscCode(cc2); } - - return isRepeatableControl; } private void handleMidrowCtrl(byte cc2) {