src/types/level.ts
import LevelDetails from '../loader/level-details';
import AttrList from '../utils/attr-list';
export interface LevelParsed {
attrs: LevelAttributes
audioCodec?: string
bitrate: number
details?: LevelDetails
height?: number
id?: number
level?: number
name: string
textCodec?: string
unknownCodecs?: string[]
url: string
videoCodec?: string
width?: number
}
export interface LevelAttributes extends AttrList {
AUDIO?: string
AUTOSELECT?: string
'AVERAGE-BANDWIDTH'?: string
BANDWIDTH?: string
BYTERANGE?: string
'CLOSED-CAPTIONS'?: string
CODECS?: string
DEFAULT?: string
FORCED?: string
'FRAME-RATE'?: string
LANGUAGE?: string
NAME?: string
'PROGRAM-ID'?: string
RESOLUTION?: string
SUBTITLES?: string
TYPE?: string
URI?: string
}
export enum HlsSkip {
No = '',
Yes = 'YES',
v2 = 'v2'
}
export function getSkipValue (details: LevelDetails, msn: number): HlsSkip {
const { canSkipUntil, canSkipDateRanges, endSN } = details;
const snChangeGoal = msn - endSN;
if (canSkipUntil && snChangeGoal < canSkipUntil) {
if (canSkipDateRanges) {
return HlsSkip.v2;
}
return HlsSkip.Yes;
}
return HlsSkip.No;
}
export class HlsUrlParameters {
msn: number;
part?: number;
skip?: HlsSkip;
constructor (msn: number, part?: number, skip?: HlsSkip) {
this.msn = msn;
this.part = part;
this.skip = skip;
}
addDirectives (uri: string): string | never {
const url: URL = new self.URL(uri);
const searchParams: URLSearchParams = url.searchParams;
searchParams.set('_HLS_msn', this.msn.toString());
if (this.part !== undefined) {
searchParams.set('_HLS_part', this.part.toString());
}
if (this.skip) {
searchParams.set('_HLS_skip', this.skip);
}
searchParams.sort();
url.search = searchParams.toString();
return url.toString();
}
}
export class Level {
public attrs: LevelAttributes;
public audioCodec?: string;
public audioGroupIds?: string[];
public bitrate: number;
public details?: LevelDetails;
public fragmentError: boolean = false;
public height: number;
public id: number;
public loadError: number = 0;
public loaded?: { bytes: number, duration: number };
public name: string | undefined;
public realBitrate: number = 0;
public textGroupIds?: string[];
public url: string[];
public videoCodec?: string;
public width: number;
public unknownCodecs: string[] | undefined;
private _urlId: number = 0;
constructor (data: LevelParsed) {
this.url = [data.url];
this.attrs = data.attrs;
this.bitrate = data.bitrate;
this.details = data.details;
this.id = data.id || 0;
this.name = data.name;
this.width = data.width || 0;
this.height = data.height || 0;
this.audioCodec = data.audioCodec;
this.videoCodec = data.videoCodec;
this.unknownCodecs = data.unknownCodecs;
}
get maxBitrate (): number {
return Math.max(this.realBitrate, this.bitrate);
}
get uri (): string {
return this.url[this._urlId] || '';
}
get urlId (): number {
return this._urlId;
}
set urlId (value: number) {
const newValue = value % this.url.length;
if (this._urlId !== newValue) {
this.details = undefined;
this._urlId = newValue;
}
}
}