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