diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js
index adfecb0ee..bab7d098b 100644
--- a/src/components/playback/playbackmanager.js
+++ b/src/components/playback/playbackmanager.js
@@ -309,7 +309,8 @@ function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, apiCl
PlaySessionId: startingPlaySession,
StartTimeTicks: startPosition || 0,
EnableRedirection: true,
- EnableRemoteMedia: appHost.supports('remoteaudio')
+ EnableRemoteMedia: appHost.supports('remoteaudio'),
+ EnableAudioVbrEncoding: transcodingProfile.EnableAudioVbrEncoding
});
}
diff --git a/src/components/playbackSettings/playbackSettings.js b/src/components/playbackSettings/playbackSettings.js
index 021b9a8cc..69b748bb3 100644
--- a/src/components/playbackSettings/playbackSettings.js
+++ b/src/components/playbackSettings/playbackSettings.js
@@ -190,6 +190,9 @@ function loadForm(context, user, userSettings, systemInfo, apiClient) {
context.querySelector('.chkLimitSupportedVideoResolution').checked = appSettings.limitSupportedVideoResolution();
context.querySelector('#selectPreferredTranscodeVideoCodec').value = appSettings.preferredTranscodeVideoCodec();
context.querySelector('#selectPreferredTranscodeVideoAudioCodec').value = appSettings.preferredTranscodeVideoAudioCodec();
+ context.querySelector('.chkDisableVbrAudioEncoding').checked = appSettings.disableVbrAudio();
+ context.querySelector('.chkAlwaysRemuxFlac').checked = appSettings.alwaysRemuxFlac();
+ context.querySelector('.chkAlwaysRemuxMp3').checked = appSettings.alwaysRemuxMp3();
setMaxBitrateIntoField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
setMaxBitrateIntoField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
@@ -232,6 +235,9 @@ function saveUser(context, user, userSettingsInstance, apiClient) {
appSettings.enableTrueHd(context.querySelector('.chkEnableTrueHd').checked);
appSettings.enableHi10p(context.querySelector('.chkEnableHi10p').checked);
+ appSettings.disableVbrAudio(context.querySelector('.chkDisableVbrAudioEncoding').checked);
+ appSettings.alwaysRemuxFlac(context.querySelector('.chkAlwaysRemuxFlac').checked);
+ appSettings.alwaysRemuxMp3(context.querySelector('.chkAlwaysRemuxMp3').checked);
setMaxBitrateFromField(context.querySelector('.selectVideoInNetworkQuality'), true, 'Video');
setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
diff --git a/src/components/playbackSettings/playbackSettings.template.html b/src/components/playbackSettings/playbackSettings.template.html
index bc708ee1e..0ad66b3c9 100644
--- a/src/components/playbackSettings/playbackSettings.template.html
+++ b/src/components/playbackSettings/playbackSettings.template.html
@@ -22,6 +22,14 @@
${LabelPlayDefaultAudioTrack}
+
+
+
+
${DisableVbrAudioEncodingHelp}
+
@@ -80,15 +88,6 @@
${TabAdvanced}
-
-
-
${SelectAudioNormalizationHelp}
-
-
+
+
diff --git a/src/scripts/browserDeviceProfile.js b/src/scripts/browserDeviceProfile.js
index 908ab59de..34fad3c23 100644
--- a/src/scripts/browserDeviceProfile.js
+++ b/src/scripts/browserDeviceProfile.js
@@ -738,10 +738,19 @@ export default function (options) {
});
}
- profile.DirectPlayProfiles.push({
- Container: audioFormat,
- Type: 'Audio'
- });
+ if (audioFormat === 'flac' && appSettings.alwaysRemuxFlac()) {
+ // force remux flac in fmp4. Clients not supporting this configuration should disable this option
+ profile.DirectPlayProfiles.push({
+ Container: 'mp4',
+ AudioCodec: 'flac',
+ Type: 'Audio'
+ });
+ } else if (audioFormat !== 'mp3' || !appSettings.alwaysRemuxMp3()) { // mp3 remux profile is already injected
+ profile.DirectPlayProfiles.push({
+ Container: audioFormat,
+ Type: 'Audio'
+ });
+ }
// https://www.webmproject.org/about/faq/
if (audioFormat === 'opus' || audioFormat === 'webma') {
@@ -794,7 +803,8 @@ export default function (options) {
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? '2' : '1',
- BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
+ BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames,
+ EnableAudioVbrEncoding: !appSettings.disableVbrAudio()
});
}
diff --git a/src/scripts/settings/appSettings.js b/src/scripts/settings/appSettings.js
index 07c028850..cf6cbea28 100644
--- a/src/scripts/settings/appSettings.js
+++ b/src/scripts/settings/appSettings.js
@@ -195,6 +195,45 @@ class AppSettings {
return toBoolean(this.get('enableHi10p'), false);
}
+ /**
+ * Get or set 'Disable VBR audio encoding' state.
+ * @param {boolean|undefined} val - Flag to enable 'Disable VBR audio encoding' or undefined.
+ * @return {boolean} 'Disable VBR audio encoding' state.
+ */
+ disableVbrAudio(val) {
+ if (val !== undefined) {
+ return this.set('disableVbrAudio', val.toString());
+ }
+
+ return toBoolean(this.get('disableVbrAudio'), false);
+ }
+
+ /**
+ * Get or set 'Always remux FLAC audio files' state.
+ * @param {boolean|undefined} val - Flag to enable 'Always remux FLAC audio files' or undefined.
+ * @return {boolean} 'Always remux FLAC audio files' state.
+ */
+ alwaysRemuxFlac(val) {
+ if (val !== undefined) {
+ return this.set('alwaysRemuxFlac', val.toString());
+ }
+
+ return toBoolean(this.get('alwaysRemuxFlac'), false);
+ }
+
+ /**
+ * Get or set 'Always remux MP3 audio files' state.
+ * @param {boolean|undefined} val - Flag to enable 'Always remux MP3 audio files' or undefined.
+ * @return {boolean} 'Always remux MP3 audio files' state.
+ */
+ alwaysRemuxMp3(val) {
+ if (val !== undefined) {
+ return this.set('alwaysRemuxMp3', val.toString());
+ }
+
+ return toBoolean(this.get('alwaysRemuxMp3'), false);
+ }
+
set(name, value, userId) {
const currentValue = this.get(name, userId);
localStorage.setItem(this.#getKey(name, userId), value);
diff --git a/src/strings/en-us.json b/src/strings/en-us.json
index 560b469b9..642d082cc 100644
--- a/src/strings/en-us.json
+++ b/src/strings/en-us.json
@@ -47,6 +47,8 @@
"AllowTonemappingSoftwareHelp": "Tone-mapping can transform the dynamic range of a video from HDR to SDR while maintaining image details and colors, which are very important information for representing the original scene. Currently works only with 10bit HDR10, HLG and DoVi videos.",
"AlwaysPlaySubtitles": "Always Play",
"AlwaysPlaySubtitlesHelp": "Subtitles matching the language preference will be loaded regardless of the audio language.",
+ "AlwaysRemuxFlacAudioFilesHelp": "If you have files that your browser rejects to play or where it inaccurately calculates timestamps, enable this as a workaround.",
+ "AlwaysRemuxMp3AudioFilesHelp": "If you have files that your browser inaccurately calculates timestamps, enable this as a workaround.",
"AndOtherArtists": "{0} and {1} other artists.",
"AnyLanguage": "Any Language",
"Anytime": "Anytime",
@@ -223,6 +225,7 @@
"EnablePlugin": "Enable",
"DisableCustomCss": "Disable server-provided custom CSS code",
"DisablePlugin": "Disable",
+ "DisableVbrAudioEncodingHelp": "Prevent the server from encoding audio with VBR for this client.",
"Disc": "Disc",
"Disconnect": "Disconnect",
"Display": "Display",
@@ -365,6 +368,7 @@
"HeaderApp": "App",
"HeaderAppearsOn": "Appears On",
"HeaderAudioBooks": "Audio Books",
+ "HeaderAudioAdvanced": "Audio Advanced",
"HeaderAudioSettings": "Audio Settings",
"HeaderAutoDiscovery": "Network Discovery",
"HeaderBlockItemsWithNoRating": "Block items with no or unrecognized rating information",
@@ -564,6 +568,8 @@
"LabelAllowedRemoteAddresses": "Remote IP address filter",
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
"LabelAllowHWTranscoding": "Allow hardware transcoding",
+ "LabelAlwaysRemuxFlacAudioFiles": "Always remux FLAC audio files",
+ "LabelAlwaysRemuxMp3AudioFiles": "Always remux MP3 audio files",
"LabelAppName": "App name",
"LabelAppNameExample": "A human readable name for identifying API keys. This setting will not affect functionality.",
"LabelArtists": "Artists",
@@ -635,6 +641,7 @@
"LabelDeinterlaceMethod": "Deinterlacing method",
"LabelDeveloper": "Developer",
"LabelDisableCustomCss": "Disable custom CSS code for theming/branding provided from the server.",
+ "LabelDisableVbrAudioEncoding": "Disable VBR audio encoding",
"LabelDiscNumber": "Disc number",
"LabelDisplayLanguage": "Display language",
"LabelDisplayLanguageHelp": "Translating Jellyfin is an ongoing project.",