Skip to content

Commit

Permalink
Merge pull request #6071 from gnattu/bitrate-control
Browse files Browse the repository at this point in the history
Separate bitrate control from resolution
  • Loading branch information
thornbill authored Sep 20, 2024
2 parents 37c8370 + d9d2f8d commit 5e17cbe
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 73 deletions.
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

0 comments on commit 5e17cbe

Please sign in to comment.