adjust bitrate limit for HLS audio codecs

This commit is contained in:
nyanmisaka 2020-11-11 17:08:50 +08:00
parent 0b01acbe91
commit 57e5b59b93
7 changed files with 245 additions and 48 deletions

View File

@ -222,7 +222,7 @@ namespace Jellyfin.Api.Helpers
EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration); EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; 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; var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);

View File

@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.'); 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; state.OutputAudioCodec = streamingRequest.AudioCodec;
@ -196,7 +196,27 @@ namespace Jellyfin.Api.Helpers
encodingHelper.TryStreamCopy(state); 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( var resolution = ResolutionNormalizer.Normalize(
state.VideoStream?.BitRate, state.VideoStream?.BitRate,
@ -212,6 +232,7 @@ namespace Jellyfin.Api.Helpers
state.VideoRequest.MaxHeight = resolution.MaxHeight; state.VideoRequest.MaxHeight = resolution.MaxHeight;
} }
} }
}
ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static); ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static);

View File

@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
{ {
return .5; return .6;
} }
return 1; return 1;
@ -1424,36 +1424,48 @@ namespace MediaBrowser.Controller.MediaEncoding
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream) public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
{ {
if (audioStream == null) return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
{
return null;
} }
if (request.AudioBitRate.HasValue) public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
{
// 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)
{ {
if (audioStream == null) if (audioStream == null)
{ {
return 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); 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 // Empty bitrate area is not allow on iOS
// Default audio bitrate to 128K if it is not being requested // Default audio bitrate to 128K if it is not being requested
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options

View File

@ -234,8 +234,8 @@ namespace MediaBrowser.MediaEncoding.Probing
var channelsValue = channels.Value; var channelsValue = channels.Value;
if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) || if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)
string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) || string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
{ {
if (channelsValue <= 2) 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; return null;
} }
@ -774,6 +802,35 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.BitRate = bitrate; 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; var disposition = streamInfo.Disposition;
if (disposition != null) 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) private void SetSize(InternalMediaInfoResult data, MediaInfo info)
{ {
if (data.Format != null) if (data.Format != null)

View File

@ -79,11 +79,11 @@ namespace MediaBrowser.Model.Dlna
private static double GetVideoBitrateScaleFactor(string codec) private static double GetVideoBitrateScaleFactor(string codec)
{ {
if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
{ {
return .5; return .6;
} }
return 1; return 1;

View File

@ -872,11 +872,34 @@ namespace MediaBrowser.Model.Dlna
return playlistItem; 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; return 192000;
@ -897,14 +920,27 @@ namespace MediaBrowser.Model.Dlna
} }
else 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 // Reduce the bitrate if we're downmixing.
defaultBitrate = targetAudioChannels.Value < 2 ? 128000 : 192000; 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 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. // Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
@ -938,9 +974,29 @@ namespace MediaBrowser.Model.Dlna
{ {
return 448000; return 448000;
} }
else if (totalBitrate <= 4000000)
{
return 640000; 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( private (PlayMethod?, List<TranscodeReason>) GetVideoDirectPlayProfile(
VideoOptions options, VideoOptions options,

View File

@ -787,7 +787,7 @@ namespace MediaBrowser.Model.Dlna
public int? GetTargetAudioChannels(string codec) public int? GetTargetAudioChannels(string codec)
{ {
var defaultValue = GlobalMaxAudioChannels; var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
var value = GetOption(codec, "audiochannels"); var value = GetOption(codec, "audiochannels");
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))