Skip to content

Commit

Permalink
Add videoCodec field to videoPreference options (#6483)
Browse files Browse the repository at this point in the history
* Add videoCodec field to VideoSelectionOption and improve SDR/HDR initial selection results

* Consider selection of lowest level in start tier when higher options are passed over
  • Loading branch information
robwalch committed Jun 11, 2024
1 parent b02deeb commit e349f03
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 21 deletions.
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

0 comments on commit e349f03

Please sign in to comment.