Skip to content

Commit

Permalink
text: Add ISupportedTextTrackFormat concept
Browse files Browse the repository at this point in the history
  • Loading branch information
peaBerberian committed Jan 29, 2025
1 parent c8c29c5 commit 40d24a3
Show file tree
Hide file tree
Showing 20 changed files with 175 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import log from "../../../../log";
import type { ITextDisplayer } from "../../../../main_thread/types";
import type { ITextTrackSegmentData } from "../../../../transports";
import type {
ISupportedTextTrackFormat,
ITextTrackSegmentData,
} from "../../../../transports";
import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
import getMonotonicTimeStamp from "../../../../utils/monotonic_timestamp";
import type { IRange } from "../../../../utils/ranges";
Expand Down Expand Up @@ -149,7 +152,7 @@ export interface ITextTracksBufferSegmentData<
/** The text track data, in the format indicated in `type`. */
data: T;
/** The format of `data` (examples: "ttml", "srt" or "vtt") */
type: string;
type: ISupportedTextTrackFormat;
/**
* Language in which the text track is, as a language code.
* This is mostly needed for "sami" subtitles, to know which cues can / should
Expand Down
10 changes: 7 additions & 3 deletions src/features/list/__tests__/html_vtt_parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { describe, it, expect } from "vitest";
import HTMLTextDisplayer from "../../../main_thread/text_displayer/html";
import vttParser from "../../../parsers/texttracks/webvtt/html";
import {
parseWebVTTMp4,
parseWebVTTPlainText,
} from "../../../parsers/texttracks/webvtt//html";
import type { IFeaturesObject } from "../../types";
import addHTMLVTTFeature from "../html_vtt_parser";

Expand All @@ -11,9 +14,10 @@ describe("Features list - HTML VTT Parser", () => {
} as unknown as IFeaturesObject;
addHTMLVTTFeature(featureObject);
expect(featureObject).toEqual({
htmlTextTracksParsers: { vtt: vttParser },
htmlTextTracksParsers: { vtt: parseWebVTTPlainText, mp4vtt: parseWebVTTMp4 },
htmlTextDisplayer: HTMLTextDisplayer,
});
expect(featureObject.htmlTextTracksParsers.vtt).toBe(vttParser);
expect(featureObject.htmlTextTracksParsers.vtt).toBe(parseWebVTTPlainText);
expect(featureObject.htmlTextTracksParsers.mp4vtt).toBe(parseWebVTTMp4);
});
});
15 changes: 12 additions & 3 deletions src/features/list/__tests__/native_vtt_parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { describe, it, expect } from "vitest";
import NativeTextDisplayer from "../../../main_thread/text_displayer/native";
import vttParser from "../../../parsers/texttracks/webvtt/native";
import {
parseMp4EmbeddedWebVttToVTTCues,
parseWebVTTPlainTextToVTTCues,
} from "../../../parsers/texttracks/webvtt/native";
import type { IFeaturesObject } from "../../types";
import addNativevttFeature from "../native_vtt_parser";

Expand All @@ -11,9 +14,15 @@ describe("Features list - native vtt Parser", () => {
} as unknown as IFeaturesObject;
addNativevttFeature(featureObject);
expect(featureObject).toEqual({
nativeTextTracksParsers: { vtt: vttParser },
nativeTextTracksParsers: {
vtt: parseWebVTTPlainTextToVTTCues,
mp4vtt: parseMp4EmbeddedWebVttToVTTCues,
},
nativeTextDisplayer: NativeTextDisplayer,
});
expect(featureObject.nativeTextTracksParsers.vtt).toBe(vttParser);
expect(featureObject.nativeTextTracksParsers.vtt).toBe(parseWebVTTPlainTextToVTTCues);
expect(featureObject.nativeTextTracksParsers.mp4vtt).toBe(
parseMp4EmbeddedWebVttToVTTCues,
);
});
});
8 changes: 6 additions & 2 deletions src/features/list/html_vtt_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
*/

