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

Separate bitrate control from resolution #6071

Merged
merged 9 commits into from
Sep 20, 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
9 changes: 5 additions & 4 deletions src/components/playback/playersettingsmenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ function showQualityMenu(player, btn) {
const videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
return stream.Type === 'Video';
})[0];
const videoWidth = videoStream ? videoStream.Width : null;
const videoHeight = videoStream ? videoStream.Height : null;

const videoCodec = videoStream ? videoStream.Codec : null;
const videoBitRate = videoStream ? videoStream.BitRate : null;

const options = qualityoptions.getVideoQualityOptions({
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
videoWidth: videoWidth,
videoHeight: videoHeight,
videoCodec,
videoBitRate,
enableAuto: true
});

Expand Down
32 changes: 0 additions & 32 deletions src/components/playbackSettings/playbackSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,36 +277,6 @@ function save(instance, context, userId, userSettings, apiClient, enableSaveConf
});
}

function setSelectValue(select, value, defaultValue) {
select.value = value;

if (select.selectedIndex < 0) {
select.value = defaultValue;
}
}

function onMaxVideoWidthChange(e) {
const context = this.options.element;

const selectVideoInNetworkQuality = context.querySelector('.selectVideoInNetworkQuality');
const selectVideoInternetQuality = context.querySelector('.selectVideoInternetQuality');
const selectChromecastVideoQuality = context.querySelector('.selectChromecastVideoQuality');

const selectVideoInNetworkQualityValue = selectVideoInNetworkQuality.value;
const selectVideoInternetQualityValue = selectVideoInternetQuality.value;
const selectChromecastVideoQualityValue = selectChromecastVideoQuality.value;

const maxVideoWidth = parseInt(e.target.value || '0', 10) || 0;

fillQuality(selectVideoInNetworkQuality, true, 'Video', maxVideoWidth);
fillQuality(selectVideoInternetQuality, false, 'Video', maxVideoWidth);
fillChromecastQuality(selectChromecastVideoQuality, maxVideoWidth);

setSelectValue(selectVideoInNetworkQuality, selectVideoInNetworkQualityValue, '');
setSelectValue(selectVideoInternetQuality, selectVideoInternetQualityValue, '');
setSelectValue(selectChromecastVideoQuality, selectChromecastVideoQualityValue, '');
}

