Source: lib/cea/cea608_memory.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.cea.Cea608Memory');
  7. goog.require('shaka.cea.CeaUtils');
  8. goog.require('shaka.text.Cue');
  9. goog.requireType('shaka.cea.ICaptionDecoder');
  10. /**
  11. * CEA-608 captions memory/buffer.
  12. */
  13. shaka.cea.Cea608Memory = class {
  14. /**
  15. * @param {number} fieldNum Field number.
  16. * @param {number} channelNum Channel number.
  17. */
  18. constructor(fieldNum, channelNum) {
  19. /**
  20. * Buffer for storing decoded characters.
  21. * @private @const {!Array<!Array<!shaka.cea.CeaUtils.StyledChar>>}
  22. */
  23. this.rows_ = [];
  24. /**
  25. * Current row.
  26. * @private {number}
  27. */
  28. this.row_ = 1;
  29. /**
  30. * Number of rows in the scroll window. Used for rollup mode.
  31. * @private {number}
  32. */
  33. this.scrollRows_ = 0;
  34. /**
  35. * Field number.
  36. * @private {number}
  37. */
  38. this.fieldNum_ = fieldNum;
  39. /**
  40. * Channel number.
  41. * @private {number}
  42. */
  43. this.channelNum_ = channelNum;
  44. /**
  45. * @private {boolean}
  46. */
  47. this.underline_ = false;
  48. /**
  49. * @private {boolean}
  50. */
  51. this.italics_ = false;
  52. /**
  53. * @private {string}
  54. */
  55. this.textColor_ = shaka.cea.CeaUtils.DEFAULT_TXT_COLOR;
  56. /**
  57. * @private {string}
  58. */
  59. this.backgroundColor_ = shaka.cea.CeaUtils.DEFAULT_BG_COLOR;
  60. this.reset();
  61. }
  62. /**
  63. * Emits a closed caption based on the state of the buffer.
  64. * @param {number} startTime Start time of the cue.
  65. * @param {number} endTime End time of the cue.
  66. * @return {?shaka.cea.ICaptionDecoder.ClosedCaption}
  67. */
  68. forceEmit(startTime, endTime) {
  69. const stream = `CC${(this.fieldNum_<< 1) | this.channelNum_ +1}`;
  70. const topLevelCue = new shaka.text.Cue(
  71. startTime, endTime, /* payload= */ '');
  72. return shaka.cea.CeaUtils.getParsedCaption(
  73. topLevelCue, stream, this.rows_, startTime, endTime);
  74. }
  75. /**
  76. * Resets the memory buffer.
  77. */
  78. reset() {
  79. this.resetAllRows();
  80. this.row_ = 1;
  81. }
  82. /**
  83. * @return {number}
  84. */
  85. getRow() {
  86. return this.row_;
  87. }
  88. /**
  89. * @param {number} row
  90. */
  91. setRow(row) {
  92. this.row_ = row;
  93. }
  94. /**
  95. * @return {number}
  96. */
  97. getScrollSize() {
  98. return this.scrollRows_;
  99. }
  100. /**
  101. * @param {number} scrollRows
  102. */
  103. setScrollSize(scrollRows) {
  104. this.scrollRows_ = scrollRows;
  105. }
  106. /**
  107. * Adds a character to the buffer.
  108. * @param {!shaka.cea.Cea608Memory.CharSet} set Character set.
  109. * @param {number} b CC byte to add.
  110. */
  111. addChar(set, b) {
  112. // Valid chars are in the range [0x20, 0x7f]
  113. if (b < 0x20 || b > 0x7f) {
  114. return;
  115. }
  116. let char = '';
  117. switch (set) {
  118. case shaka.cea.Cea608Memory.CharSet.BASIC_NORTH_AMERICAN:
  119. if (shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars.has(b)) {
  120. char =
  121. shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars.get(b);
  122. } else {
  123. // Regular ASCII
  124. char = String.fromCharCode(b);
  125. }
  126. break;
  127. case shaka.cea.Cea608Memory.CharSet.SPECIAL_NORTH_AMERICAN:
  128. char =
  129. shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars.get(b);
  130. break;
  131. case shaka.cea.Cea608Memory.CharSet.SPANISH_FRENCH:
  132. // Extended charset does a BS over preceding char, 6.4.2 EIA-608-B.
  133. this.eraseChar();
  134. char =
  135. shaka.cea.Cea608Memory.CharSet.ExtendedSpanishFrench.get(b);
  136. break;
  137. case shaka.cea.Cea608Memory.CharSet.PORTUGUESE_GERMAN:
  138. this.eraseChar();
  139. char =
  140. shaka.cea.Cea608Memory.CharSet.ExtendedPortugueseGerman.get(b);
  141. break;
  142. }
  143. if (char) {
  144. const styledChar = new shaka.cea.CeaUtils.StyledChar(
  145. char, this.underline_, this.italics_,
  146. this.backgroundColor_, this.textColor_);
  147. this.rows_[this.row_].push(styledChar);
  148. }
  149. }
  150. /**
  151. * Erases a character from the buffer.
  152. */
  153. eraseChar() {
  154. this.rows_[this.row_].pop();
  155. }
  156. /**
  157. * Moves rows of characters.
  158. * @param {number} dst Destination row index.
  159. * @param {number} src Source row index.
  160. * @param {number} count Count of rows to move.
  161. */
  162. moveRows(dst, src, count) {
  163. if (src < 0 || dst < 0) {
  164. return;
  165. }
  166. if (dst >= src) {
  167. for (let i = count-1; i >= 0; i--) {
  168. this.rows_[dst + i] = this.rows_[src + i].map((e) => e);
  169. }
  170. } else {
  171. for (let i = 0; i < count; i++) {
  172. this.rows_[dst + i] = this.rows_[src + i].map((e) => e);
  173. }
  174. }
  175. }
  176. /**
  177. * Resets rows of characters.
  178. * @param {number} idx Starting index.
  179. * @param {number} count Count of rows to reset.
  180. */
  181. resetRows(idx, count) {
  182. for (let i = 0; i <= count; i++) {
  183. this.rows_[idx + i] = [];
  184. }
  185. }
  186. /**
  187. * Resets the entire memory buffer.
  188. */
  189. resetAllRows() {
  190. this.resetRows(0, shaka.cea.Cea608Memory.CC_ROWS);
  191. }
  192. /**
  193. * Erases entire memory buffer.
  194. * Doesn't change scroll state or number of rows.
  195. */
  196. eraseBuffer() {
  197. this.row_ = (this.scrollRows_ > 0) ? this.scrollRows_ : 0;
  198. this.resetAllRows();
  199. }
  200. /**
  201. * @param {boolean} underline
  202. */
  203. setUnderline(underline) {
  204. this.underline_ = underline;
  205. }
  206. /**
  207. * @param {boolean} italics
  208. */
  209. setItalics(italics) {
  210. this.italics_ = italics;
  211. }
  212. /**
  213. * @param {string} color
  214. */
  215. setTextColor(color) {
  216. this.textColor_ = color;
  217. }
  218. /**
  219. * @param {string} color
  220. */
  221. setBackgroundColor(color) {
  222. this.backgroundColor_ = color;
  223. }
  224. };
  225. /**
  226. * Maximum number of rows in the buffer.
  227. * @const {number}
  228. */
  229. shaka.cea.Cea608Memory.CC_ROWS = 15;
  230. /**
  231. * Characters sets.
  232. * @const @enum {number}
  233. */
  234. shaka.cea.Cea608Memory.CharSet = {
  235. BASIC_NORTH_AMERICAN: 0,
  236. SPECIAL_NORTH_AMERICAN: 1,
  237. SPANISH_FRENCH: 2,
  238. PORTUGUESE_GERMAN: 3,
  239. };
  240. /**
  241. * Basic North American char set deviates from ASCII with these exceptions.
  242. * @private @const {!Map<number, string>}
  243. */
  244. shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars = new Map([
  245. [0x27, '’'], [0x2a, 'á'], [0x5c, 'é'], [0x5c, 'é'], [0x5e, 'í'], [0x5f, 'ó'],
  246. [0x60, 'ú'], [0x7b, 'ç'], [0x7c, '÷'], [0x7d, 'Ñ'], [0x7e, 'ñ'], [0x7f, '█'],
  247. ]);
  248. /**
  249. * Special North American char set.
  250. * Note: Transparent Space is currently implemented as a regular space.
  251. * @private @const {!Map<number, string>}
  252. */
  253. shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars = new Map([
  254. [0x30, '®'], [0x31, '°'], [0x32, '½'], [0x33, '¿'], [0x34, '™'], [0x35, '¢'],
  255. [0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, '⠀'], [0x3a, 'è'], [0x3b, 'â'],
  256. [0x3c, 'ê'], [0x3d, 'î'], [0x3e, 'ô'], [0x3f, 'û'],
  257. ]);
  258. /**
  259. * Extended Spanish/Misc/French char set.
  260. * @private @const {!Map<number, string>}
  261. */
  262. shaka.cea.Cea608Memory.CharSet.ExtendedSpanishFrench = new Map([
  263. [0x20, 'Á'], [0x21, 'É'], [0x22, 'Ó'], [0x23, 'Ú'], [0x24, 'Ü'], [0x25, 'ü'],
  264. [0x26, '‘'], [0x27, '¡'], [0x28, '*'], [0x29, '\''], [0x2a, '─'], [0x2b, '©'],
  265. [0x2c, '℠'], [0x2d, '·'], [0x2e, '“'], [0x2f, '”'], [0x30, 'À'], [0x31, 'Â'],
  266. [0x32, 'Ç'], [0x33, 'È'], [0x34, 'Ê'], [0x35, 'Ë'], [0x36, 'ë'], [0x37, 'Î'],
  267. [0x38, 'Ï'], [0x39, 'ï'], [0x3a, 'Ô'], [0x3b, 'Ù'], [0x3c, 'ù'], [0x3d, 'Û'],
  268. [0x3e, '«'], [0x3f, '»'],
  269. ]);
  270. /**
  271. * Extended Portuguese/German/Danish char set.
  272. * @private @const {!Map<number, string>}
  273. */
  274. shaka.cea.Cea608Memory.CharSet.ExtendedPortugueseGerman = new Map([
  275. [0x20, 'Ã'], [0x21, 'ã'], [0x22, 'Í'], [0x23, 'Ì'], [0x24, 'ì'], [0x25, 'Ò'],
  276. [0x26, 'ò'], [0x27, 'Õ'], [0x28, 'õ'], [0x29, '{'], [0x2a, '}'], [0x2b, '\\'],
  277. [0x2c, '^'], [0x2d, '_'], [0x2e, '|'], [0x2f, '~'], [0x30, 'Ä'], [0x31, 'ä'],
  278. [0x32, 'Ö'], [0x33, 'ö'], [0x34, 'ß'], [0x35, '¥'], [0x36, '¤'], [0x37, '│'],
  279. [0x38, 'Å'], [0x39, 'å'], [0x3a, 'Ø'], [0x3b, 'ø'], [0x3c, '┌'], [0x3d, '┐'],
  280. [0x3e, '└'], [0x3f, '┘'],
  281. ]);