Merge pull request #6414 from richlander/mediabrowser-controller-mediaencoding

pull/6267/head^2
Bond-009 3 years ago committed by GitHub
commit f83a5bb08e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,6 +10,15 @@ namespace MediaBrowser.Controller.MediaEncoding
{
public class BaseEncodingJobOptions
{
public BaseEncodingJobOptions()
{
EnableAutoStreamCopy = true;
AllowVideoStreamCopy = true;
AllowAudioStreamCopy = true;
Context = EncodingContext.Streaming;
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// Gets or sets the id.
/// </summary>
@ -191,14 +200,5 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
public BaseEncodingJobOptions()
{
EnableAutoStreamCopy = true;
AllowVideoStreamCopy = true;
AllowAudioStreamCopy = true;
Context = EncodingContext.Streaming;
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
}

@ -161,6 +161,9 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the name of the output video codec.
/// </summary>
/// <param name="state">Encording state.</param>
/// <param name="encodingOptions">Encoding options.</param>
/// <returns>Encoder string.</returns>
public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
{
var codec = state.OutputVideoCodec;
@ -315,6 +318,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return container;
}
/// <summary>
/// Gets decoder from a codec.
/// </summary>
/// <param name="codec">Codec to use.</param>
/// <returns>Decoder string.</returns>
public string GetDecoderFromCodec(string codec)
{
// For these need to find out the ffmpeg names
@ -344,6 +352,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Infers the audio codec based on the url.
/// </summary>
/// <param name="container">Container to use.</param>
/// <returns>Codec string.</returns>
public string InferAudioCodec(string container)
{
var ext = "." + (container ?? string.Empty);
@ -489,6 +499,9 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="encodingOptions">Encoding options.</param>
/// <returns>Input arguments.</returns>
public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
{
var arg = new StringBuilder();
@ -965,6 +978,11 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the video bitrate to specify on the command line.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="videoEncoder">Video encoder to use.</param>
/// <param name="encodingOptions">Encoding options.</param>
/// <param name="defaultPreset">Default present to use for encoding.</param>
/// <returns>Video bitrate.</returns>
public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset)
{
var param = string.Empty;
@ -1966,8 +1984,12 @@ namespace MediaBrowser.Controller.MediaEncoding
}
/// <summary>
/// Gets the graphical subtitle param.
/// Gets the graphical subtitle parameter.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="options">Encoding options.</param>
/// <param name="outputVideoCodec">Video codec to use.</param>
/// <returns>Graphical subtitle parameter.</returns>
public string GetGraphicalSubtitleParam(
EncodingJobInfo state,
EncodingOptions options,
@ -2485,6 +2507,13 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam);
}
/// <summary>
/// Gets the output size parameter.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="options">Encoding options.</param>
/// <param name="outputVideoCodec">Video codec to use.</param>
/// <returns>The output size parameter.</returns>
public string GetOutputSizeParam(
EncodingJobInfo state,
EncodingOptions options,
@ -2495,8 +2524,13 @@ namespace MediaBrowser.Controller.MediaEncoding
}
/// <summary>
/// Gets the output size parameter.
/// If we're going to put a fixed size on the command line, this will calculate it.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="options">Encoding options.</param>
/// <param name="outputVideoCodec">Video codec to use.</param>
/// <returns>The output size parameter.</returns>
public string GetOutputSizeParamInternal(
EncodingJobInfo state,
EncodingOptions options,
@ -2908,6 +2942,10 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the number of threads.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="encodingOptions">Encoding options.</param>
/// <param name="outputVideoCodec">Video codec to use.</param>
/// <returns>Number of threads.</returns>
#nullable enable
public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec)
{
@ -3551,6 +3589,11 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets a hw decoder name.
/// </summary>
/// <param name="options">Encoding options.</param>
/// <param name="decoder">Decoder to use.</param>
/// <param name="videoCodec">Video codec to use.</param>
/// <param name="isColorDepth10">Specifies if color depth 10.</param>
/// <returns>Hardware decoder name.</returns>
public string GetHwDecoderName(EncodingOptions options, string decoder, string videoCodec, bool isColorDepth10)
{
var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoder) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase);
@ -3569,6 +3612,11 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system.
/// </summary>
/// <param name="state">Encoding state.</param>
/// <param name="options">Encoding options.</param>
/// <param name="videoCodec">Video codec to use.</param>
/// <param name="isColorDepth10">Specifies if color depth 10.</param>
/// <returns>Hardware accelerator type.</returns>
public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec, bool isColorDepth10)
{
var isWindows = OperatingSystem.IsWindows();

@ -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)
{

@ -16,6 +16,13 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Refreshes the chapter images.
/// </summary>
/// <param name="video">Video to use.</param>
/// <param name="directoryService">Directory service to use.</param>
/// <param name="chapters">Set of chapters to refresh.</param>
/// <param name="extractImages">Option to extract images.</param>
/// <param name="saveChapters">Option to save chapters.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns><c>true</c> if successful, <c>false</c> if not.</returns>
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
}
}

@ -71,13 +71,42 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Extracts the video image.
/// </summary>
/// <param name="inputFile">Input file.</param>
/// <param name="container">Video container type.</param>
/// <param name="mediaSource">Media source information.</param>
/// <param name="videoStream">Media stream information.</param>
/// <param name="threedFormat">Video 3D format.</param>
/// <param name="offset">Time offset.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>Location of video image.</returns>
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
/// <summary>
/// Extracts the video image.
/// </summary>
/// <param name="inputFile">Input file.</param>
/// <param name="container">Video container type.</param>
/// <param name="mediaSource">Media source information.</param>
/// <param name="imageStream">Media stream information.</param>
/// <param name="imageStreamIndex">Index of the stream to extract from.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>Location of video image.</returns>
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary>
/// Extracts the video images on interval.
/// </summary>
/// <param name="inputFile">Input file.</param>
/// <param name="container">Video container type.</param>
/// <param name="videoStream">Media stream information.</param>
/// <param name="mediaSource">Media source information.</param>
/// <param name="threedFormat">Video 3D format.</param>
/// <param name="interval">Time interval.</param>
/// <param name="targetDirectory">Directory to write images.</param>
/// <param name="filenamePrefix">Filename prefix to use.</param>
/// <param name="maxWidth">Maximum width of image.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>A task.</returns>
Task ExtractVideoImagesOnInterval(
string inputFile,
string container,
@ -122,10 +151,24 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.String.</returns>
string EscapeSubtitleFilterPath(string path);
/// <summary>
/// Sets the path to find FFmpeg.
/// </summary>
void SetFFmpegPath();
/// <summary>
/// Updates the encoder path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="pathType">The type of path.</param>
void UpdateEncoderPath(string path, string pathType);
/// <summary>
/// Gets the primary playlist of .vob files.
/// </summary>
/// <param name="path">The to the .vob files.</param>
/// <param name="titleNumber">The title number to start with.</param>
/// <returns>A playlist.</returns>
IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber);
}
}

@ -15,6 +15,14 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the subtitles.
/// </summary>
/// <param name="item">Item to use.</param>
/// <param name="mediaSourceId">Media source.</param>
/// <param name="subtitleStreamIndex">Subtitle stream to use.</param>
/// <param name="outputFormat">Output format to use.</param>
/// <param name="startTimeTicks">Start time.</param>
/// <param name="endTimeTicks">End time.</param>
/// <param name="preserveOriginalTimestamps">Option to preserve original timestamps.</param>
/// <param name="cancellationToken">The cancellation token for the operation.</param>
/// <returns>Task{Stream}.</returns>
Task<Stream> GetSubtitles(
BaseItem item,

Loading…
Cancel
Save