eia608.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /**********************************************************************************************/
  2. /* The MIT License */
  3. /* */
  4. /* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved. */
  5. /* */
  6. /* Permission is hereby granted, free of charge, to any person obtaining a copy */
  7. /* of this software and associated documentation files (the "Software"), to deal */
  8. /* in the Software without restriction, including without limitation the rights */
  9. /* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
  10. /* copies of the Software, and to permit persons to whom the Software is */
  11. /* furnished to do so, subject to the following conditions: */
  12. /* */
  13. /* The above copyright notice and this permission notice shall be included in */
  14. /* all copies or substantial portions of the Software. */
  15. /* */
  16. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
  17. /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
  18. /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
  19. /* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
  20. /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
  21. /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN */
  22. /* THE SOFTWARE. */
  23. /**********************************************************************************************/
  24. #include "eia608.h"
  25. #include <stdio.h>
  26. #include <string.h>
  27. ////////////////////////////////////////////////////////////////////////////////
  28. int eia608_row_map[] = { 10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9 };
  29. int eia608_reverse_row_map[] = { 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1 };
  30. const char* eia608_style_map[] = {
  31. "white",
  32. "green",
  33. "blue",
  34. "cyan",
  35. "red",
  36. "yellow",
  37. "magenta",
  38. "italics",
  39. };
  40. static inline uint16_t eia608_row_pramble(int row, int chan, int x, int underline)
  41. {
  42. row = eia608_reverse_row_map[row & 0x0F];
  43. return eia608_parity(0x1040 | (chan ? 0x0800 : 0x0000) | ((row << 7) & 0x0700) | ((row << 5) & 0x0020)) | ((x << 1) & 0x001E) | (underline ? 0x0001 : 0x0000);
  44. }
  45. uint16_t eia608_row_column_pramble(int row, int col, int chan, int underline) { return eia608_row_pramble(row, chan, 0x10 | (col / 4), underline); }
  46. uint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline) { return eia608_row_pramble(row, chan, style, underline); }
  47. uint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline) { return eia608_parity(0x1120 | ((chan << 11) & 0x0800) | ((style << 1) & 0x000E) | (underline & 0x0001)); }
  48. int eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline)
  49. {
  50. (*row) = eia608_row_map[((0x0700 & cc_data) >> 7) | ((0x0020 & cc_data) >> 5)];
  51. (*chan) = !!(0x0800 & cc_data);
  52. (*underline) = 0x0001 & cc_data;
  53. if (0x0010 & cc_data) {
  54. (*style) = eia608_style_white;
  55. (*col) = 4 * ((0x000E & cc_data) >> 1);
  56. } else {
  57. (*style) = (0x000E & cc_data) >> 1;
  58. (*col) = 0;
  59. }
  60. return 1;
  61. }
  62. int eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline)
  63. {
  64. (*chan) = !!(0x0800 & cc_data);
  65. if (0x1120 == (0x7770 & cc_data)) {
  66. (*style) = (0x000E & cc_data) >> 1;
  67. (*underline) = 0x0001 & cc_data;
  68. }
  69. return 1;
  70. }
  71. ////////////////////////////////////////////////////////////////////////////////
  72. // control command
  73. eia608_control_t eia608_parse_control(uint16_t cc_data, int* cc)
  74. {
  75. if (0x0200 & cc_data) {
  76. (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00);
  77. return (eia608_control_t)(0x177F & cc_data);
  78. } else {
  79. (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00) | (cc_data & 0x0100 ? 0x02 : 0x00);
  80. return (eia608_control_t)(0x167F & cc_data);
  81. }
  82. }
  83. uint16_t eia608_control_command(eia608_control_t cmd, int cc)
  84. {
  85. uint16_t c = (cc & 0x01) ? 0x0800 : 0x0000;
  86. uint16_t f = (cc & 0x02) ? 0x0100 : 0x0000;
  87. if (eia608_tab_offset_0 == (eia608_control_t)(cmd & 0xFFC0)) {
  88. return (eia608_control_t)eia608_parity(cmd | c);
  89. } else {
  90. return (eia608_control_t)eia608_parity(cmd | c | f);
  91. }
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////
  94. // text
  95. static const char* utf8_from_index(int idx) { return (0 <= idx && EIA608_CHAR_COUNT > idx) ? eia608_char_map[idx] : ""; }
  96. static int eia608_to_index(uint16_t cc_data, int* chan, int* c1, int* c2)
  97. {
  98. (*c1) = (*c2) = -1;
  99. (*chan) = 0;
  100. cc_data &= 0x7F7F; // strip off parity bits
  101. // Handle Basic NA BEFORE we strip the channel bit
  102. if (eia608_is_basicna(cc_data)) {
  103. // we got first char, yes. But what about second char?
  104. (*c1) = (cc_data >> 8) - 0x20;
  105. cc_data &= 0x00FF;
  106. if (0x0020 <= cc_data && 0x0080 > cc_data) {
  107. (*c2) = cc_data - 0x20;
  108. return 2;
  109. }
  110. return 1;
  111. }
  112. // Check then strip second channel toggle
  113. (*chan) = cc_data & 0x0800;
  114. cc_data = cc_data & 0xF7FF;
  115. if (eia608_is_specialna(cc_data)) {
  116. // Special North American character
  117. (*c1) = cc_data - 0x1130 + 0x60;
  118. return 1;
  119. }
  120. if (0x1220 <= cc_data && 0x1240 > cc_data) {
  121. // Extended Western European character set, Spanish/Miscellaneous/French
  122. (*c1) = cc_data - 0x1220 + 0x70;
  123. return 1;
  124. }
  125. if (0x1320 <= cc_data && 0x1340 > cc_data) {
  126. // Extended Western European character set, Portuguese/German/Danish
  127. (*c1) = cc_data - 0x1320 + 0x90;
  128. return 1;
  129. }
  130. return 0;
  131. }
  132. int eia608_to_utf8(uint16_t c, int* chan, char* str1, char* str2)
  133. {
  134. int c1, c2;
  135. int size = (int)eia608_to_index(c, chan, &c1, &c2);
  136. utf8_char_copy(str1, utf8_from_index(c1));
  137. utf8_char_copy(str2, utf8_from_index(c2));
  138. return size;
  139. }
  140. uint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2)
  141. {
  142. if (!eia608_is_basicna(bna1) || !eia608_is_basicna(bna2)) {
  143. return 0;
  144. }
  145. return eia608_parity((0xFF00 & bna1) | ((0xFF00 & bna2) >> 8));
  146. }
  147. // prototype for re2c generated function
  148. uint16_t _eia608_from_utf8(const utf8_char_t* s);
  149. uint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan)
  150. {
  151. uint16_t cc_data = _eia608_from_utf8(c);
  152. if (0 == cc_data) {
  153. return cc_data;
  154. }
  155. if (chan && !eia608_is_basicna(cc_data)) {
  156. cc_data |= 0x0800;
  157. }
  158. return eia608_parity(cc_data);
  159. }
  160. uint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2)
  161. {
  162. uint16_t cc1 = _eia608_from_utf8(c1);
  163. uint16_t cc2 = _eia608_from_utf8(c2);
  164. return eia608_from_basicna(cc1, cc2);
  165. }
  166. ////////////////////////////////////////////////////////////////////////////////
  167. void eia608_dump(uint16_t cc_data)
  168. {
  169. eia608_style_t style;
  170. const char* text = 0;
  171. char char1[5], char2[5];
  172. char1[0] = char2[0] = 0;
  173. int row, col, chan, underline;
  174. if (!eia608_parity_varify(cc_data)) {
  175. text = "parity failed";
  176. } else if (0 == eia608_parity_strip(cc_data)) {
  177. text = "pad";
  178. } else if (eia608_is_basicna(cc_data)) {
  179. text = "basicna";
  180. eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);
  181. } else if (eia608_is_specialna(cc_data)) {
  182. text = "specialna";
  183. eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);
  184. } else if (eia608_is_westeu(cc_data)) {
  185. text = "westeu";
  186. eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);
  187. } else if (eia608_is_xds(cc_data)) {
  188. text = "xds";
  189. } else if (eia608_is_midrowchange(cc_data)) {
  190. text = "midrowchange";
  191. } else if (eia608_is_norpak(cc_data)) {
  192. text = "norpak";
  193. } else if (eia608_is_preamble(cc_data)) {
  194. text = "preamble";
  195. eia608_parse_preamble(cc_data, &row, &col, &style, &chan, &underline);
  196. fprintf(stderr, "preamble %d %d %d %d %d\n", row, col, style, chan, underline);
  197. } else if (eia608_is_control(cc_data)) {
  198. switch (eia608_parse_control(cc_data, &chan)) {
  199. default:
  200. text = "unknown_control";
  201. break;
  202. case eia608_tab_offset_0:
  203. text = "eia608_tab_offset_0";
  204. break;
  205. case eia608_tab_offset_1:
  206. text = "eia608_tab_offset_1";
  207. break;
  208. case eia608_tab_offset_2:
  209. text = "eia608_tab_offset_2";
  210. break;
  211. case eia608_tab_offset_3:
  212. text = "eia608_tab_offset_3";
  213. break;
  214. case eia608_control_resume_caption_loading:
  215. text = "eia608_control_resume_caption_loading";
  216. break;
  217. case eia608_control_backspace:
  218. text = "eia608_control_backspace";
  219. break;
  220. case eia608_control_alarm_off:
  221. text = "eia608_control_alarm_off";
  222. break;
  223. case eia608_control_alarm_on:
  224. text = "eia608_control_alarm_on";
  225. break;
  226. case eia608_control_delete_to_end_of_row:
  227. text = "eia608_control_delete_to_end_of_row";
  228. break;
  229. case eia608_control_roll_up_2:
  230. text = "eia608_control_roll_up_2";
  231. break;
  232. case eia608_control_roll_up_3:
  233. text = "eia608_control_roll_up_3";
  234. break;
  235. case eia608_control_roll_up_4:
  236. text = "eia608_control_roll_up_4";
  237. break;
  238. case eia608_control_resume_direct_captioning:
  239. text = "eia608_control_resume_direct_captioning";
  240. break;
  241. case eia608_control_text_restart:
  242. text = "eia608_control_text_restart";
  243. break;
  244. case eia608_control_text_resume_text_display:
  245. text = "eia608_control_text_resume_text_display";
  246. break;
  247. case eia608_control_erase_display_memory:
  248. text = "eia608_control_erase_display_memory";
  249. break;
  250. case eia608_control_carriage_return:
  251. text = "eia608_control_carriage_return";
  252. break;
  253. case eia608_control_erase_non_displayed_memory:
  254. text = "eia608_control_erase_non_displayed_memory";
  255. break;
  256. case eia608_control_end_of_caption:
  257. text = "eia608_control_end_of_caption";
  258. break;
  259. }
  260. } else {
  261. text = "unhandled";
  262. }
  263. fprintf(stderr, "cc %04X (%04X) '%s' '%s' (%s)\n", cc_data, eia608_parity_strip(cc_data), char1, char2, text);
  264. }