mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-16 16:16:40 +03:00
44cb8a2ed8
Due to issue around channel calculation, shaka was never using CC4 track and data from there were landing in CC2 track.
334 lines
8.3 KiB
JavaScript
334 lines
8.3 KiB
JavaScript
/*! @license
|
||
* Shaka Player
|
||
* Copyright 2016 Google LLC
|
||
* SPDX-License-Identifier: Apache-2.0
|
||
*/
|
||
|
||
goog.provide('shaka.cea.Cea608Memory');
|
||
|
||
goog.require('shaka.cea.CeaUtils');
|
||
goog.require('shaka.text.Cue');
|
||
|
||
|
||
/**
|
||
* CEA-608 captions memory/buffer.
|
||
*/
|
||
shaka.cea.Cea608Memory = class {
|
||
/**
|
||
* @param {number} fieldNum Field number.
|
||
* @param {number} channelNum Channel number.
|
||
*/
|
||
constructor(fieldNum, channelNum) {
|
||
/**
|
||
* Buffer for storing decoded characters.
|
||
* @private @const {!Array<!Array<!shaka.cea.CeaUtils.StyledChar>>}
|
||
*/
|
||
this.rows_ = [];
|
||
|
||
/**
|
||
* Current row.
|
||
* @private {number}
|
||
*/
|
||
this.row_ = 1;
|
||
|
||
/**
|
||
* Number of rows in the scroll window. Used for rollup mode.
|
||
* @private {number}
|
||
*/
|
||
this.scrollRows_ = 0;
|
||
|
||
/**
|
||
* Field number.
|
||
* @private {number}
|
||
*/
|
||
this.fieldNum_ = fieldNum;
|
||
|
||
/**
|
||
* Channel number.
|
||
* @private {number}
|
||
*/
|
||
this.channelNum_ = channelNum;
|
||
|
||
/**
|
||
* @private {boolean}
|
||
*/
|
||
this.underline_ = false;
|
||
|
||
/**
|
||
* @private {boolean}
|
||
*/
|
||
this.italics_ = false;
|
||
|
||
/**
|
||
* @private {string}
|
||
*/
|
||
this.textColor_ = shaka.cea.CeaUtils.DEFAULT_TXT_COLOR;
|
||
|
||
/**
|
||
* @private {string}
|
||
*/
|
||
this.backgroundColor_ = shaka.cea.CeaUtils.DEFAULT_BG_COLOR;
|
||
|
||
this.reset();
|
||
}
|
||
|
||
/**
|
||
* Emits a closed caption based on the state of the buffer.
|
||
* @param {number} startTime Start time of the cue.
|
||
* @param {number} endTime End time of the cue.
|
||
* @return {?shaka.extern.ICaptionDecoder.ClosedCaption}
|
||
*/
|
||
forceEmit(startTime, endTime) {
|
||
const stream = `CC${((this.fieldNum_<< 1) | this.channelNum_) + 1}`;
|
||
const topLevelCue = new shaka.text.Cue(
|
||
startTime, endTime, /* payload= */ '');
|
||
topLevelCue.lineInterpretation =
|
||
shaka.text.Cue.lineInterpretation.PERCENTAGE;
|
||
const line = shaka.cea.Cea608Memory.ROW_TO_LINE_CONVERSION_.get(this.row_);
|
||
if (line) {
|
||
topLevelCue.line = line;
|
||
}
|
||
return shaka.cea.CeaUtils.getParsedCaption(
|
||
topLevelCue, stream, this.rows_, startTime, endTime);
|
||
}
|
||
|
||
/**
|
||
* Resets the memory buffer.
|
||
*/
|
||
reset() {
|
||
this.resetAllRows();
|
||
this.row_ = 1;
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
getRow() {
|
||
return this.row_;
|
||
}
|
||
|
||
/**
|
||
* @param {number} row
|
||
*/
|
||
setRow(row) {
|
||
this.row_ = row;
|
||
}
|
||
|
||
/**
|
||
* @return {number}
|
||
*/
|
||
getScrollSize() {
|
||
return this.scrollRows_;
|
||
}
|
||
|
||
/**
|
||
* @param {number} scrollRows
|
||
*/
|
||
setScrollSize(scrollRows) {
|
||
this.scrollRows_ = scrollRows;
|
||
}
|
||
|
||
/**
|
||
* Adds a character to the buffer.
|
||
* @param {!shaka.cea.Cea608Memory.CharSet} set Character set.
|
||
* @param {number} b CC byte to add.
|
||
*/
|
||
addChar(set, b) {
|
||
// Valid chars are in the range [0x20, 0x7f]
|
||
if (b < 0x20 || b > 0x7f) {
|
||
return;
|
||
}
|
||
|
||
let char = '';
|
||
switch (set) {
|
||
case shaka.cea.Cea608Memory.CharSet.BASIC_NORTH_AMERICAN:
|
||
if (shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars.has(b)) {
|
||
char =
|
||
shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars.get(b);
|
||
} else {
|
||
// Regular ASCII
|
||
char = String.fromCharCode(b);
|
||
}
|
||
break;
|
||
case shaka.cea.Cea608Memory.CharSet.SPECIAL_NORTH_AMERICAN:
|
||
char =
|
||
shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars.get(b);
|
||
break;
|
||
case shaka.cea.Cea608Memory.CharSet.SPANISH_FRENCH:
|
||
// Extended charset does a BS over preceding char, 6.4.2 EIA-608-B.
|
||
this.eraseChar();
|
||
char =
|
||
shaka.cea.Cea608Memory.CharSet.ExtendedSpanishFrench.get(b);
|
||
break;
|
||
case shaka.cea.Cea608Memory.CharSet.PORTUGUESE_GERMAN:
|
||
this.eraseChar();
|
||
char =
|
||
shaka.cea.Cea608Memory.CharSet.ExtendedPortugueseGerman.get(b);
|
||
break;
|
||
}
|
||
|
||
if (char) {
|
||
const styledChar = new shaka.cea.CeaUtils.StyledChar(
|
||
char, this.underline_, this.italics_,
|
||
this.backgroundColor_, this.textColor_);
|
||
this.rows_[this.row_].push(styledChar);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Erases a character from the buffer.
|
||
*/
|
||
eraseChar() {
|
||
this.rows_[this.row_].pop();
|
||
}
|
||
|
||
/**
|
||
* Moves rows of characters.
|
||
* @param {number} dst Destination row index.
|
||
* @param {number} src Source row index.
|
||
* @param {number} count Count of rows to move.
|
||
*/
|
||
moveRows(dst, src, count) {
|
||
if (src < 0 || dst < 0) {
|
||
return;
|
||
}
|
||
|
||
if (dst >= src) {
|
||
for (let i = count-1; i >= 0; i--) {
|
||
this.rows_[dst + i] = this.rows_[src + i].map((e) => e);
|
||
}
|
||
} else {
|
||
for (let i = 0; i < count; i++) {
|
||
this.rows_[dst + i] = this.rows_[src + i].map((e) => e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resets rows of characters.
|
||
* @param {number} idx Starting index.
|
||
* @param {number} count Count of rows to reset.
|
||
*/
|
||
resetRows(idx, count) {
|
||
for (let i = 0; i <= count; i++) {
|
||
this.rows_[idx + i] = [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Resets the entire memory buffer.
|
||
*/
|
||
resetAllRows() {
|
||
this.resetRows(0, shaka.cea.Cea608Memory.CC_ROWS);
|
||
}
|
||
|
||
/**
|
||
* Erases entire memory buffer.
|
||
* Doesn't change scroll state or number of rows.
|
||
*/
|
||
eraseBuffer() {
|
||
this.row_ = (this.scrollRows_ > 0) ? this.scrollRows_ : 0;
|
||
this.resetAllRows();
|
||
}
|
||
|
||
/**
|
||
* @param {boolean} underline
|
||
*/
|
||
setUnderline(underline) {
|
||
this.underline_ = underline;
|
||
}
|
||
|
||
/**
|
||
* @param {boolean} italics
|
||
*/
|
||
setItalics(italics) {
|
||
this.italics_ = italics;
|
||
}
|
||
|
||
/**
|
||
* @param {string} color
|
||
*/
|
||
setTextColor(color) {
|
||
this.textColor_ = color;
|
||
}
|
||
|
||
/**
|
||
* @param {string} color
|
||
*/
|
||
setBackgroundColor(color) {
|
||
this.backgroundColor_ = color;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Maximum number of rows in the buffer.
|
||
* @const {number}
|
||
*/
|
||
shaka.cea.Cea608Memory.CC_ROWS = 15;
|
||
|
||
/**
|
||
* Characters sets.
|
||
* @const @enum {number}
|
||
*/
|
||
shaka.cea.Cea608Memory.CharSet = {
|
||
BASIC_NORTH_AMERICAN: 0,
|
||
SPECIAL_NORTH_AMERICAN: 1,
|
||
SPANISH_FRENCH: 2,
|
||
PORTUGUESE_GERMAN: 3,
|
||
};
|
||
|
||
/**
|
||
* Basic North American char set deviates from ASCII with these exceptions.
|
||
* @private @const {!Map<number, string>}
|
||
*/
|
||
shaka.cea.Cea608Memory.CharSet.BasicNorthAmericanChars = new Map([
|
||
[0x27, '’'], [0x2a, 'á'], [0x5c, 'é'], [0x5c, 'é'], [0x5e, 'í'], [0x5f, 'ó'],
|
||
[0x60, 'ú'], [0x7b, 'ç'], [0x7c, '÷'], [0x7d, 'Ñ'], [0x7e, 'ñ'], [0x7f, '█'],
|
||
]);
|
||
|
||
/**
|
||
* Special North American char set.
|
||
* Note: Transparent Space is currently implemented as a regular space.
|
||
* @private @const {!Map<number, string>}
|
||
*/
|
||
shaka.cea.Cea608Memory.CharSet.SpecialNorthAmericanChars = new Map([
|
||
[0x30, '®'], [0x31, '°'], [0x32, '½'], [0x33, '¿'], [0x34, '™'], [0x35, '¢'],
|
||
[0x36, '£'], [0x37, '♪'], [0x38, 'à'], [0x39, '⠀'], [0x3a, 'è'], [0x3b, 'â'],
|
||
[0x3c, 'ê'], [0x3d, 'î'], [0x3e, 'ô'], [0x3f, 'û'],
|
||
]);
|
||
|
||
/**
|
||
* Extended Spanish/Misc/French char set.
|
||
* @private @const {!Map<number, string>}
|
||
*/
|
||
shaka.cea.Cea608Memory.CharSet.ExtendedSpanishFrench = new Map([
|
||
[0x20, 'Á'], [0x21, 'É'], [0x22, 'Ó'], [0x23, 'Ú'], [0x24, 'Ü'], [0x25, 'ü'],
|
||
[0x26, '‘'], [0x27, '¡'], [0x28, '*'], [0x29, '\''], [0x2a, '─'], [0x2b, '©'],
|
||
[0x2c, '℠'], [0x2d, '·'], [0x2e, '“'], [0x2f, '”'], [0x30, 'À'], [0x31, 'Â'],
|
||
[0x32, 'Ç'], [0x33, 'È'], [0x34, 'Ê'], [0x35, 'Ë'], [0x36, 'ë'], [0x37, 'Î'],
|
||
[0x38, 'Ï'], [0x39, 'ï'], [0x3a, 'Ô'], [0x3b, 'Ù'], [0x3c, 'ù'], [0x3d, 'Û'],
|
||
[0x3e, '«'], [0x3f, '»'],
|
||
]);
|
||
|
||
/**
|
||
* Extended Portuguese/German/Danish char set.
|
||
* @private @const {!Map<number, string>}
|
||
*/
|
||
shaka.cea.Cea608Memory.CharSet.ExtendedPortugueseGerman = new Map([
|
||
[0x20, 'Ã'], [0x21, 'ã'], [0x22, 'Í'], [0x23, 'Ì'], [0x24, 'ì'], [0x25, 'Ò'],
|
||
[0x26, 'ò'], [0x27, 'Õ'], [0x28, 'õ'], [0x29, '{'], [0x2a, '}'], [0x2b, '\\'],
|
||
[0x2c, '^'], [0x2d, '_'], [0x2e, '|'], [0x2f, '~'], [0x30, 'Ä'], [0x31, 'ä'],
|
||
[0x32, 'Ö'], [0x33, 'ö'], [0x34, 'ß'], [0x35, '¥'], [0x36, '¤'], [0x37, '│'],
|
||
[0x38, 'Å'], [0x39, 'å'], [0x3a, 'Ø'], [0x3b, 'ø'], [0x3c, '┌'], [0x3d, '┐'],
|
||
[0x3e, '└'], [0x3f, '┘'],
|
||
]);
|
||
|
||
/**
|
||
* @private @const {!Map<number, number>}
|
||
*/
|
||
shaka.cea.Cea608Memory.ROW_TO_LINE_CONVERSION_ = new Map([
|
||
[1, 10], [2, 15.33], [3, 20.66], [4, 26], [5, 31.33], [6, 36.66], [7, 42],
|
||
[8, 47.33], [9, 52.66], [10, 58], [11, 63.33], [12, 68.66], [13, 74],
|
||
[14, 79.33], [15, 84.66],
|
||
]);
|