aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/MediaEncoding
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-02-20 11:37:41 -0500
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-02-20 11:37:41 -0500
commit888b8d619aec031f57cfd03410ccda52edcca958 (patch)
tree143080937fb8811d107c610507a15376d6761955 /MediaBrowser.Controller/MediaEncoding
parent160d14208809a13791e34530a3758b079d6b9638 (diff)
added encoding manager interface
Diffstat (limited to 'MediaBrowser.Controller/MediaEncoding')
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs17
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs33
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs108
-rw-r--r--MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs311
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs365
5 files changed, 834 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs b/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs
new file mode 100644
index 000000000..e11bd6cdf
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/ChapterImageRefreshOptions.cs
@@ -0,0 +1,17 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ public class ChapterImageRefreshOptions
+ {
+ public Video Video { get; set; }
+
+ public List<ChapterInfo> Chapters { get; set; }
+
+ public bool SaveChapters { get; set; }
+
+ public bool ExtractImages { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
new file mode 100644
index 000000000..d1e40e3f0
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
@@ -0,0 +1,33 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ public interface IEncodingManager
+ {
+ /// <summary>
+ /// Gets the subtitle cache path.
+ /// </summary>
+ /// <param name="originalSubtitlePath">The original subtitle path.</param>
+ /// <param name="outputSubtitleExtension">The output subtitle extension.</param>
+ /// <returns>System.String.</returns>
+ string GetSubtitleCachePath(string originalSubtitlePath, string outputSubtitleExtension);
+
+ /// <summary>
+ /// Gets the subtitle cache path.
+ /// </summary>
+ /// <param name="mediaPath">The media path.</param>
+ /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
+ /// <param name="outputSubtitleExtension">The output subtitle extension.</param>
+ /// <returns>System.String.</returns>
+ string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension);
+
+ /// <summary>
+ /// Refreshes the chapter images.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{System.Boolean}.</returns>
+ Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
new file mode 100644
index 000000000..119688fa7
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -0,0 +1,108 @@
+using MediaBrowser.Model.Entities;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ /// <summary>
+ /// Interface IMediaEncoder
+ /// </summary>
+ public interface IMediaEncoder
+ {
+ /// <summary>
+ /// Gets the encoder path.
+ /// </summary>
+ /// <value>The encoder path.</value>
+ string EncoderPath { get; }
+
+ /// <summary>
+ /// Gets the version.
+ /// </summary>
+ /// <value>The version.</value>
+ string Version { get; }
+
+ /// <summary>
+ /// Extracts the image.
+ /// </summary>
+ /// <param name="inputFiles">The input files.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
+ /// <param name="threedFormat">The threed format.</param>
+ /// <param name="offset">The offset.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{Stream}.</returns>
+ Task<Stream> ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Extracts the text subtitle.
+ /// </summary>
+ /// <param name="inputFiles">The input files.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
+ /// <param name="copySubtitleStream">if set to true, copy stream instead of converting.</param>
+ /// <param name="outputPath">The output path.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Converts the text subtitle to ass.
+ /// </summary>
+ /// <param name="inputPath">The input path.</param>
+ /// <param name="outputPath">The output path.</param>
+ /// <param name="language">The language.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the media info.
+ /// </summary>
+ /// <param name="inputFiles">The input files.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type, bool isAudio, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the probe size argument.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <returns>System.String.</returns>
+ string GetProbeSizeArgument(InputType type);
+
+ /// <summary>
+ /// Gets the input argument.
+ /// </summary>
+ /// <param name="inputFiles">The input files.</param>
+ /// <param name="type">The type.</param>
+ /// <returns>System.String.</returns>
+ string GetInputArgument(string[] inputFiles, InputType type);
+ }
+
+ /// <summary>
+ /// Enum InputType
+ /// </summary>
+ public enum InputType
+ {
+ /// <summary>
+ /// The file
+ /// </summary>
+ File,
+ /// <summary>
+ /// The bluray
+ /// </summary>
+ Bluray,
+ /// <summary>
+ /// The DVD
+ /// </summary>
+ Dvd,
+ /// <summary>
+ /// The URL
+ /// </summary>
+ Url
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs
new file mode 100644
index 000000000..e113521ec
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs
@@ -0,0 +1,311 @@
+using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ /// <summary>
+ /// Class MediaInfoResult
+ /// </summary>
+ public class InternalMediaInfoResult
+ {
+ /// <summary>
+ /// Gets or sets the streams.
+ /// </summary>
+ /// <value>The streams.</value>
+ public MediaStreamInfo[] streams { get; set; }
+
+ /// <summary>
+ /// Gets or sets the format.
+ /// </summary>
+ /// <value>The format.</value>
+ public MediaFormatInfo format { get; set; }
+
+ /// <summary>
+ /// Gets or sets the chapters.
+ /// </summary>
+ /// <value>The chapters.</value>
+ public List<ChapterInfo> Chapters { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a stream within the output
+ /// </summary>
+ public class MediaStreamInfo
+ {
+ /// <summary>
+ /// Gets or sets the index.
+ /// </summary>
+ /// <value>The index.</value>
+ public int index { get; set; }
+
+ /// <summary>
+ /// Gets or sets the profile.
+ /// </summary>
+ /// <value>The profile.</value>
+ public string profile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_name.
+ /// </summary>
+ /// <value>The codec_name.</value>
+ public string codec_name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_long_name.
+ /// </summary>
+ /// <value>The codec_long_name.</value>
+ public string codec_long_name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_type.
+ /// </summary>
+ /// <value>The codec_type.</value>
+ public string codec_type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_rate.
+ /// </summary>
+ /// <value>The sample_rate.</value>
+ public string sample_rate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channels.
+ /// </summary>
+ /// <value>The channels.</value>
+ public int channels { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel_layout.
+ /// </summary>
+ /// <value>The channel_layout.</value>
+ public string channel_layout { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avg_frame_rate.
+ /// </summary>
+ /// <value>The avg_frame_rate.</value>
+ public string avg_frame_rate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ /// <value>The duration.</value>
+ public string duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bit_rate.
+ /// </summary>
+ /// <value>The bit_rate.</value>
+ public string bit_rate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the width.
+ /// </summary>
+ /// <value>The width.</value>
+ public int width { get; set; }
+
+ /// <summary>
+ /// Gets or sets the height.
+ /// </summary>
+ /// <value>The height.</value>
+ public int height { get; set; }
+
+ /// <summary>
+ /// Gets or sets the display_aspect_ratio.
+ /// </summary>
+ /// <value>The display_aspect_ratio.</value>
+ public string display_aspect_ratio { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ /// <value>The tags.</value>
+ public Dictionary<string, string> tags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bits_per_sample.
+ /// </summary>
+ /// <value>The bits_per_sample.</value>
+ public int bits_per_sample { get; set; }
+
+ /// <summary>
+ /// Gets or sets the r_frame_rate.
+ /// </summary>
+ /// <value>The r_frame_rate.</value>
+ public string r_frame_rate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the has_b_frames.
+ /// </summary>
+ /// <value>The has_b_frames.</value>
+ public int has_b_frames { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_aspect_ratio.
+ /// </summary>
+ /// <value>The sample_aspect_ratio.</value>
+ public string sample_aspect_ratio { get; set; }
+
+ /// <summary>
+ /// Gets or sets the pix_fmt.
+ /// </summary>
+ /// <value>The pix_fmt.</value>
+ public string pix_fmt { get; set; }
+
+ /// <summary>
+ /// Gets or sets the level.
+ /// </summary>
+ /// <value>The level.</value>
+ public int level { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time_base.
+ /// </summary>
+ /// <value>The time_base.</value>
+ public string time_base { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_time.
+ /// </summary>
+ /// <value>The start_time.</value>
+ public string start_time { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_time_base.
+ /// </summary>
+ /// <value>The codec_time_base.</value>
+ public string codec_time_base { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_tag.
+ /// </summary>
+ /// <value>The codec_tag.</value>
+ public string codec_tag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codec_tag_string.
+ /// </summary>
+ /// <value>The codec_tag_string.</value>
+ public string codec_tag_string { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sample_fmt.
+ /// </summary>
+ /// <value>The sample_fmt.</value>
+ public string sample_fmt { get; set; }
+
+ /// <summary>
+ /// Gets or sets the dmix_mode.
+ /// </summary>
+ /// <value>The dmix_mode.</value>
+ public string dmix_mode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_pts.
+ /// </summary>
+ /// <value>The start_pts.</value>
+ public string start_pts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the is_avc.
+ /// </summary>
+ /// <value>The is_avc.</value>
+ public string is_avc { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nal_length_size.
+ /// </summary>
+ /// <value>The nal_length_size.</value>
+ public string nal_length_size { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ltrt_cmixlev.
+ /// </summary>
+ /// <value>The ltrt_cmixlev.</value>
+ public string ltrt_cmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ltrt_surmixlev.
+ /// </summary>
+ /// <value>The ltrt_surmixlev.</value>
+ public string ltrt_surmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the loro_cmixlev.
+ /// </summary>
+ /// <value>The loro_cmixlev.</value>
+ public string loro_cmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the loro_surmixlev.
+ /// </summary>
+ /// <value>The loro_surmixlev.</value>
+ public string loro_surmixlev { get; set; }
+
+ /// <summary>
+ /// Gets or sets the disposition.
+ /// </summary>
+ /// <value>The disposition.</value>
+ public Dictionary<string, string> disposition { get; set; }
+ }
+
+ /// <summary>
+ /// Class MediaFormat
+ /// </summary>
+ public class MediaFormatInfo
+ {
+ /// <summary>
+ /// Gets or sets the filename.
+ /// </summary>
+ /// <value>The filename.</value>
+ public string filename { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nb_streams.
+ /// </summary>
+ /// <value>The nb_streams.</value>
+ public int nb_streams { get; set; }
+
+ /// <summary>
+ /// Gets or sets the format_name.
+ /// </summary>
+ /// <value>The format_name.</value>
+ public string format_name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the format_long_name.
+ /// </summary>
+ /// <value>The format_long_name.</value>
+ public string format_long_name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start_time.
+ /// </summary>
+ /// <value>The start_time.</value>
+ public string start_time { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ /// <value>The duration.</value>
+ public string duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the size.
+ /// </summary>
+ /// <value>The size.</value>
+ public string size { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bit_rate.
+ /// </summary>
+ /// <value>The bit_rate.</value>
+ public string bit_rate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ /// <value>The tags.</value>
+ public Dictionary<string, string> tags { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
new file mode 100644
index 000000000..b2b9e2af3
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -0,0 +1,365 @@
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+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="videoPath">The video path.</param>
+ /// <param name="isRemote">if set to <c>true</c> [is remote].</param>
+ /// <param name="videoType">Type of the video.</param>
+ /// <param name="isoType">Type of the iso.</param>
+ /// <param name="isoMount">The iso mount.</param>
+ /// <param name="playableStreamFileNames">The playable stream file names.</param>
+ /// <param name="type">The type.</param>
+ /// <returns>System.String[][].</returns>
+ public static string[] GetInputArgument(string videoPath, bool isRemote, VideoType videoType, IsoType? isoType, IIsoMount isoMount, IEnumerable<string> playableStreamFileNames, out InputType type)
+ {
+ var inputPath = isoMount == null ? new[] { videoPath } : new[] { isoMount.MountedPath };
+
+ type = InputType.File;
+
+ switch (videoType)
+ {
+ case VideoType.BluRay:
+ type = InputType.Bluray;
+ break;
+ case VideoType.Dvd:
+ type = InputType.Dvd;
+ inputPath = GetPlayableStreamFiles(inputPath[0], playableStreamFileNames).ToArray();
+ break;
+ case VideoType.Iso:
+ if (isoType.HasValue)
+ {
+ switch (isoType.Value)
+ {
+ case IsoType.BluRay:
+ type = InputType.Bluray;
+ break;
+ case IsoType.Dvd:
+ type = InputType.Dvd;
+ inputPath = GetPlayableStreamFiles(inputPath[0], playableStreamFileNames).ToArray();
+ break;
+ }
+ }
+ break;
+ case VideoType.VideoFile:
+ {
+ if (isRemote)
+ {
+ type = InputType.Url;
+ }
+ break;
+ }
+ }
+
+ return inputPath;
+ }
+
+ public static List<string> GetPlayableStreamFiles(string rootPath, IEnumerable<string> filenames)
+ {
+ var allFiles = Directory
+ .EnumerateFiles(rootPath, "*", SearchOption.AllDirectories)
+ .ToList();
+
+ return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
+ .Where(f => !string.IsNullOrEmpty(f))
+ .ToList();
+ }
+
+ /// <summary>
+ /// Gets the type of the input.
+ /// </summary>
+ /// <param name="videoType">Type of the video.</param>
+ /// <param name="isoType">Type of the iso.</param>
+ /// <returns>InputType.</returns>
+ public static InputType GetInputType(VideoType? videoType, IsoType? isoType)
+ {
+ var type = InputType.File;
+
+ if (videoType.HasValue)
+ {
+ switch (videoType.Value)
+ {
+ case VideoType.BluRay:
+ type = InputType.Bluray;
+ break;
+ case VideoType.Dvd:
+ type = InputType.Dvd;
+ break;
+ case VideoType.Iso:
+ if (isoType.HasValue)
+ {
+ switch (isoType.Value)
+ {
+ case IsoType.BluRay:
+ type = InputType.Bluray;
+ break;
+ case IsoType.Dvd:
+ type = InputType.Dvd;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return type;
+ }
+
+ public static Model.Entities.MediaInfo GetMediaInfo(InternalMediaInfoResult data)
+ {
+ var internalStreams = data.streams ?? new MediaStreamInfo[] { };
+
+ var info = new Model.Entities.MediaInfo();
+
+ info.MediaStreams = internalStreams.Select(s => GetMediaStream(s, data.format))
+ .Where(i => i != null)
+ .ToList();
+
+ if (data.format != null)
+ {
+ info.Format = data.format.format_name;
+ }
+
+ return info;
+ }
+
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ /// <summary>
+ /// Converts ffprobe stream info to our MediaStream class
+ /// </summary>
+ /// <param name="streamInfo">The stream info.</param>
+ /// <param name="formatInfo">The format info.</param>
+ /// <returns>MediaStream.</returns>
+ private static MediaStream GetMediaStream(MediaStreamInfo streamInfo, MediaFormatInfo formatInfo)
+ {
+ var stream = new MediaStream
+ {
+ Codec = streamInfo.codec_name,
+ Profile = streamInfo.profile,
+ Level = streamInfo.level,
+ Index = streamInfo.index
+ };
+
+ if (streamInfo.tags != null)
+ {
+ stream.Language = GetDictionaryValue(streamInfo.tags, "language");
+ }
+
+ if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.Type = MediaStreamType.Audio;
+
+ stream.Channels = streamInfo.channels;
+
+ if (!string.IsNullOrEmpty(streamInfo.sample_rate))
+ {
+ stream.SampleRate = int.Parse(streamInfo.sample_rate, UsCulture);
+ }
+
+ stream.ChannelLayout = ParseChannelLayout(streamInfo.channel_layout);
+ }
+ else if (string.Equals(streamInfo.codec_type, "subtitle", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.Type = MediaStreamType.Subtitle;
+ }
+ else if (string.Equals(streamInfo.codec_type, "video", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.Type = MediaStreamType.Video;
+
+ stream.Width = streamInfo.width;
+ stream.Height = streamInfo.height;
+ stream.AspectRatio = GetAspectRatio(streamInfo);
+
+ stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate);
+ stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate);
+ }
+ else
+ {
+ return null;
+ }
+
+ // Get stream bitrate
+ if (stream.Type != MediaStreamType.Subtitle)
+ {
+ var bitrate = 0;
+
+ if (!string.IsNullOrEmpty(streamInfo.bit_rate))
+ {
+ bitrate = int.Parse(streamInfo.bit_rate, UsCulture);
+ }
+ else if (formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate))
+ {
+ // If the stream info doesn't have a bitrate get the value from the media format info
+ bitrate = int.Parse(formatInfo.bit_rate, UsCulture);
+ }
+
+ if (bitrate > 0)
+ {
+ stream.BitRate = bitrate;
+ }
+ }
+
+ if (streamInfo.disposition != null)
+ {
+ var isDefault = GetDictionaryValue(streamInfo.disposition, "default");
+ var isForced = GetDictionaryValue(streamInfo.disposition, "forced");
+
+ stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase);
+
+ stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase);
+ }
+
+ return stream;
+ }
+
+ /// <summary>
+ /// Gets a string from an FFProbeResult tags dictionary
+ /// </summary>
+ /// <param name="tags">The tags.</param>
+ /// <param name="key">The key.</param>
+ /// <returns>System.String.</returns>
+ private static string GetDictionaryValue(Dictionary<string, string> tags, string key)
+ {
+ if (tags == null)
+ {
+ return null;
+ }
+
+ string val;
+
+ tags.TryGetValue(key, out val);
+ return val;
+ }
+
+ private static string ParseChannelLayout(string input)
+ {
+ if (string.IsNullOrEmpty(input))
+ {
+ return input;
+ }
+
+ return input.Split('(').FirstOrDefault();
+ }
+
+ private static string GetAspectRatio(MediaStreamInfo info)
+ {
+ var original = info.display_aspect_ratio;
+
+ int height;
+ int width;
+
+ var parts = (original ?? string.Empty).Split(':');
+ if (!(parts.Length == 2 &&
+ int.TryParse(parts[0], NumberStyles.Any, UsCulture, out width) &&
+ int.TryParse(parts[1], NumberStyles.Any, UsCulture, out height) &&
+ width > 0 &&
+ height > 0))
+ {
+ width = info.width;
+ height = info.height;
+ }
+
+ if (width > 0 && height > 0)
+ {
+ double ratio = width;
+ ratio /= height;
+
+ if (IsClose(ratio, 1.777777778, .03))
+ {
+ return "16:9";
+ }
+
+ if (IsClose(ratio, 1.3333333333, .05))
+ {
+ return "4:3";
+ }
+
+ if (IsClose(ratio, 1.41))
+ {
+ return "1.41:1";
+ }
+
+ if (IsClose(ratio, 1.5))
+ {
+ return "1.5:1";
+ }
+
+ if (IsClose(ratio, 1.6))
+ {
+ return "1.6:1";
+ }
+
+ if (IsClose(ratio, 1.66666666667))
+ {
+ return "5:3";
+ }
+
+ if (IsClose(ratio, 1.85, .02))
+ {
+ return "1.85:1";
+ }
+
+ if (IsClose(ratio, 2.35, .025))
+ {
+ return "2.35:1";
+ }
+
+ if (IsClose(ratio, 2.4, .025))
+ {
+ return "2.40:1";
+ }
+ }
+
+ return original;
+ }
+
+ private static bool IsClose(double d1, double d2, double variance = .005)
+ {
+ return Math.Abs(d1 - d2) <= variance;
+ }
+
+ /// <summary>
+ /// Gets a frame rate from a string value in ffprobe output
+ /// This could be a number or in the format of 2997/125.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>System.Nullable{System.Single}.</returns>
+ private static float? GetFrameRate(string value)
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ var parts = value.Split('/');
+
+ float result;
+
+ if (parts.Length == 2)
+ {
+ result = float.Parse(parts[0], UsCulture) / float.Parse(parts[1], UsCulture);
+ }
+ else
+ {
+ result = float.Parse(parts[0], UsCulture);
+ }
+
+ return float.IsNaN(result) ? (float?)null : result;
+ }
+
+ return null;
+ }
+
+ }
+}