import HTMLTextDisplayer from "../../main_thread/text_displayer/html";
import vttParser from "../../parsers/texttracks/webvtt/html";
import {
parseWebVTTMp4,
parseWebVTTPlainText,
} from "../../parsers/texttracks/webvtt//html";
import type { IFeaturesObject } from "../types";

/**
* Add ability to parse WebVTT text tracks in an HTML textrack mode.
* @param {Object} features
*/
function addHTMLVTTFeature(features: IFeaturesObject): void {
features.htmlTextTracksParsers.vtt = vttParser;
features.htmlTextTracksParsers.vtt = parseWebVTTPlainText;
features.htmlTextTracksParsers.mp4vtt = parseWebVTTMp4;
features.htmlTextDisplayer = HTMLTextDisplayer;
}

Expand Down
8 changes: 6 additions & 2 deletions src/features/list/native_vtt_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
*/

import NativeTextDisplayer from "../../main_thread/text_displayer/native";
import vttParser from "../../parsers/texttracks/webvtt/native";
import {
parseMp4EmbeddedWebVttToVTTCues,
parseWebVTTPlainTextToVTTCues,
} from "../../parsers/texttracks/webvtt/native";
import type { IFeaturesObject } from "../types";

/**
* Add ability to parse WebVTT text tracks in a native textrack mode.
* @param {Object} features
*/
function addNativeVTTFeature(features: IFeaturesObject): void {
features.nativeTextTracksParsers.vtt = vttParser;
features.nativeTextTracksParsers.vtt = parseWebVTTPlainTextToVTTCues;
features.nativeTextTracksParsers.mp4vtt = parseMp4EmbeddedWebVttToVTTCues;
features.nativeTextDisplayer = NativeTextDisplayer;
}

Expand Down
10 changes: 7 additions & 3 deletions src/features/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import type {
IHTMLTextTracksParserFn,
INativeTextTracksParserFn,
} from "../parsers/texttracks";
import type { ITransportFunction } from "../transports";
import type { ISupportedTextTrackFormat, ITransportFunction } from "../transports";
import type { CancellationSignal } from "../utils/task_canceller";

/**
Expand Down Expand Up @@ -112,7 +112,9 @@ export interface IFeaturesObject {
* RxPlayer.
* Those parsers are specifically destined to be displayed in DOM elements.
*/
htmlTextTracksParsers: Partial<Record<string, IHTMLTextTracksParserFn>>;
htmlTextTracksParsers: Partial<
Record<ISupportedTextTrackFormat, IHTMLTextTracksParserFn>
>;
/**
* Feature allowing to load contents through MediaSource API through the
* main thread.
Expand Down Expand Up @@ -156,7 +158,9 @@ export interface IFeaturesObject {
* Those parsers are specifically destined to be displayed in `<track>` HTML
* elements.
*/
nativeTextTracksParsers: Partial<Record<string, INativeTextTracksParserFn>>;
nativeTextTracksParsers: Partial<
Record<ISupportedTextTrackFormat, INativeTextTracksParserFn>
>;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/main_thread/text_displayer/html/html_parsers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import features from "../../../features";
import log from "../../../log";
import type { ISupportedTextTrackFormat } from "../../../transports";

