FFProbe.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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 async static Task<FFProbeResult> Run(Audio item, string outputCachePath)
  16. {
  17. // Use try catch to avoid having to use File.Exists
  18. try
  19. {
  20. return GetCachedResult(outputCachePath);
  21. }
  22. catch (FileNotFoundException)
  23. {
  24. }
  25. await Run(item.Path, outputCachePath).ConfigureAwait(false);
  26. return GetCachedResult(item.Path);
  27. }
  28. public static FFProbeResult GetCachedResult(string path)
  29. {
  30. using (FileStream stream = File.OpenRead(path))
  31. {
  32. return JsonSerializer.DeserializeFromStream<FFProbeResult>(stream);
  33. }
  34. }
  35. public async static Task<FFProbeResult> Run(Video item, string outputCachePath)
  36. {
  37. // Use try catch to avoid having to use File.Exists
  38. try
  39. {
  40. return GetCachedResult(outputCachePath);
  41. }
  42. catch (FileNotFoundException)
  43. {
  44. }
  45. await Run(item.Path, outputCachePath).ConfigureAwait(false);
  46. return GetCachedResult(item.Path);
  47. }
  48. private async static Task Run(string input, string output)
  49. {
  50. ProcessStartInfo startInfo = new ProcessStartInfo();
  51. startInfo.CreateNoWindow = true;
  52. startInfo.UseShellExecute = false;
  53. // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
  54. startInfo.RedirectStandardOutput = true;
  55. startInfo.RedirectStandardError = true;
  56. startInfo.FileName = Kernel.Instance.ApplicationPaths.FFProbePath;
  57. startInfo.WorkingDirectory = Kernel.Instance.ApplicationPaths.FFMpegDirectory;
  58. startInfo.Arguments = string.Format("\"{0}\" -v quiet -print_format json -show_streams -show_format", input);
  59. //Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
  60. Process process = new Process();
  61. process.StartInfo = startInfo;
  62. FileStream stream = new FileStream(output, FileMode.Create);
  63. try
  64. {
  65. process.Start();
  66. // MUST read both stdout and stderr asynchronously or a deadlock may occurr
  67. // If we ever decide to disable the ffmpeg log then you must uncomment the below line.
  68. process.BeginErrorReadLine();
  69. await process.StandardOutput.BaseStream.CopyToAsync(stream).ConfigureAwait(false);
  70. process.WaitForExit();
  71. stream.Dispose();
  72. if (process.ExitCode != 0)
  73. {
  74. Logger.LogInfo("FFProbe exited with code {0} for {1}", process.ExitCode, input);
  75. }
  76. }
  77. catch (Exception ex)
  78. {
  79. Logger.LogException(ex);
  80. stream.Dispose();
  81. // Hate having to do this
  82. try
  83. {
  84. process.Kill();
  85. }
  86. catch
  87. {
  88. }
  89. File.Delete(output);
  90. }
  91. finally
  92. {
  93. process.Dispose();
  94. }
  95. }
  96. }
  97. }