From 3e856845aedb2507eed96668c81f5f701e3da70c Mon Sep 17 00:00:00 2001 From: Vlad Jerca Date: Mon, 1 Apr 2019 12:38:58 +0300 Subject: [PATCH] FFProbe: improve metadata extraction #3 --- FFMpegCore/FFMPEG/FFMpegStreamMetadata.cs | 41 +++++++++++ FFMpegCore/FFMPEG/FFProbe.cs | 88 ++++++++++------------- 2 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 FFMpegCore/FFMPEG/FFMpegStreamMetadata.cs diff --git a/FFMpegCore/FFMPEG/FFMpegStreamMetadata.cs b/FFMpegCore/FFMPEG/FFMpegStreamMetadata.cs new file mode 100644 index 0000000..68269cf --- /dev/null +++ b/FFMpegCore/FFMPEG/FFMpegStreamMetadata.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace FFMpegCore.FFMPEG +{ + internal class Stream + { + [JsonProperty("index")] + internal int Index { get; set; } + + [JsonProperty("codec_name")] + internal string CodecName { get; set; } + + [JsonProperty("bit_rate")] + internal string BitRate { get; set; } + + [JsonProperty("profile")] + internal string Profile { get; set; } + + [JsonProperty("codec_type")] + internal string CodecType { get; set; } + + [JsonProperty("width")] + internal int Width { get; set; } + + [JsonProperty("height")] + internal int Height { get; set; } + + [JsonProperty("duration")] + internal string Duration { get; set; } + + [JsonProperty("r_frame_rate")] + internal string FrameRate { get; set; } + } + + internal class FFMpegStreamMetadata + { + [JsonProperty("streams")] + internal List Streams { get; set; } + } +} diff --git a/FFMpegCore/FFMPEG/FFProbe.cs b/FFMpegCore/FFMPEG/FFProbe.cs index d285186..ad7a9bc 100644 --- a/FFMpegCore/FFMPEG/FFProbe.cs +++ b/FFMpegCore/FFMPEG/FFProbe.cs @@ -1,14 +1,15 @@ -using FFMpegCore.Helpers; +using FFMpegCore.FFMPEG.Exceptions; +using FFMpegCore.Helpers; using Newtonsoft.Json; using System; -using System.Collections.Generic; using System.Globalization; -using System.IO; namespace FFMpegCore.FFMPEG { public sealed class FFProbe : FFBase { + static readonly double BITS_TO_MB = 1024 * 1024 * 8; + public FFProbe(): base() { FFProbeHelper.RootExceptionCheck(FFMpegOptions.Options.RootDirectory); @@ -38,71 +39,56 @@ public VideoInfo ParseVideoInfo(VideoInfo info) var jsonOutput = RunProcess($"-v quiet -print_format json -show_streams \"{info.FullName}\""); - var metadata = JsonConvert.DeserializeObject>(jsonOutput); - int videoIndex = metadata["streams"][0]["codec_type"] == "video" ? 0 : 1, - audioIndex = 1 - videoIndex; + var metadata = JsonConvert.DeserializeObject(jsonOutput); - var bitRate = Convert.ToDouble(metadata["streams"][videoIndex]["bit_rate"], CultureInfo.InvariantCulture); - - try + if (metadata.Streams == null || metadata.Streams.Count == 0) { - var duration = Convert.ToDouble(metadata["streams"][videoIndex]["duration"], CultureInfo.InvariantCulture); - info.Duration = TimeSpan.FromSeconds(duration); - info.Duration = info.Duration.Subtract(TimeSpan.FromMilliseconds(info.Duration.Milliseconds)); + throw new FFMpegException(FFMpegExceptionType.File, $"No video or audio streams could be detected. Source: ${info.FullName}"); } - catch (Exception) + + var video = metadata.Streams.Find(s => s.CodecType == "video"); + var audio = metadata.Streams.Find(s => s.CodecType == "audio"); + + double videoSize = 0d; + double audioSize = 0d; + + var duration = TimeSpan.FromSeconds(double.TryParse((video ?? audio).Duration, out var output) ? output : 0); + info.Duration = duration.Subtract(TimeSpan.FromMilliseconds(duration.Milliseconds)); + + if (video != null) { - info.Duration = TimeSpan.FromSeconds(0); - } + var bitRate = Convert.ToDouble(video.BitRate, CultureInfo.InvariantCulture); + var fr = video.FrameRate.Split('/'); + var commonDenominator = FFProbeHelper.Gcd(video.Width, video.Height); + videoSize = bitRate * duration.TotalSeconds / BITS_TO_MB; - // Get video size in megabytes - double videoSize = 0, - audioSize = 0; - - try - { - info.VideoFormat = metadata["streams"][videoIndex]["codec_name"]; - videoSize = bitRate * info.Duration / 8388608; - } - catch (Exception) + info.VideoFormat = video.CodecName; + info.Width = video.Width; + info.Height = video.Height; + info.FrameRate = Math.Round( + Convert.ToDouble(fr[0], CultureInfo.InvariantCulture) / + Convert.ToDouble(fr[1], CultureInfo.InvariantCulture), + 3); + info.Ratio = video.Width / commonDenominator + ":" + video.Height / commonDenominator; + } else { info.VideoFormat = "none"; } - // Get audio format - wrap for exceptions if the video has no audio - try + if (audio != null) { - info.AudioFormat = metadata["streams"][audioIndex]["codec_name"]; - audioSize = bitRate * info.Duration / 8388608; - } - catch (Exception) + var bitRate = Convert.ToDouble(audio.BitRate, CultureInfo.InvariantCulture); + info.AudioFormat = audio.CodecName; + audioSize = bitRate * duration.TotalSeconds / BITS_TO_MB; + } else { info.AudioFormat = "none"; + } - // Get video format - - - // Get video width - info.Width = metadata["streams"][videoIndex]["width"]; - - // Get video height - info.Height = metadata["streams"][videoIndex]["height"]; - info.Size = Math.Round(videoSize + audioSize, 2); - // Get video aspect ratio - var cd = FFProbeHelper.Gcd(info.Width, info.Height); - info.Ratio = info.Width / cd + ":" + info.Height / cd; - - // Get video framerate - var fr = ((string)metadata["streams"][videoIndex]["r_frame_rate"]).Split('/'); - info.FrameRate = Math.Round( - Convert.ToDouble(fr[0], CultureInfo.InvariantCulture) / - Convert.ToDouble(fr[1], CultureInfo.InvariantCulture), - 3); - return info; }