export interface IHTMLCue {
start: number;
Expand All @@ -19,7 +20,7 @@ export interface IHTMLCue {
* @throws Error - Throw if no parser is found for the given type
*/
export default function parseTextTrackToElements(
type: string,
type: ISupportedTextTrackFormat,
data: string | BufferSource,
timescale: number,
timestampOffset: number,
Expand Down
3 changes: 2 additions & 1 deletion src/main_thread/text_displayer/native/native_parsers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ICompatVTTCue } from "../../../compat/browser_compatibility_types";
import features from "../../../features";
import log from "../../../log";
import type { ISupportedTextTrackFormat } from "../../../transports";

/**
* Convert text track data into timed VTT Cues.
Expand All @@ -14,7 +15,7 @@ import log from "../../../log";
* @throws Error - Throw if no parser is found for the given type
*/
export default function parseTextTrackToCues(
type: string,
type: ISupportedTextTrackFormat,
data: string | BufferSource,
timescale: number,
timestampOffset: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { describe, beforeEach, it, expect, vi } from "vitest";
import type IParseWebVTT from "../parse_webvtt_to_div";
import type IParseWebVTTPlainText from "../parse_webvtt_plain_text";

describe("parsers - webvtt - parseWebVTT", () => {
beforeEach(() => {
vi.resetModules();
});

it("should throw if text is empty", async () => {
const parseWebVTT = (await vi.importActual("../parse_webvtt_to_div"))
.default as typeof IParseWebVTT;
const parseWebVTT = (await vi.importActual("../parse_webvtt_plain_text.ts"))
.default as typeof IParseWebVTTPlainText;
expect(() => parseWebVTT("", 1, 0)).toThrowError("Can't parse WebVTT: Invalid File.");
});

it("should throw if file seems to be invalid", async () => {
const parseWebVTT = (await vi.importActual("../parse_webvtt_to_div"))
.default as typeof IParseWebVTT;
const parseWebVTT = (await vi.importActual("../parse_webvtt_plain_text.ts"))
.default as typeof IParseWebVTTPlainText;
expect(() => parseWebVTT("WEBWTT\n", 1, 0)).toThrowError(
"Can't parse WebVTT: Invalid File.",
);
Expand Down Expand Up @@ -71,8 +71,8 @@ describe("parsers - webvtt - parseWebVTT", () => {
getFirstLineAfterHeader: spyGetFirstLineAfterHeader,
}));

const parseWebVTT = (await vi.importActual("../parse_webvtt_to_div"))
.default as typeof IParseWebVTT;
const parseWebVTT = (await vi.importActual("../parse_webvtt_plain_text.ts"))
.default as typeof IParseWebVTTPlainText;
expect(parseWebVTT("WEBVTT\n", 1, 0)).toEqual([
{
element: document.createElement("div"),
Expand Down Expand Up @@ -138,8 +138,8 @@ describe("parsers - webvtt - parseWebVTT", () => {
getFirstLineAfterHeader: spyGetFirstLineAfterHeader,
}));

const parseWebVTT = (await vi.importActual("../parse_webvtt_to_div"))
.default as typeof IParseWebVTT;
const parseWebVTT = (await vi.importActual("../parse_webvtt_plain_text.ts"))
.default as typeof IParseWebVTTPlainText;
expect(parseWebVTT("WEBVTT\n", 1, 0)).toEqual([]);
expect(spyGetFirstLineAfterHeader).toHaveBeenCalledTimes(1);
expect(spyGetStyleBlock).toHaveBeenCalledTimes(1);
Expand Down
11 changes: 8 additions & 3 deletions src/parsers/texttracks/webvtt/html/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
* It always should be imported through the `features` object.
*/

import parseWebVTTToDiv from "./parse_webvtt_to_div";
export type { IVTTHTMLCue } from "./to_html";
import parseWebVTTMp4 from "../../webvtt/html/parse_webvtt_mp4";
import parseWebVTTPlainText from "../../webvtt/html/parse_webvtt_plain_text";

export default parseWebVTTToDiv;
/**
* /!\ This file is feature-switchable.
* It always should be imported through the `features` object.
*/
export { parseWebVTTPlainText, parseWebVTTMp4 };
export type { IVTTHTMLCue } from "./to_html";
24 changes: 24 additions & 0 deletions src/parsers/texttracks/webvtt/html/parse_webvtt_mp4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import bufferSourceToUint8 from "../../../../utils/buffer_source_to_uint8";
import { strToUtf8 } from "../../../../utils/string_parsing";
import parseMp4EmbeddedWebVtt from "../parse_mp4_embedded_wvtt";
import type { IVTTHTMLCue } from "./to_html";
import toHTML from "./to_html";

/**
* Parse WebVTT subtitles format when embedded in an MP4 file.
* @throws Error - Throws if the given WebVTT format.
* @param {string | BufferSource} text - The whole webvtt subtitles to parse
* @param {Number} timescale
* @param {Number} timeOffset - Offset to add to start and end times, in seconds
* @return {Array.<Object>}
*/
export default function parseWebVTTMp4(
text: string | BufferSource,
timescale: number,
timeOffset: number,
): IVTTHTMLCue[] {
if (typeof text === "string") {
return parseMp4EmbeddedWebVtt(strToUtf8(text), timescale, timeOffset, toHTML);
}
return parseMp4EmbeddedWebVtt(bufferSourceToUint8(text), timescale, timeOffset, toHTML);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,15 @@
*/

import bufferSourceToUint8 from "../../../../utils/buffer_source_to_uint8";
import { strToUtf8, utf8ToStr } from "../../../../utils/string_parsing";
import { utf8ToStr } from "../../../../utils/string_parsing";
import getCueBlocks from "../get_cue_blocks";
import getStyleBlocks from "../get_style_blocks";
import parseCueBlock from "../parse_cue_block";
import parseMp4EmbeddedWebVtt from "../parse_mp4_embedded_wvtt";
import parseStyleBlocks from "../parse_style_block";
import seemsMp4EmbeddedFormat from "../seems_mp4_embedded_format";
import { getFirstLineAfterHeader } from "../utils";
import type { IVTTHTMLCue } from "./to_html";
import toHTML from "./to_html";

/**
* Parse WebVTT subtitles format.
* @throws Error - Throws if the given WebVTT format.
* @param {string | BufferSource} text - The whole webvtt subtitles to parse
* @param {Number} timescale
* @param {Number} timeOffset - Offset to add to start and end times, in seconds
* @return {Array.<Object>}
*/
export default function parseWebVttToDiv(
text: string | BufferSource,
timescale: number,
timeOffset: number,
): IVTTHTMLCue[] {
if (seemsMp4EmbeddedFormat(text)) {
if (typeof text === "string") {
return parseMp4EmbeddedWebVtt(strToUtf8(text), timescale, timeOffset, toHTML);
} else {
return parseMp4EmbeddedWebVtt(
bufferSourceToUint8(text),
timescale,
timeOffset,
toHTML,
);
}
} else if (typeof text === "string") {
return parseWebVTTText(text, timescale, timeOffset);
} else {
return parseWebVTTText(
// Assume UTF-8
utf8ToStr(bufferSourceToUint8(text)),
timescale,
timeOffset,
);
}
}

/**
* Parse WebVTT from text. Returns an array with:
* - start : start of current cue, in seconds
Expand All @@ -72,16 +34,23 @@ export default function parseWebVttToDiv(
* Specific style is parsed and applied to class element.
*
* @throws Error - Throws if the given WebVTT string is invalid.
* @param {string } textStr - The whole webvtt subtitles to parse
* @param {string|BufferSource} text - The whole webvtt subtitles to parse
* @param {Number} _timescale
* @param {Number} timeOffset - Offset to add to start and end times, in seconds
* @return {Array.<Object>}
*/
function parseWebVTTText(
textStr: string,
export default function parseWebVTTPlainText(
text: string | BufferSource,
_timescale: number,
timeOffset: number,
): IVTTHTMLCue[] {
let textStr: string;
if (typeof text === "string") {
textStr = text;
} else {
// Assume UTF-8
textStr = utf8ToStr(bufferSourceToUint8(text));
}
const newLineChar = /\r\n|\n|\r/g; // CRLF|LF|CR
const linified = textStr.split(newLineChar);

Expand Down
6 changes: 4 additions & 2 deletions src/parsers/texttracks/webvtt/native/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@
* It always should be imported through the `features` object.
*/

import parseVttToNative from "./parse_vtt_to_cues";
export default parseVttToNative;
import parseWebVTTPlainTextToVTTCues from "./parse_vtt_plain_text_to_cues";
import parseMp4EmbeddedWebVttToVTTCues from "./parse_webvtt_mp4_to_cues";

export { parseMp4EmbeddedWebVttToVTTCues, parseWebVTTPlainTextToVTTCues };
Loading

0 comments on commit 40d24a3

Please sign in to comment.