mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-03-16 07:06:18 +00:00
Add GPL modules
This commit is contained in:
@@ -0,0 +1 @@
|
||||
4366f8d8bf9886d71e3e9ddae8480d953caf02cf
|
||||
2503
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
Normal file
2503
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
775
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
Normal file
775
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
Normal file
@@ -0,0 +1,775 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
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 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.IsNullOrEmpty(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 string[] SupportedAudioCodecs { get; set; }
|
||||
public string[] SupportedVideoCodecs { get; set; }
|
||||
public string InputContainer { get; set; }
|
||||
public IsoType? IsoType { 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.IsNullOrEmpty(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.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 new 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");
|
||||
int result;
|
||||
if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out 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");
|
||||
int result;
|
||||
if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out 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");
|
||||
int result;
|
||||
if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int? GetRequestedAudioChannels(string codec)
|
||||
{
|
||||
if (BaseRequest.MaxAudioChannels.HasValue)
|
||||
{
|
||||
return BaseRequest.MaxAudioChannels;
|
||||
}
|
||||
if (BaseRequest.AudioChannels.HasValue)
|
||||
{
|
||||
return BaseRequest.AudioChannels;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(codec))
|
||||
{
|
||||
var value = BaseRequest.GetOption(codec, "audiochannels");
|
||||
int result;
|
||||
if (!string.IsNullOrEmpty(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, IMediaSourceManager unused, TranscodingJobType jobType)
|
||||
{
|
||||
_logger = logger;
|
||||
TranscodingType = jobType;
|
||||
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
PlayableStreamFileNames = new string[] { };
|
||||
SupportedAudioCodecs = new string[] { };
|
||||
SupportedVideoCodecs = new string[] { };
|
||||
SupportedSubtitleCodecs = new 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 ?? 0,
|
||||
BaseRequest.Height ?? 0,
|
||||
BaseRequest.MaxWidth ?? 0,
|
||||
BaseRequest.MaxHeight ?? 0);
|
||||
|
||||
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 ?? 0,
|
||||
BaseRequest.Height ?? 0,
|
||||
BaseRequest.MaxWidth ?? 0,
|
||||
BaseRequest.MaxHeight ?? 0);
|
||||
|
||||
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 || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.Level;
|
||||
}
|
||||
|
||||
var level = GetRequestedLevel(ActualOutputVideoCodec);
|
||||
double result;
|
||||
if (!string.IsNullOrEmpty(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
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.BitDepth;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target reference frames.
|
||||
/// </summary>
|
||||
/// <value>The target reference frames.</value>
|
||||
public int? TargetRefFrames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
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
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate);
|
||||
}
|
||||
|
||||
return BaseRequest.MaxFramerate ?? BaseRequest.Framerate;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.PacketLength;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public string TargetVideoProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.Profile;
|
||||
}
|
||||
|
||||
var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(requestedProfile))
|
||||
{
|
||||
return requestedProfile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Predicts the audio sample rate that will be in the output stream
|
||||
/// </summary>
|
||||
public string TargetVideoRange
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.VideoRange;
|
||||
}
|
||||
|
||||
return "SDR";
|
||||
}
|
||||
}
|
||||
|
||||
public string TargetAudioProfile
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return AudioStream == null ? null : AudioStream.Profile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string TargetVideoCodecTag
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoStream == null ? null : VideoStream.CodecTag;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool? IsTargetAnamorphic
|
||||
{
|
||||
get
|
||||
{
|
||||
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
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 || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
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 || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
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 IProgress<double> Progress { get; set; }
|
||||
public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) {
|
||||
Progress.Report(percentComplete.Value);
|
||||
}
|
||||
|
||||
public virtual void Dispose () {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum TranscodingJobType
|
||||
/// </summary>
|
||||
public enum TranscodingJobType
|
||||
{
|
||||
/// <summary>
|
||||
/// The progressive
|
||||
/// </summary>
|
||||
Progressive,
|
||||
/// <summary>
|
||||
/// The HLS
|
||||
/// </summary>
|
||||
Hls,
|
||||
/// <summary>
|
||||
/// The dash
|
||||
/// </summary>
|
||||
Dash
|
||||
}
|
||||
}
|
||||
265
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
Normal file
265
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
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.ToString("");
|
||||
MediaSourceId = info.MediaSourceId;
|
||||
AudioCodec = info.TargetAudioCodec.FirstOrDefault();
|
||||
MaxAudioChannels = info.GlobalMaxAudioChannels;
|
||||
AudioBitRate = info.AudioBitrate;
|
||||
AudioSampleRate = info.TargetAudioSampleRate;
|
||||
DeviceProfile = deviceProfile;
|
||||
VideoCodec = info.TargetVideoCodec.FirstOrDefault();
|
||||
VideoBitRate = info.VideoBitrate;
|
||||
AudioStreamIndex = info.AudioStreamIndex;
|
||||
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; }
|
||||
|
||||
public bool EnableMpegtsM2TsMode { 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 Dictionary<string, string> StreamOptions { get; set; }
|
||||
|
||||
public string GetOption(string qualifier, string name)
|
||||
{
|
||||
var value = GetOption(qualifier + "-" + name);
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
value = GetOption(name);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public string TempDirectory { get; set; }
|
||||
public string DeviceId { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string Container { get; set; }
|
||||
public DeviceProfile DeviceProfile { get; set; }
|
||||
}
|
||||
}
|
||||
17
MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
Normal file
17
MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
124
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
Normal file
124
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
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);
|
||||
|
||||
void 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);
|
||||
}
|
||||
}
|
||||
33
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
Normal file
33
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public interface ISubtitleEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the subtitles.
|
||||
/// </summary>
|
||||
/// <returns>Task{Stream}.</returns>
|
||||
Task<Stream> GetSubtitles(BaseItem item,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
149
MediaBrowser.Controller/MediaEncoding/JobLogger.cs
Normal file
149
MediaBrowser.Controller/MediaEncoding/JobLogger.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class JobLogger
|
||||
{
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public JobLogger(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async void StartStreamingLog(EncodingJobInfo state, Stream source, Stream target)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var reader = new StreamReader(source))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
|
||||
ParseLogLine(line, state);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
||||
|
||||
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
await target.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error reading ffmpeg log", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseLogLine(string line, EncodingJobInfo state)
|
||||
{
|
||||
float? framerate = null;
|
||||
double? percent = null;
|
||||
TimeSpan? transcodingPosition = null;
|
||||
long? bytesTranscoded = null;
|
||||
int? bitRate = null;
|
||||
|
||||
var parts = line.Split(' ');
|
||||
|
||||
var totalMs = state.RunTimeTicks.HasValue
|
||||
? TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds
|
||||
: 0;
|
||||
|
||||
var startMs = state.BaseRequest.StartTimeTicks.HasValue
|
||||
? TimeSpan.FromTicks(state.BaseRequest.StartTimeTicks.Value).TotalMilliseconds
|
||||
: 0;
|
||||
|
||||
for (var i = 0; i < parts.Length; i++)
|
||||
{
|
||||
var part = parts[i];
|
||||
|
||||
if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) &&
|
||||
(i + 1 < parts.Length))
|
||||
{
|
||||
var rate = parts[i + 1];
|
||||
float val;
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
|
||||
{
|
||||
framerate = val;
|
||||
}
|
||||
}
|
||||
else if (state.RunTimeTicks.HasValue &&
|
||||
part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var time = part.Split(new[] { '=' }, 2).Last();
|
||||
TimeSpan val;
|
||||
|
||||
if (TimeSpan.TryParse(time, _usCulture, out val))
|
||||
{
|
||||
var currentMs = startMs + val.TotalMilliseconds;
|
||||
|
||||
var percentVal = currentMs / totalMs;
|
||||
percent = 100 * percentVal;
|
||||
|
||||
transcodingPosition = val;
|
||||
}
|
||||
}
|
||||
else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var size = part.Split(new[] { '=' }, 2).Last();
|
||||
|
||||
int? scale = null;
|
||||
if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
scale = 1024;
|
||||
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
long val;
|
||||
|
||||
if (long.TryParse(size, NumberStyles.Any, _usCulture, out val))
|
||||
{
|
||||
bytesTranscoded = val * scale.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (part.StartsWith("bitrate=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var rate = part.Split(new[] { '=' }, 2).Last();
|
||||
|
||||
int? scale = null;
|
||||
if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
scale = 1024;
|
||||
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
float val;
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out val))
|
||||
{
|
||||
bitRate = (int)Math.Ceiling(val * scale.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (framerate.HasValue || percent.HasValue)
|
||||
{
|
||||
state.ReportTranscodingProgress(transcodingPosition, 0, percent, 0, bitRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
Normal file
53
MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
Normal file
24
MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class MediaInfoRequest
|
||||
{
|
||||
public MediaSourceInfo MediaSource { get; set; }
|
||||
public bool ExtractChapters { get; set; }
|
||||
public DlnaProfileType MediaType { get; set; }
|
||||
public IIsoMount MountedIso { get; set; }
|
||||
public string[] PlayableStreamFileNames { get; set; }
|
||||
|
||||
public MediaInfoRequest()
|
||||
{
|
||||
PlayableStreamFileNames = new string[] {};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user