Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add videoCodec field to videoPreference options #6483

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3495,6 +3495,7 @@ export type VariableMap = Record<string, string>;
export type VideoSelectionOption = {
preferHDR?: boolean;
allowedVideoRanges?: Array<VideoRange>;
videoCodec?: string;
};

// (No @packageDocumentation comment for this package)
Expand Down
20 changes: 15 additions & 5 deletions src/controller/abr-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ class AbrController extends Logger implements AbrComponentAPI {
const audioTracksByGroup =
this.audioTracksByGroup ||
(this.audioTracksByGroup = getAudioTracksByGroup(allAudioTracks));
let minStartIndex = -1;
if (firstSelection) {
if (this.firstSelection !== -1) {
return this.firstSelection;
Expand All @@ -694,8 +695,15 @@ class AbrController extends Logger implements AbrComponentAPI {
audioPreference,
videoPreference,
);
const { codecSet, videoRanges, minFramerate, minBitrate, preferHDR } =
startTier;
const {
codecSet,
videoRanges,
minFramerate,
minBitrate,
minIndex,
preferHDR,
} = startTier;
minStartIndex = minIndex;
currentCodecSet = codecSet;
currentVideoRange = preferHDR
? videoRanges[videoRanges.length - 1]
Expand Down Expand Up @@ -789,8 +797,10 @@ class AbrController extends Logger implements AbrComponentAPI {
(levelInfo.supportedResult &&
!levelInfo.supportedResult.decodingInfoResults?.[0].smooth)
) {
levelsSkipped.push(i);
continue;
if (firstSelection && i !== minStartIndex) {
levelsSkipped.push(i);
continue;
}
}

const levelDetails = levelInfo.details;
Expand Down Expand Up @@ -869,7 +879,7 @@ class AbrController extends Logger implements AbrComponentAPI {
1,
)} fetchDuration:${fetchDuration.toFixed(
1,
)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`,
)} firstSelection:${firstSelection} codecSet:${level.codecSet} videoRange:${level.videoRange} hls.loadLevel:${loadLevel}`,
);
}
if (firstSelection) {
Expand Down
7 changes: 4 additions & 3 deletions src/hls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ export default class Hls implements HlsEventEmitter {
public setAudioOption(
audioOption: MediaPlaylist | AudioSelectionOption | undefined,
): MediaPlaylist | null {
return this.audioTrackController?.setAudioOption(audioOption);
return this.audioTrackController?.setAudioOption(audioOption) || null;
}
/**
* Find and select the best matching subtitle track, making a level switch when a Group change is necessary.
Expand All @@ -822,8 +822,9 @@ export default class Hls implements HlsEventEmitter {
public setSubtitleOption(
subtitleOption: MediaPlaylist | SubtitleSelectionOption | undefined,
): MediaPlaylist | null {
this.subtitleTrackController?.setSubtitleOption(subtitleOption);
return null;
return (
this.subtitleTrackController?.setSubtitleOption(subtitleOption) || null
);
}

/**
Expand Down
10 changes: 9 additions & 1 deletion src/types/media-playlist.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AttrList } from '../utils/attr-list';
import type { LevelDetails } from '../loader/level-details';
import type { VideoRange } from './level';
import type { Level, VideoRange } from './level';
import type { PlaylistLevelType } from './loader';

export type AudioPlaylistType = 'AUDIO';

Expand All @@ -10,9 +11,16 @@ export type SubtitlePlaylistType = 'SUBTITLES' | 'CLOSED-CAPTIONS';

export type MediaPlaylistType = MainPlaylistType | SubtitlePlaylistType;

export type MediaSelection = {
[PlaylistLevelType.MAIN]: Level;
[PlaylistLevelType.AUDIO]?: MediaPlaylist;
[PlaylistLevelType.SUBTITLE]?: MediaPlaylist;
};

export type VideoSelectionOption = {
preferHDR?: boolean;
allowedVideoRanges?: Array<VideoRange>;
videoCodec?: string;
};

export type AudioSelectionOption = {
Expand Down
11 changes: 4 additions & 7 deletions src/utils/hdr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,13 @@ export function getVideoSelectionOptions(
if (videoPreference) {
allowedVideoRanges =
videoPreference.allowedVideoRanges || VideoRangeValues.slice(0);
const allowAutoPreferHDR =
allowedVideoRanges.join('') !== 'SDR' && !videoPreference.videoCodec;
preferHDR =
videoPreference.preferHDR !== undefined
? videoPreference.preferHDR
: isHdrSupported();

if (preferHDR) {
allowedVideoRanges = allowedVideoRanges.filter(
(range: VideoRange) => range !== 'SDR',
);
} else {
: allowAutoPreferHDR && isHdrSupported();
if (!preferHDR) {
allowedVideoRanges = ['SDR'];
}
}
Expand Down
31 changes: 26 additions & 5 deletions src/utils/rendition-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type CodecSetTier = {
minBitrate: number;
minHeight: number;
minFramerate: number;
minIndex: number;
maxScore: number;
videoRanges: Record<string, number>;
channels: Record<string, number>;
Expand All @@ -32,6 +33,7 @@ type StartParameters = {
preferHDR: boolean;
minFramerate: number;
minBitrate: number;
minIndex: number;
};

export function getStartCodecTier(
Expand All @@ -44,13 +46,15 @@ export function getStartCodecTier(
const codecSets = Object.keys(codecTiers);
const channelsPreference = audioPreference?.channels;
const audioCodecPreference = audioPreference?.audioCodec;
const videoCodecPreference = videoPreference?.videoCodec;
const preferStereo = channelsPreference && parseInt(channelsPreference) === 2;
// Use first level set to determine stereo, and minimum resolution and framerate
let hasStereo = true;
let hasStereo = false;
let hasCurrentVideoRange = false;
let minHeight = Infinity;
let minFramerate = Infinity;
let minBitrate = Infinity;
let minIndex = Infinity;
let selectedScore = 0;
let videoRanges: Array<VideoRange> = [];

Expand All @@ -61,7 +65,7 @@ export function getStartCodecTier(

for (let i = codecSets.length; i--; ) {
const tier = codecTiers[codecSets[i]];
hasStereo = tier.channels[2] > 0;
hasStereo ||= tier.channels[2] > 0;
minHeight = Math.min(minHeight, tier.minHeight);
minFramerate = Math.min(minFramerate, tier.minFramerate);
minBitrate = Math.min(minBitrate, tier.minBitrate);
Expand All @@ -70,7 +74,6 @@ export function getStartCodecTier(
);
if (matchingVideoRanges.length > 0) {
hasCurrentVideoRange = true;
videoRanges = matchingVideoRanges;
}
}
minHeight = Number.isFinite(minHeight) ? minHeight : 0;
Expand All @@ -82,7 +85,6 @@ export function getStartCodecTier(
// If there are no variants with matching preference, set currentVideoRange to undefined
if (!hasCurrentVideoRange) {
currentVideoRange = undefined;
videoRanges = [];
}
const codecSet = codecSets.reduce(
(selected: string | undefined, candidate: string) => {
Expand All @@ -91,6 +93,11 @@ export function getStartCodecTier(
if (candidate === selected) {
return selected;
}
videoRanges = hasCurrentVideoRange
? allowedVideoRanges.filter(
(range) => candidateTier.videoRanges[range] > 0,
)
: [];
if (candidateTier.minBitrate > currentBw) {
logStartCodecCandidateIgnored(
candidate,
Expand Down Expand Up @@ -159,6 +166,16 @@ export function getStartCodecTier(
);
return selected;
}
if (
videoCodecPreference &&
candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0
) {
logStartCodecCandidateIgnored(
candidate,
`video codec preference "${videoCodecPreference}" not found`,
);
return selected;
}
if (candidateTier.maxScore < selectedScore) {
logStartCodecCandidateIgnored(
candidate,
Expand All @@ -175,6 +192,7 @@ export function getStartCodecTier(
) {
return selected;
}
minIndex = candidateTier.minIndex;
selectedScore = candidateTier.maxScore;
return candidate;
},
Expand All @@ -186,6 +204,7 @@ export function getStartCodecTier(
preferHDR,
minFramerate,
minBitrate,
minIndex,
};
}

Expand Down Expand Up @@ -243,7 +262,7 @@ export function getCodecTiers(
): Record<string, CodecSetTier> {
return levels
.slice(minAutoLevel, maxAutoLevel + 1)
.reduce((tiers: Record<string, CodecSetTier>, level) => {
.reduce((tiers: Record<string, CodecSetTier>, level, index) => {
if (!level.codecSet) {
return tiers;
}
Expand All @@ -254,6 +273,7 @@ export function getCodecTiers(
minBitrate: Infinity,
minHeight: Infinity,
minFramerate: Infinity,
minIndex: index,
maxScore: 0,
videoRanges: { SDR: 0 },
channels: { '2': 0 },
Expand All @@ -265,6 +285,7 @@ export function getCodecTiers(
const lesserWidthOrHeight = Math.min(level.height, level.width);
tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight);
tier.minFramerate = Math.min(tier.minFramerate, level.frameRate);
tier.minIndex = Math.min(tier.minIndex, index);
tier.maxScore = Math.max(tier.maxScore, level.score);
tier.fragmentError += level.fragmentError;
tier.videoRanges[level.videoRange] =
Expand Down
Loading