mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-17 15:53:42 +01:00
Update to 3.5.2 and .net core 2.1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,669 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
// For now, a common base class until the API and MediaEncoding classes are unified
|
||||
public abstract class EncodingJobInfo
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MediaStream VideoStream { get; set; }
|
||||
public VideoType VideoType { get; set; }
|
||||
public Dictionary<string, string> RemoteHttpHeaders { get; set; }
|
||||
public string OutputVideoCodec { get; set; }
|
||||
public MediaProtocol InputProtocol { get; set; }
|
||||
public string MediaPath { get; set; }
|
||||
public bool IsInputVideo { get; set; }
|
||||
public IIsoMount IsoMount { get; set; }
|
||||
public string[] PlayableStreamFileNames { get; set; }
|
||||
public string OutputAudioCodec { get; set; }
|
||||
public int? OutputVideoBitrate { get; set; }
|
||||
public MediaStream SubtitleStream { get; set; }
|
||||
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
|
||||
public List<string> SupportedSubtitleCodecs { get; set; }
|
||||
|
||||
public int InternalSubtitleStreamOffset { get; set; }
|
||||
public MediaSourceInfo MediaSource { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
public long? RunTimeTicks { get; set; }
|
||||
|
||||
public bool ReadInputAtNativeFramerate { get; set; }
|
||||
|
||||
private TranscodeReason[] _transcodeReasons = null;
|
||||
public TranscodeReason[] TranscodeReasons
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_transcodeReasons == null)
|
||||
{
|
||||
_transcodeReasons = (BaseRequest.TranscodeReasons ?? string.Empty)
|
||||
.Split(',')
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return _transcodeReasons;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IgnoreInputDts
|
||||
{
|
||||
get
|
||||
{
|
||||
return MediaSource.IgnoreDts;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IgnoreInputIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return MediaSource.IgnoreIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool GenPtsInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return MediaSource.GenPtsInput;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DiscardCorruptFramesInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableFastSeekInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool GenPtsOutput
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string OutputContainer { get; set; }
|
||||
|
||||
public string OutputVideoSync
|
||||
{
|
||||
get
|
||||
{
|
||||
// For live tv + in progress recordings
|
||||
if (string.Equals(InputContainer, "mpegts", StringComparison.OrdinalIgnoreCase) || string.Equals(InputContainer, "ts", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!MediaSource.RunTimeTicks.HasValue)
|
||||
{
|
||||
return "cfr";
|
||||
}
|
||||
}
|
||||
|
||||
return "-1";
|
||||
}
|
||||
}
|
||||
|
||||
public string AlbumCoverPath { get; set; }
|
||||
|
||||
public string InputAudioSync { get; set; }
|
||||
public string InputVideoSync { get; set; }
|
||||
public TransportStreamTimestamp InputTimestamp { get; set; }
|
||||
|
||||
public MediaStream AudioStream { get; set; }
|
||||
public List<string> SupportedAudioCodecs { get; set; }
|
||||
public List<string> SupportedVideoCodecs { get; set; }
|
||||
public string InputContainer { get; set; }
|
||||
public IsoType? IsoType { get; set; }
|
||||
|
||||
public bool EnableMpegtsM2TsMode { get; set; }
|
||||
|
||||
public BaseEncodingJobOptions BaseRequest { get; set; }
|
||||
|
||||
public long? StartTimeTicks
|
||||
{
|
||||
get { return BaseRequest.StartTimeTicks; }
|
||||
}
|
||||
|
||||
public bool CopyTimestamps
|
||||
{
|
||||
get { return BaseRequest.CopyTimestamps; }
|
||||
}
|
||||
|
||||
public int? OutputAudioBitrate;
|
||||
public int? OutputAudioChannels;
|
||||
|
||||
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
||||
{
|
||||
var videoStream = VideoStream;
|
||||
var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
|
||||
|
||||
if (!isInputInterlaced)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Support general param
|
||||
if (BaseRequest.DeInterlace)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoCodec))
|
||||
{
|
||||
if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (forceDeinterlaceIfSourceIsInterlaced)
|
||||
{
|
||||
if (isInputInterlaced)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string[] GetRequestedProfiles(string codec)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(BaseRequest.Profile))
|
||||
{
|
||||
return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(codec))
|
||||
{
|
||||
var profile = BaseRequest.GetOption(codec, "profile");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(profile))
|
||||
{
|
||||
return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
public string GetRequestedLevel(string codec)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
|
||||
{
|
||||
return BaseRequest.Level;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(codec))
|
||||
{
|
||||
return BaseRequest.GetOption(codec, "level");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int? GetRequestedMaxRefFrames(string codec)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
|
||||
{
|
||||
return BaseRequest.MaxRefFrames;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(codec))
|
||||
{
|
||||
var value = BaseRequest.GetOption(codec, "maxrefframes");
|
||||
int result;
|
||||
if (!string.IsNullOrWhiteSpace(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsVideoRequest { get; set; }
|
||||
public TranscodingJobType TranscodingType { get; set; }
|
||||
|
||||
public EncodingJobInfo(ILogger logger, TranscodingJobType jobType)
|
||||
{
|
||||
_logger = logger;
|
||||
TranscodingType = jobType;
|
||||
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
PlayableStreamFileNames = new string[] { };
|
||||
SupportedAudioCodecs = new List<string>();
|
||||
SupportedVideoCodecs = new List<string>();
|
||||
SupportedSubtitleCodecs = new List<string>();
|
||||
}
|
||||
|
||||
public bool IsSegmentedLiveStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableBreakOnNonKeyFrames(string videoCodec)
|
||||
{
|
||||
if (TranscodingType != TranscodingJobType.Progressive)
|
||||
{
|
||||
if (IsSegmentedLiveStream)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseRequest.BreakOnNonKeyFrames && string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int? TotalOutputBitrate
|
||||
{
|
||||
get
|
||||
{
|
||||
return (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
public int? OutputWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
|
||||
{
|
||||
var size = new ImageSize
|
||||
{
|
||||
Width = VideoStream.Width.Value,
|
||||
Height = VideoStream.Height.Value
|
||||
};
|
||||
|
||||
var newSize = DrawingUtils.Resize(size,
|
||||
BaseRequest.Width,
|
||||
BaseRequest.Height,
|
||||
BaseRequest.MaxWidth,
|
||||
BaseRequest.MaxHeight);
|
||||
|
||||
return Convert.ToInt32(newSize.Width);
|
||||
}
|
||||
|
||||
if (!IsVideoRequest)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return BaseRequest.MaxWidth ?? BaseRequest.Width;
|
||||
}
|
||||
}
|
||||
|
||||
public int? OutputHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue)
|
||||
{
|
||||
var size = new ImageSize
|
||||
{
|
||||
Width = VideoStream.Width.Value,
|
||||
Height = VideoStream.Height.Value
|
||||
};
|
||||
|
||||
var newSize = DrawingUtils.Resize(size,
|
||||
BaseRequest.Width,
|
||||
BaseRequest.Height,
|
||||
BaseRequest.MaxWidth,
|
||||
BaseRequest.MaxHeight);
|
||||
|
||||
return Convert.ToInt32(newSize.Height);
|
||||
}
|
||||
|
||||
if (!IsVideoRequest)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return BaseRequest.MaxHeight ?? BaseRequest.Height;
|
||||
}
|
||||
}
|
||||
|
||||
public int? OutputAudioSampleRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (AudioStream != null)
|
||||
{
|
||||
return AudioStream.SampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
else if (BaseRequest.AudioSampleRate.HasValue)
|
||||
{
|
||||
// Don't exceed what the encoder supports
|
||||
// Seeing issues of attempting to encode to 88200
|
||||
return Math.Min(44100, BaseRequest.AudioSampleRate.Value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int? OutputAudioBitDepth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (AudioStream != null)
|
||||
{
|
||||
return AudioStream.BitDepth;
|
||||
}
|
||||
}
|
||||
|
||||
//else if (BaseRequest.AudioSampleRate.HasValue)
|
||||
//{
|
||||
// // Don't exceed what the encoder supports
|
||||
// // Seeing issues of attempting to encode to 88200
|
||||
// return Math.Min(44100, BaseRequest.AudioSampleRate.Value);
|
||||
//}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public double? TargetVideoLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.Level;
|
||||
}
|
||||
|
||||
var level = GetRequestedLevel(ActualOutputVideoCodec);
|
||||
double result;
|
||||
if (!string.IsNullOrWhiteSpace(level) && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public int? TargetVideoBitDepth
|
||||
{
|
||||
get
|
||||
{
|
||||
var stream = VideoStream;
|
||||
return stream == null || !BaseRequest.Static ? null : stream.BitDepth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target reference frames.
|
||||
/// </summary>
|
||||
/// <value>The target reference frames.</value>
|
||||
public int? TargetRefFrames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.RefFrames;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public float? TargetFramerate
|
||||
{
|
||||
get
|
||||
{
|
||||
var stream = VideoStream;
|
||||
var requestedFramerate = BaseRequest.MaxFramerate ?? BaseRequest.Framerate;
|
||||
|
||||
return requestedFramerate.HasValue && !BaseRequest.Static
|
||||
? requestedFramerate
|
||||
: stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
|
||||
}
|
||||
}
|
||||
|
||||
public TransportStreamTimestamp TargetTimestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
|
||||
TransportStreamTimestamp.Valid :
|
||||
TransportStreamTimestamp.None;
|
||||
|
||||
return !BaseRequest.Static
|
||||
? defaultValue
|
||||
: InputTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public int? TargetPacketLength
|
||||
{
|
||||
get
|
||||
{
|
||||
var stream = VideoStream;
|
||||
return !BaseRequest.Static
|
||||
? null
|
||||
: stream == null ? null : stream.PacketLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public string TargetVideoProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.Profile;
|
||||
}
|
||||
|
||||
var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(requestedProfile))
|
||||
{
|
||||
return requestedProfile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string TargetVideoCodecTag
|
||||
{
|
||||
get
|
||||
{
|
||||
var stream = VideoStream;
|
||||
return !BaseRequest.Static
|
||||
? null
|
||||
: stream == null ? null : stream.CodecTag;
|
||||
}
|
||||
}
|
||||
|
||||
public bool? IsTargetAnamorphic
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.IsAnamorphic;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string ActualOutputVideoCodec
|
||||
{
|
||||
get
|
||||
{
|
||||
var codec = OutputVideoCodec;
|
||||
|
||||
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var stream = VideoStream;
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
return stream.Codec;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
||||
public bool? IsTargetInterlaced
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
|
||||
}
|
||||
|
||||
if (DeInterlace(ActualOutputVideoCodec, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
|
||||
}
|
||||
}
|
||||
|
||||
public bool? IsTargetAVC
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.IsAVC;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int? TargetVideoStreamCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
|
||||
}
|
||||
return GetMediaStreamCount(MediaStreamType.Video, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public int? TargetAudioStreamCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static)
|
||||
{
|
||||
return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
|
||||
}
|
||||
return GetMediaStreamCount(MediaStreamType.Audio, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
||||
{
|
||||
var count = MediaSource.GetStreamCount(type);
|
||||
|
||||
if (count.HasValue)
|
||||
{
|
||||
count = Math.Min(count.Value, limit);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
protected void DisposeIsoMount()
|
||||
{
|
||||
if (IsoMount != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsoMount.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error disposing iso mount", ex);
|
||||
}
|
||||
|
||||
IsoMount = null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum TranscodingJobType
|
||||
/// </summary>
|
||||
public enum TranscodingJobType
|
||||
{
|
||||
/// <summary>
|
||||
/// The progressive
|
||||
/// </summary>
|
||||
Progressive,
|
||||
/// <summary>
|
||||
/// The HLS
|
||||
/// </summary>
|
||||
Hls,
|
||||
/// <summary>
|
||||
/// The dash
|
||||
/// </summary>
|
||||
Dash
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class EncodingJobOptions : BaseEncodingJobOptions
|
||||
{
|
||||
public string OutputDirectory { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
public string ItemId { get; set; }
|
||||
public string MediaSourceId { get; set; }
|
||||
public string AudioCodec { get; set; }
|
||||
|
||||
public DeviceProfile DeviceProfile { get; set; }
|
||||
|
||||
public bool ReadInputAtNativeFramerate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has fixed resolution.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
|
||||
public bool HasFixedResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
return Width.HasValue || Height.HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
|
||||
{
|
||||
OutputContainer = info.Container;
|
||||
StartTimeTicks = info.StartPositionTicks;
|
||||
MaxWidth = info.MaxWidth;
|
||||
MaxHeight = info.MaxHeight;
|
||||
MaxFramerate = info.MaxFramerate;
|
||||
ItemId = info.ItemId;
|
||||
MediaSourceId = info.MediaSourceId;
|
||||
AudioCodec = info.TargetAudioCodec.FirstOrDefault();
|
||||
MaxAudioChannels = info.MaxAudioChannels;
|
||||
AudioBitRate = info.AudioBitrate;
|
||||
AudioSampleRate = info.TargetAudioSampleRate;
|
||||
DeviceProfile = deviceProfile;
|
||||
VideoCodec = info.TargetVideoCodec.FirstOrDefault();
|
||||
VideoBitRate = info.VideoBitrate;
|
||||
AudioStreamIndex = info.AudioStreamIndex;
|
||||
MaxVideoBitDepth = info.MaxVideoBitDepth;
|
||||
SubtitleMethod = info.SubtitleDeliveryMethod;
|
||||
Context = info.Context;
|
||||
TranscodingMaxAudioChannels = info.TranscodingMaxAudioChannels;
|
||||
|
||||
if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
|
||||
{
|
||||
SubtitleStreamIndex = info.SubtitleStreamIndex;
|
||||
}
|
||||
StreamOptions = info.StreamOptions;
|
||||
}
|
||||
}
|
||||
|
||||
// For now until api and media encoding layers are unified
|
||||
public class BaseEncodingJobOptions
|
||||
{
|
||||
[ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool EnableAutoStreamCopy { get; set; }
|
||||
|
||||
public bool AllowVideoStreamCopy { get; set; }
|
||||
public bool AllowAudioStreamCopy { get; set; }
|
||||
public bool BreakOnNonKeyFrames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio sample rate.
|
||||
/// </summary>
|
||||
/// <value>The audio sample rate.</value>
|
||||
[ApiMember(Name = "AudioSampleRate", Description = "Optional. Specify a specific audio sample rate, e.g. 44100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? AudioSampleRate { get; set; }
|
||||
|
||||
public int? MaxAudioBitDepth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio bit rate.
|
||||
/// </summary>
|
||||
/// <value>The audio bit rate.</value>
|
||||
[ApiMember(Name = "AudioBitRate", Description = "Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? AudioBitRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio channels.
|
||||
/// </summary>
|
||||
/// <value>The audio channels.</value>
|
||||
[ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? AudioChannels { get; set; }
|
||||
|
||||
[ApiMember(Name = "MaxAudioChannels", Description = "Optional. Specify a maximum number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? MaxAudioChannels { get; set; }
|
||||
|
||||
[ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool Static { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the profile.
|
||||
/// </summary>
|
||||
/// <value>The profile.</value>
|
||||
[ApiMember(Name = "Profile", Description = "Optional. Specify a specific h264 profile, e.g. main, baseline, high.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Profile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level.
|
||||
/// </summary>
|
||||
/// <value>The level.</value>
|
||||
[ApiMember(Name = "Level", Description = "Optional. Specify a level for the h264 profile, e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the framerate.
|
||||
/// </summary>
|
||||
/// <value>The framerate.</value>
|
||||
[ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
|
||||
public float? Framerate { get; set; }
|
||||
|
||||
[ApiMember(Name = "MaxFramerate", Description = "Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
|
||||
public float? MaxFramerate { get; set; }
|
||||
|
||||
[ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool CopyTimestamps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start time ticks.
|
||||
/// </summary>
|
||||
/// <value>The start time ticks.</value>
|
||||
[ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public long? StartTimeTicks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width.
|
||||
/// </summary>
|
||||
/// <value>The width.</value>
|
||||
[ApiMember(Name = "Width", Description = "Optional. The fixed horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height.
|
||||
/// </summary>
|
||||
/// <value>The height.</value>
|
||||
[ApiMember(Name = "Height", Description = "Optional. The fixed vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the max.
|
||||
/// </summary>
|
||||
/// <value>The width of the max.</value>
|
||||
[ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? MaxWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the max.
|
||||
/// </summary>
|
||||
/// <value>The height of the max.</value>
|
||||
[ApiMember(Name = "MaxHeight", Description = "Optional. The maximum vertical resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? MaxHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the video bit rate.
|
||||
/// </summary>
|
||||
/// <value>The video bit rate.</value>
|
||||
[ApiMember(Name = "VideoBitRate", Description = "Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? VideoBitRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the subtitle stream.
|
||||
/// </summary>
|
||||
/// <value>The index of the subtitle stream.</value>
|
||||
[ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
[ApiMember(Name = "SubtitleMethod", Description = "Optional. Specify the subtitle delivery method.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public SubtitleDeliveryMethod SubtitleMethod { get; set; }
|
||||
|
||||
[ApiMember(Name = "MaxRefFrames", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? MaxRefFrames { get; set; }
|
||||
|
||||
[ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? MaxVideoBitDepth { get; set; }
|
||||
public bool RequireAvc { get; set; }
|
||||
public bool DeInterlace { get; set; }
|
||||
public bool RequireNonAnamorphic { get; set; }
|
||||
public int? TranscodingMaxAudioChannels { get; set; }
|
||||
public int? CpuCoreLimit { get; set; }
|
||||
public string OutputContainer { get; set; }
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the video codec.
|
||||
/// </summary>
|
||||
/// <value>The video codec.</value>
|
||||
[ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string VideoCodec { get; set; }
|
||||
|
||||
public string SubtitleCodec { get; set; }
|
||||
|
||||
public string TranscodeReasons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the audio stream.
|
||||
/// </summary>
|
||||
/// <value>The index of the audio stream.</value>
|
||||
[ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the video stream.
|
||||
/// </summary>
|
||||
/// <value>The index of the video stream.</value>
|
||||
[ApiMember(Name = "VideoStreamIndex", Description = "Optional. The index of the video stream to use. If omitted the first video stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? VideoStreamIndex { get; set; }
|
||||
|
||||
public EncodingContext Context { get; set; }
|
||||
|
||||
public void SetOption(string qualifier, string name, string value)
|
||||
{
|
||||
SetOption(qualifier + "-" + name, value);
|
||||
}
|
||||
|
||||
public Dictionary<string, string> StreamOptions { get; set; }
|
||||
|
||||
public void SetOption(string name, string value)
|
||||
{
|
||||
StreamOptions[name] = value;
|
||||
}
|
||||
|
||||
public string GetOption(string qualifier, string name)
|
||||
{
|
||||
return GetOption(qualifier + "-" + name);
|
||||
}
|
||||
|
||||
public string GetOption(string name)
|
||||
{
|
||||
string value;
|
||||
if (StreamOptions.TryGetValue(name, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public BaseEncodingJobOptions()
|
||||
{
|
||||
EnableAutoStreamCopy = true;
|
||||
AllowVideoStreamCopy = true;
|
||||
AllowAudioStreamCopy = true;
|
||||
Context = EncodingContext.Streaming;
|
||||
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public interface IEncodingManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Refreshes the chapter images.
|
||||
/// </summary>
|
||||
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface IMediaEncoder
|
||||
/// </summary>
|
||||
public interface IMediaEncoder : ITranscoderSupport
|
||||
{
|
||||
string EncoderLocationType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoder path.
|
||||
/// </summary>
|
||||
/// <value>The encoder path.</value>
|
||||
string EncoderPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Supportses the decoder.
|
||||
/// </summary>
|
||||
/// <param name="decoder">The decoder.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
bool SupportsDecoder(string decoder);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the audio image.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="imageStreamIndex">Index of the image stream.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the video image.
|
||||
/// </summary>
|
||||
Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
|
||||
|
||||
Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the video images on interval.
|
||||
/// </summary>
|
||||
Task ExtractVideoImagesOnInterval(string[] inputFiles,
|
||||
string container,
|
||||
MediaStream videoStream,
|
||||
MediaProtocol protocol,
|
||||
Video3DFormat? threedFormat,
|
||||
TimeSpan interval,
|
||||
string targetDirectory,
|
||||
string filenamePrefix,
|
||||
int? maxWidth,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media info.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
/// <param name="inputFiles">The input files.</param>
|
||||
/// <param name="protocol">The protocol.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string GetInputArgument(string[] inputFiles, MediaProtocol protocol);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time parameter.
|
||||
/// </summary>
|
||||
/// <param name="ticks">The ticks.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string GetTimeParameter(long ticks);
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the audio.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task<string> EncodeAudio(EncodingJobOptions options,
|
||||
IProgress<double> progress,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the video.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task<System.String>.</returns>
|
||||
Task<string> EncodeVideo(EncodingJobOptions options,
|
||||
IProgress<double> progress,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task ConvertImage(string inputPath, string outputPath);
|
||||
|
||||
/// <summary>
|
||||
/// Escapes the subtitle filter path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
string EscapeSubtitleFilterPath(string path);
|
||||
|
||||
Task Init();
|
||||
|
||||
void UpdateEncoderPath(string path, string pathType);
|
||||
bool SupportsEncoder(string encoder);
|
||||
|
||||
string[] GetPlayableStreamFileNames(string path, VideoType videoType);
|
||||
IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public interface ISubtitleEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the subtitles.
|
||||
/// </summary>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
Task<Stream> GetSubtitles(string itemId,
|
||||
string mediaSourceId,
|
||||
int subtitleStreamIndex,
|
||||
string outputFormat,
|
||||
long startTimeTicks,
|
||||
long? endTimeTicks,
|
||||
bool preserveOriginalTimestamps,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subtitle language encoding parameter.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="protocol">The protocol.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
Task<string> GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class ImageEncodingOptions
|
||||
{
|
||||
public string InputPath { get; set; }
|
||||
|
||||
public int? Width { get; set; }
|
||||
|
||||
public int? Height { get; set; }
|
||||
|
||||
public int? MaxWidth { get; set; }
|
||||
|
||||
public int? MaxHeight { get; set; }
|
||||
|
||||
public int? Quality { get; set; }
|
||||
|
||||
public string Format { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MediaEncoderHelpers
|
||||
/// </summary>
|
||||
public static class MediaEncoderHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="videoPath">The video path.</param>
|
||||
/// <param name="protocol">The protocol.</param>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <param name="playableStreamFileNames">The playable stream file names.</param>
|
||||
/// <returns>System.String[][].</returns>
|
||||
public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, MediaProtocol protocol, IIsoMount isoMount, string[] playableStreamFileNames)
|
||||
{
|
||||
if (playableStreamFileNames.Length > 0)
|
||||
{
|
||||
if (isoMount == null)
|
||||
{
|
||||
return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames);
|
||||
}
|
||||
return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames);
|
||||
}
|
||||
|
||||
return new[] {videoPath};
|
||||
}
|
||||
|
||||
private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, string[] filenames)
|
||||
{
|
||||
if (filenames.Length == 0)
|
||||
{
|
||||
return new string[]{};
|
||||
}
|
||||
|
||||
var allFiles = fileSystem
|
||||
.GetFilePaths(rootPath, true)
|
||||
.ToArray();
|
||||
|
||||
return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
|
||||
.Where(f => !string.IsNullOrEmpty(f))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class MediaInfoRequest
|
||||
{
|
||||
public string InputPath { get; set; }
|
||||
public MediaProtocol Protocol { get; set; }
|
||||
public bool ExtractChapters { get; set; }
|
||||
public DlnaProfileType MediaType { get; set; }
|
||||
public IIsoMount MountedIso { get; set; }
|
||||
public VideoType VideoType { get; set; }
|
||||
public string[] PlayableStreamFileNames { get; set; }
|
||||
public int AnalyzeDurationMs { get; set; }
|
||||
|
||||
public MediaInfoRequest()
|
||||
{
|
||||
PlayableStreamFileNames = new string[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public static class MediaStreamSelector
|
||||
{
|
||||
public static int? GetDefaultAudioStreamIndex(List<MediaStream> streams, IEnumerable<string> preferredLanguages, bool preferDefaultTrack)
|
||||
{
|
||||
streams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages.ToList())
|
||||
.ToList();
|
||||
|
||||
if (preferDefaultTrack)
|
||||
{
|
||||
var defaultStream = streams.FirstOrDefault(i => i.IsDefault);
|
||||
|
||||
if (defaultStream != null)
|
||||
{
|
||||
return defaultStream.Index;
|
||||
}
|
||||
}
|
||||
|
||||
var stream = streams.FirstOrDefault();
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
return stream.Index;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams,
|
||||
List<string> preferredLanguages,
|
||||
SubtitlePlaybackMode mode,
|
||||
string audioTrackLanguage)
|
||||
{
|
||||
streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
|
||||
.ToList();
|
||||
|
||||
MediaStream stream = null;
|
||||
|
||||
if (mode == SubtitlePlaybackMode.None)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mode == SubtitlePlaybackMode.Default)
|
||||
{
|
||||
// Prefer embedded metadata over smart logic
|
||||
|
||||
stream = streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) ??
|
||||
streams.FirstOrDefault(s => s.IsForced) ??
|
||||
streams.FirstOrDefault(s => s.IsDefault);
|
||||
|
||||
// if the audio language is not understood by the user, load their preferred subs, if there are any
|
||||
if (stream == null && !ContainsOrdinal(preferredLanguages, audioTrackLanguage))
|
||||
{
|
||||
stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
|
||||
}
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.Smart)
|
||||
{
|
||||
// Prefer smart logic over embedded metadata
|
||||
|
||||
// if the audio language is not understood by the user, load their preferred subs, if there are any
|
||||
if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
|
||||
{
|
||||
stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)) ??
|
||||
streams.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
|
||||
}
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.Always)
|
||||
{
|
||||
// always load the most suitable full subtitles
|
||||
stream = streams.FirstOrDefault(s => !s.IsForced);
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.OnlyForced)
|
||||
{
|
||||
// always load the most suitable full subtitles
|
||||
stream = streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) ??
|
||||
streams.FirstOrDefault(s => s.IsForced);
|
||||
}
|
||||
|
||||
// load forced subs if we have found no suitable full subtitles
|
||||
stream = stream ?? streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
return stream.Index;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool ContainsOrdinal(IEnumerable<string> list, string item)
|
||||
{
|
||||
return list.Any(i => string.Equals(i, item, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, List<string> languagePreferences)
|
||||
{
|
||||
// Give some preferance to external text subs for better performance
|
||||
return streams.Where(i => i.Type == type)
|
||||
.OrderBy(i =>
|
||||
{
|
||||
var index = languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return index == -1 ? 100 : index;
|
||||
})
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsDefault))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsExternal))
|
||||
.ThenBy(i => i.Index);
|
||||
}
|
||||
|
||||
public static void SetSubtitleStreamScores(List<MediaStream> streams,
|
||||
List<string> preferredLanguages,
|
||||
SubtitlePlaybackMode mode,
|
||||
string audioTrackLanguage)
|
||||
{
|
||||
if (mode == SubtitlePlaybackMode.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
|
||||
.ToList();
|
||||
|
||||
var filteredStreams = new List<MediaStream>();
|
||||
|
||||
if (mode == SubtitlePlaybackMode.Default)
|
||||
{
|
||||
// Prefer embedded metadata over smart logic
|
||||
|
||||
filteredStreams = streams.Where(s => s.IsForced || s.IsDefault)
|
||||
.ToList();
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.Smart)
|
||||
{
|
||||
// Prefer smart logic over embedded metadata
|
||||
|
||||
// if the audio language is not understood by the user, load their preferred subs, if there are any
|
||||
if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
|
||||
{
|
||||
filteredStreams = streams.Where(s => !s.IsForced && ContainsOrdinal(preferredLanguages, s.Language))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.Always)
|
||||
{
|
||||
// always load the most suitable full subtitles
|
||||
filteredStreams = streams.Where(s => !s.IsForced)
|
||||
.ToList();
|
||||
}
|
||||
else if (mode == SubtitlePlaybackMode.OnlyForced)
|
||||
{
|
||||
// always load the most suitable full subtitles
|
||||
filteredStreams = streams.Where(s => s.IsForced).ToList();
|
||||
}
|
||||
|
||||
// load forced subs if we have found no suitable full subtitles
|
||||
if (filteredStreams.Count == 0)
|
||||
{
|
||||
filteredStreams = streams
|
||||
.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
foreach (var stream in filteredStreams)
|
||||
{
|
||||
stream.Score = GetSubtitleScore(stream, preferredLanguages);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetSubtitleScore(MediaStream stream, List<string> languagePreferences)
|
||||
{
|
||||
var values = new List<int>();
|
||||
|
||||
var index = languagePreferences.FindIndex(l => string.Equals(stream.Language, l, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
values.Add(index == -1 ? 0 : 100 - index);
|
||||
|
||||
values.Add(stream.IsForced ? 1 : 0);
|
||||
values.Add(stream.IsDefault ? 1 : 0);
|
||||
values.Add(stream.SupportsExternalStream ? 1 : 0);
|
||||
values.Add(stream.IsTextSubtitleStream ? 1 : 0);
|
||||
values.Add(stream.IsExternal ? 1 : 0);
|
||||
|
||||
values.Reverse();
|
||||
var scale = 1;
|
||||
var score = 0;
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
score += scale * (value + 1);
|
||||
scale *= 10;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int GetBooleanOrderBy(bool value)
|
||||
{
|
||||
return value ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user