UNPKG

rx-player

Version:
188 lines (178 loc) 5.17 kB
/** * Copyright 2015 CANAL+ Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import log from "../../log"; import type { ISegment } from "../../manifest"; import { getMDAT } from "../../parsers/containers/isobmff"; import startsWith from "../../utils/starts_with"; import { utf8ToStr } from "../../utils/string_parsing"; import type { IChunkTimeInfo, ISegmentContext, ITextTrackSegmentData } from "../types"; /** * Returns the a string expliciting the format of a text track when that text * track is embedded into a ISOBMFF file. * @param {string|undefined} codecs * @returns {string} */ export function getISOBMFFTextTrackFormat(codecs: string | undefined): "ttml" | "vtt" { if (codecs === undefined) { throw new Error("Cannot parse subtitles: unknown format"); } switch (codecs.toLowerCase()) { case "stpp": // stpp === TTML in MP4 case "stpp.ttml": case "stpp.ttml.im1t": return "ttml"; case "wvtt": // wvtt === WebVTT in MP4 return "vtt"; } throw new Error( "The codec used for the subtitles " + `"${codecs}" is not managed yet.`, ); } /** * Returns the a string expliciting the format of a text track in plain text. * @param {string|undefined} codecs * @param {string|undefined} mimeType * @returns {string} */ export function getPlainTextTrackFormat( codecs: string | undefined, mimeType: string | undefined, ): "ttml" | "sami" | "vtt" | "srt" { switch (mimeType) { case "application/ttml+xml": return "ttml"; case "application/x-sami": case "application/smil": return "sami"; case "text/vtt": return "vtt"; } if (codecs !== undefined) { const codeLC = codecs.toLowerCase(); if (codeLC === "srt") { return "srt"; } } throw new Error(`could not find a text-track parser for the type ${mimeType ?? ""}`); } /** * @param {Object} content * @param {ArrayBuffer|UInt8Array|null} chunkBytes * @param {number|undefined} initTimescale * @param {Object|null} chunkInfos * @param {boolean} isChunked * @returns {Object|null} */ export function getISOBMFFEmbeddedTextTrackData( { segment, language, codecs, }: { segment: ISegment; codecs?: string | undefined; language?: string | undefined; }, chunkBytes: Uint8Array<ArrayBuffer>, initTimescale: number | undefined, chunkInfos: IChunkTimeInfo | null, isChunked: boolean, ): ITextTrackSegmentData | null { if (segment.isInit) { return null; } let startTime: number | undefined; let endTime: number | undefined; if (chunkInfos === null) { if (!isChunked) { log.warn("utils", "Unavailable time data for current text track."); } else { startTime = segment.time; endTime = segment.end; } } else { startTime = chunkInfos.time; if (chunkInfos.duration !== undefined) { endTime = startTime + chunkInfos.duration; } else if (!isChunked && segment.complete) { endTime = startTime + segment.duration; } } const type: "ttml" | "vtt" = getISOBMFFTextTrackFormat(codecs); const mdat = getMDAT(chunkBytes); const mdatStr = mdat !== null ? utf8ToStr(mdat) : ""; if ( codecs === "wvtt" && !startsWith(mdatStr, "WEBVTT") && !startsWith(mdatStr, "\xfe\xffWEBVTT") ) { // From how I understand it, we're here in a special WebVTT format embedded // in an MP4 where the whole chunk contains information, now just the MDAT return { data: chunkBytes, type: "mp4vtt", language, start: startTime, end: endTime, initTimescale: initTimescale ?? null, }; } return { data: mdatStr, type, language, start: startTime, end: endTime, initTimescale: initTimescale ?? null, }; } /** * @param {Object} context * @param {ArrayBuffer|UInt8Array|null} textTrackData * @param {number|undefined} initTimescale * @param {boolean} isChunked * @returns {Object|null} */ export function getPlainTextTrackData( context: ISegmentContext, textTrackData: string, initTimescale: number | undefined, isChunked: boolean, ): ITextTrackSegmentData | null { const { segment } = context; if (segment.isInit) { return null; } let start; let end; if (isChunked) { log.warn("utils", "Unavailable time data for current text track."); } else { start = segment.time; if (segment.complete) { end = segment.time + segment.duration; } } const type = getPlainTextTrackFormat(context.codecs, context.mimeType); return { data: textTrackData, type, language: context.language, start, end, initTimescale: initTimescale ?? null, }; }