Files
v10/dist/dev/media/utils/tracks.js
T

130 lines
5.3 KiB
JavaScript

import { isResolvedTrack } from "../types/index.js";
//#region src/media/utils/tracks.ts
/**
* Get the tracks of the given type from a presentation's first switching set.
*
* Returns `[]` when the presentation is unresolved, when no selection set of
* `type` exists, or when its first switching set is empty. Returned tracks may
* be partially resolved (URL only) or fully resolved (with segments) — callers
* narrow as needed.
*
* The "first switching set" assumption matches the rest of the codebase
* (HLS typically has one switching set per type); multi-group / multi-period
* support would generalize this.
*/
function getTracksByType(presentation, type) {
return presentation.selectionSets?.find(({ type: t }) => t === type)?.switchingSets[0]?.tracks ?? [];
}
/**
* Find a track of the given type and id within a presentation.
*
* Returns the matching track from the first switching set of the matching
* selection set, or `undefined` if either is missing. The returned track may
* be partially resolved (URL only) or fully resolved (with segments) — callers
* narrow as needed.
*/
function findTrack(presentation, type, trackId) {
return getTracksByType(presentation, type).find(({ id }) => id === trackId);
}
/**
* Find a track by id across all selection sets in a presentation, without
* knowing its type up front. Used when the caller has a track id obtained
* from a downstream consumer (e.g. `SourceBufferActor.initTrackId`) and
* needs to locate the corresponding track in the presentation.
*
* Track ids are unique within a presentation per the HLS spec; the first
* match wins.
*/
function findTrackById(presentation, trackId) {
for (const selectionSet of presentation.selectionSets ?? []) {
const track = selectionSet.switchingSets[0]?.tracks.find(({ id }) => id === trackId);
if (track) return track;
}
}
/**
* Find a text track of the given id within a presentation and narrow it to
* the fully-resolved `TextTrack` shape (segments populated). Returns
* `undefined` if no track matches the id, the matching track isn't a text
* track, or it hasn't been resolved yet.
*
* The segments-non-empty check stays at the call site — a resolved track
* with zero segments is a valid state, distinct from "ready to load."
*/
function findResolvedTextTrack(presentation, trackId) {
if (!presentation || !trackId) return void 0;
const track = findTrack(presentation, "text", trackId);
if (track?.type !== "text" || !isResolvedTrack(track)) return void 0;
return track;
}
function findResolvedVideoTrack(presentation, trackId) {
if (!presentation || !trackId) return void 0;
const track = findTrack(presentation, "video", trackId);
if (track?.type !== "video" || !isResolvedTrack(track)) return void 0;
return track;
}
function findResolvedAudioTrack(presentation, trackId) {
if (!presentation || !trackId) return void 0;
const track = findTrack(presentation, "audio", trackId);
if (track?.type !== "audio" || !isResolvedTrack(track)) return void 0;
return track;
}
/**
* Whether a track carries a non-empty `codecs` array. Both partially-
* resolved and fully-resolved tracks may carry codecs — they come from
* the multivariant playlist's `EXT-X-STREAM-INF` line, not from the
* per-type media playlist — so this works at either resolution stage.
*
* `TextTrack` doesn't declare a `codecs` field; the `'codecs' in track`
* check narrows it out for the false branch.
*/
function hasCodecs(track) {
return !!track && "codecs" in track && !!track.codecs?.length;
}
/**
* Set `mimeType` on every track of one `type` (immutably). Used to propagate a
* detected container across a type's renditions: an ABR ladder is the same
* content at different bitrates, so one rendition's container holds for all of
* them — capability probing + SourceBuffer setup then get the right MIME for the
* whole type from a single resolved media playlist, without fetching the rest.
*
* Scoped to one type on purpose: propagating *across* audio/video would be wrong
* for mixed-container sources (e.g. muxed-TS video + raw-`.aac` audio) and races
* concurrent per-type resolution. Same-type writes are disjoint and safe.
* Idempotent — tracks already at `mimeType` are left as-is.
*/
function applyContainerMimeType(presentation, type, mimeType) {
return {
...presentation,
selectionSets: presentation.selectionSets.map((selectionSet) => selectionSet.type === type ? {
...selectionSet,
switchingSets: selectionSet.switchingSets.map((switchingSet) => ({
...switchingSet,
tracks: switchingSet.tracks.map((track) => track.mimeType === mimeType ? track : {
...track,
mimeType
})
}))
} : selectionSet)
};
}
/**
* Updates a track within a presentation (immutably). Generic — works for
* video, audio, or text tracks.
*/
function updateTrackInPresentation(presentation, resolvedTrack) {
const trackId = resolvedTrack.id;
return {
...presentation,
selectionSets: presentation.selectionSets.map((selectionSet) => ({
...selectionSet,
switchingSets: selectionSet.switchingSets.map((switchingSet) => ({
...switchingSet,
tracks: switchingSet.tracks.map((track) => track.id === trackId ? resolvedTrack : track)
}))
}))
};
}
//#endregion
export { applyContainerMimeType, findResolvedAudioTrack, findResolvedTextTrack, findResolvedVideoTrack, findTrack, findTrackById, getTracksByType, hasCodecs, updateTrackInPresentation };
//# sourceMappingURL=tracks.js.map