|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
#nullable disable
|
|
|
|
|
|
|
|
|
|
#pragma warning disable CS1591
|
|
|
|
|
#pragma warning disable CS1591, SA1401
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
@ -20,6 +20,44 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
// For now, a common base class until the API and MediaEncoding classes are unified
|
|
|
|
|
public class EncodingJobInfo
|
|
|
|
|
{
|
|
|
|
|
public int? OutputAudioBitrate;
|
|
|
|
|
public int? OutputAudioChannels;
|
|
|
|
|
|
|
|
|
|
private TranscodeReason[] _transcodeReasons = null;
|
|
|
|
|
|
|
|
|
|
public EncodingJobInfo(TranscodingJobType jobType)
|
|
|
|
|
{
|
|
|
|
|
TranscodingType = jobType;
|
|
|
|
|
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
SupportedAudioCodecs = Array.Empty<string>();
|
|
|
|
|
SupportedVideoCodecs = Array.Empty<string>();
|
|
|
|
|
SupportedSubtitleCodecs = Array.Empty<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TranscodeReason[] TranscodeReasons
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_transcodeReasons == null)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.TranscodeReasons == null)
|
|
|
|
|
{
|
|
|
|
|
return Array.Empty<TranscodeReason>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_transcodeReasons = BaseRequest.TranscodeReasons
|
|
|
|
|
.Split(',')
|
|
|
|
|
.Where(i => !string.IsNullOrEmpty(i))
|
|
|
|
|
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _transcodeReasons;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IProgress<double> Progress { get; set; }
|
|
|
|
|
|
|
|
|
|
public MediaStream VideoStream { get; set; }
|
|
|
|
|
|
|
|
|
|
public VideoType VideoType { get; set; }
|
|
|
|
@ -58,40 +96,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
|
|
|
|
|
public string MimeType { get; set; }
|
|
|
|
|
|
|
|
|
|
public string GetMimeType(string outputPath, bool enableStreamDefault = true)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(MimeType))
|
|
|
|
|
{
|
|
|
|
|
return MimeType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TranscodeReason[] _transcodeReasons = null;
|
|
|
|
|
|
|
|
|
|
public TranscodeReason[] TranscodeReasons
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_transcodeReasons == null)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.TranscodeReasons == null)
|
|
|
|
|
{
|
|
|
|
|
return Array.Empty<TranscodeReason>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_transcodeReasons = BaseRequest.TranscodeReasons
|
|
|
|
|
.Split(',')
|
|
|
|
|
.Where(i => !string.IsNullOrEmpty(i))
|
|
|
|
|
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _transcodeReasons;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IgnoreInputDts => MediaSource.IgnoreDts;
|
|
|
|
|
|
|
|
|
|
public bool IgnoreInputIndex => MediaSource.IgnoreIndex;
|
|
|
|
@ -144,196 +148,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
|
|
|
|
|
public BaseEncodingJobOptions BaseRequest { get; set; }
|
|
|
|
|
|
|
|
|
|
public long? StartTimeTicks => BaseRequest.StartTimeTicks;
|
|
|
|
|
|
|
|
|
|
public bool CopyTimestamps => 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.IsNullOrEmpty(videoCodec))
|
|
|
|
|
{
|
|
|
|
|
if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string[] GetRequestedProfiles(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(BaseRequest.Profile))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var profile = BaseRequest.GetOption(codec, "profile");
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(profile))
|
|
|
|
|
{
|
|
|
|
|
return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Array.Empty<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetRequestedLevel(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(BaseRequest.Level))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.Level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.GetOption(codec, "level");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedMaxRefFrames(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxRefFrames.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxRefFrames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "maxrefframes");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedVideoBitDepth(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxVideoBitDepth.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxVideoBitDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "videobitdepth");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedAudioBitDepth(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxAudioBitDepth.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxAudioBitDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "audiobitdepth");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedAudioChannels(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "audiochannels");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.MaxAudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxAudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.AudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.AudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.TranscodingMaxAudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsVideoRequest { get; set; }
|
|
|
|
|
|
|
|
|
|
public TranscodingJobType TranscodingType { get; set; }
|
|
|
|
|
|
|
|
|
|
public EncodingJobInfo(TranscodingJobType jobType)
|
|
|
|
|
{
|
|
|
|
|
TranscodingType = jobType;
|
|
|
|
|
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
SupportedAudioCodecs = Array.Empty<string>();
|
|
|
|
|
SupportedVideoCodecs = Array.Empty<string>();
|
|
|
|
|
SupportedSubtitleCodecs = Array.Empty<string>();
|
|
|
|
|
}
|
|
|
|
|
public long? StartTimeTicks => BaseRequest.StartTimeTicks;
|
|
|
|
|
|
|
|
|
|
public bool CopyTimestamps => BaseRequest.CopyTimestamps;
|
|
|
|
|
|
|
|
|
|
public bool IsSegmentedLiveStream
|
|
|
|
|
=> TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
|
|
|
|
|
|
|
|
|
|
public bool EnableBreakOnNonKeyFrames(string videoCodec)
|
|
|
|
|
{
|
|
|
|
|
if (TranscodingType != TranscodingJobType.Progressive)
|
|
|
|
|
{
|
|
|
|
|
if (IsSegmentedLiveStream)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? TotalOutputBitrate => (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
|
|
|
|
|
|
|
|
|
|
public int? OutputWidth
|
|
|
|
@ -682,6 +507,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
|
|
|
|
|
public int HlsListSize => 0;
|
|
|
|
|
|
|
|
|
|
public bool EnableBreakOnNonKeyFrames(string videoCodec)
|
|
|
|
|
{
|
|
|
|
|
if (TranscodingType != TranscodingJobType.Progressive)
|
|
|
|
|
{
|
|
|
|
|
if (IsSegmentedLiveStream)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
|
|
|
|
{
|
|
|
|
|
var count = MediaSource.GetStreamCount(type);
|
|
|
|
@ -694,7 +534,167 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IProgress<double> Progress { get; set; }
|
|
|
|
|
public string GetMimeType(string outputPath, bool enableStreamDefault = true)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(MimeType))
|
|
|
|
|
{
|
|
|
|
|
return MimeType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.IsNullOrEmpty(videoCodec))
|
|
|
|
|
{
|
|
|
|
|
if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string[] GetRequestedProfiles(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(BaseRequest.Profile))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var profile = BaseRequest.GetOption(codec, "profile");
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(profile))
|
|
|
|
|
{
|
|
|
|
|
return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Array.Empty<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetRequestedLevel(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(BaseRequest.Level))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.Level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.GetOption(codec, "level");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedMaxRefFrames(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxRefFrames.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxRefFrames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "maxrefframes");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedVideoBitDepth(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxVideoBitDepth.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxVideoBitDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "videobitdepth");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedAudioBitDepth(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (BaseRequest.MaxAudioBitDepth.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxAudioBitDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "audiobitdepth");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int? GetRequestedAudioChannels(string codec)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(codec))
|
|
|
|
|
{
|
|
|
|
|
var value = BaseRequest.GetOption(codec, "audiochannels");
|
|
|
|
|
if (!string.IsNullOrEmpty(value)
|
|
|
|
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.MaxAudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.MaxAudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.AudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.AudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return BaseRequest.TranscodingMaxAudioChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
|
|
|
|
|
{
|
|
|
|
|