From 374b6ca0e22719df25b8d0aa51c510515b928104 Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 23 Apr 2024 00:23:36 +0800 Subject: Only apply custom downmix to 5.1 audios (#11401) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 49fc2f3d7..24edd7958 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1712,12 +1712,11 @@ public class DynamicHlsController : BaseJellyfinApiController var channels = state.OutputAudioChannels; + var useDownMixAlgorithm = state.AudioStream.Channels is 6 && _encodingOptions.DownMixStereoAlgorithm != DownMixStereoAlgorithms.None; + if (channels.HasValue && (channels.Value != 2 - || (state.AudioStream is not null - && state.AudioStream.Channels.HasValue - && state.AudioStream.Channels.Value > 5 - && _encodingOptions.DownMixStereoAlgorithm == DownMixStereoAlgorithms.None))) + || (state.AudioStream?.Channels != null && !useDownMixAlgorithm))) { args += " -ac " + channels.Value; } -- cgit v1.2.3 From 2459b7e62e9c5da75963323ec3c6e43b8568370f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 14 Apr 2024 23:24:34 +0200 Subject: Properly await Task.Delay() --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 24edd7958..68602c80d 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1481,7 +1481,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (currentTranscodingIndex.HasValue) { - DeleteLastFile(playlistPath, segmentExtension, 0); + await DeleteLastFile(playlistPath, segmentExtension, 0).ConfigureAwait(false); } streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks; @@ -2009,17 +2009,19 @@ public class DynamicHlsController : BaseJellyfinApiController } } - private void DeleteLastFile(string playlistPath, string segmentExtension, int retryCount) + private Task DeleteLastFile(string playlistPath, string segmentExtension, int retryCount) { var file = GetLastTranscodingFile(playlistPath, segmentExtension, _fileSystem); - if (file is not null) + if (file is null) { - DeleteFile(file.FullName, retryCount); + return Task.CompletedTask; } + + return DeleteFile(file.FullName, retryCount); } - private void DeleteFile(string path, int retryCount) + private async Task DeleteFile(string path, int retryCount) { if (retryCount >= 5) { @@ -2036,9 +2038,8 @@ public class DynamicHlsController : BaseJellyfinApiController { _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path); - var task = Task.Delay(100); - task.Wait(); - DeleteFile(path, retryCount + 1); + await Task.Delay(100).ConfigureAwait(false); + await DeleteFile(path, retryCount + 1).ConfigureAwait(false); } catch (Exception ex) { -- cgit v1.2.3 From 78929418cc3aa783db3abaf99d15ba55e41cc2ac Mon Sep 17 00:00:00 2001 From: gnattu Date: Mon, 22 Apr 2024 21:02:20 +0800 Subject: Use better audio VBR settings LAME's VBR mode only has advantage over a certain bitrate range. For very low and very high bitrate, use the ABR mode instead. aac_at's CVBR mode produces very good quality and is not worse than its TVBR mode in blind testing. Use this mode for convenience. The ffmpeg native aac encoder will have quality regression with its VBR mode. Always use CBR mode for ffmpeg's native aac encoder. Signed-off-by: gnattu --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +-- .../MediaEncoding/EncodingHelper.cs | 33 +++++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 68602c80d..1e99a9ee3 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1671,7 +1671,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (audioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { - var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, audioBitrate.Value / (audioChannels ?? 2)); + var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, audioBitrate.Value, audioChannels ?? 2); if (_encodingOptions.EnableAudioVbr && vbrParam is not null) { audioTranscodeParams += vbrParam; @@ -1724,7 +1724,7 @@ public class DynamicHlsController : BaseJellyfinApiController var bitrate = state.OutputAudioBitrate; if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { - var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, bitrate.Value / (channels ?? 2)); + var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, bitrate.Value, channels ?? 2); if (_encodingOptions.EnableAudioVbr && vbrParam is not null) { args += vbrParam; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9d7d2fd12..a122f3d19 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2586,8 +2586,9 @@ namespace MediaBrowser.Controller.MediaEncoding return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2); } - public string GetAudioVbrModeParam(string encoder, int bitratePerChannel) + public string GetAudioVbrModeParam(string encoder, int bitrate, int channels) { + var bitratePerChannel = bitrate / channels; if (string.Equals(encoder, "libfdk_aac", StringComparison.OrdinalIgnoreCase)) { return " -vbr:a " + bitratePerChannel switch @@ -2602,14 +2603,26 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(encoder, "libmp3lame", StringComparison.OrdinalIgnoreCase)) { - return " -qscale:a " + bitratePerChannel switch + // lame's VBR is only good for a certain bitrate range + // For very low and very high bitrate, use abr mode + if (bitratePerChannel is < 122500 and > 48000) { - < 48000 => "8", - < 64000 => "6", - < 88000 => "4", - < 112000 => "2", - _ => "0" - }; + return " -qscale:a " + bitratePerChannel switch + { + < 64000 => "6", + < 88000 => "4", + < 112000 => "2", + _ => "0" + }; + } + + return " -abr:a 1" + " -b:a " + bitrate; + } + + if (string.Equals(encoder, "aac_at", StringComparison.OrdinalIgnoreCase)) + { + // aac_at's CVBR mode + return " -aac_at_mode:a 2" + " -b:a " + bitrate; } if (string.Equals(encoder, "libvorbis", StringComparison.OrdinalIgnoreCase)) @@ -6962,7 +6975,7 @@ namespace MediaBrowser.Controller.MediaEncoding var bitrate = state.OutputAudioBitrate; if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase)) { - var vbrParam = GetAudioVbrModeParam(codec, bitrate.Value / (channels ?? 2)); + var vbrParam = GetAudioVbrModeParam(codec, bitrate.Value, channels ?? 2); if (encodingOptions.EnableAudioVbr && vbrParam is not null) { args += vbrParam; @@ -6993,7 +7006,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase)) { - var vbrParam = GetAudioVbrModeParam(GetAudioEncoder(state), bitrate.Value / (channels ?? 2)); + var vbrParam = GetAudioVbrModeParam(GetAudioEncoder(state), bitrate.Value, channels ?? 2); if (encodingOptions.EnableAudioVbr && vbrParam is not null) { audioTranscodeParams.Add(vbrParam); -- cgit v1.2.3 From 0381c5a288bc56e20aa5def05e3d41bacf3519a7 Mon Sep 17 00:00:00 2001 From: gnattu Date: Mon, 6 May 2024 12:48:50 +0800 Subject: Add EnableAudioVbrEncoding to TranscodingProfile This will allow the client selectively disable VBR audio when it causes problems. Signed-off-by: gnattu --- Jellyfin.Api/Controllers/AudioController.cs | 14 ++++-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 53 +++++++++++++++------- Jellyfin.Api/Controllers/VideosController.cs | 14 ++++-- .../MediaEncoding/BaseEncodingJobOptions.cs | 2 + .../MediaEncoding/EncodingHelper.cs | 4 +- .../MediaEncoding/EncodingJobInfo.cs | 8 ++++ MediaBrowser.Model/Dlna/StreamBuilder.cs | 1 + MediaBrowser.Model/Dlna/StreamInfo.cs | 4 ++ MediaBrowser.Model/Dlna/TranscodingProfile.cs | 4 ++ 9 files changed, 78 insertions(+), 26 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 72be55513..06b7e6c9f 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -83,6 +83,7 @@ public class AudioController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Audio stream returned. /// A containing the audio file. [HttpGet("{itemId}/stream", Name = "GetAudioStream")] @@ -138,7 +139,8 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary? streamOptions) + [FromQuery] Dictionary? streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { StreamingRequestDto streamingRequest = new StreamingRequestDto { @@ -189,7 +191,8 @@ public class AudioController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Static, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await _audioHelper.GetAudioStream(_transcodingJobType, streamingRequest).ConfigureAwait(false); @@ -247,6 +250,7 @@ public class AudioController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Audio stream returned. /// A containing the audio file. [HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")] @@ -302,7 +306,8 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary? streamOptions) + [FromQuery] Dictionary? streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { StreamingRequestDto streamingRequest = new StreamingRequestDto { @@ -353,7 +358,8 @@ public class AudioController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Static, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await _audioHelper.GetAudioStream(_transcodingJobType, streamingRequest).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 1e99a9ee3..a7fc9928e 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -156,6 +156,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The max width. /// Optional. The max height. /// Optional. Whether to enable subtitles in the manifest. + /// Optional. Whether to enable Audio Encoding. /// Hls live stream retrieved. /// A containing the hls file. [HttpGet("Videos/{itemId}/live.m3u8")] @@ -213,7 +214,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] Dictionary streamOptions, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, - [FromQuery] bool? enableSubtitlesInManifest) + [FromQuery] bool? enableSubtitlesInManifest, + [FromQuery] bool? enableAudioVbrEncoding) { VideoRequestDto streamingRequest = new VideoRequestDto { @@ -267,7 +269,8 @@ public class DynamicHlsController : BaseJellyfinApiController StreamOptions = streamOptions, MaxHeight = maxHeight, MaxWidth = maxWidth, - EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true + EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; // CTS lifecycle is managed internally. @@ -393,6 +396,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The streaming options. /// Enable adaptive bitrate streaming. /// Enable trickplay image playlists being added to master playlist. + /// Whether to enable Audio Encoding. /// Video stream returned. /// A containing the playlist file. [HttpGet("Videos/{itemId}/master.m3u8")] @@ -451,7 +455,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true, - [FromQuery] bool enableTrickplay = true) + [FromQuery] bool enableTrickplay = true, + [FromQuery] bool enableAudioVbrEncoding = true) { var streamingRequest = new HlsVideoRequestDto { @@ -505,7 +510,8 @@ public class DynamicHlsController : BaseJellyfinApiController Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming, - EnableTrickplay = enableTrickplay + EnableTrickplay = enableTrickplay, + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); @@ -564,6 +570,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The . /// Optional. The streaming options. /// Enable adaptive bitrate streaming. + /// Optional. Whether to enable Audio Encoding. /// Audio stream returned. /// A containing the playlist file. [HttpGet("Audio/{itemId}/master.m3u8")] @@ -620,7 +627,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool enableAdaptiveBitrateStreaming = true) + [FromQuery] bool enableAdaptiveBitrateStreaming = true, + [FromQuery] bool enableAudioVbrEncoding = true) { var streamingRequest = new HlsAudioRequestDto { @@ -671,7 +679,8 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming + EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming, + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); @@ -730,6 +739,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Videos/{itemId}/main.m3u8")] @@ -785,7 +795,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new VideoRequestDto @@ -838,7 +849,8 @@ public class DynamicHlsController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -897,6 +909,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Audio stream returned. /// A containing the audio file. [HttpGet("Audio/{itemId}/main.m3u8")] @@ -951,7 +964,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new StreamingRequestDto @@ -1002,7 +1016,8 @@ public class DynamicHlsController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -1067,6 +1082,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] @@ -1128,7 +1144,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { var streamingRequest = new VideoRequestDto { @@ -1183,7 +1200,8 @@ public class DynamicHlsController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await GetDynamicSegment(streamingRequest, segmentId) @@ -1247,6 +1265,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] @@ -1307,7 +1326,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { var streamingRequest = new StreamingRequestDto { @@ -1360,7 +1380,8 @@ public class DynamicHlsController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; return await GetDynamicSegment(streamingRequest, segmentId) @@ -1672,7 +1693,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (audioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, audioBitrate.Value, audioChannels ?? 2); - if (_encodingOptions.EnableAudioVbr && vbrParam is not null) + if (_encodingOptions.EnableAudioVbr && state.EnableAudioVbrEncoding && vbrParam is not null) { audioTranscodeParams += vbrParam; } @@ -1725,7 +1746,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { var vbrParam = _encodingHelper.GetAudioVbrModeParam(audioCodec, bitrate.Value, channels ?? 2); - if (_encodingOptions.EnableAudioVbr && vbrParam is not null) + if (_encodingOptions.EnableAudioVbr && state.EnableAudioVbrEncoding && vbrParam is not null) { args += vbrParam; } diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index a9e1d4484..f6050bdf7 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -306,6 +306,7 @@ public class VideosController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Video stream returned. /// A containing the audio file. [HttpGet("{itemId}/stream")] @@ -363,7 +364,8 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; // CTS lifecycle is managed internally. @@ -419,7 +421,8 @@ public class VideosController : BaseJellyfinApiController AudioStreamIndex = audioStreamIndex, VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, - StreamOptions = streamOptions + StreamOptions = streamOptions, + EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true }; var state = await StreamingHelpers.GetStreamingState( @@ -544,6 +547,7 @@ public class VideosController : BaseJellyfinApiController /// Optional. The index of the video stream to use. If omitted the first video stream will be used. /// Optional. The . /// Optional. The streaming options. + /// Optional. Whether to enable Audio Encoding. /// Video stream returned. /// A containing the audio file. [HttpGet("{itemId}/stream.{container}")] @@ -601,7 +605,8 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, - [FromQuery] Dictionary streamOptions) + [FromQuery] Dictionary streamOptions, + [FromQuery] bool? enableAudioVbrEncoding) { return GetVideoStream( itemId, @@ -654,6 +659,7 @@ public class VideosController : BaseJellyfinApiController audioStreamIndex, videoStreamIndex, context, - streamOptions); + streamOptions, + enableAudioVbrEncoding); } } diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs index 29dd190ab..03ec6c658 100644 --- a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs @@ -191,6 +191,8 @@ namespace MediaBrowser.Controller.MediaEncoding public Dictionary StreamOptions { get; set; } + public bool EnableAudioVbrEncoding { get; set; } + public string GetOption(string qualifier, string name) { var value = GetOption(qualifier + "-" + name); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a122f3d19..ce2414cd1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -6976,7 +6976,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase)) { var vbrParam = GetAudioVbrModeParam(codec, bitrate.Value, channels ?? 2); - if (encodingOptions.EnableAudioVbr && vbrParam is not null) + if (encodingOptions.EnableAudioVbr && state.EnableAudioVbrEncoding && vbrParam is not null) { args += vbrParam; } @@ -7007,7 +7007,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase)) { var vbrParam = GetAudioVbrModeParam(GetAudioEncoder(state), bitrate.Value, channels ?? 2); - if (encodingOptions.EnableAudioVbr && vbrParam is not null) + if (encodingOptions.EnableAudioVbr && state.EnableAudioVbrEncoding && vbrParam is not null) { audioTranscodeParams.Add(vbrParam); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index f2a0b906d..526aa8f99 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -508,6 +508,14 @@ namespace MediaBrowser.Controller.MediaEncoding } } + public bool EnableAudioVbrEncoding + { + get + { + return BaseRequest.EnableAudioVbrEncoding; + } + } + public int HlsListSize => 0; public bool EnableBreakOnNonKeyFrames(string videoCodec) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 55d1c3d51..ba958c030 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -542,6 +542,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; playlistItem.BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames; + playlistItem.EnableAudioVbrEncoding = transcodingProfile.EnableAudioVbrEncoding; if (transcodingProfile.MinSegments > 0) { diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 75e5b6d18..c8a341d41 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -108,6 +108,8 @@ namespace MediaBrowser.Model.Dlna public string? MediaSourceId => MediaSource?.Id; + public bool EnableAudioVbrEncoding { get; set; } + public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay) && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay; @@ -768,6 +770,8 @@ namespace MediaBrowser.Model.Dlna } list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); + + list.Add(new NameValuePair("EnableAudioVbrEncoding", item.EnableAudioVbrEncoding.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); } list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty)); diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index 891448c66..d535e8c18 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -70,6 +70,10 @@ namespace MediaBrowser.Model.Dlna public ProfileCondition[] Conditions { get; set; } + [DefaultValue(true)] + [XmlAttribute("enableAudioVbrEncoding")] + public bool EnableAudioVbrEncoding { get; set; } + public string[] GetAudioCodecs() { return ContainerProfile.SplitValue(AudioCodec); -- cgit v1.2.3 From fd5df98616408fe54944ff39310aacd1751c45b5 Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Jul 2024 21:52:44 +0800 Subject: Move default value to api spec Signed-off-by: gnattu --- Jellyfin.Api/Controllers/AudioController.cs | 4 ++-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 20 ++++++++++---------- Jellyfin.Api/Controllers/UniversalAudioController.cs | 4 ++-- Jellyfin.Api/Controllers/VideosController.cs | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index c62005735..8954c8ef5 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -307,7 +307,7 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary? streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { StreamingRequestDto streamingRequest = new StreamingRequestDto { @@ -359,7 +359,7 @@ public class AudioController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Static, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await _audioHelper.GetAudioStream(_transcodingJobType, streamingRequest).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index a7fc9928e..429cc542c 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -215,7 +215,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, [FromQuery] bool? enableSubtitlesInManifest, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { VideoRequestDto streamingRequest = new VideoRequestDto { @@ -270,7 +270,7 @@ public class DynamicHlsController : BaseJellyfinApiController MaxHeight = maxHeight, MaxWidth = maxWidth, EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; // CTS lifecycle is managed internally. @@ -796,7 +796,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new VideoRequestDto @@ -850,7 +850,7 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -965,7 +965,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new StreamingRequestDto @@ -1017,7 +1017,7 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -1145,7 +1145,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { var streamingRequest = new VideoRequestDto { @@ -1201,7 +1201,7 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await GetDynamicSegment(streamingRequest, segmentId) @@ -1327,7 +1327,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { var streamingRequest = new StreamingRequestDto { @@ -1381,7 +1381,7 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await GetDynamicSegment(streamingRequest, segmentId) diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index c858c17ba..908794512 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -112,7 +112,7 @@ public class UniversalAudioController : BaseJellyfinApiController [FromQuery] int? maxAudioSampleRate, [FromQuery] int? maxAudioBitDepth, [FromQuery] bool? enableRemoteMedia, - [FromQuery] bool? enableAudioVbrEncoding, + [FromQuery] bool enableAudioVbrEncoding = true, [FromQuery] bool breakOnNonKeyFrames = false, [FromQuery] bool enableRedirection = true) { @@ -212,7 +212,7 @@ public class UniversalAudioController : BaseJellyfinApiController Context = EncodingContext.Static, StreamOptions = new Dictionary(), EnableAdaptiveBitrateStreaming = true, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType.Hls, dynamicHlsRequestDto, true) diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index f6050bdf7..7f9608378 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -365,7 +365,7 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { var isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head; // CTS lifecycle is managed internally. @@ -422,7 +422,7 @@ public class VideosController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding ?? true + EnableAudioVbrEncoding = enableAudioVbrEncoding }; var state = await StreamingHelpers.GetStreamingState( @@ -606,7 +606,7 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool? enableAudioVbrEncoding) + [FromQuery] bool enableAudioVbrEncoding = true) { return GetVideoStream( itemId, -- cgit v1.2.3 From 8851ace5436c6a22b9489f88d20e1aab582ffddf Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 18 Jul 2024 00:35:40 +0800 Subject: Enable Dolby AC-4 decoder (#11486) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 7 +++++++ MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 2 files changed, 8 insertions(+) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 68602c80d..2201225a8 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1735,6 +1735,13 @@ public class DynamicHlsController : BaseJellyfinApiController } } + if (state.AudioStream is not null && state.AudioStream.CodecTag.Equals("ac-4", StringComparison.Ordinal)) + { + // ac-4 audio tends to hava a super weird sample rate that will fail most encoders + // force resample it to 48KHz + args += " -ar 48000"; + } + if (state.OutputAudioSampleRate.HasValue) { args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 30bb21dcb..06e2d3783 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "msmpeg4", "dca", "ac3", + "ac4", "aac", "mp3", "flac", -- cgit v1.2.3 From 992eed5ef7f2cfb3882e5670cca76b09d87cf9df Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 18 Jul 2024 11:45:16 +0800 Subject: Fix AC-4 Sample rate check Some Audio codec will have a null CodecTag, check for that to avoid null reference If the client already requests a specific sample rate, use that instead of our default sample rate for AC-4 Signed-off-by: gnattu --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 2201225a8..ccaa5b19a 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1735,18 +1735,17 @@ public class DynamicHlsController : BaseJellyfinApiController } } - if (state.AudioStream is not null && state.AudioStream.CodecTag.Equals("ac-4", StringComparison.Ordinal)) + if (state.OutputAudioSampleRate.HasValue) + { + args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); + } + else if (state.AudioStream?.CodecTag is not null && state.AudioStream.CodecTag.Equals("ac-4", StringComparison.Ordinal)) { // ac-4 audio tends to hava a super weird sample rate that will fail most encoders // force resample it to 48KHz args += " -ar 48000"; } - if (state.OutputAudioSampleRate.HasValue) - { - args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); - } - args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions); return args; -- cgit v1.2.3