mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-31 04:48:27 +01:00
Merge remote-tracking branch 'upstream/master' into network-rewrite
This commit is contained in:
@@ -1,128 +1,247 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Model.Configuration
|
||||
namespace MediaBrowser.Model.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Class EncodingOptions.
|
||||
/// </summary>
|
||||
public class EncodingOptions
|
||||
{
|
||||
public class EncodingOptions
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EncodingOptions" /> class.
|
||||
/// </summary>
|
||||
public EncodingOptions()
|
||||
{
|
||||
public EncodingOptions()
|
||||
{
|
||||
EnableFallbackFont = false;
|
||||
DownMixAudioBoost = 2;
|
||||
MaxMuxingQueueSize = 2048;
|
||||
EnableThrottling = false;
|
||||
ThrottleDelaySeconds = 180;
|
||||
EncodingThreadCount = -1;
|
||||
// This is a DRM device that is almost guaranteed to be there on every intel platform,
|
||||
// plus it's the default one in ffmpeg if you don't specify anything
|
||||
VaapiDevice = "/dev/dri/renderD128";
|
||||
EnableTonemapping = false;
|
||||
EnableVppTonemapping = false;
|
||||
TonemappingAlgorithm = "bt2390";
|
||||
TonemappingRange = "auto";
|
||||
TonemappingDesat = 0;
|
||||
TonemappingThreshold = 0.8;
|
||||
TonemappingPeak = 100;
|
||||
TonemappingParam = 0;
|
||||
VppTonemappingBrightness = 0;
|
||||
VppTonemappingContrast = 1.2;
|
||||
H264Crf = 23;
|
||||
H265Crf = 28;
|
||||
DeinterlaceDoubleRate = false;
|
||||
DeinterlaceMethod = "yadif";
|
||||
EnableDecodingColorDepth10Hevc = true;
|
||||
EnableDecodingColorDepth10Vp9 = true;
|
||||
EnableEnhancedNvdecDecoder = false;
|
||||
PreferSystemNativeHwDecoder = true;
|
||||
EnableIntelLowPowerH264HwEncoder = false;
|
||||
EnableIntelLowPowerHevcHwEncoder = false;
|
||||
EnableHardwareEncoding = true;
|
||||
AllowHevcEncoding = false;
|
||||
EnableSubtitleExtraction = true;
|
||||
AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
|
||||
HardwareDecodingCodecs = new string[] { "h264", "vc1" };
|
||||
}
|
||||
|
||||
public int EncodingThreadCount { get; set; }
|
||||
|
||||
public string TranscodingTempPath { get; set; }
|
||||
|
||||
public string FallbackFontPath { get; set; }
|
||||
|
||||
public bool EnableFallbackFont { get; set; }
|
||||
|
||||
public double DownMixAudioBoost { get; set; }
|
||||
|
||||
public int MaxMuxingQueueSize { get; set; }
|
||||
|
||||
public bool EnableThrottling { get; set; }
|
||||
|
||||
public int ThrottleDelaySeconds { get; set; }
|
||||
|
||||
public string HardwareAccelerationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the FFmpeg path as set by the user via the UI.
|
||||
/// </summary>
|
||||
public string EncoderAppPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
|
||||
/// </summary>
|
||||
public string EncoderAppPathDisplay { get; set; }
|
||||
|
||||
public string VaapiDevice { get; set; }
|
||||
|
||||
public bool EnableTonemapping { get; set; }
|
||||
|
||||
public bool EnableVppTonemapping { get; set; }
|
||||
|
||||
public string TonemappingAlgorithm { get; set; }
|
||||
|
||||
public string TonemappingRange { get; set; }
|
||||
|
||||
public double TonemappingDesat { get; set; }
|
||||
|
||||
public double TonemappingThreshold { get; set; }
|
||||
|
||||
public double TonemappingPeak { get; set; }
|
||||
|
||||
public double TonemappingParam { get; set; }
|
||||
|
||||
public double VppTonemappingBrightness { get; set; }
|
||||
|
||||
public double VppTonemappingContrast { get; set; }
|
||||
|
||||
public int H264Crf { get; set; }
|
||||
|
||||
public int H265Crf { get; set; }
|
||||
|
||||
public string EncoderPreset { get; set; }
|
||||
|
||||
public bool DeinterlaceDoubleRate { get; set; }
|
||||
|
||||
public string DeinterlaceMethod { get; set; }
|
||||
|
||||
public bool EnableDecodingColorDepth10Hevc { get; set; }
|
||||
|
||||
public bool EnableDecodingColorDepth10Vp9 { get; set; }
|
||||
|
||||
public bool EnableEnhancedNvdecDecoder { get; set; }
|
||||
|
||||
public bool PreferSystemNativeHwDecoder { get; set; }
|
||||
|
||||
public bool EnableIntelLowPowerH264HwEncoder { get; set; }
|
||||
|
||||
public bool EnableIntelLowPowerHevcHwEncoder { get; set; }
|
||||
|
||||
public bool EnableHardwareEncoding { get; set; }
|
||||
|
||||
public bool AllowHevcEncoding { get; set; }
|
||||
|
||||
public bool EnableSubtitleExtraction { get; set; }
|
||||
|
||||
public string[] HardwareDecodingCodecs { get; set; }
|
||||
|
||||
public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; }
|
||||
EnableFallbackFont = false;
|
||||
DownMixAudioBoost = 2;
|
||||
DownMixStereoAlgorithm = DownMixStereoAlgorithms.None;
|
||||
MaxMuxingQueueSize = 2048;
|
||||
EnableThrottling = false;
|
||||
ThrottleDelaySeconds = 180;
|
||||
EncodingThreadCount = -1;
|
||||
// This is a DRM device that is almost guaranteed to be there on every intel platform,
|
||||
// plus it's the default one in ffmpeg if you don't specify anything
|
||||
VaapiDevice = "/dev/dri/renderD128";
|
||||
EnableTonemapping = false;
|
||||
EnableVppTonemapping = false;
|
||||
TonemappingAlgorithm = "bt2390";
|
||||
TonemappingRange = "auto";
|
||||
TonemappingDesat = 0;
|
||||
TonemappingThreshold = 0.8;
|
||||
TonemappingPeak = 100;
|
||||
TonemappingParam = 0;
|
||||
VppTonemappingBrightness = 0;
|
||||
VppTonemappingContrast = 1.2;
|
||||
H264Crf = 23;
|
||||
H265Crf = 28;
|
||||
DeinterlaceDoubleRate = false;
|
||||
DeinterlaceMethod = "yadif";
|
||||
EnableDecodingColorDepth10Hevc = true;
|
||||
EnableDecodingColorDepth10Vp9 = true;
|
||||
EnableEnhancedNvdecDecoder = false;
|
||||
PreferSystemNativeHwDecoder = true;
|
||||
EnableIntelLowPowerH264HwEncoder = false;
|
||||
EnableIntelLowPowerHevcHwEncoder = false;
|
||||
EnableHardwareEncoding = true;
|
||||
AllowHevcEncoding = false;
|
||||
EnableSubtitleExtraction = true;
|
||||
AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
|
||||
HardwareDecodingCodecs = new string[] { "h264", "vc1" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the thread count used for encoding.
|
||||
/// </summary>
|
||||
public int EncodingThreadCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the temporary transcoding path.
|
||||
/// </summary>
|
||||
public string TranscodingTempPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the fallback font.
|
||||
/// </summary>
|
||||
public string FallbackFontPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use the fallback font.
|
||||
/// </summary>
|
||||
public bool EnableFallbackFont { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio boost applied when downmixing audio.
|
||||
/// </summary>
|
||||
public double DownMixAudioBoost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the algorithm used for downmixing audio to stereo.
|
||||
/// </summary>
|
||||
public DownMixStereoAlgorithms DownMixStereoAlgorithm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum size of the muxing queue.
|
||||
/// </summary>
|
||||
public int MaxMuxingQueueSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether throttling is enabled.
|
||||
/// </summary>
|
||||
public bool EnableThrottling { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay after which throttling happens.
|
||||
/// </summary>
|
||||
public int ThrottleDelaySeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hardware acceleration type.
|
||||
/// </summary>
|
||||
public string HardwareAccelerationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the FFmpeg path as set by the user via the UI.
|
||||
/// </summary>
|
||||
public string EncoderAppPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
|
||||
/// </summary>
|
||||
public string EncoderAppPathDisplay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the VA-API device.
|
||||
/// </summary>
|
||||
public string VaapiDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether tonemapping is enabled.
|
||||
/// </summary>
|
||||
public bool EnableTonemapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether VPP tonemapping is enabled.
|
||||
/// </summary>
|
||||
public bool EnableVppTonemapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping algorithm.
|
||||
/// </summary>
|
||||
public string TonemappingAlgorithm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping range.
|
||||
/// </summary>
|
||||
public string TonemappingRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping desaturation.
|
||||
/// </summary>
|
||||
public double TonemappingDesat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping threshold.
|
||||
/// </summary>
|
||||
public double TonemappingThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping peak.
|
||||
/// </summary>
|
||||
public double TonemappingPeak { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tone-mapping parameters.
|
||||
/// </summary>
|
||||
public double TonemappingParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the VPP tone-mapping brightness.
|
||||
/// </summary>
|
||||
public double VppTonemappingBrightness { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the VPP tone-mapping contrast.
|
||||
/// </summary>
|
||||
public double VppTonemappingContrast { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the H264 CRF.
|
||||
/// </summary>
|
||||
public int H264Crf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the H265 CRF.
|
||||
/// </summary>
|
||||
public int H265Crf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the encoder preset.
|
||||
/// </summary>
|
||||
public string EncoderPreset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the framerate is doubled when deinterlacing.
|
||||
/// </summary>
|
||||
public bool DeinterlaceDoubleRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the deinterlace method.
|
||||
/// </summary>
|
||||
public string DeinterlaceMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 10bit HEVC decoding is enabled.
|
||||
/// </summary>
|
||||
public bool EnableDecodingColorDepth10Hevc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether 10bit VP9 decoding is enabled.
|
||||
/// </summary>
|
||||
public bool EnableDecodingColorDepth10Vp9 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the enhanced NVDEC is enabled.
|
||||
/// </summary>
|
||||
public bool EnableEnhancedNvdecDecoder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the system native hardware decoder should be used.
|
||||
/// </summary>
|
||||
public bool PreferSystemNativeHwDecoder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Intel H264 low-power hardware encoder should be used.
|
||||
/// </summary>
|
||||
public bool EnableIntelLowPowerH264HwEncoder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Intel HEVC low-power hardware encoder should be used.
|
||||
/// </summary>
|
||||
public bool EnableIntelLowPowerHevcHwEncoder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether hardware encoding is enabled.
|
||||
/// </summary>
|
||||
public bool EnableHardwareEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether HEVC encoding is enabled.
|
||||
/// </summary>
|
||||
public bool AllowHevcEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether subtitle extraction is enabled.
|
||||
/// </summary>
|
||||
public bool EnableSubtitleExtraction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the codecs hardware encoding is used for.
|
||||
/// </summary>
|
||||
public string[] HardwareDecodingCodecs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for.
|
||||
/// </summary>
|
||||
public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; }
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace MediaBrowser.Model.Configuration
|
||||
|
||||
public string[] CodecsUsed { get; set; } = Array.Empty<string>();
|
||||
|
||||
public List<RepositoryInfo> PluginRepositories { get; set; } = new List<RepositoryInfo>();
|
||||
public RepositoryInfo[] PluginRepositories { get; set; } = Array.Empty<RepositoryInfo>();
|
||||
|
||||
public bool EnableExternalContentInSuggestions { get; set; } = true;
|
||||
|
||||
@@ -259,5 +259,11 @@ namespace MediaBrowser.Model.Configuration
|
||||
/// </summary>
|
||||
/// <value>The chapter image resolution.</value>
|
||||
public ImageResolution ChapterImageResolution { get; set; } = ImageResolution.MatchSource;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the limit for parallel image encoding.
|
||||
/// </summary>
|
||||
/// <value>The limit for parallel image encoding.</value>
|
||||
public int ParallelImageEncodingLimit { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dto;
|
||||
@@ -7,11 +6,14 @@ using MediaBrowser.Model.Dto;
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class AudioOptions.
|
||||
/// Class MediaOptions.
|
||||
/// </summary>
|
||||
public class AudioOptions
|
||||
public class MediaOptions
|
||||
{
|
||||
public AudioOptions()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MediaOptions"/> class.
|
||||
/// </summary>
|
||||
public MediaOptions()
|
||||
{
|
||||
Context = EncodingContext.Streaming;
|
||||
|
||||
@@ -19,20 +21,49 @@ namespace MediaBrowser.Model.Dlna
|
||||
EnableDirectStream = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct playback is allowed.
|
||||
/// </summary>
|
||||
public bool EnableDirectPlay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct streaming is allowed.
|
||||
/// </summary>
|
||||
public bool EnableDirectStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct playback is forced.
|
||||
/// </summary>
|
||||
public bool ForceDirectPlay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether direct streaming is forced.
|
||||
/// </summary>
|
||||
public bool ForceDirectStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether audio stream copy is allowed.
|
||||
/// </summary>
|
||||
public bool AllowAudioStreamCopy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether video stream copy is allowed.
|
||||
/// </summary>
|
||||
public bool AllowVideoStreamCopy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item id.
|
||||
/// </summary>
|
||||
public Guid ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the media sources.
|
||||
/// </summary>
|
||||
public MediaSourceInfo[] MediaSources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device profile.
|
||||
/// </summary>
|
||||
public DeviceProfile Profile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -40,6 +71,9 @@ namespace MediaBrowser.Model.Dlna
|
||||
/// </summary>
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device id.
|
||||
/// </summary>
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +83,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
public int? MaxAudioChannels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application's configured quality setting.
|
||||
/// Gets or sets the application's configured maximum bitrate.
|
||||
/// </summary>
|
||||
public int? MaxBitrate { get; set; }
|
||||
|
||||
@@ -65,6 +99,16 @@ namespace MediaBrowser.Model.Dlna
|
||||
/// <value>The audio transcoding bitrate.</value>
|
||||
public int? AudioTranscodingBitrate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an override for the audio stream index.
|
||||
/// </summary>
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an override for the subtitle stream index.
|
||||
/// </summary>
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum bitrate.
|
||||
/// </summary>
|
||||
@@ -1,5 +1,4 @@
|
||||
#nullable disable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,6 +12,9 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StreamBuilder.
|
||||
/// </summary>
|
||||
public class StreamBuilder
|
||||
{
|
||||
// Aliases
|
||||
@@ -24,42 +26,56 @@ namespace MediaBrowser.Model.Dlna
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITranscoderSupport _transcoderSupport;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="transcoderSupport">The <see cref="ITranscoderSupport"/> object.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> object.</param>
|
||||
public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
|
||||
{
|
||||
_transcoderSupport = transcoderSupport;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamBuilder"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The <see cref="ILogger"/> object.</param>
|
||||
public StreamBuilder(ILogger<StreamBuilder> logger)
|
||||
: this(new FullTranscoderSupport(), logger)
|
||||
{
|
||||
}
|
||||
|
||||
public StreamInfo BuildAudioItem(AudioOptions options)
|
||||
/// <summary>
|
||||
/// Gets the optimal audio stream.
|
||||
/// </summary>
|
||||
/// <param name="options">The <see cref="MediaOptions"/> object to get the audio stream from.</param>
|
||||
/// <returns>The <see cref="StreamInfo"/> of the optimal audio stream.</returns>
|
||||
public StreamInfo GetOptimalAudioStream(MediaOptions options)
|
||||
{
|
||||
ValidateAudioInput(options);
|
||||
ValidateMediaOptions(options, false);
|
||||
|
||||
var mediaSources = new List<MediaSourceInfo>();
|
||||
foreach (MediaSourceInfo i in options.MediaSources)
|
||||
foreach (var mediaSource in options.MediaSources)
|
||||
{
|
||||
if (string.IsNullOrEmpty(options.MediaSourceId) ||
|
||||
string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
|
||||
string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mediaSources.Add(i);
|
||||
mediaSources.Add(mediaSource);
|
||||
}
|
||||
}
|
||||
|
||||
var streams = new List<StreamInfo>();
|
||||
foreach (MediaSourceInfo i in mediaSources)
|
||||
foreach (var mediaSourceInfo in mediaSources)
|
||||
{
|
||||
StreamInfo streamInfo = BuildAudioItem(i, options);
|
||||
StreamInfo streamInfo = GetOptimalAudioStream(mediaSourceInfo, options);
|
||||
if (streamInfo is not null)
|
||||
{
|
||||
streams.Add(streamInfo);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (StreamInfo stream in streams)
|
||||
foreach (var stream in streams)
|
||||
{
|
||||
stream.DeviceId = options.DeviceId;
|
||||
stream.DeviceProfileId = options.Profile.Id;
|
||||
@@ -68,31 +84,137 @@ namespace MediaBrowser.Model.Dlna
|
||||
return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0);
|
||||
}
|
||||
|
||||
public StreamInfo BuildVideoItem(VideoOptions options)
|
||||
private StreamInfo GetOptimalAudioStream(MediaSourceInfo item, MediaOptions options)
|
||||
{
|
||||
ValidateInput(options);
|
||||
var playlistItem = new StreamInfo
|
||||
{
|
||||
ItemId = options.ItemId,
|
||||
MediaType = DlnaProfileType.Audio,
|
||||
MediaSource = item,
|
||||
RunTimeTicks = item.RunTimeTicks,
|
||||
Context = options.Context,
|
||||
DeviceProfile = options.Profile
|
||||
};
|
||||
|
||||
if (options.ForceDirectPlay)
|
||||
{
|
||||
playlistItem.PlayMethod = PlayMethod.DirectPlay;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
if (options.ForceDirectStream)
|
||||
{
|
||||
playlistItem.PlayMethod = PlayMethod.DirectStream;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
MediaStream audioStream = item.GetDefaultAudioStream(null);
|
||||
|
||||
var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
|
||||
|
||||
var directPlayMethod = directPlayInfo.PlayMethod;
|
||||
var transcodeReasons = directPlayInfo.TranscodeReasons;
|
||||
|
||||
var inputAudioChannels = audioStream?.Channels;
|
||||
var inputAudioBitrate = audioStream?.BitDepth;
|
||||
var inputAudioSampleRate = audioStream?.SampleRate;
|
||||
var inputAudioBitDepth = audioStream?.BitDepth;
|
||||
|
||||
if (directPlayMethod.HasValue)
|
||||
{
|
||||
var profile = options.Profile;
|
||||
var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
|
||||
var audioFailureReasons = AggregateFailureConditions(item, profile, "AudioCodecProfile", audioFailureConditions);
|
||||
transcodeReasons |= audioFailureReasons;
|
||||
|
||||
if (audioFailureReasons == 0)
|
||||
{
|
||||
playlistItem.PlayMethod = directPlayMethod.Value;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio, directPlayInfo.Profile);
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
}
|
||||
|
||||
TranscodingProfile transcodingProfile = null;
|
||||
foreach (var tcProfile in options.Profile.TranscodingProfiles)
|
||||
{
|
||||
if (tcProfile.Type == playlistItem.MediaType
|
||||
&& tcProfile.Context == options.Context
|
||||
&& _transcoderSupport.CanEncodeToAudioCodec(transcodingProfile.AudioCodec ?? tcProfile.Container))
|
||||
{
|
||||
transcodingProfile = tcProfile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transcodingProfile != null)
|
||||
{
|
||||
if (!item.SupportsTranscoding)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
|
||||
|
||||
var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
|
||||
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
|
||||
|
||||
// Honor requested max channels
|
||||
playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
|
||||
|
||||
var configuredBitrate = options.GetMaxBitrate(true);
|
||||
|
||||
long transcodingBitrate = options.AudioTranscodingBitrate
|
||||
?? (options.Context == EncodingContext.Streaming ? options.Profile.MusicStreamingTranscodingBitrate : null)
|
||||
?? configuredBitrate
|
||||
?? 128000;
|
||||
|
||||
if (configuredBitrate.HasValue)
|
||||
{
|
||||
transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
|
||||
}
|
||||
|
||||
var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
|
||||
playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
|
||||
}
|
||||
|
||||
playlistItem.TranscodeReasons = transcodeReasons;
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optimal video stream.
|
||||
/// </summary>
|
||||
/// <param name="options">The <see cref="MediaOptions"/> object to get the video stream from.</param>
|
||||
/// <returns>The <see cref="StreamInfo"/> of the optimal video stream.</returns>
|
||||
public StreamInfo GetOptimalVideoStream(MediaOptions options)
|
||||
{
|
||||
ValidateMediaOptions(options, true);
|
||||
|
||||
var mediaSources = new List<MediaSourceInfo>();
|
||||
foreach (MediaSourceInfo i in options.MediaSources)
|
||||
foreach (var mediaSourceInfo in options.MediaSources)
|
||||
{
|
||||
if (string.IsNullOrEmpty(options.MediaSourceId) ||
|
||||
string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
|
||||
string.Equals(mediaSourceInfo.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mediaSources.Add(i);
|
||||
mediaSources.Add(mediaSourceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
var streams = new List<StreamInfo>();
|
||||
foreach (MediaSourceInfo i in mediaSources)
|
||||
foreach (var mediaSourceInfo in mediaSources)
|
||||
{
|
||||
var streamInfo = BuildVideoItem(i, options);
|
||||
var streamInfo = BuildVideoItem(mediaSourceInfo, options);
|
||||
if (streamInfo is not null)
|
||||
{
|
||||
streams.Add(streamInfo);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (StreamInfo stream in streams)
|
||||
foreach (var stream in streams)
|
||||
{
|
||||
stream.DeviceId = options.DeviceId;
|
||||
stream.DeviceProfileId = options.Profile.Id;
|
||||
@@ -236,6 +358,14 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes input container.
|
||||
/// </summary>
|
||||
/// <param name="inputContainer">The input container.</param>
|
||||
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
|
||||
/// <param name="type">The <see cref="DlnaProfileType"/>.</param>
|
||||
/// <param name="playProfile">The <see cref="DirectPlayProfile"/> object to get the video stream from.</param>
|
||||
/// <returns>The the normalized input container.</returns>
|
||||
public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type, DirectPlayProfile playProfile = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputContainer))
|
||||
@@ -264,108 +394,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
return formats[0];
|
||||
}
|
||||
|
||||
private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options)
|
||||
{
|
||||
StreamInfo playlistItem = new StreamInfo
|
||||
{
|
||||
ItemId = options.ItemId,
|
||||
MediaType = DlnaProfileType.Audio,
|
||||
MediaSource = item,
|
||||
RunTimeTicks = item.RunTimeTicks,
|
||||
Context = options.Context,
|
||||
DeviceProfile = options.Profile
|
||||
};
|
||||
|
||||
if (options.ForceDirectPlay)
|
||||
{
|
||||
playlistItem.PlayMethod = PlayMethod.DirectPlay;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
if (options.ForceDirectStream)
|
||||
{
|
||||
playlistItem.PlayMethod = PlayMethod.DirectStream;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
var audioStream = item.GetDefaultAudioStream(null);
|
||||
|
||||
var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
|
||||
|
||||
var directPlayMethod = directPlayInfo.PlayMethod;
|
||||
var transcodeReasons = directPlayInfo.TranscodeReasons;
|
||||
|
||||
int? inputAudioChannels = audioStream?.Channels;
|
||||
int? inputAudioBitrate = audioStream?.BitDepth;
|
||||
int? inputAudioSampleRate = audioStream?.SampleRate;
|
||||
int? inputAudioBitDepth = audioStream?.BitDepth;
|
||||
|
||||
if (directPlayMethod.HasValue)
|
||||
{
|
||||
var profile = options.Profile;
|
||||
var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
|
||||
var audioFailureReasons = AggregateFailureConditions(item, profile, "AudioCodecProfile", audioFailureConditions);
|
||||
transcodeReasons |= audioFailureReasons;
|
||||
|
||||
if (audioFailureReasons == 0)
|
||||
{
|
||||
playlistItem.PlayMethod = directPlayMethod.Value;
|
||||
playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio, directPlayInfo.Profile);
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
}
|
||||
|
||||
TranscodingProfile transcodingProfile = null;
|
||||
foreach (var i in options.Profile.TranscodingProfiles)
|
||||
{
|
||||
if (i.Type == playlistItem.MediaType
|
||||
&& i.Context == options.Context
|
||||
&& _transcoderSupport.CanEncodeToAudioCodec(i.AudioCodec ?? i.Container))
|
||||
{
|
||||
transcodingProfile = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transcodingProfile is not null)
|
||||
{
|
||||
if (!item.SupportsTranscoding)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
|
||||
|
||||
var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
|
||||
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
|
||||
|
||||
// Honor requested max channels
|
||||
playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
|
||||
|
||||
var configuredBitrate = options.GetMaxBitrate(true);
|
||||
|
||||
long transcodingBitrate = options.AudioTranscodingBitrate ??
|
||||
(options.Context == EncodingContext.Streaming ? options.Profile.MusicStreamingTranscodingBitrate : null) ??
|
||||
configuredBitrate ??
|
||||
128000;
|
||||
|
||||
if (configuredBitrate.HasValue)
|
||||
{
|
||||
transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
|
||||
}
|
||||
|
||||
var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
|
||||
playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
|
||||
}
|
||||
|
||||
playlistItem.TranscodeReasons = transcodeReasons;
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
private (DirectPlayProfile Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
|
||||
private (DirectPlayProfile Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, MediaOptions options)
|
||||
{
|
||||
var directPlayProfile = options.Profile.DirectPlayProfiles
|
||||
.FirstOrDefault(x => x.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(x, item, audioStream));
|
||||
@@ -388,7 +417,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
// If device requirements are satisfied then allow both direct stream and direct play
|
||||
if (item.SupportsDirectPlay)
|
||||
{
|
||||
if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
|
||||
if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
|
||||
{
|
||||
if (options.EnableDirectPlay)
|
||||
{
|
||||
@@ -404,7 +433,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
// While options takes the network and other factors into account. Only applies to direct stream
|
||||
if (item.SupportsDirectStream)
|
||||
{
|
||||
if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
|
||||
if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
|
||||
{
|
||||
if (options.EnableDirectStream)
|
||||
{
|
||||
@@ -427,7 +456,6 @@ namespace MediaBrowser.Model.Dlna
|
||||
var containerSupported = false;
|
||||
var audioSupported = false;
|
||||
var videoSupported = false;
|
||||
TranscodeReason reasons = 0;
|
||||
|
||||
foreach (var profile in directPlayProfiles)
|
||||
{
|
||||
@@ -447,6 +475,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
TranscodeReason reasons = 0;
|
||||
if (!containerSupported)
|
||||
{
|
||||
reasons |= TranscodeReason.ContainerNotSupported;
|
||||
@@ -547,7 +576,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetStreamInfoOptionsFromDirectPlayProfile(VideoOptions options, MediaSourceInfo item, StreamInfo playlistItem, DirectPlayProfile directPlayProfile)
|
||||
private static void SetStreamInfoOptionsFromDirectPlayProfile(MediaOptions options, MediaSourceInfo item, StreamInfo playlistItem, DirectPlayProfile directPlayProfile)
|
||||
{
|
||||
var container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
|
||||
var protocol = "http";
|
||||
@@ -562,7 +591,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile.AudioCodec);
|
||||
}
|
||||
|
||||
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
|
||||
private StreamInfo BuildVideoItem(MediaSourceInfo item, MediaOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
|
||||
@@ -601,11 +630,15 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
var videoStream = item.VideoStream;
|
||||
|
||||
var directPlayBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
|
||||
var directStreamBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
|
||||
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayBitrateEligibility == 0);
|
||||
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamBitrateEligibility == 0);
|
||||
var transcodeReasons = directPlayBitrateEligibility | directStreamBitrateEligibility;
|
||||
var bitrateLimitExceeded = IsBitrateLimitExceeded(item, options.GetMaxBitrate(false) ?? 0);
|
||||
var isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || !bitrateLimitExceeded);
|
||||
var isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || !bitrateLimitExceeded);
|
||||
TranscodeReason transcodeReasons = 0;
|
||||
|
||||
if (bitrateLimitExceeded)
|
||||
{
|
||||
transcodeReasons = TranscodeReason.ContainerBitrateExceedsLimit;
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
"Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
|
||||
@@ -702,7 +735,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
_logger.LogDebug(
|
||||
"StreamBuilder.BuildVideoItem( Profile={0}, Path={1}, AudioStreamIndex={2}, SubtitleStreamIndex={3} ) => ( PlayMethod={4}, TranscodeReason={5} ) {6}",
|
||||
options.Profile.Name ?? "Anonymous Profile",
|
||||
item.Path ?? "Unknown path",
|
||||
@@ -716,7 +749,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
private TranscodingProfile GetVideoTranscodeProfile(MediaSourceInfo item, VideoOptions options, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, MediaStream subtitleStream, StreamInfo playlistItem)
|
||||
private TranscodingProfile GetVideoTranscodeProfile(MediaSourceInfo item, MediaOptions options, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, MediaStream subtitleStream, StreamInfo playlistItem)
|
||||
{
|
||||
if (!(item.SupportsTranscoding || item.SupportsDirectStream))
|
||||
{
|
||||
@@ -763,7 +796,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
return transcodingProfiles.FirstOrDefault();
|
||||
}
|
||||
|
||||
private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
|
||||
private void BuildStreamVideoItem(StreamInfo playlistItem, MediaOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
|
||||
{
|
||||
// Prefer matching video codecs
|
||||
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
||||
@@ -867,7 +900,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
// Honor requested max channels
|
||||
playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
|
||||
|
||||
int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(false) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
|
||||
int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(true) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
|
||||
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
|
||||
|
||||
bool? isSecondaryAudio = audioStream is null ? null : item.IsSecondaryAudio(audioStream);
|
||||
@@ -882,14 +915,14 @@ namespace MediaBrowser.Model.Dlna
|
||||
i.ContainsAnyCodec(audioCodec, container) &&
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
|
||||
isFirstAppliedCodecProfile = true;
|
||||
foreach (var i in appliedAudioConditions)
|
||||
foreach (var codecProfile in appliedAudioConditions)
|
||||
{
|
||||
var transcodingAudioCodecs = ContainerProfile.SplitValue(audioCodec);
|
||||
foreach (var transcodingAudioCodec in transcodingAudioCodecs)
|
||||
{
|
||||
if (i.ContainsAnyCodec(transcodingAudioCodec, container))
|
||||
if (codecProfile.ContainsAnyCodec(transcodingAudioCodec, container))
|
||||
{
|
||||
ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
|
||||
ApplyTranscodingConditions(playlistItem, codecProfile.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
|
||||
isFirstAppliedCodecProfile = false;
|
||||
break;
|
||||
}
|
||||
@@ -1050,7 +1083,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
}
|
||||
|
||||
private (DirectPlayProfile Profile, PlayMethod? PlayMethod, int? AudioStreamIndex, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
|
||||
VideoOptions options,
|
||||
MediaOptions options,
|
||||
MediaSourceInfo mediaSource,
|
||||
MediaStream videoStream,
|
||||
MediaStream audioStream,
|
||||
@@ -1237,7 +1270,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
return (Profile: null, PlayMethod: null, AudioStreamIndex: null, TranscodeReasons: failureReasons);
|
||||
}
|
||||
|
||||
private TranscodeReason CheckVideoAudioStreamDirectPlay(VideoOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
|
||||
private TranscodeReason CheckVideoAudioStreamDirectPlay(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
|
||||
{
|
||||
var profile = options.Profile;
|
||||
var audioFailureConditions = GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioStream.Codec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, mediaSource.IsSecondaryAudio(audioStream));
|
||||
@@ -1274,23 +1307,17 @@ namespace MediaBrowser.Model.Dlna
|
||||
mediaSource.Path ?? "Unknown path");
|
||||
}
|
||||
|
||||
private TranscodeReason IsBitrateEligibleForDirectPlayback(
|
||||
MediaSourceInfo item,
|
||||
long maxBitrate,
|
||||
VideoOptions options,
|
||||
PlayMethod playMethod)
|
||||
{
|
||||
bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod);
|
||||
if (!result)
|
||||
{
|
||||
return TranscodeReason.ContainerBitrateExceedsLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes input container.
|
||||
/// </summary>
|
||||
/// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
|
||||
/// <param name="subtitleStream">The <see cref="MediaStream"/> of the subtitle stream.</param>
|
||||
/// <param name="subtitleProfiles">The list of supported <see cref="SubtitleProfile"/>s.</param>
|
||||
/// <param name="playMethod">The <see cref="PlayMethod"/>.</param>
|
||||
/// <param name="transcoderSupport">The <see cref="ITranscoderSupport"/>.</param>
|
||||
/// <param name="outputContainer">The output container.</param>
|
||||
/// <param name="transcodingSubProtocol">The subtitle transoding protocol.</param>
|
||||
/// <returns>The the normalized input container.</returns>
|
||||
public static SubtitleProfile GetSubtitleProfile(
|
||||
MediaSourceInfo mediaSource,
|
||||
MediaStream subtitleStream,
|
||||
@@ -1448,12 +1475,12 @@ namespace MediaBrowser.Model.Dlna
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod)
|
||||
private bool IsBitrateLimitExceeded(MediaSourceInfo item, long maxBitrate)
|
||||
{
|
||||
// Don't restrict bitrate if item is remote.
|
||||
if (item.IsRemote)
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no maximum bitrate is set, default to no maximum bitrate.
|
||||
@@ -1465,40 +1492,22 @@ namespace MediaBrowser.Model.Dlna
|
||||
if (itemBitrate > requestedMaxBitrate)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
|
||||
playMethod,
|
||||
"Bitrate exceeds limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
|
||||
itemBitrate,
|
||||
requestedMaxBitrate);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ValidateInput(VideoOptions options)
|
||||
{
|
||||
ValidateAudioInput(options);
|
||||
|
||||
if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
|
||||
{
|
||||
throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
|
||||
}
|
||||
|
||||
if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
|
||||
{
|
||||
throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateAudioInput(AudioOptions options)
|
||||
private static void ValidateMediaOptions(MediaOptions options, bool isMediaSource)
|
||||
{
|
||||
if (options.ItemId.Equals(default))
|
||||
{
|
||||
throw new ArgumentException("ItemId is required");
|
||||
ArgumentException.ThrowIfNullOrEmpty(options.DeviceId);
|
||||
}
|
||||
|
||||
ArgumentException.ThrowIfNullOrEmpty(options.DeviceId);
|
||||
|
||||
if (options.Profile is null)
|
||||
{
|
||||
throw new ArgumentException("Profile is required");
|
||||
@@ -1508,6 +1517,19 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
throw new ArgumentException("MediaSources is required");
|
||||
}
|
||||
|
||||
if (isMediaSource)
|
||||
{
|
||||
if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
|
||||
{
|
||||
throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
|
||||
}
|
||||
|
||||
if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
|
||||
{
|
||||
throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ProfileCondition> GetProfileConditionsForVideoAudio(
|
||||
@@ -1825,8 +1847,8 @@ namespace MediaBrowser.Model.Dlna
|
||||
continue;
|
||||
}
|
||||
|
||||
// change from split by | to comma
|
||||
// strip spaces to avoid having to encode
|
||||
// Change from split by | to comma
|
||||
// Strip spaces to avoid having to encode
|
||||
var values = value
|
||||
.Split('|', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Class VideoOptions.
|
||||
/// </summary>
|
||||
public class VideoOptions : AudioOptions
|
||||
{
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
public bool AllowVideoStreamCopy { get; set; }
|
||||
}
|
||||
}
|
||||
23
MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs
Normal file
23
MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace MediaBrowser.Model.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// An enum representing an algorithm to downmix 6ch+ to stereo.
|
||||
/// Algorithms sourced from https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg/1410620#1410620.
|
||||
/// </summary>
|
||||
public enum DownMixStereoAlgorithms
|
||||
{
|
||||
/// <summary>
|
||||
/// No special algorithm.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Algorithm by Dave_750.
|
||||
/// </summary>
|
||||
Dave750 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Nightmode Dialogue algorithm.
|
||||
/// </summary>
|
||||
NightmodeDialogue = 2
|
||||
}
|
||||
@@ -635,11 +635,12 @@ namespace MediaBrowser.Model.Entities
|
||||
|
||||
// sub = external .sub file
|
||||
|
||||
return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) &&
|
||||
!codec.Contains("dvd", StringComparison.OrdinalIgnoreCase) &&
|
||||
!codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) &&
|
||||
!string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
|
||||
return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase)
|
||||
&& !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase)
|
||||
&& !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool SupportsSubtitleConversionTo(string toCodec)
|
||||
|
||||
@@ -24,24 +24,27 @@ namespace MediaBrowser.Model.Extensions
|
||||
requestedLanguage = "en";
|
||||
}
|
||||
|
||||
var isRequestedLanguageEn = string.Equals(requestedLanguage, "en", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return remoteImageInfos.OrderByDescending(i =>
|
||||
{
|
||||
// Image priority ordering:
|
||||
// - Images that match the requested language
|
||||
// - Images with no language
|
||||
// - TODO: Images that match the original language
|
||||
// - Images in English
|
||||
// - Images that don't match the requested language
|
||||
|
||||
if (string.Equals(requestedLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(i.Language))
|
||||
{
|
||||
// Assume empty image language is likely to be English.
|
||||
return isRequestedLanguageEn ? 3 : 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!isRequestedLanguageEn && string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Prioritize English over non-requested languages.
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,5 +40,9 @@ namespace MediaBrowser.Model.LiveTv
|
||||
public string RecordingPostProcessor { get; set; }
|
||||
|
||||
public string RecordingPostProcessorArguments { get; set; }
|
||||
|
||||
public bool SaveRecordingNFO { get; set; } = true;
|
||||
|
||||
public bool SaveRecordingImages { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -79,8 +79,9 @@ namespace MediaBrowser.Model.System
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance can self restart.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
|
||||
public bool CanSelfRestart { get; set; }
|
||||
/// <value><c>true</c>.</value>
|
||||
[Obsolete("This is always true")]
|
||||
public bool CanSelfRestart { get; set; } = true;
|
||||
|
||||
public bool CanLaunchWebBrowser { get; set; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user