function onSubmit(e) {
const self = this;
const apiClient = ServerConnections.getApiClient(self.options.serverId);
Expand Down Expand Up @@ -334,8 +304,6 @@ function embed(options, self) {
options.element.querySelector('.btnSave').classList.remove('hide');
}

options.element.querySelector('.selectMaxVideoWidth').addEventListener('change', onMaxVideoWidthChange.bind(self));

self.loadData();

if (options.autoFocus) {
Expand Down
72 changes: 35 additions & 37 deletions src/components/qualityOptions.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { appHost } from '../components/apphost';
import globalize from '../lib/globalize';
import appSettings from '../scripts/settings/appSettings';

export function getVideoQualityOptions(options) {
const maxStreamingBitrate = options.currentMaxBitrate;
let videoWidth = options.videoWidth;
const videoHeight = options.videoHeight;
const videoBitRate = options.videoBitRate ?? -1;
const videoCodec = options.videoCodec;
let referenceBitRate = videoBitRate;

// If the aspect ratio is less than 16/9 (1.77), set the width as if it were pillarboxed.
// 4:3 1440x1080 -> 1920x1080
if (videoWidth / videoHeight < 16 / 9) {
videoWidth = videoHeight * (16 / 9);
}

const maxVideoWidth = options.maxVideoWidth == null ? appSettings.maxVideoWidth() : options.maxVideoWidth;

const hostScreenWidth = (maxVideoWidth < 0 ? appHost.screen()?.maxAllowedWidth : maxVideoWidth) || 4096;
const maxAllowedWidth = videoWidth || 4096;
// Quality options are indexed by bitrate. If you must duplicate them, make sure each of them are unique (by making the last digit a 1)
// Question: the maxHeight field seems not be used anywhere, is it safe to remove those?
const bitrateConfigurations = [
{ name: '120 Mbps', maxHeight: 2160, bitrate: 120000000 },
{ name: '80 Mbps', maxHeight: 2160, bitrate: 80000000 },
{ name: '60 Mbps', maxHeight: 2160, bitrate: 60000000 },
{ name: '40 Mbps', maxHeight: 2160, bitrate: 40000000 },
{ name: '20 Mbps', maxHeight: 2160, bitrate: 20000000 },
{ name: '15 Mbps', maxHeight: 1440, bitrate: 15000000 },
{ name: '10 Mbps', maxHeight: 1440, bitrate: 10000000 },
{ name: '8 Mbps', maxHeight: 1080, bitrate: 8000000 },
{ name: '6 Mbps', maxHeight: 1080, bitrate: 6000000 },
{ name: '4 Mbps', maxHeight: 720, bitrate: 4000000 },
{ name: '3 Mbps', maxHeight: 720, bitrate: 3000000 },
{ name: '1.5 Mbps', maxHeight: 720, bitrate: 1500000 },
{ name: '720 kbps', maxHeight: 480, bitrate: 720000 },
{ name: '420 kbps', maxHeight: 360, bitrate: 420000 }
];

const qualityOptions = [];

Expand All @@ -30,31 +37,22 @@ export function getVideoQualityOptions(options) {
qualityOptions.push(autoQualityOption);
}

// Quality options are indexed by bitrate. If you must duplicate them, make sure each of them are unique (by making the last digit a 1)
if (maxAllowedWidth >= 3800 && hostScreenWidth >= 1930) {
qualityOptions.push({ name: '4K - 120 Mbps', maxHeight: 2160, bitrate: 120000000 });
qualityOptions.push({ name: '4K - 80 Mbps', maxHeight: 2160, bitrate: 80000000 });
}
// Some 1080- videos are reported as 1912?
if (maxAllowedWidth >= 1900 && hostScreenWidth >= 1290) {
qualityOptions.push({ name: '1080p - 60 Mbps', maxHeight: 1080, bitrate: 60000000 });
qualityOptions.push({ name: '1080p - 40 Mbps', maxHeight: 1080, bitrate: 40000000 });
qualityOptions.push({ name: '1080p - 20 Mbps', maxHeight: 1080, bitrate: 20000000 });
qualityOptions.push({ name: '1080p - 15 Mbps', maxHeight: 1080, bitrate: 15000000 });
qualityOptions.push({ name: '1080p - 10 Mbps', maxHeight: 1080, bitrate: 10000000 });
}
if (maxAllowedWidth >= 1260 && hostScreenWidth >= 650) {
qualityOptions.push({ name: '720p - 8 Mbps', maxHeight: 720, bitrate: 8000000 });
qualityOptions.push({ name: '720p - 6 Mbps', maxHeight: 720, bitrate: 6000000 });
qualityOptions.push({ name: '720p - 4 Mbps', maxHeight: 720, bitrate: 4000000 });
}
if (maxAllowedWidth >= 620) {
qualityOptions.push({ name: '480p - 3 Mbps', maxHeight: 480, bitrate: 3000000 });
qualityOptions.push({ name: '480p - 1.5 Mbps', maxHeight: 480, bitrate: 1500000 });
qualityOptions.push({ name: '480p - 720 kbps', maxHeight: 480, bitrate: 720000 });
if (videoBitRate > 0 && videoBitRate < bitrateConfigurations[0].bitrate) {
// Slightly increase reference bitrate for high efficiency codecs when it is not too high
// Ideally we only need to do this for transcoding to h264, but we need extra api request to get that info which is not ideal for this
if (videoCodec && ['hevc', 'av1', 'vp9'].includes(videoCodec) && referenceBitRate <= 20000000) {
referenceBitRate *= 1.5;
}
// Push one entry that has higher limit than video bitrate to allow using source bitrate when Auto is also limited
const sourceOptions = bitrateConfigurations.filter((c) => c.bitrate > referenceBitRate).pop();
qualityOptions.push(sourceOptions);
}

qualityOptions.push({ name: '360p - 420 kbps', maxHeight: 360, bitrate: 420000 });
bitrateConfigurations.forEach((c) => {
if (videoBitRate <= 0 || c.bitrate <= referenceBitRate) {
qualityOptions.push(c);
}
});

if (maxStreamingBitrate) {
let selectedIndex = qualityOptions.length - 1;
Expand Down
Loading