/// <summary>
        /// Reads all tags from matroska file and parses the XML for the requested tags (<paramref name="tagsToExtract"/>).
        /// </summary>
        /// <param name="tagsToExtract">Dictionary with tag names as keys.</param>
        public async Task ReadTagsAsync(IDictionary <string, IList <string> > tagsToExtract)
        {
            ProcessExecutionResult executionResult = null;
            // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
            var arguments = string.Format("tags \"{0}\"", _lfsra.LocalFileSystemPath);
            await MKVEXTRACT_THROTTLE_LOCK.WaitAsync().ConfigureAwait(false);

            try
            {
                executionResult = await _lfsra.ExecuteWithResourceAccessAsync(_mkvExtractPath, arguments, _priorityClass, PROCESS_TIMEOUT_MS).ConfigureAwait(false);
            }
            catch (TaskCanceledException)
            {
                ServiceRegistration.Get <ILogger>().Warn("MatroskaInfoReader.ReadTags: External process aborted due to timeout: Executable='{0}', Arguments='{1}'", _mkvExtractPath, arguments);
            }
            finally
            {
                MKVEXTRACT_THROTTLE_LOCK.Release();
            }
            if (executionResult != null && executionResult.Success && !string.IsNullOrEmpty(executionResult.StandardOutput))
            {
                XDocument doc = XDocument.Parse(executionResult.StandardOutput);

                foreach (string key in new List <string>(tagsToExtract.Keys))
                {
                    string[] parts      = key.Split('.');
                    int?     targetType = null;
                    string   tagName;
                    if (parts.Length == 2)
                    {
                        targetType = int.Parse(parts[0]);
                        tagName    = parts[1];
                    }
                    else
                    {
                        tagName = parts[0];
                    }

                    var result = from simpleTag in GetTagsForTargetType(doc, targetType).Elements("Simple")
                                 let nameElement = simpleTag.Element("Name")
                                                   let stringElement = simpleTag.Element("String")
                                                                       where nameElement != null && nameElement.Value == tagName &&
                                                                       stringElement != null && !string.IsNullOrWhiteSpace(stringElement.Value)
                                                                       select stringElement.Value;

                    var resultList = result.ToList();
                    if (resultList.Any())
                    {
                        tagsToExtract[key] = resultList.ToList();
                    }
                }
            }
        }
Example #2
0
 /// <summary>
 /// Executes FFProbe and ensures that it has access to the respective resource
 /// </summary>
 /// <param name="lfsra"><see cref="ILocalFsResourceAccessor"/> to which FFProbe needs access to</param>
 /// <param name="arguments">Arguments for FFProbe</param>
 /// <param name="priorityClass">Process priority</param>
 /// <param name="maxWaitMs">Maximum time to wait for completion</param>
 /// <returns>A <see cref="Task"/> representing the result of executing FFProbe</returns>
 /// <remarks>
 /// This is a convenience method that enables executing FFProbe directly on the <see cref="ILocalFsResourceAccessor"/>
 /// interface to which FFProbe needs access. The purpose of an <see cref="ILocalFsResourceAccessor"/> is providing
 /// access to a resource - not executing programs which is why this method is implemented as an extension method instead of
 /// a method directly on the interface.
 /// </remarks>
 public static Task <ProcessExecutionResult> FFProbeExecuteWithResourceAccessAsync(ILocalFsResourceAccessor lfsra, string arguments, ProcessPriorityClass priorityClass, int maxWaitMs)
 {
     return(lfsra.ExecuteWithResourceAccessAsync(_ffProbeBinPath, arguments, priorityClass, maxWaitMs));
 }
Example #3
0
    private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
      // VideoAspect must be present to be sure it is actually a video resource.
      if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
        return false;

      byte[] thumb;
      // We only want to create missing thumbnails here, so check for existing ones first
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
        return true;

      // Check for a reasonable time offset
      long defaultVideoOffset = 720;
      long videoDuration;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
      {
        if (defaultVideoOffset > videoDuration * 1 / 3)
          defaultVideoOffset = videoDuration * 1 / 3;
      }

      string downscale = ",scale=iw/2:-1"; // Reduces the video frame size to a half of original

      int videoWidth;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_WIDTH, out videoWidth))
      {
        // Don't downscale SD video frames, quality is already quite low.
        if (videoWidth > 0 && videoWidth <= 720)
          downscale = "";
      }

      // ToDo: Move creation of temp file names to FileUtils class
      string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
      string executable = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
      string arguments = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
        defaultVideoOffset,
        // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
        lfsra.LocalFileSystemPath,
        tempFileName,
        downscale);

      try
      {
        bool success;
        lock (FFMPEG_THROTTLE_LOCK)
          success = lfsra.ExecuteWithResourceAccessAsync(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS).Result.Success;
        if (success && File.Exists(tempFileName))
        {
          var binary = FileUtils.ReadFile(tempFileName);
          MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
          // Calling EnsureLocalFileSystemAccess not necessary; only string operation
          ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
        }
        else
          // Calling EnsureLocalFileSystemAccess not necessary; only string operation
          ServiceRegistration.Get<ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
      }
      catch (AggregateException ae)
      {
        ae.Handle(e =>
        {
          if (e is TaskCanceledException)
          {
            ServiceRegistration.Get<ILogger>().Warn("VideoThumbnailer.ExtractThumbnail: External process aborted due to timeout: Executable='{0}', Arguments='{1}', Timeout='{2}'", executable, arguments, PROCESS_TIMEOUT_MS);
            return true;
          }
          return false;
        });
      }
      finally
      {
        if (File.Exists(tempFileName))
          File.Delete(tempFileName);
      }
      return true;
    }
        private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            byte[] thumb;
            // We only want to create missing thumbnails here, so check for existing ones first
            if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
            {
                return(true);
            }

            // Check for a reasonable time offset
            long defaultVideoOffset = 720;
            long videoDuration;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
            {
                if (defaultVideoOffset > videoDuration * 1 / 3)
                {
                    defaultVideoOffset = videoDuration * 1 / 3;
                }
            }

            string downscale = ",scale=iw/2:-1"; // Reduces the video frame size to a half of original

            int videoWidth;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_WIDTH, out videoWidth))
            {
                // Don't downscale SD video frames, quality is already quite low.
                if (videoWidth > 0 && videoWidth <= 720)
                {
                    downscale = "";
                }
            }

            // ToDo: Move creation of temp file names to FileUtils class
            string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
            string executable   = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
            string arguments    = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
                                                defaultVideoOffset,
                                                // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
                                                lfsra.LocalFileSystemPath,
                                                tempFileName,
                                                downscale);

            try
            {
                bool success;
                lock (FFMPEG_THROTTLE_LOCK)
                    success = lfsra.ExecuteWithResourceAccessAsync(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS).Result.Success;
                if (success && File.Exists(tempFileName))
                {
                    var binary = FileUtils.ReadFile(tempFileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
                else
                {
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
            }
            catch (AggregateException ae)
            {
                ae.Handle(e =>
                {
                    if (e is TaskCanceledException)
                    {
                        ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer.ExtractThumbnail: External process aborted due to timeout: Executable='{0}', Arguments='{1}', Timeout='{2}'", executable, arguments, PROCESS_TIMEOUT_MS);
                        return(true);
                    }
                    return(false);
                });
            }
            finally
            {
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }
            }
            return(true);
        }