From 02938e7bcb0459a44dfa6c9e7dfa2a04be3467be Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 12 Dec 2015 01:49:03 -0500 Subject: update keyframe setting --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 157 +-------------------- 1 file changed, 2 insertions(+), 155 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c8361ea04..a61b15ac5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -135,9 +135,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); - var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; - - return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval, + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } @@ -171,18 +169,16 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The primary path. /// The protocol. /// if set to true [extract chapters]. - /// if set to true [extract key frame interval]. /// The probe size argument. /// if set to true [is audio]. /// Type of the video. /// The cancellation token. /// Task{MediaInfoResult}. - /// + /// ffprobe failed - streams and format are both null. private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, - bool extractKeyFrameInterval, string probeSizeArgument, bool isAudio, VideoType videoType, @@ -262,31 +258,6 @@ namespace MediaBrowser.MediaEncoding.Encoder var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); - if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue) - { - if (ConfigurationManager.Configuration.EnableVideoFrameByFrameAnalysis && mediaInfo.Size.HasValue) - { - foreach (var stream in mediaInfo.MediaStreams) - { - if (EnableKeyframeExtraction(mediaInfo, stream)) - { - try - { - stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error getting key frame interval", ex); - } - } - } - } - } - return mediaInfo; } catch @@ -300,132 +271,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _ffProbeResourcePool.Release(); } } - - throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath)); - } - - private bool EnableKeyframeExtraction(MediaSourceInfo mediaSource, MediaStream videoStream) - { - if (videoStream.Type == MediaStreamType.Video && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && - !videoStream.IsInterlaced && - !(videoStream.IsAnamorphic ?? false)) - { - var audioStreams = mediaSource.MediaStreams.Where(i => i.Type == MediaStreamType.Audio).ToList(); - - // If it has aac audio then it will probably direct stream anyway, so don't bother with this - if (audioStreams.Count == 1 && string.Equals(audioStreams[0].Codec, "aac", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - return false; - } - - private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) - { - inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"'); - - const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\""; - - var process = new Process - { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. - RedirectStandardOutput = true, - RedirectStandardError = true, - FileName = FFProbePath, - Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - - EnableRaisingEvents = true - }; - - _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - - using (process) - { - var start = DateTime.UtcNow; - - process.Start(); - - var lines = new List(); - - try - { - process.BeginErrorReadLine(); - - await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - if (cancellationToken.IsCancellationRequested) - { - throw; - } - } - - process.WaitForExit(); - - _logger.Info("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds); - //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray())); - return lines; - } } - private async Task StartReadingOutput(Stream source, List keyframes, CancellationToken cancellationToken) - { - try - { - using (var reader = new StreamReader(source)) - { - var text = await reader.ReadToEndAsync().ConfigureAwait(false); - - var lines = StringHelper.RegexSplit(text, "[\r\n]+"); - foreach (var line in lines) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var values = line.Split('|') - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => i.Split('=')) - .Where(i => i.Length == 2) - .ToDictionary(i => i[0], i => i[1]); - - string flags; - if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase)) - { - string pts_time; - double frameSeconds; - if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds)) - { - var ms = frameSeconds * 1000; - keyframes.Add(Convert.ToInt32(ms)); - } - } - } - } - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error reading ffprobe output", ex); - } - } /// /// The us culture /// -- cgit v1.2.3 From 949d23f06aeff62f1cd38ddf074b89a1307eedb0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 12:45:50 -0500 Subject: fix merge conflict --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 346 ++++++++++++++++++++- 1 file changed, 344 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a61b15ac5..1dc1a4215 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -135,7 +135,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); - return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, + var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; + + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval, GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } @@ -169,16 +171,18 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The primary path. /// The protocol. /// if set to true [extract chapters]. + /// if set to true [extract key frame interval]. /// The probe size argument. /// if set to true [is audio]. /// Type of the video. /// The cancellation token. /// Task{MediaInfoResult}. - /// ffprobe failed - streams and format are both null. + /// private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, + bool extractKeyFrameInterval, string probeSizeArgument, bool isAudio, VideoType videoType, @@ -258,6 +262,43 @@ namespace MediaBrowser.MediaEncoding.Encoder var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); + var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); + + if (videoStream != null) + { + var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); + + if (isInterlaced) + { + videoStream.IsInterlaced = true; + } + } + + if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue) + { + if (ConfigurationManager.Configuration.EnableVideoFrameByFrameAnalysis && mediaInfo.Size.HasValue) + { + foreach (var stream in mediaInfo.MediaStreams) + { + if (EnableKeyframeExtraction(mediaInfo, stream)) + { + try + { + stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + + } + catch (Exception ex) + { + _logger.ErrorException("Error getting key frame interval", ex); + } + } + } + } + } + return mediaInfo; } catch @@ -271,8 +312,309 @@ namespace MediaBrowser.MediaEncoding.Encoder _ffProbeResourcePool.Release(); } } + + throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath)); + } + + private async Task DetectInterlaced(MediaSourceInfo video, MediaStream videoStream, string inputPath, string probeSizeArgument) + { + if (video.Protocol != MediaProtocol.File) + { + return false; + } + + var formats = (video.Container ?? string.Empty).Split(',').ToList(); + + // Take a shortcut and limit this to containers that are likely to have interlaced content + if (!formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + var args = "{0} -i {1} -map 0:v:{2} -filter:v idet -frames:v 500 -an -f null /dev/null"; + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + FileName = FFMpegPath, + Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(), + + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + + EnableRaisingEvents = true + }; + + _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + var idetFoundInterlaced = false; + + using (var processWrapper = new ProcessWrapper(process, this, _logger)) + { + try + { + StartProcess(processWrapper); + } + catch (Exception ex) + { + _logger.ErrorException("Error starting ffprobe", ex); + + throw; + } + + try + { + process.BeginOutputReadLine(); + + using (var reader = new StreamReader(process.StandardError.BaseStream)) + { + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync().ConfigureAwait(false); + + if (line.StartsWith("[Parsed_idet", StringComparison.OrdinalIgnoreCase)) + { + var idetResult = AnalyzeIdetResult(line); + + if (idetResult.HasValue) + { + if (!idetResult.Value) + { + return false; + } + + idetFoundInterlaced = true; + } + } + } + } + + } + catch + { + StopProcess(processWrapper, 100, true); + + throw; + } + } + + return idetFoundInterlaced; + } + + private bool? AnalyzeIdetResult(string line) + { + // As you can see, the filter only guessed one frame as progressive. + // Results like this are pretty typical. So if less than 30% of the detections are in the "Undetermined" category, then I only consider the video to be interlaced if at least 65% of the identified frames are in either the TFF or BFF category. + // In this case (310 + 311)/(622) = 99.8% which is well over the 65% metric. I may refine that number with more testing but I honestly do not believe I will need to. + // http://awel.domblogger.net/videoTranscode/interlace.html + var index = line.IndexOf("detection:", StringComparison.OrdinalIgnoreCase); + + if (index == -1) + { + return null; + } + + line = line.Substring(index).Trim(); + var parts = line.Split(' ').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => i.Trim()).ToList(); + + if (parts.Count < 2) + { + return null; + } + double tff = 0; + double bff = 0; + double progressive = 0; + double undetermined = 0; + double total = 0; + + for (var i = 0; i < parts.Count - 1; i++) + { + var part = parts[i]; + + if (string.Equals(part, "tff:", StringComparison.OrdinalIgnoreCase)) + { + tff = GetNextPart(parts, i); + total += tff; + } + else if (string.Equals(part, "bff:", StringComparison.OrdinalIgnoreCase)) + { + bff = GetNextPart(parts, i); + total += tff; + } + else if (string.Equals(part, "progressive:", StringComparison.OrdinalIgnoreCase)) + { + progressive = GetNextPart(parts, i); + total += progressive; + } + else if (string.Equals(part, "undetermined:", StringComparison.OrdinalIgnoreCase)) + { + undetermined = GetNextPart(parts, i); + total += undetermined; + } + } + + if (total == 0) + { + return null; + } + + if ((undetermined / total) >= .3) + { + return false; + } + + if (((tff + bff) / total) >= .65) + { + return true; + } + + return false; + } + + private int GetNextPart(List parts, int index) + { + var next = parts[index + 1]; + + int value; + if (int.TryParse(next, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) + { + return value; + } + return 0; } + private bool EnableKeyframeExtraction(MediaSourceInfo mediaSource, MediaStream videoStream) + { + if (videoStream.Type == MediaStreamType.Video && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && + !videoStream.IsInterlaced && + !(videoStream.IsAnamorphic ?? false)) + { + var audioStreams = mediaSource.MediaStreams.Where(i => i.Type == MediaStreamType.Audio).ToList(); + + // If it has aac audio then it will probably direct stream anyway, so don't bother with this + if (audioStreams.Count == 1 && string.Equals(audioStreams[0].Codec, "aac", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return true; + } + return false; + } + + private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) + { + inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"'); + + const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\""; + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + RedirectStandardOutput = true, + RedirectStandardError = true, + FileName = FFProbePath, + Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(), + + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + + EnableRaisingEvents = true + }; + + _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + + using (process) + { + var start = DateTime.UtcNow; + + process.Start(); + + var lines = new List(); + + try + { + process.BeginErrorReadLine(); + + await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (cancellationToken.IsCancellationRequested) + { + throw; + } + } + + process.WaitForExit(); + + _logger.Info("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds); + //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray())); + return lines; + } + } + + private async Task StartReadingOutput(Stream source, List keyframes, CancellationToken cancellationToken) + { + try + { + using (var reader = new StreamReader(source)) + { + var text = await reader.ReadToEndAsync().ConfigureAwait(false); + + var lines = StringHelper.RegexSplit(text, "[\r\n]+"); + foreach (var line in lines) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var values = line.Split('|') + .Where(i => !string.IsNullOrWhiteSpace(i)) + .Select(i => i.Split('=')) + .Where(i => i.Length == 2) + .ToDictionary(i => i[0], i => i[1]); + + string flags; + if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase)) + { + string pts_time; + double frameSeconds; + if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds)) + { + var ms = frameSeconds * 1000; + keyframes.Add(Convert.ToInt32(ms)); + } + } + } + } + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Error reading ffprobe output", ex); + } + } /// /// The us culture /// -- cgit v1.2.3 From 6ea8ef9107275bfddf41bb88a10e913762f868d2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 12:48:42 -0500 Subject: restore changes --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 157 +--- .../MediaBrowser.ServerApplication.csproj | 805 +++++++++++++++++++-- MediaBrowser.sln | 4 + 3 files changed, 733 insertions(+), 233 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1dc1a4215..be43079c5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -135,9 +135,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); - var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; - - return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval, + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } @@ -171,18 +169,16 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The primary path. /// The protocol. /// if set to true [extract chapters]. - /// if set to true [extract key frame interval]. /// The probe size argument. /// if set to true [is audio]. /// Type of the video. /// The cancellation token. /// Task{MediaInfoResult}. - /// + /// ffprobe failed - streams and format are both null. private async Task GetMediaInfoInternal(string inputPath, string primaryPath, MediaProtocol protocol, bool extractChapters, - bool extractKeyFrameInterval, string probeSizeArgument, bool isAudio, VideoType videoType, @@ -274,31 +270,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue) - { - if (ConfigurationManager.Configuration.EnableVideoFrameByFrameAnalysis && mediaInfo.Size.HasValue) - { - foreach (var stream in mediaInfo.MediaStreams) - { - if (EnableKeyframeExtraction(mediaInfo, stream)) - { - try - { - stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error getting key frame interval", ex); - } - } - } - } - } - return mediaInfo; } catch @@ -312,8 +283,6 @@ namespace MediaBrowser.MediaEncoding.Encoder _ffProbeResourcePool.Release(); } } - - throw new ApplicationException(string.Format("FFProbe failed for {0}", inputPath)); } private async Task DetectInterlaced(MediaSourceInfo video, MediaStream videoStream, string inputPath, string probeSizeArgument) @@ -493,128 +462,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return 0; } - private bool EnableKeyframeExtraction(MediaSourceInfo mediaSource, MediaStream videoStream) - { - if (videoStream.Type == MediaStreamType.Video && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && - !videoStream.IsInterlaced && - !(videoStream.IsAnamorphic ?? false)) - { - var audioStreams = mediaSource.MediaStreams.Where(i => i.Type == MediaStreamType.Audio).ToList(); - - // If it has aac audio then it will probably direct stream anyway, so don't bother with this - if (audioStreams.Count == 1 && string.Equals(audioStreams[0].Codec, "aac", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - return false; - } - - private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) - { - inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"'); - - const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\""; - - var process = new Process - { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. - RedirectStandardOutput = true, - RedirectStandardError = true, - FileName = FFProbePath, - Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - - EnableRaisingEvents = true - }; - - _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); - - using (process) - { - var start = DateTime.UtcNow; - - process.Start(); - - var lines = new List(); - - try - { - process.BeginErrorReadLine(); - - await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - if (cancellationToken.IsCancellationRequested) - { - throw; - } - } - - process.WaitForExit(); - - _logger.Info("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds); - //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray())); - return lines; - } - } - - private async Task StartReadingOutput(Stream source, List keyframes, CancellationToken cancellationToken) - { - try - { - using (var reader = new StreamReader(source)) - { - var text = await reader.ReadToEndAsync().ConfigureAwait(false); - - var lines = StringHelper.RegexSplit(text, "[\r\n]+"); - foreach (var line in lines) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var values = line.Split('|') - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => i.Split('=')) - .Where(i => i.Length == 2) - .ToDictionary(i => i[0], i => i[1]); - - string flags; - if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase)) - { - string pts_time; - double frameSeconds; - if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds)) - { - var ms = frameSeconds * 1000; - keyframes.Add(Convert.ToInt32(ms)); - } - } - } - } - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error reading ffprobe output", ex); - } - } /// /// The us culture /// diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 5813dcac5..382bb1c61 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - true + false AnyCPU @@ -64,8 +64,9 @@ False ..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll - - ..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll + + False + ..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll ..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\MediaBrowser.IsoMounter.dll @@ -176,230 +177,878 @@ x86\SQLite.Interop.dll PreserveNewest - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + PreserveNewest - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.sln b/MediaBrowser.sln index d0fbd743e..a9b5020ec 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Performance2.psess = Performance2.psess Performance3.psess = Performance3.psess Performance4.psess = Performance4.psess + Performance5.psess = Performance5.psess EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget (2)", ".nuget (2)", "{E60FB157-87E2-4A41-8B04-27EA49B63B4D}" @@ -520,4 +521,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal -- cgit v1.2.3 From 8d6acbbc680299851192f7f68fae3cc07c87d3f8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 12:50:44 -0500 Subject: adjust logging --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index be43079c5..6f6f2b6a4 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -325,7 +325,7 @@ namespace MediaBrowser.MediaEncoding.Encoder EnableRaisingEvents = true }; - _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); var idetFoundInterlaced = false; using (var processWrapper = new ProcessWrapper(process, this, _logger)) -- cgit v1.2.3 From 4595c3854c397dd57a75db491c3d8f15acea1cc1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 13:02:32 -0500 Subject: update interlaced detection formats --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 6f6f2b6a4..9f23a305a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -295,7 +295,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var formats = (video.Container ?? string.Empty).Split(',').ToList(); // Take a shortcut and limit this to containers that are likely to have interlaced content - if (!formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && + if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) { -- cgit v1.2.3 From 255747337a4b6017e35ce1b58192076bb3a90c61 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 13:23:13 -0500 Subject: fix merge conflict --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 9f23a305a..f44d922d5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -258,17 +258,17 @@ namespace MediaBrowser.MediaEncoding.Encoder var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); - var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); + //var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - if (videoStream != null) - { - var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); + //if (videoStream != null) + //{ + // var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); - if (isInterlaced) - { - videoStream.IsInterlaced = true; - } - } + // if (isInterlaced) + // { + // videoStream.IsInterlaced = true; + // } + //} return mediaInfo; } -- cgit v1.2.3 From 3c433e95b9ea8b0b19a1a1315ae19d1c7fca0e88 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 13:29:53 -0500 Subject: update interlaced detection --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 35 ++++++++++++---------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f44d922d5..dedacccfa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -258,17 +258,17 @@ namespace MediaBrowser.MediaEncoding.Encoder var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); - //var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); + var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - //if (videoStream != null) - //{ - // var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); + if (videoStream != null) + { + var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); - // if (isInterlaced) - // { - // videoStream.IsInterlaced = true; - // } - //} + if (isInterlaced) + { + videoStream.IsInterlaced = true; + } + } return mediaInfo; } @@ -294,14 +294,17 @@ namespace MediaBrowser.MediaEncoding.Encoder var formats = (video.Container ?? string.Empty).Split(',').ToList(); - // Take a shortcut and limit this to containers that are likely to have interlaced content - if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) + // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content + if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) == -1) { - return false; + if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) + { + return false; + } } var args = "{0} -i {1} -map 0:v:{2} -filter:v idet -frames:v 500 -an -f null /dev/null"; -- cgit v1.2.3 From 59277211c7482e00a58cc74fc67068dab594d863 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 19 Dec 2015 13:33:40 -0500 Subject: update interlaced detection --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index dedacccfa..f436ca3a0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -292,11 +292,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - var formats = (video.Container ?? string.Empty).Split(',').ToList(); - // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) == -1) { + var formats = (video.Container ?? string.Empty).Split(',').ToList(); + if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && -- cgit v1.2.3 From 81fb823c024a6652d967bb818cb4fe83417132c4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 11 Jan 2016 11:52:22 -0500 Subject: record mediastream comment --- .../MediaEncoding/IMediaEncoder.cs | 3 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 14 +++++----- .../Probing/ProbeResultNormalizer.cs | 1 + MediaBrowser.Model/Entities/MediaStream.cs | 6 ++++ .../MediaInfo/AudioImageProvider.cs | 29 ++++++++++++++------ .../Persistence/MediaStreamColumns.cs | 32 ++++++++++++++++++++++ .../Persistence/SqliteItemRepository.cs | 11 ++++++-- 7 files changed, 78 insertions(+), 18 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 427af6f6d..76ef054de 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -35,9 +35,10 @@ namespace MediaBrowser.Controller.MediaEncoding /// Extracts the audio image. /// /// The path. + /// Index of the image stream. /// The cancellation token. /// Task{Stream}. - Task ExtractAudioImage(string path, CancellationToken cancellationToken); + Task ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken); /// /// Extracts the video image. diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f436ca3a0..3c6a99c65 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -472,18 +472,18 @@ namespace MediaBrowser.MediaEncoding.Encoder /// protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public Task ExtractAudioImage(string path, CancellationToken cancellationToken) + public Task ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) { - return ExtractImage(new[] { path }, MediaProtocol.File, true, null, null, cancellationToken); + return ExtractImage(new[] { path }, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); } public Task ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { - return ExtractImage(inputFiles, protocol, false, threedFormat, offset, cancellationToken); + return ExtractImage(inputFiles, null, protocol, false, threedFormat, offset, cancellationToken); } - private async Task ExtractImage(string[] inputFiles, MediaProtocol protocol, bool isAudio, + private async Task ExtractImage(string[] inputFiles, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) { var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool; @@ -494,7 +494,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { try { - return await ExtractImageInternal(inputArgument, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false); + return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false); } catch (ArgumentException) { @@ -506,10 +506,10 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - return await ExtractImageInternal(inputArgument, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false); + return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false); } - private async Task ExtractImageInternal(string inputPath, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + private async Task ExtractImageInternal(string inputPath, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index d98efffe7..1df8896d5 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -141,6 +141,7 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo.tags != null) { stream.Language = GetDictionaryValue(streamInfo.tags, "language"); + stream.Comment = GetDictionaryValue(streamInfo.tags, "comment"); } if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index d089f0aa9..7f4cc2f84 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -30,6 +30,12 @@ namespace MediaBrowser.Model.Entities /// The language. public string Language { get; set; } + /// + /// Gets or sets the comment. + /// + /// The comment. + public string Comment { get; set; } + /// /// Gets or sets a value indicating whether this instance is interlaced. /// diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 8884412d2..c98a67bbd 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using System; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -43,20 +44,27 @@ namespace MediaBrowser.Providers.MediaInfo { var audio = (Audio)item; + var imageStreams = + audio.GetMediaSources(false) + .Take(1) + .SelectMany(i => i.MediaStreams) + .Where(i => i.Type == MediaStreamType.EmbeddedImage) + .ToList(); + // Can't extract if we didn't find a video stream in the file - if (!audio.GetMediaSources(false).Take(1).SelectMany(i => i.MediaStreams).Any(i => i.Type == MediaStreamType.EmbeddedImage)) + if (imageStreams.Count == 0) { return Task.FromResult(new DynamicImageResponse { HasImage = false }); } - return GetImage((Audio)item, cancellationToken); + return GetImage((Audio)item, imageStreams, cancellationToken); } - public async Task GetImage(Audio item, CancellationToken cancellationToken) + public async Task GetImage(Audio item, List imageStreams, CancellationToken cancellationToken) { var path = GetAudioImagePath(item); - if (!_fileSystem.FileExists(path)) + if (!_fileSystem.FileExists(path)) { var semaphore = GetLock(path); @@ -66,11 +74,16 @@ namespace MediaBrowser.Providers.MediaInfo try { // Check again in case it was saved while waiting for the lock - if (!_fileSystem.FileExists(path)) + if (!_fileSystem.FileExists(path)) { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + + var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ?? + imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1); + + var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index; - using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, cancellationToken).ConfigureAwait(false)) + using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs index c983dd547..7e46db5a7 100644 --- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs +++ b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs @@ -28,6 +28,38 @@ namespace MediaBrowser.Server.Implementations.Persistence AddKeyFramesColumn(); AddRefFramesCommand(); AddCodecTagColumn(); + AddCommentColumn(); + } + + private void AddCommentColumn() + { + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(mediastreams)"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + if (!reader.IsDBNull(1)) + { + var name = reader.GetString(1); + + if (string.Equals(name, "Comment", StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + } + } + + var builder = new StringBuilder(); + + builder.AppendLine("alter table mediastreams"); + builder.AppendLine("add column Comment TEXT"); + + _connection.RunQueries(new[] { builder.ToString() }, _logger); } private void AddCodecTagColumn() diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 1d3ac293e..3d5ddcf49 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); var createMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, CodecTag TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, CodecTag TEXT NULL, Comment TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { @@ -385,7 +385,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "IsAnamorphic", "RefFrames", "IsCabac", - "CodecTag" + "CodecTag", + "Comment" }; /// @@ -2683,6 +2684,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(index++).Value = stream.IsCabac; _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag; + _saveStreamCommand.GetParameter(index++).Value = stream.Comment; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); @@ -2841,6 +2843,11 @@ namespace MediaBrowser.Server.Implementations.Persistence item.CodecTag = reader.GetString(26); } + if (!reader.IsDBNull(27)) + { + item.Comment = reader.GetString(27); + } + return item; } -- cgit v1.2.3