From ae248b045a923449c9f957c0205ef387ad8a4047 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 26 Mar 2014 17:05:31 -0400 Subject: [PATCH] use ffprobe -show_chapters command --- .../MediaEncoding/InternalMediaInfoResult.cs | 16 +++- .../MediaInfo/FFProbeAudioInfo.cs | 6 +- .../MediaInfo/FFProbeVideoInfo.cs | 27 +++++- .../MediaEncoder/MediaEncoder.cs | 90 ++----------------- 4 files changed, 49 insertions(+), 90 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs index e113521ecf..39d1c32202 100644 --- a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs +++ b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Model.Entities; -using System.Collections.Generic; +using System.Collections.Generic; namespace MediaBrowser.Controller.MediaEncoding { @@ -24,7 +23,18 @@ namespace MediaBrowser.Controller.MediaEncoding /// Gets or sets the chapters. /// /// The chapters. - public List Chapters { get; set; } + public MediaChapter[] Chapters { get; set; } + } + + public class MediaChapter + { + public int id { get; set; } + public string time_base { get; set; } + public long start { get; set; } + public string start_time { get; set; } + public long end { get; set; } + public string end_time { get; set; } + public Dictionary tags { get; set; } } /// diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index a27fa057ca..75a9d9c36a 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -50,12 +50,16 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } + private const string SchemaVersion = "1"; + private async Task GetMediaInfo(BaseItem item, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var idString = item.Id.ToString("N"); - var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-audio", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); + var cachePath = Path.Combine(_appPaths.CachePath, + "ffprobe-audio", + idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); try { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index d516a8221f..58fe7f66d4 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -107,6 +107,8 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } + private const string SchemaVersion = "1"; + private async Task GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -114,7 +116,9 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); var idString = item.Id.ToString("N"); - var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); + var cachePath = Path.Combine(_appPaths.CachePath, + "ffprobe-video", + idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); try { @@ -161,7 +165,8 @@ namespace MediaBrowser.Providers.MediaInfo var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams; - var chapters = data.Chapters ?? new List(); + var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList(); + var chapters = mediaChapters.Select(GetChapterInfo).ToList(); if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) { @@ -200,6 +205,24 @@ namespace MediaBrowser.Providers.MediaInfo await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); } + private ChapterInfo GetChapterInfo(MediaChapter chapter) + { + var info = new ChapterInfo(); + + if (chapter.tags != null) + { + string name; + if (chapter.tags.TryGetValue("title", out name)) + { + info.Name = name; + } + } + + info.StartPositionTicks = chapter.start/100; + + return info; + } + private void FetchBdInfo(BaseItem item, List chapters, List mediaStreams, BlurayDiscInfo blurayInfo) { var video = (Video)item; diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index fddba76626..c646d80bce 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -175,6 +175,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder string probeSizeArgument, CancellationToken cancellationToken) { + var args = extractChapters + ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format" + : "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format"; + var process = new Process { StartInfo = new ProcessStartInfo @@ -186,8 +190,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder RedirectStandardOutput = true, RedirectStandardError = true, FileName = FFProbePath, - Arguments = string.Format( - "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format", + Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(), WindowStyle = ProcessWindowStyle.Hidden, @@ -204,7 +207,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); InternalMediaInfoResult result; - string standardError = null; try { @@ -221,24 +223,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder try { - Task standardErrorReadTask = null; - - // MUST read both stdout and stderr asynchronously or a deadlock may occurr - if (extractChapters) - { - standardErrorReadTask = process.StandardError.ReadToEndAsync(); - } - else - { - process.BeginErrorReadLine(); - } + process.BeginErrorReadLine(); result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); - - if (extractChapters) - { - standardError = await standardErrorReadTask.ConfigureAwait(false); - } } catch { @@ -282,11 +269,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } } - if (extractChapters && !string.IsNullOrEmpty(standardError)) - { - AddChapters(result, standardError); - } - return result; } @@ -295,66 +277,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// - /// Adds the chapters. - /// - /// The result. - /// The standard error. - private void AddChapters(InternalMediaInfoResult result, string standardError) - { - var lines = standardError.Split('\n').Select(l => l.TrimStart()); - - var chapters = new List(); - - ChapterInfo lastChapter = null; - - foreach (var line in lines) - { - if (line.StartsWith("Chapter", StringComparison.OrdinalIgnoreCase)) - { - // Example: - // Chapter #0.2: start 400.534, end 4565.435 - const string srch = "start "; - var start = line.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - - if (start == -1) - { - continue; - } - - var subString = line.Substring(start + srch.Length); - subString = subString.Substring(0, subString.IndexOf(',')); - - double seconds; - - if (double.TryParse(subString, NumberStyles.Any, UsCulture, out seconds)) - { - lastChapter = new ChapterInfo - { - StartPositionTicks = TimeSpan.FromSeconds(seconds).Ticks - }; - - chapters.Add(lastChapter); - } - } - - else if (line.StartsWith("title", StringComparison.OrdinalIgnoreCase)) - { - if (lastChapter != null && string.IsNullOrEmpty(lastChapter.Name)) - { - var index = line.IndexOf(':'); - - if (index != -1) - { - lastChapter.Name = line.Substring(index + 1).Trim().TrimEnd('\r'); - } - } - } - } - - result.Chapters = chapters; - } - /// /// Processes the exited. ///