mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge pull request #9409 from Shadowghost/output-bitrate-channels-release
Multiple HLS codec and bitrate fixes (10.8.z)
This commit is contained in:
commit
e6313d01eb
@ -13,6 +13,7 @@ using Jellyfin.Api.Constants;
|
|||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.Models.PlaybackDtos;
|
using Jellyfin.Api.Models.PlaybackDtos;
|
||||||
using Jellyfin.Api.Models.StreamingDtos;
|
using Jellyfin.Api.Models.StreamingDtos;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using Jellyfin.MediaEncoding.Hls.Playlist;
|
using Jellyfin.MediaEncoding.Hls.Playlist;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -1694,7 +1695,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
|
|
||||||
audioTranscodeParams += "-acodec " + audioCodec;
|
audioTranscodeParams += "-acodec " + audioCodec;
|
||||||
|
|
||||||
if (state.OutputAudioBitrate.HasValue)
|
if (state.OutputAudioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture);
|
audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
@ -1715,11 +1716,11 @@ namespace Jellyfin.Api.Controllers
|
|||||||
|
|
||||||
// dts, flac, opus and truehd are experimental in mp4 muxer
|
// dts, flac, opus and truehd are experimental in mp4 muxer
|
||||||
var strictArgs = string.Empty;
|
var strictArgs = string.Empty;
|
||||||
|
var actualOutputAudioCodec = state.ActualOutputAudioCodec;
|
||||||
if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(state.ActualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(state.ActualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
strictArgs = " -strict -2";
|
strictArgs = " -strict -2";
|
||||||
}
|
}
|
||||||
@ -1748,8 +1749,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bitrate = state.OutputAudioBitrate;
|
var bitrate = state.OutputAudioBitrate;
|
||||||
|
if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||||
if (bitrate.HasValue)
|
|
||||||
{
|
{
|
||||||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Models.StreamingDtos;
|
using Jellyfin.Api.Models.StreamingDtos;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
@ -203,6 +204,13 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
if (state.VideoStream != null && state.VideoRequest != null)
|
if (state.VideoStream != null && state.VideoRequest != null)
|
||||||
{
|
{
|
||||||
|
// Provide a workaround for the case issue between flac and fLaC.
|
||||||
|
var flacWaPlaylist = ApplyFlacCaseWorkaround(state, basicPlaylist.ToString());
|
||||||
|
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||||
|
{
|
||||||
|
builder.Append(flacWaPlaylist);
|
||||||
|
}
|
||||||
|
|
||||||
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
||||||
|
|
||||||
// Provide SDR HEVC entrance for backward compatibility.
|
// Provide SDR HEVC entrance for backward compatibility.
|
||||||
@ -221,10 +229,25 @@ namespace Jellyfin.Api.Helpers
|
|||||||
sdrVideoUrl += "&AllowVideoStreamCopy=false";
|
sdrVideoUrl += "&AllowVideoStreamCopy=false";
|
||||||
|
|
||||||
var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
|
var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
|
||||||
var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
|
var sdrOutputAudioBitrate = 0;
|
||||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
if (EncodingHelper.LosslessAudioCodecs.Contains(state.VideoRequest.AudioCodec, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
sdrOutputAudioBitrate = state.AudioStream.BitRate ?? 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream, state.OutputAudioChannels) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
|
||||||
|
var sdrPlaylist = AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
|
||||||
|
|
||||||
|
// Provide a workaround for the case issue between flac and fLaC.
|
||||||
|
flacWaPlaylist = ApplyFlacCaseWorkaround(state, sdrPlaylist.ToString());
|
||||||
|
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||||
|
{
|
||||||
|
builder.Append(flacWaPlaylist);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the video codec
|
// Restore the video codec
|
||||||
state.OutputVideoCodec = "copy";
|
state.OutputVideoCodec = "copy";
|
||||||
@ -254,6 +277,13 @@ namespace Jellyfin.Api.Helpers
|
|||||||
state.VideoStream.Level = originalLevel;
|
state.VideoStream.Level = originalLevel;
|
||||||
var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField);
|
var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField);
|
||||||
builder.Append(newPlaylist);
|
builder.Append(newPlaylist);
|
||||||
|
|
||||||
|
// Provide a workaround for the case issue between flac and fLaC.
|
||||||
|
flacWaPlaylist = ApplyFlacCaseWorkaround(state, newPlaylist);
|
||||||
|
if (!string.IsNullOrEmpty(flacWaPlaylist))
|
||||||
|
{
|
||||||
|
builder.Append(flacWaPlaylist);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,6 +642,11 @@ namespace Jellyfin.Api.Helpers
|
|||||||
return HlsCodecStringHelpers.GetALACString();
|
return HlsCodecStringHelpers.GetALACString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return HlsCodecStringHelpers.GetOPUSString();
|
||||||
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,7 +745,19 @@ namespace Jellyfin.Api.Helpers
|
|||||||
return oldPlaylist.Replace(
|
return oldPlaylist.Replace(
|
||||||
oldValue.ToString(),
|
oldValue.ToString(),
|
||||||
newValue.ToString(),
|
newValue.ToString(),
|
||||||
StringComparison.OrdinalIgnoreCase);
|
StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ApplyFlacCaseWorkaround(StreamState state, string srcPlaylist)
|
||||||
|
{
|
||||||
|
if (!string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPlaylist = srcPlaylist.Replace(",flac\"", ",fLaC\"", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
return newPlaylist.Contains(",fLaC\"", StringComparison.Ordinal) ? newPlaylist : string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,18 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Codec name for FLAC.
|
/// Codec name for FLAC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string FLAC = "fLaC";
|
public const string FLAC = "flac";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Codec name for ALAC.
|
/// Codec name for ALAC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ALAC = "alac";
|
public const string ALAC = "alac";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Codec name for OPUS.
|
||||||
|
/// </summary>
|
||||||
|
public const string OPUS = "opus";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a MP3 codec string.
|
/// Gets a MP3 codec string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -101,6 +106,15 @@ namespace Jellyfin.Api.Helpers
|
|||||||
return ALAC;
|
return ALAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an OPUS codec string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>OPUS codec string.</returns>
|
||||||
|
public static string GetOPUSString()
|
||||||
|
{
|
||||||
|
return OPUS;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a H.264 codec string.
|
/// Gets a H.264 codec string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -182,12 +182,18 @@ namespace Jellyfin.Api.Helpers
|
|||||||
: GetOutputFileExtension(state, mediaSource);
|
: GetOutputFileExtension(state, mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outputAudioCodec = streamingRequest.AudioCodec;
|
||||||
|
if (EncodingHelper.LosslessAudioCodecs.Contains(outputAudioCodec))
|
||||||
|
{
|
||||||
|
state.OutputAudioBitrate = state.AudioStream.BitRate ?? 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream, state.OutputAudioChannels) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.OutputAudioCodec = outputAudioCodec;
|
||||||
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
|
||||||
|
|
||||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
|
|
||||||
|
|
||||||
state.OutputAudioCodec = streamingRequest.AudioCodec;
|
|
||||||
|
|
||||||
state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
|
state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
|
||||||
|
|
||||||
if (state.VideoRequest != null)
|
if (state.VideoRequest != null)
|
||||||
|
@ -62,6 +62,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
"Main10"
|
"Main10"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly string[] LosslessAudioCodecs = new string[]
|
||||||
|
{
|
||||||
|
"alac",
|
||||||
|
"ape",
|
||||||
|
"flac",
|
||||||
|
"mlp",
|
||||||
|
"truehd",
|
||||||
|
"wavpack"
|
||||||
|
};
|
||||||
|
|
||||||
public EncodingHelper(
|
public EncodingHelper(
|
||||||
IApplicationPaths appPaths,
|
IApplicationPaths appPaths,
|
||||||
IMediaEncoder mediaEncoder,
|
IMediaEncoder mediaEncoder,
|
||||||
@ -548,6 +558,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return "flac";
|
return "flac";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "dca";
|
||||||
|
}
|
||||||
|
|
||||||
return codec.ToLowerInvariant();
|
return codec.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1955,9 +1970,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video bitrate must fall within requested value
|
// Audio bitrate must fall within requested value
|
||||||
if (request.AudioBitRate.HasValue
|
if (request.AudioBitRate.HasValue
|
||||||
&& audioStream.BitDepth.HasValue
|
&& audioStream.BitRate.HasValue
|
||||||
&& audioStream.BitRate.Value > request.AudioBitRate.Value)
|
&& audioStream.BitRate.Value > request.AudioBitRate.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -2066,56 +2081,55 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return Convert.ToInt32(scaleFactor * bitrate);
|
return Convert.ToInt32(scaleFactor * bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
|
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream, int? outputAudioChannels)
|
||||||
{
|
{
|
||||||
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
|
return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream, outputAudioChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
|
public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream, int? outputAudioChannels)
|
||||||
{
|
{
|
||||||
if (audioStream == null)
|
if (audioStream == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
|
var inputChannels = audioStream.Channels ?? 0;
|
||||||
|
var outputChannels = outputAudioChannels ?? 0;
|
||||||
|
var bitrate = audioBitRate ?? int.MaxValue;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(audioCodec)
|
||||||
|
|| string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return Math.Min(384000, audioBitRate.Value);
|
return (inputChannels, outputChannels) switch
|
||||||
|
{
|
||||||
|
(>= 6, >= 6 or 0) => Math.Min(640000, bitrate),
|
||||||
|
(> 0, > 0) => Math.Min(outputChannels * 128000, bitrate),
|
||||||
|
(> 0, _) => Math.Min(inputChannels * 128000, bitrate),
|
||||||
|
(_, _) => Math.Min(384000, bitrate)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
|
if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
|
return (inputChannels, outputChannels) switch
|
||||||
|| string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
if ((audioStream.Channels ?? 0) >= 6)
|
(>= 6, >= 6 or 0) => Math.Min(768000, bitrate),
|
||||||
{
|
(> 0, > 0) => Math.Min(outputChannels * 136000, bitrate),
|
||||||
return Math.Min(640000, audioBitRate.Value);
|
(> 0, _) => Math.Min(inputChannels * 136000, bitrate),
|
||||||
}
|
(_, _) => Math.Min(672000, bitrate)
|
||||||
|
};
|
||||||
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 per channel if we don't have codec specific defaults
|
||||||
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
// https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
|
||||||
return 128000;
|
return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions)
|
public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||||
@ -5285,15 +5299,23 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputChannels = audioStream == null ? 6 : audioStream.Channels ?? 6;
|
var inputChannels = audioStream is null ? 6 : audioStream.Channels ?? 6;
|
||||||
|
var shiftAudioCodecs = new List<string>();
|
||||||
if (inputChannels >= 6)
|
if (inputChannels >= 6)
|
||||||
{
|
{
|
||||||
return;
|
// DTS and TrueHD are not supported by HLS
|
||||||
|
// Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
|
||||||
|
shiftAudioCodecs.Add("dca");
|
||||||
|
shiftAudioCodecs.Add("truehd");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Transcoding to 2ch ac3 or eac3 almost always causes a playback failure
|
||||||
|
// Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
|
||||||
|
shiftAudioCodecs.Add("ac3");
|
||||||
|
shiftAudioCodecs.Add("eac3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transcoding to 2ch ac3 almost always causes a playback failure
|
|
||||||
// Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used
|
|
||||||
var shiftAudioCodecs = new[] { "ac3", "eac3" };
|
|
||||||
if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -5537,7 +5559,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
var bitrate = state.OutputAudioBitrate;
|
var bitrate = state.OutputAudioBitrate;
|
||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
@ -5557,8 +5579,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
var audioTranscodeParams = new List<string>();
|
var audioTranscodeParams = new List<string>();
|
||||||
|
|
||||||
var bitrate = state.OutputAudioBitrate;
|
var bitrate = state.OutputAudioBitrate;
|
||||||
|
var channels = state.OutputAudioChannels;
|
||||||
|
var outputCodec = state.OutputAudioCodec;
|
||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture));
|
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
@ -5568,7 +5592,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
|
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(outputCodec))
|
||||||
|
{
|
||||||
|
audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// opus only supports specific sampling rates
|
// opus only supports specific sampling rates
|
||||||
var sampleRate = state.OutputAudioSampleRate;
|
var sampleRate = state.OutputAudioSampleRate;
|
||||||
|
@ -25,11 +25,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
"mpeg2video",
|
"mpeg2video",
|
||||||
"mpeg4",
|
"mpeg4",
|
||||||
"msmpeg4",
|
"msmpeg4",
|
||||||
"dts",
|
"dca",
|
||||||
"ac3",
|
"ac3",
|
||||||
"aac",
|
"aac",
|
||||||
"mp3",
|
"mp3",
|
||||||
"flac",
|
"flac",
|
||||||
|
"truehd",
|
||||||
"h264_qsv",
|
"h264_qsv",
|
||||||
"hevc_qsv",
|
"hevc_qsv",
|
||||||
"mpeg2_qsv",
|
"mpeg2_qsv",
|
||||||
@ -58,10 +59,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
"aac",
|
"aac",
|
||||||
"libfdk_aac",
|
"libfdk_aac",
|
||||||
"ac3",
|
"ac3",
|
||||||
|
"dca",
|
||||||
"libmp3lame",
|
"libmp3lame",
|
||||||
"libopus",
|
"libopus",
|
||||||
"libvorbis",
|
"libvorbis",
|
||||||
"flac",
|
"flac",
|
||||||
|
"truehd",
|
||||||
"srt",
|
"srt",
|
||||||
"h264_amf",
|
"h264_amf",
|
||||||
"hevc_amf",
|
"hevc_amf",
|
||||||
|
@ -23,6 +23,9 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ITranscoderSupport _transcoderSupport;
|
private readonly ITranscoderSupport _transcoderSupport;
|
||||||
|
private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc" };
|
||||||
|
private static readonly string[] _supportedHlsAudioCodecsTs = new string[] { "aac", "ac3", "eac3", "mp3" };
|
||||||
|
private static readonly string[] _supportedHlsAudioCodecsMp4 = new string[] { "aac", "ac3", "eac3", "mp3", "alac", "flac", "opus", "dca", "truehd" };
|
||||||
|
|
||||||
public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
|
public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
|
||||||
{
|
{
|
||||||
@ -770,6 +773,13 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
{
|
{
|
||||||
// Prefer matching video codecs
|
// Prefer matching video codecs
|
||||||
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
||||||
|
|
||||||
|
// Enforce HLS video codec restrictions
|
||||||
|
if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
videoCodecs = videoCodecs.Where(codec => _supportedHlsVideoCodecs.Contains(codec)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
|
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
|
||||||
if (directVideoCodec != null)
|
if (directVideoCodec != null)
|
||||||
{
|
{
|
||||||
@ -805,6 +815,20 @@ namespace MediaBrowser.Model.Dlna
|
|||||||
|
|
||||||
// Prefer matching audio codecs, could do better here
|
// Prefer matching audio codecs, could do better here
|
||||||
var audioCodecs = ContainerProfile.SplitValue(audioCodec);
|
var audioCodecs = ContainerProfile.SplitValue(audioCodec);
|
||||||
|
|
||||||
|
// Enforce HLS audio codec restrictions
|
||||||
|
if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (string.Equals(playlistItem.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsMp4.Contains(codec)).ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsTs.Contains(codec)).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
|
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
|
||||||
playlistItem.AudioCodecs = audioCodecs;
|
playlistItem.AudioCodecs = audioCodecs;
|
||||||
if (directAudioStream != null)
|
if (directAudioStream != null)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user