FFProbe.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Threading.Tasks;
  5. using MediaBrowser.Common.Logging;
  6. using MediaBrowser.Common.Serialization;
  7. using MediaBrowser.Model.Entities;
  8. namespace MediaBrowser.Controller.FFMpeg
  9. {
  10. /// <summary>
  11. /// Runs FFProbe against a media file and returns metadata.
  12. /// </summary>
  13. public static class FFProbe
  14. {
  15. public static FFProbeResult Run(Audio item)
  16. {
  17. // Use try catch to avoid having to use File.Exists
  18. try
  19. {
  20. return GetCachedResult(GetFFProbeAudioCachePath(item));
  21. }
  22. catch (FileNotFoundException)
  23. {
  24. }
  25. catch (Exception ex)
  26. {
  27. Logger.LogException(ex);
  28. }
  29. FFProbeResult result = Run(item.Path);
  30. // Fire and forget
  31. CacheResult(result, GetFFProbeAudioCachePath(item));
  32. return result;
  33. }
  34. private static FFProbeResult GetCachedResult(string path)
  35. {
  36. return ProtobufSerializer.DeserializeFromFile<FFProbeResult>(path);
  37. }
  38. private static async void CacheResult(FFProbeResult result, string outputCachePath)
  39. {
  40. await Task.Run(() =>
  41. {
  42. try
  43. {
  44. ProtobufSerializer.SerializeToFile<FFProbeResult>(result, outputCachePath);
  45. }
  46. catch (Exception ex)
  47. {
  48. Logger.LogException(ex);
  49. }
  50. }).ConfigureAwait(false);
  51. }
  52. public static FFProbeResult Run(Video item)
  53. {
  54. // Use try catch to avoid having to use File.Exists
  55. try
  56. {
  57. return GetCachedResult(GetFFProbeVideoCachePath(item));
  58. }
  59. catch (FileNotFoundException)
  60. {
  61. }
  62. catch (Exception ex)
  63. {
  64. Logger.LogException(ex);
  65. }
  66. FFProbeResult result = Run(item.Path);
  67. // Fire and forget
  68. CacheResult(result, GetFFProbeVideoCachePath(item));
  69. return result;
  70. }
  71. private static FFProbeResult Run(string input)
  72. {
  73. ProcessStartInfo startInfo = new ProcessStartInfo();
  74. startInfo.CreateNoWindow = true;
  75. startInfo.UseShellExecute = false;
  76. // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
  77. startInfo.RedirectStandardOutput = true;
  78. startInfo.RedirectStandardError = true;
  79. startInfo.FileName = Kernel.Instance.ApplicationPaths.FFProbePath;
  80. startInfo.WorkingDirectory = Kernel.Instance.ApplicationPaths.FFMpegDirectory;
  81. startInfo.Arguments = string.Format("\"{0}\" -v quiet -print_format json -show_streams -show_format", input);
  82. //Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
  83. Process process = new Process();
  84. process.StartInfo = startInfo;
  85. try
  86. {
  87. process.Start();
  88. // MUST read both stdout and stderr asynchronously or a deadlock may occurr
  89. // If we ever decide to disable the ffmpeg log then you must uncomment the below line.
  90. process.BeginErrorReadLine();
  91. return JsonSerializer.DeserializeFromStream<FFProbeResult>(process.StandardOutput.BaseStream);
  92. }
  93. catch (Exception ex)
  94. {
  95. Logger.LogException(ex);
  96. // Hate having to do this
  97. try
  98. {
  99. process.Kill();
  100. }
  101. catch
  102. {
  103. }
  104. return null;
  105. }
  106. finally
  107. {
  108. process.Dispose();
  109. }
  110. }
  111. private static string GetFFProbeAudioCachePath(BaseEntity item)
  112. {
  113. string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, item.Id.ToString().Substring(0, 1));
  114. return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".pb");
  115. }
  116. private static string GetFFProbeVideoCachePath(BaseEntity item)
  117. {
  118. string outputDirectory = Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeVideoCacheDirectory, item.Id.ToString().Substring(0, 1));
  119. return Path.Combine(outputDirectory, item.Id + "-" + item.DateModified.Ticks + ".pb");
  120. }
  121. }
  122. }