Home Reference Source

src/utils/cues.ts

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