Home Reference Source

src/utils/cues.ts

  1. import { fixLineBreaks } from './vttparser';
  2. import type { CaptionScreen, Row } from './cea-608-parser';
  3. import { generateCueId } from './webvtt-parser';
  4. import { addCueToTrack } from './texttrack-utils';
  5.  
  6. const WHITESPACE_CHAR = /\s/;
  7.  
  8. export interface CuesInterface {
  9. newCue(
  10. track: TextTrack | null,
  11. startTime: number,
  12. endTime: number,
  13. captionScreen: CaptionScreen
  14. ): VTTCue[];
  15. }
  16.  
  17. const Cues: CuesInterface = {
  18. newCue(
  19. track: TextTrack | null,
  20. startTime: number,
  21. endTime: number,
  22. captionScreen: CaptionScreen
  23. ): VTTCue[] {
  24. const result: VTTCue[] = [];
  25. let row: Row;
  26. // the type data states this is VTTCue, but it can potentially be a TextTrackCue on old browsers
  27. let cue: VTTCue;
  28. let indenting: boolean;
  29. let indent: number;
  30. let text: string;
  31. const Cue = (self.VTTCue || self.TextTrackCue) as any;
  32.  
  33. for (let r = 0; r < captionScreen.rows.length; r++) {
  34. row = captionScreen.rows[r];
  35. indenting = true;
  36. indent = 0;
  37. text = '';
  38.  
  39. if (!row.isEmpty()) {
  40. for (let c = 0; c < row.chars.length; c++) {
  41. if (WHITESPACE_CHAR.test(row.chars[c].uchar) && indenting) {
  42. indent++;
  43. } else {
  44. text += row.chars[c].uchar;
  45. indenting = false;
  46. }
  47. }
  48. // To be used for cleaning-up orphaned roll-up captions
  49. row.cueStartTime = startTime;
  50.  
  51. // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
  52. if (startTime === endTime) {
  53. endTime += 0.0001;
  54. }
  55.  
  56. if (indent >= 16) {
  57. indent--;
  58. } else {
  59. indent++;
  60. }
  61.  
  62. const cueText = fixLineBreaks(text.trim());
  63. const id = generateCueId(startTime, endTime, cueText);
  64.  
  65. // If this cue already exists in the track do not push it
  66. if (!track || !track.cues || !track.cues.getCueById(id)) {
  67. cue = new Cue(startTime, endTime, cueText);
  68. cue.id = id;
  69. cue.line = r + 1;
  70. cue.align = 'left';
  71. // Clamp the position between 10 and 80 percent (CEA-608 PAC indent code)
  72. // https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#positioning-in-cea-608
  73. // Firefox throws an exception and captions break with out of bounds 0-100 values
  74. cue.position = 10 + Math.min(80, Math.floor((indent * 8) / 32) * 10);
  75. result.push(cue);
  76. }
  77. }
  78. }
  79. if (track && result.length) {
  80. // Sort bottom cues in reverse order so that they render in line order when overlapping in Chrome
  81. result.sort((cueA, cueB) => {
  82. if (cueA.line === 'auto' || cueB.line === 'auto') {
  83. return 0;
  84. }
  85. if (cueA.line > 8 && cueB.line > 8) {
  86. return cueB.line - cueA.line;
  87. }
  88. return cueA.line - cueB.line;
  89. });
  90. result.forEach((cue) => addCueToTrack(track, cue));
  91. }
  92. return result;
  93. },
  94. };
  95.  
  96. export default Cues;