From 19de134aa659beaf6ad3255f39d0a1d3f675e56b Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 3 Jun 2019 16:34:41 +0100 Subject: [PATCH] CEA608: Handling XDS and TEXT modes --- RELEASENOTES.md | 2 + .../exoplayer2/text/cea/Cea608Decoder.java | 70 +++++++++++++++++-- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 80aa49f3f43..3ab6c7bd7ae 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -3,6 +3,8 @@ ### 2.10.2 ### * Subtitles: + * CEA-608: Handle XDS and TEXT modes + ([#5807](https://github.com/google/ExoPlayer/pull/5807)). * TTML: Fix bitmap rendering ([#5633](https://github.com/google/ExoPlayer/pull/5633)). * UI: 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 9316e4fb866..774b94a43cc 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 @@ -80,6 +80,11 @@ public final class Cea608Decoder extends CeaDecoder { * at which point the non-displayed memory becomes the displayed memory (and vice versa). */ private static final byte CTRL_RESUME_CAPTION_LOADING = 0x20; + + private static final byte CTRL_BACKSPACE = 0x21; + + private static final byte CTRL_DELETE_TO_END_OF_ROW = 0x24; + /** * Command initiating roll-up style captioning, with the maximum of 2 rows displayed * simultaneously. @@ -95,25 +100,31 @@ public final class Cea608Decoder extends CeaDecoder { * 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. + * TEXT commands are switching to TEXT service. All consecutive incoming data must be filtered out + * until a command is received that switches back to the CAPTION service. */ - private static final byte CTRL_END_OF_CAPTION = 0x2F; + private static final byte CTRL_TEXT_RESTART = 0x2A; + + private static final byte CTRL_RESUME_TEXT_DISPLAY = 0x2B; 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_DELETE_TO_END_OF_ROW = 0x24; - private static final byte CTRL_BACKSPACE = 0x21; + /** + * 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; // Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20). private static final int[] BASIC_CHARACTER_SET = new int[] { @@ -237,6 +248,11 @@ public final class Cea608Decoder extends CeaDecoder { private byte repeatableControlCc2; private int currentChannel; + // The incoming characters may belong to 3 different services based on the last received control + // codes. The 3 services are Captioning, Text and XDS. The decoder only processes Captioning + // service bytes and drops the rest. + private boolean isInCaptionService; + public Cea608Decoder(String mimeType, int accessibilityChannel) { ccData = new ParsableByteArray(); cueBuilders = new ArrayList<>(); @@ -268,6 +284,7 @@ public Cea608Decoder(String mimeType, int accessibilityChannel) { setCaptionMode(CC_MODE_UNKNOWN); resetCueBuilders(); + isInCaptionService = true; } @Override @@ -288,6 +305,7 @@ public void flush() { repeatableControlCc1 = 0; repeatableControlCc2 = 0; currentChannel = NTSC_CC_CHANNEL_1; + isInCaptionService = true; } @Override @@ -363,6 +381,12 @@ protected void decode(SubtitleInputBuffer inputBuffer) { continue; } + maybeUpdateIsInCaptionService(ccData1, ccData2); + if (!isInCaptionService) { + // Only the Captioning service is supported. Drop all other bytes. + continue; + } + // Special North American character set. // ccData1 - 0|0|0|1|C|0|0|1 // ccData2 - 0|0|1|1|X|X|X|X @@ -629,6 +653,29 @@ private void resetCueBuilders() { cueBuilders.add(currentCueBuilder); } + private void maybeUpdateIsInCaptionService(byte cc1, byte cc2) { + if (isXdsControlCode(cc1)) { + isInCaptionService = false; + } else if (isServiceSwitchCommand(cc1)) { + switch (cc2) { + case CTRL_TEXT_RESTART: + case CTRL_RESUME_TEXT_DISPLAY: + isInCaptionService = false; + break; + case CTRL_END_OF_CAPTION: + case CTRL_RESUME_CAPTION_LOADING: + case CTRL_RESUME_DIRECT_CAPTIONING: + case CTRL_ROLL_UP_CAPTIONS_2_ROWS: + case CTRL_ROLL_UP_CAPTIONS_3_ROWS: + case CTRL_ROLL_UP_CAPTIONS_4_ROWS: + isInCaptionService = true; + break; + default: + // No update. + } + } + } + private static char getChar(byte ccData) { int index = (ccData & 0x7F) - 0x20; return (char) BASIC_CHARACTER_SET[index]; @@ -683,6 +730,15 @@ private static boolean isRepeatable(byte cc1) { return (cc1 & 0xF0) == 0x10; } + private static boolean isXdsControlCode(byte cc1) { + return 0x01 <= cc1 && cc1 <= 0x0F; + } + + private static boolean isServiceSwitchCommand(byte cc1) { + // cc1 - 0|0|0|1|C|1|0|0 + return (cc1 & 0xF7) == 0x14; + } + private static class CueBuilder { // 608 captions define a 15 row by 32 column screen grid. These constants convert from 608