11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/* 21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project 31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License. 61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at 71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software 111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and 141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License. 151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.cc; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.SystemClock; 201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.IntDef; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseIntArray; 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data; 2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionColor; 2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionEvent; 2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionPenAttr; 2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionPenColor; 2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionPenLocation; 3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionWindow; 3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CaptionWindowAttr; 3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.Cea708Data.CcPacket; 3365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.util.ByteArrayBuffer; 341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 351abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.UnsupportedEncodingException; 361abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.lang.annotation.Retention; 371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.lang.annotation.RetentionPolicy; 381abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.ByteBuffer; 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.charset.StandardCharsets; 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Arrays; 411abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.TreeSet; 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * A class for parsing CEA-708, which is the standard for closed captioning for ATSC DTV. 451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>ATSC DTV closed caption data are carried on picture user data of video streams. 471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * This class starts to parse from picture user data payload, so extraction process of user_data 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * from video streams is up to outside of this code. 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>There are 4 steps to decode user_data to provide closed caption services. 511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <h3>Step 1. user_data -> CcPacket ({@link #parseClosedCaption} method)</h3> 531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>First, user_data consists of cc_data packets, which are 3-byte segments. Here, CcPacket is a 551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * collection of cc_data packets in a frame along with same presentation timestamp. Because cc_data 561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * packets must be reassembled in the frame display order, CcPackets are reordered. 571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <h3>Step 2. CcPacket -> DTVCC packet ({@link #parseCcPacket} method)</h3> 591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>Each cc_data packet has a one byte for declaring a type of itself and data validity, and the 611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * subsequent two bytes for input data of a DTVCC packet. There are 4 types for cc_data packet. 621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * We're interested in DTVCC_PACKET_START(type 3) and DTVCC_PACKET_DATA(type 2). Each DTVCC packet 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * begins with DTVCC_PACKET_START(type 3) and the following cc_data packets which has 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * DTVCC_PACKET_DATA(type 2) are appended into the DTVCC packet being assembled. 651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <h3>Step 3. DTVCC packet -> Service Blocks ({@link #parseDtvCcPacket} method)</h3> 671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>A DTVCC packet consists of multiple service blocks. Each service block represents a caption 691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * track and has a service number, which ranges from 1 to 63, that denotes caption track identity. 701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * In here, we listen at most one chosen caption track by {@link #mListenServiceNumber}. 711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Otherwise, just skip the other service blocks. 721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <h3>Step 4. Interpreting Service Block Data ({@link #parseServiceBlockData}, {@code parseXX}, 741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * and {@link #parseExt1} methods)</h3> 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>Service block data is actual caption stream. it looks similar to telnet. It uses most parts of 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * ASCII table and consists of specially defined commands and some ASCII control codes which work 781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * in a behavior slightly different from their original purpose. ASCII control codes and caption 791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * commands are explicit instructions that control the state of a closed caption service and the 801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * other ASCII and text codes are implicit instructions that send their characters to buffer. 811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>There are 4 main code groups and 4 extended code groups. Both the range of code groups are the 831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * same as the range of a byte. 841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>4 main code groups: C0, C1, G0, G1 861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <br>4 extended code groups: C2, C3, G2, G3 871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>Each code group has its own handle method. For example, {@link #parseC0} handles C0 code group 891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * and so on. And {@link #parseServiceBlockData} method maps a stream on the main code groups while 901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@link #parseExt1} method maps on the extended code groups. 911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>The main code groups: 931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <ul> 941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>C0 - contains modified ASCII control codes. It is not intended by CEA-708 but Korea TTA 951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * standard for ATSC CC uses P16 character heavily, which is unclear entity in CEA-708 doc, 961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * even for the alphanumeric characters instead of ASCII characters.</li> 971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>C1 - contains the caption commands. There are 3 categories of a caption command.</li> 981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <ul> 991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Window commands: The window commands control a caption window which is addressable area being 1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * with in the Safe title area. (CWX, CLW, DSW, HDW, TGW, DLW, SWA, DFX)</li> 1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Pen commands: Th pen commands control text style and location. (SPA, SPC, SPL)</li> 1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Job commands: The job commands make a delay and recover from the delay. (DLY, DLC, RST)</li> 1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * </ul> 1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>G0 - same as printable ASCII character set except music note character.</li> 1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>G1 - same as ISO 8859-1 Latin 1 character set.</li> 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * </ul> 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>Most of the extended code groups are being skipped. 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class Cea708Parser { 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String TAG = "Cea708Parser"; 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final boolean DEBUG = false; 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // According to CEA-708B, the maximum value of closed caption bandwidth is 9600bps. 1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int MAX_ALLOCATED_SIZE = 9600 / 8; 1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String MUSIC_NOTE_CHAR = new String( 1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "\u266B".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); 1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // The following values are denoting the type of closed caption data. 1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // See CEA-708B section 4.4.1. 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int CC_TYPE_DTVCC_PACKET_START = 3; 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int CC_TYPE_DTVCC_PACKET_DATA = 2; 1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // The following values are defined in CEA-708B Figure 4 and 6. 1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DTVCC_MAX_PACKET_SIZE = 64; 1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DTVCC_PACKET_SIZE_SCALE_FACTOR = 2; 1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DTVCC_EXTENDED_SERVICE_NUMBER_POINT = 7; 1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // The following values are for seeking closed caption tracks. 1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DISCOVERY_PERIOD_MS = 10000; // 10 sec 1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DISCOVERY_NUM_BYTES_THRESHOLD = 10; // 10 bytes 1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DISCOVERY_CC_SERVICE_NUMBER_START = 1; // CC1 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DISCOVERY_CC_SERVICE_NUMBER_END = 4; // CC4 1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final ByteArrayBuffer mDtvCcPacket = new ByteArrayBuffer(MAX_ALLOCATED_SIZE); 1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final TreeSet<CcPacket> mCcPackets = new TreeSet<>(); 1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final StringBuffer mBuffer = new StringBuffer(); 1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final SparseIntArray mDiscoveredNumBytes = new SparseIntArray(); // per service number 1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private long mLastDiscoveryLaunchedMs = SystemClock.elapsedRealtime(); 1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int mCommand = 0; 1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int mListenServiceNumber = 0; 1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private boolean mDtvCcPacking = false; 1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Assign a dummy listener in order to avoid null checks. 1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private OnCea708ParserListener mListener = new OnCea708ParserListener() { 1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void emitEvent(CaptionEvent event) { 1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // do nothing 1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void discoverServiceNumber(int serviceNumber) { 1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // do nothing 1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko }; 1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko /** 1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@link Cea708Parser} emits caption event of three different types. 1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@link OnCea708ParserListener#emitEvent} is invoked with the parameter 1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@link CaptionEvent} to pass all the results to an observer of the decoding process. 1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>{@link CaptionEvent#type} determines the type of the result and 1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@link CaptionEvent#obj} contains the output value of a caption event. 1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * The observer must do the casting to the corresponding type. 1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <ul><li>{@code CAPTION_EMIT_TYPE_BUFFER}: Passes a caption text buffer to a observer. 1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@code obj} must be of {@link String}.</li> 1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>{@code CAPTION_EMIT_TYPE_CONTROL}: Passes a caption character control code to a observer. 1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@code obj} must be of {@link Character}.</li> 1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>{@code CAPTION_EMIT_TYPE_CLEAR_COMMAND}: Passes a clear command to a observer. 1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@code obj} must be {@code NULL}.</li></ul> 1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @IntDef({CAPTION_EMIT_TYPE_BUFFER, CAPTION_EMIT_TYPE_CONTROL, CAPTION_EMIT_TYPE_COMMAND_CWX, 1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CAPTION_EMIT_TYPE_COMMAND_CLW, CAPTION_EMIT_TYPE_COMMAND_DSW, CAPTION_EMIT_TYPE_COMMAND_HDW, 1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CAPTION_EMIT_TYPE_COMMAND_TGW, CAPTION_EMIT_TYPE_COMMAND_DLW, CAPTION_EMIT_TYPE_COMMAND_DLY, 1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CAPTION_EMIT_TYPE_COMMAND_DLC, CAPTION_EMIT_TYPE_COMMAND_RST, CAPTION_EMIT_TYPE_COMMAND_SPA, 1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CAPTION_EMIT_TYPE_COMMAND_SPC, CAPTION_EMIT_TYPE_COMMAND_SPL, CAPTION_EMIT_TYPE_COMMAND_SWA, 1801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CAPTION_EMIT_TYPE_COMMAND_DFX}) 1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Retention(RetentionPolicy.SOURCE) 1821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public @interface CaptionEmitType {} 1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_BUFFER = 1; 1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_CONTROL = 2; 1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_CWX = 3; 1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_CLW = 4; 1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_DSW = 5; 1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_HDW = 6; 1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_TGW = 7; 1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_DLW = 8; 1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_DLY = 9; 1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_DLC = 10; 1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_RST = 11; 1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_SPA = 12; 1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_SPC = 13; 1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_SPL = 14; 1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_SWA = 15; 1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static final int CAPTION_EMIT_TYPE_COMMAND_DFX = 16; 1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public interface OnCea708ParserListener { 2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void emitEvent(CaptionEvent event); 2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void discoverServiceNumber(int serviceNumber); 2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void setListener(OnCea708ParserListener listener) { 2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (listener != null) { 2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener = listener; 2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void setListenServiceNumber(int serviceNumber) { 2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListenServiceNumber = serviceNumber; 2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void emitCaptionEvent(CaptionEvent captionEvent) { 2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Emit the existing string buffer before a new event is arrived. 2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionBuffer(); 2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.emitEvent(captionEvent); 2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void emitCaptionBuffer() { 2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mBuffer.length() > 0) { 2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.emitEvent(new CaptionEvent(CAPTION_EMIT_TYPE_BUFFER, mBuffer.toString())); 2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.setLength(0); 2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Step 1. user_data -> CcPacket ({@link #parseClosedCaption} method) 2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void parseClosedCaption(ByteBuffer data, long framePtsUs) { 2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int ccCount = data.limit() / 3; 2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko byte[] ccBytes = new byte[3 * ccCount]; 2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int i = 0; i < 3 * ccCount; i++) { 2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ccBytes[i] = data.get(i); 2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CcPacket ccPacket = new CcPacket(ccBytes, ccCount, framePtsUs); 2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mCcPackets.add(ccPacket); 2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean processClosedCaptions(long framePtsUs) { 2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // To get the sorted cc packets that have lower frame pts than current frame pts, 2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // the following offset divides off the lower side of the packets. 2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CcPacket offsetPacket = new CcPacket(new byte[0], 0, framePtsUs); 2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko offsetPacket = mCcPackets.lower(offsetPacket); 2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean processed = false; 2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (offsetPacket != null) { 2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko while (!mCcPackets.isEmpty() && offsetPacket.compareTo(mCcPackets.first()) >= 0) { 2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CcPacket packet = mCcPackets.pollFirst(); 2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko parseCcPacket(packet); 2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko processed = true; 2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return processed; 2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Step 2. CcPacket -> DTVCC packet ({@link #parseCcPacket} method) 2561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void parseCcPacket(CcPacket ccPacket) { 2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of cc packet, see ATSC TSG-676 - Table A8. 2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko byte[] bytes = ccPacket.bytes; 2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int pos = 0; 2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int i = 0; i < ccPacket.ccCount; ++i) { 2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean ccValid = (bytes[pos] & 0x04) != 0; 2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int ccType = bytes[pos] & 0x03; 2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // The dtvcc should be considered complete: 2651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // - if either ccValid is set and ccType is 3 2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // - or ccValid is clear and ccType is 2 or 3. 2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (ccValid) { 2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (ccType == CC_TYPE_DTVCC_PACKET_START) { 2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mDtvCcPacking) { 2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko parseDtvCcPacket(mDtvCcPacket.buffer(), mDtvCcPacket.length()); 2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.clear(); 2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacking = true; 2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.append(bytes[pos + 1]); 2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.append(bytes[pos + 2]); 2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mDtvCcPacking && ccType == CC_TYPE_DTVCC_PACKET_DATA) { 2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.append(bytes[pos + 1]); 2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.append(bytes[pos + 2]); 2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if ((ccType == CC_TYPE_DTVCC_PACKET_START || ccType == CC_TYPE_DTVCC_PACKET_DATA) 2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mDtvCcPacking) { 2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacking = false; 2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko parseDtvCcPacket(mDtvCcPacket.buffer(), mDtvCcPacket.length()); 2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDtvCcPacket.clear(); 2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 3; 2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Step 3. DTVCC packet -> Service Blocks ({@link #parseDtvCcPacket} method) 2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private void parseDtvCcPacket(byte[] data, int limit) { 2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of DTVCC packet, see CEA-708B Figure 4. 2951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int pos = 0; 2961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int packetSize = data[pos] & 0x3f; 2971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (packetSize == 0) { 2981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko packetSize = DTVCC_MAX_PACKET_SIZE; 2991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int calculatedPacketSize = packetSize * DTVCC_PACKET_SIZE_SCALE_FACTOR; 3011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (limit != calculatedPacketSize) { 3021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 3031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 3051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int len = pos + calculatedPacketSize; 3061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko while (pos < len) { 3071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of Service Block, see CEA-708B Figure 5 and 6. 3081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int serviceNumber = (data[pos] & 0xe0) >> 5; 3091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int blockSize = data[pos] & 0x1f; 3101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 3111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (serviceNumber == DTVCC_EXTENDED_SERVICE_NUMBER_POINT) { 3121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko serviceNumber = (data[pos] & 0x3f); 3131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 3141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Return if invalid service number 3161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (serviceNumber < DTVCC_EXTENDED_SERVICE_NUMBER_POINT) { 3171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 3181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (pos + blockSize > limit) { 3211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return; 3221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Send parsed service number in order to find unveiled closed caption tracks which 3251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // are not specified in any ATSC PSIP sections. Since some broadcasts send empty closed 3261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // caption tracks, it detects the proper closed caption tracks by counting the number of 3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // bytes sent with the same service number during a discovery period. 3281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // The viewer in most TV sets chooses between CC1, CC2, CC3, CC4 to view different 3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // language captions. Therefore, only CC1, CC2, CC3, CC4 are allowed to be reported. 3301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (blockSize > 0 && serviceNumber >= DISCOVERY_CC_SERVICE_NUMBER_START 3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && serviceNumber <= DISCOVERY_CC_SERVICE_NUMBER_END) { 3321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDiscoveredNumBytes.put( 3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko serviceNumber, blockSize + mDiscoveredNumBytes.get(serviceNumber, 0)); 3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mLastDiscoveryLaunchedMs + DISCOVERY_PERIOD_MS < SystemClock.elapsedRealtime()) { 3361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko for (int i = 0; i < mDiscoveredNumBytes.size(); ++i) { 3371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int discoveredNumBytes = mDiscoveredNumBytes.valueAt(i); 3381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (discoveredNumBytes >= DISCOVERY_NUM_BYTES_THRESHOLD) { 3391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int discoveredServiceNumber = mDiscoveredNumBytes.keyAt(i); 3401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mListener.discoverServiceNumber(discoveredServiceNumber); 3411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDiscoveredNumBytes.clear(); 3441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mLastDiscoveryLaunchedMs = SystemClock.elapsedRealtime(); 3451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Skip current service block if either there is no block data or the service number 3481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // is not same as listening service number. 3491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (blockSize == 0 || serviceNumber != mListenServiceNumber) { 3501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += blockSize; 3511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko continue; 3521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // From this point, starts to read DTVCC coding layer. 3551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // First, identify code groups, which is defined in CEA-708B Section 7.1. 3561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int blockLimit = pos + blockSize; 3571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko while (pos < blockLimit) { 3581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseServiceBlockData(data, pos); 3591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Emit the buffer after reading codes. 3621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionBuffer(); 3631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = blockLimit; 3641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Step 4. Main code groups 3681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseServiceBlockData(byte[] data, int pos) { 3691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of the ranges of DTVCC code groups, see CEA-708B Table 6. 3701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mCommand = data[pos] & 0xff; 3711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 3721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand == Cea708Data.CODE_C0_EXT1) { 3731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseExt1(data, pos); 3741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C0_RANGE_START 3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C0_RANGE_END) { 3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseC0(data, pos); 3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C1_RANGE_START 3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C1_RANGE_END) { 3791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseC1(data, pos); 3801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_G0_RANGE_START 3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_G0_RANGE_END) { 3821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseG0(data, pos); 3831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_G1_RANGE_START 3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_G1_RANGE_END) { 3851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseG1(data, pos); 3861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 3881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 3891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseC0(byte[] data, int pos) { 3911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C0 code group, see CEA-708B Section 7.4.1. 3921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // CL Group: C0 Subset of ASCII Control codes 3931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand >= Cea708Data.CODE_C0_SKIP2_RANGE_START 3941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C0_SKIP2_RANGE_END) { 3951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand == Cea708Data.CODE_C0_P16) { 3961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : P16 escapes next two bytes for the large character maps.(no standard rule) 3971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : For korea broadcasting, express whole letters by using this. 3981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko try { 3991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (data[pos] == 0) { 4001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append((char) data[pos + 1]); 4011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 4021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko String value = new String( 4031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Arrays.copyOfRange(data, pos, pos + 2), 4041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "EUC-KR"); 4051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append(value); 4061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } catch (UnsupportedEncodingException e) { 4081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.e(TAG, "P16 Code - Could not find supported encoding", e); 4091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 2; 4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C0_SKIP1_RANGE_START 4131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C0_SKIP1_RANGE_END) { 4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 4151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 4161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // NUL, BS, FF, CR interpreted as they are in ASCII control codes. 4171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // HCR moves the pen location to th beginning of the current line and deletes contents. 4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // FF clears the screen and moves the pen location to (0,0). 4191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // ETX is the NULL command which is used to flush text to the current window when no 4201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // other command is pending. 4211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko switch (mCommand) { 4221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_NUL: 4231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_ETX: 4251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand)); 4261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_BS: 4281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand)); 4291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_FF: 4311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand)); 4321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_CR: 4341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append('\n'); 4351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C0_HCR: 4371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand)); 4381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko default: 4401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 4441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseC1(byte[] data, int pos) { 4471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C1 code group, see CEA-708B Section 8.10. 4481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // CR Group: C1 Caption Control Codes 4491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko switch (mCommand) { 4501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW0: 4511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW1: 4521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW2: 4531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW3: 4541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW4: 4551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW5: 4561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW6: 4571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CW7: { 4581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // SetCurrentWindow0-7 4591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowId = mCommand - Cea708Data.CODE_C1_CW0; 4601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CWX, windowId)); 4611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 4621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand CWX windowId: %d", windowId)); 4631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_CLW: { 4681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // ClearWindows 4691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowBitmap = data[pos] & 0xff; 4701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 4711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CLW, windowBitmap)); 4721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 4731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand CLW windowBitmap: %d", windowBitmap)); 4741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DSW: { 4791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // DisplayWindows 4801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowBitmap = data[pos] & 0xff; 4811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 4821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DSW, windowBitmap)); 4831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 4841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand DSW windowBitmap: %d", windowBitmap)); 4851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_HDW: { 4901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // HideWindows 4911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowBitmap = data[pos] & 0xff; 4921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 4931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_HDW, windowBitmap)); 4941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 4951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand HDW windowBitmap: %d", windowBitmap)); 4961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 4981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_TGW: { 5011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // ToggleWindows 5021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowBitmap = data[pos] & 0xff; 5031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_TGW, windowBitmap)); 5051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand TGW windowBitmap: %d", windowBitmap)); 5071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DLW: { 5121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // DeleteWindows 5131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowBitmap = data[pos] & 0xff; 5141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLW, windowBitmap)); 5161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand DLW windowBitmap: %d", windowBitmap)); 5181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DLY: { 5231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Delay 5241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int tenthsOfSeconds = data[pos] & 0xff; 5251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLY, tenthsOfSeconds)); 5271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand DLY %d tenths of seconds", 5291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko tenthsOfSeconds)); 5301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DLC: { 5341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // DelayCancel 5351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLC, null)); 5361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, "CaptionCommand DLC"); 5381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_RST: { 5431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Reset 5441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_RST, null)); 5451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, "CaptionCommand RST"); 5471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_SPA: { 5521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // SetPenAttributes 5531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int textTag = (data[pos] & 0xf0) >> 4; 5541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int penSize = data[pos] & 0x03; 5551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int penOffset = (data[pos] & 0x0c) >> 2; 5561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean italic = (data[pos + 1] & 0x80) != 0; 5571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean underline = (data[pos + 1] & 0x40) != 0; 5581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int edgeType = (data[pos + 1] & 0x38) >> 3; 5591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int fontTag = data[pos + 1] & 0x7; 5601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 2; 5611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPA, 5621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko new CaptionPenAttr(penSize, penOffset, textTag, fontTag, edgeType, 5631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko underline, italic))); 5641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format( 5661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "CaptionCommand SPA penSize: %d, penOffset: %d, textTag: %d, " 5671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "fontTag: %d, edgeType: %d, underline: %s, italic: %s", 5681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko penSize, penOffset, textTag, fontTag, edgeType, underline, italic)); 5691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 5711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 5721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 5731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_SPC: { 5741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // SetPenColor 5751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int opacity = (data[pos] & 0xc0) >> 6; 5761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int red = (data[pos] & 0x30) >> 4; 5771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int green = (data[pos] & 0x0c) >> 2; 5781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int blue = data[pos] & 0x03; 5791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor foregroundColor = new CaptionColor(opacity, red, green, blue); 5801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko opacity = (data[pos] & 0xc0) >> 6; 5821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko red = (data[pos] & 0x30) >> 4; 5831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko green = (data[pos] & 0x0c) >> 2; 5841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko blue = data[pos] & 0x03; 5851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor backgroundColor = new CaptionColor(opacity, red, green, blue); 5861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko red = (data[pos] & 0x30) >> 4; 5881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko green = (data[pos] & 0x0c) >> 2; 5891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko blue = data[pos] & 0x03; 5901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor edgeColor = new CaptionColor( 5911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor.OPACITY_SOLID, red, green, blue); 5921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 5931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPC, 5941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko new CaptionPenColor(foregroundColor, backgroundColor, edgeColor))); 5951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 5961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format( 5971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "CaptionCommand SPC foregroundColor %s backgroundColor %s edgeColor %s", 5981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko foregroundColor, backgroundColor, edgeColor)); 5991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 6011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 6031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_SPL: { 6041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // SetPenLocation 6051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // column is normally 0-31 for 4:3 formats, and 0-41 for 16:9 formats 6061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int row = data[pos] & 0x0f; 6071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int column = data[pos + 1] & 0x3f; 6081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 2; 6091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPL, 6101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko new CaptionPenLocation(row, column))); 6111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 6121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format("CaptionCommand SPL row: %d, column: %d", 6131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko row, column)); 6141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 6161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 6181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_SWA: { 6191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // SetWindowAttributes 6201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int opacity = (data[pos] & 0xc0) >> 6; 6211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int red = (data[pos] & 0x30) >> 4; 6221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int green = (data[pos] & 0x0c) >> 2; 6231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int blue = data[pos] & 0x03; 6241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor fillColor = new CaptionColor(opacity, red, green, blue); 6251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int borderType = (data[pos + 1] & 0xc0) >> 6 | (data[pos + 2] & 0x80) >> 5; 6261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko red = (data[pos + 1] & 0x30) >> 4; 6271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko green = (data[pos + 1] & 0x0c) >> 2; 6281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko blue = data[pos + 1] & 0x03; 6291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor borderColor = new CaptionColor( 6301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko CaptionColor.OPACITY_SOLID, red, green, blue); 6311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean wordWrap = (data[pos + 2] & 0x40) != 0; 6321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int printDirection = (data[pos + 2] & 0x30) >> 4; 6331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int scrollDirection = (data[pos + 2] & 0x0c) >> 2; 6341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int justify = (data[pos + 2] & 0x03); 6351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int effectSpeed = (data[pos + 3] & 0xf0) >> 4; 6361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int effectDirection = (data[pos + 3] & 0x0c) >> 2; 6371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int displayEffect = data[pos + 3] & 0x3; 6381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 4; 6391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SWA, 6401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko new CaptionWindowAttr(fillColor, borderColor, borderType, wordWrap, 6411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko printDirection, scrollDirection, justify, 6421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko effectDirection, effectSpeed, displayEffect))); 6431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 6441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format( 6451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "CaptionCommand SWA fillColor: %s, borderColor: %s, borderType: %d" 6461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "wordWrap: %s, printDirection: %d, scrollDirection: %d, " 6471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "justify: %s, effectDirection: %d, effectSpeed: %d, " 6481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "displayEffect: %d", 6491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko fillColor, borderColor, borderType, wordWrap, printDirection, 6501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko scrollDirection, justify, effectDirection, effectSpeed, displayEffect)); 6511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 6531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 6551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF0: 6561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF1: 6571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF2: 6581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF3: 6591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF4: 6601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF5: 6611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF6: 6621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_C1_DF7: { 6631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // DefineWindow0-7 6641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowId = mCommand - Cea708Data.CODE_C1_DF0; 6651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean visible = (data[pos] & 0x20) != 0; 6661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean rowLock = (data[pos] & 0x10) != 0; 6671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean columnLock = (data[pos] & 0x08) != 0; 6681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int priority = data[pos] & 0x07; 6691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko boolean relativePositioning = (data[pos + 1] & 0x80) != 0; 6701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int anchorVertical = data[pos + 1] & 0x7f; 6711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int anchorHorizontal = data[pos + 2] & 0xff; 6721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int anchorId = (data[pos + 3] & 0xf0) >> 4; 6731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int rowCount = data[pos + 3] & 0x0f; 6741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int columnCount = data[pos + 4] & 0x3f; 6751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int windowStyle = (data[pos + 5] & 0x38) >> 3; 6761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko int penStyle = data[pos + 5] & 0x07; 6771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 6; 6781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DFX, 6791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko new CaptionWindow(windowId, visible, rowLock, columnLock, priority, 6801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko relativePositioning, anchorVertical, anchorHorizontal, anchorId, 6811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko rowCount, columnCount, penStyle, windowStyle))); 6821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) { 6831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.d(TAG, String.format( 6841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko "CaptionCommand DFx windowId: %d, priority: %d, columnLock: %s, " 6851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "rowLock: %s, visible: %s, anchorVertical: %d, " 6861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "relativePositioning: %s, anchorHorizontal: %d, " 6871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "rowCount: %d, anchorId: %d, columnCount: %d, penStyle: %d, " 6881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko + "windowStyle: %d", 6891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko windowId, priority, columnLock, rowLock, visible, anchorVertical, 6901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko relativePositioning, anchorHorizontal, rowCount, anchorId, columnCount, 6911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko penStyle, windowStyle)); 6921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 6941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 6961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko default: 6971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 6981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 6991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseG0(byte[] data, int pos) { 7031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of G0 code group, see CEA-708B Section 7.4.3. 7041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // GL Group: G0 Modified version of ANSI X3.4 Printable Character Set (ASCII) 7051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand == Cea708Data.CODE_G0_MUSICNOTE) { 7061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Music note. 7071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append(MUSIC_NOTE_CHAR); 7081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else { 7091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Put ASCII code into buffer. 7101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append((char) mCommand); 7111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseG1(byte[] data, int pos) { 7161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of G0 code group, see CEA-708B Section 7.4.4. 7171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // GR Group: G1 ISO 8859-1 Latin 1 Characters 7181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Put ASCII Extended character set into buffer. 7191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mBuffer.append((char) mCommand); 7201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Step 4. Extended code groups 7241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseExt1(byte[] data, int pos) { 7251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of EXT1 code group, see CEA-708B Section 7.2. 7261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mCommand = data[pos] & 0xff; 7271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 7281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand >= Cea708Data.CODE_C2_RANGE_START 7291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C2_RANGE_END) { 7301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseC2(data, pos); 7311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C3_RANGE_START 7321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C3_RANGE_END) { 7331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseC3(data, pos); 7341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_G2_RANGE_START 7351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_G2_RANGE_END) { 7361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseG2(data, pos); 7371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_G3_RANGE_START 7381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_G3_RANGE_END) { 7391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos = parseG3(data ,pos); 7401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseC2(byte[] data, int pos) { 7451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C2 code group, see CEA-708B Section 7.4.7. 7461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Extended Miscellaneous Control Codes 7471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // C2 Table : No commands as of CEA-708B. A decoder must skip. 7481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand >= Cea708Data.CODE_C2_SKIP0_RANGE_START 7491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C2_SKIP0_RANGE_END) { 7501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Do nothing. 7511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C2_SKIP1_RANGE_START 7521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C2_SKIP1_RANGE_END) { 7531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ++pos; 7541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C2_SKIP2_RANGE_START 7551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C2_SKIP2_RANGE_END) { 7561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 2; 7571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C2_SKIP3_RANGE_START 7581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C2_SKIP3_RANGE_END) { 7591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 3; 7601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseC3(byte[] data, int pos) { 7651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C3 code group, see CEA-708B Section 7.4.8. 7661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Extended Control Code Set 2 7671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // C3 Table : No commands as of CEA-708B. A decoder must skip. 7681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand >= Cea708Data.CODE_C3_SKIP4_RANGE_START 7691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C3_SKIP4_RANGE_END) { 7701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 4; 7711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } else if (mCommand >= Cea708Data.CODE_C3_SKIP5_RANGE_START 7721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko && mCommand <= Cea708Data.CODE_C3_SKIP5_RANGE_END) { 7731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko pos += 5; 7741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseG2(byte[] data, int pos) { 7791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C3 code group, see CEA-708B Section 7.4.5. 7801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Extended Control Code Set 1(G2 Table) 7811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko switch (mCommand) { 7821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_G2_TSP: 7831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : TSP is the Transparent space 7841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 7851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_G2_NBTSP: 7861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : NBTSP is Non-Breaking Transparent Space. 7871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 7881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko case Cea708Data.CODE_G2_BLK: 7891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : BLK indicates a solid block which fills the entire character block 7901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : with a solid foreground color. 7911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 7921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko default: 7931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko break; 7941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 7961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 7971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 7981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private int parseG3(byte[] data, int pos) { 7991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // For the details of C3 code group, see CEA-708B Section 7.4.6. 8001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Future characters and icons(G3 Table) 8011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mCommand == Cea708Data.CODE_G3_CC) { 8021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // TODO : [CC] icon with square corners 8031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 8041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 8051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Do nothing 8061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return pos; 8071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 8081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 809