mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
adjust bitrate limit for HLS audio codecs
This commit is contained in:
parent
0b01acbe91
commit
57e5b59b93
@ -222,7 +222,7 @@ namespace Jellyfin.Api.Helpers
|
||||
|
||||
EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
|
||||
var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
|
||||
var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest.AudioBitRate, state.AudioStream) ?? 0;
|
||||
var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
|
||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
||||
|
||||
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
||||
|
@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
|
||||
|
||||
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
||||
|
||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, state.AudioStream);
|
||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
|
||||
|
||||
state.OutputAudioCodec = streamingRequest.AudioCodec;
|
||||
|
||||
@ -196,7 +196,27 @@ namespace Jellyfin.Api.Helpers
|
||||
|
||||
encodingHelper.TryStreamCopy(state);
|
||||
|
||||
if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
|
||||
if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.OutputVideoBitrate.HasValue)
|
||||
{
|
||||
var isVideoResolutionNotRequested = !state.VideoRequest.Width.HasValue
|
||||
&& !state.VideoRequest.Height.HasValue
|
||||
&& !state.VideoRequest.MaxWidth.HasValue
|
||||
&& !state.VideoRequest.MaxHeight.HasValue;
|
||||
|
||||
if (isVideoResolutionNotRequested
|
||||
&& state.VideoRequest.VideoBitRate.HasValue
|
||||
&& state.VideoStream.BitRate.HasValue
|
||||
&& state.VideoRequest.VideoBitRate.Value >= state.VideoStream.BitRate.Value)
|
||||
{
|
||||
// Don't downscale the resolution if the width/height/MaxWidth/MaxHeight is not requested,
|
||||
// and the requested video bitrate is higher than source video bitrate.
|
||||
if (state.VideoStream.Width.HasValue || state.VideoStream.Height.HasValue)
|
||||
{
|
||||
state.VideoRequest.MaxWidth = state.VideoStream?.Width;
|
||||
state.VideoRequest.MaxHeight = state.VideoStream?.Height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var resolution = ResolutionNormalizer.Normalize(
|
||||
state.VideoStream?.BitRate,
|
||||
@ -212,6 +232,7 @@ namespace Jellyfin.Api.Helpers
|
||||
state.VideoRequest.MaxHeight = resolution.MaxHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static);
|
||||
|
||||
|
@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return .5;
|
||||
return .6;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1424,36 +1424,48 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
|
||||
{
|
||||
if (audioStream == null)
|
||||
{
|
||||
return null;
|
||||
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
|
||||
}
|
||||
|
||||
if (request.AudioBitRate.HasValue)
|
||||
{
|
||||
// Don't encode any higher than this
|
||||
return Math.Min(384000, request.AudioBitRate.Value);
|
||||
}
|
||||
|
||||
// Empty bitrate area is not allow on iOS
|
||||
// Default audio bitrate to 128K if it is not being requested
|
||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
||||
return 128000;
|
||||
}
|
||||
|
||||
public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream)
|
||||
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
|
||||
{
|
||||
if (audioStream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (audioBitRate.HasValue)
|
||||
if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
|
||||
{
|
||||
// Don't encode any higher than this
|
||||
return Math.Min(384000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
|
||||
{
|
||||
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
{
|
||||
return Math.Min(640000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
return Math.Min(384000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
{
|
||||
return Math.Min(3584000, audioBitRate.Value);
|
||||
}
|
||||
|
||||
return Math.Min(1536000, audioBitRate.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty bitrate area is not allow on iOS
|
||||
// Default audio bitrate to 128K if it is not being requested
|
||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
||||
|
@ -234,8 +234,8 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
|
||||
var channelsValue = channels.Value;
|
||||
|
||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (channelsValue <= 2)
|
||||
{
|
||||
@ -248,6 +248,34 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (channelsValue <= 2)
|
||||
{
|
||||
return 192000;
|
||||
}
|
||||
|
||||
if (channelsValue >= 5)
|
||||
{
|
||||
return 640000;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (channelsValue <= 2)
|
||||
{
|
||||
return 960000;
|
||||
}
|
||||
|
||||
if (channelsValue >= 5)
|
||||
{
|
||||
return 2880000;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -774,6 +802,35 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
stream.BitRate = bitrate;
|
||||
}
|
||||
|
||||
// Extract bitrate info from tag "BPS" if possible.
|
||||
if (!stream.BitRate.HasValue
|
||||
&& (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var bps = GetBPSFromTags(streamInfo);
|
||||
if (bps != null && bps > 0)
|
||||
{
|
||||
stream.BitRate = bps;
|
||||
}
|
||||
}
|
||||
|
||||
// Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible.
|
||||
if (!stream.BitRate.HasValue
|
||||
&& (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo);
|
||||
var bytes = GetNumberOfBytesFromTags(streamInfo);
|
||||
if (durationInSeconds != null && bytes != null)
|
||||
{
|
||||
var bps = Convert.ToInt32(bytes * 8 / durationInSeconds);
|
||||
if (bps > 0)
|
||||
{
|
||||
stream.BitRate = bps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var disposition = streamInfo.Disposition;
|
||||
if (disposition != null)
|
||||
{
|
||||
@ -963,6 +1020,57 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
}
|
||||
}
|
||||
|
||||
private int? GetBPSFromTags(MediaStreamInfo streamInfo)
|
||||
{
|
||||
if (streamInfo != null && streamInfo.Tags != null)
|
||||
{
|
||||
var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS");
|
||||
if (!string.IsNullOrEmpty(bps))
|
||||
{
|
||||
if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps))
|
||||
{
|
||||
return parsedBps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo)
|
||||
{
|
||||
if (streamInfo != null && streamInfo.Tags != null)
|
||||
{
|
||||
var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION");
|
||||
if (!string.IsNullOrEmpty(duration))
|
||||
{
|
||||
if (TimeSpan.TryParse(duration, out var parsedDuration))
|
||||
{
|
||||
return parsedDuration.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo)
|
||||
{
|
||||
if (streamInfo != null && streamInfo.Tags != null)
|
||||
{
|
||||
var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES");
|
||||
if (!string.IsNullOrEmpty(numberOfBytes))
|
||||
{
|
||||
if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes))
|
||||
{
|
||||
return parsedBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetSize(InternalMediaInfoResult data, MediaInfo info)
|
||||
{
|
||||
if (data.Format != null)
|
||||
|
@ -79,11 +79,11 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
private static double GetVideoBitrateScaleFactor(string codec)
|
||||
{
|
||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return .5;
|
||||
return .6;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -872,11 +872,34 @@ namespace MediaBrowser.Model.Dlna
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
private static int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
|
||||
private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels)
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
if (!string.IsNullOrEmpty(audioCodec))
|
||||
{
|
||||
return 384000;
|
||||
// Default to a higher bitrate for stream copy
|
||||
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if ((audioChannels ?? 0) < 2)
|
||||
{
|
||||
return 128000;
|
||||
}
|
||||
|
||||
return (audioChannels ?? 0) >= 6 ? 640000 : 384000;
|
||||
}
|
||||
|
||||
if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if ((audioChannels ?? 0) < 2)
|
||||
{
|
||||
return 768000;
|
||||
}
|
||||
|
||||
return (audioChannels ?? 0) >= 6 ? 3584000 : 1536000;
|
||||
}
|
||||
}
|
||||
|
||||
return 192000;
|
||||
@ -897,14 +920,27 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetAudioChannels.HasValue && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
|
||||
if (targetAudioChannels.HasValue
|
||||
&& audioStream.Channels.HasValue
|
||||
&& audioStream.Channels.Value > targetAudioChannels.Value)
|
||||
{
|
||||
// Reduce the bitrate if we're downmixing
|
||||
defaultBitrate = targetAudioChannels.Value < 2 ? 128000 : 192000;
|
||||
// Reduce the bitrate if we're downmixing.
|
||||
defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
|
||||
}
|
||||
else if (targetAudioChannels.HasValue
|
||||
&& audioStream.Channels.HasValue
|
||||
&& audioStream.Channels.Value <= targetAudioChannels.Value
|
||||
&& !string.IsNullOrEmpty(audioStream.Codec)
|
||||
&& targetAudioCodecs != null
|
||||
&& targetAudioCodecs.Length > 0
|
||||
&& !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// Shift the bitrate if we're transcoding to a different audio codec.
|
||||
defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, audioStream.Channels.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
|
||||
defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
|
||||
}
|
||||
|
||||
// Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
|
||||
@ -938,9 +974,29 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
return 448000;
|
||||
}
|
||||
|
||||
else if (totalBitrate <= 4000000)
|
||||
{
|
||||
return 640000;
|
||||
}
|
||||
else if (totalBitrate <= 5000000)
|
||||
{
|
||||
return 768000;
|
||||
}
|
||||
else if (totalBitrate <= 10000000)
|
||||
{
|
||||
return 1536000;
|
||||
}
|
||||
else if (totalBitrate <= 15000000)
|
||||
{
|
||||
return 2304000;
|
||||
}
|
||||
else if (totalBitrate <= 20000000)
|
||||
{
|
||||
return 3584000;
|
||||
}
|
||||
|
||||
return 7168000;
|
||||
}
|
||||
|
||||
private (PlayMethod?, List<TranscodeReason>) GetVideoDirectPlayProfile(
|
||||
VideoOptions options,
|
||||
|
@ -787,7 +787,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
public int? GetTargetAudioChannels(string codec)
|
||||
{
|
||||
var defaultValue = GlobalMaxAudioChannels;
|
||||
var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
|
||||
|
||||
var value = GetOption(codec, "audiochannels");
|
||||
if (string.IsNullOrEmpty(value))
|
||||
|
Loading…
x
Reference in New Issue
Block a user