mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
130 lines
5.3 KiB
JavaScript
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
|