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