EncoderValidator.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using MediaBrowser.Model.Diagnostics;
  5. using Microsoft.Extensions.Logging;
  6. namespace MediaBrowser.MediaEncoding.Encoder
  7. {
  8. public class EncoderValidator
  9. {
  10. private readonly ILogger _logger;
  11. private readonly IProcessFactory _processFactory;
  12. public EncoderValidator(ILogger logger, IProcessFactory processFactory)
  13. {
  14. _logger = logger;
  15. _processFactory = processFactory;
  16. }
  17. public Tuple<List<string>, List<string>> Validate(string encoderPath)
  18. {
  19. _logger.LogInformation("Validating media encoder at {0}", encoderPath);
  20. var decoders = GetDecoders(encoderPath);
  21. var encoders = GetEncoders(encoderPath);
  22. _logger.LogInformation("Encoder validation complete");
  23. return new Tuple<List<string>, List<string>>(decoders, encoders);
  24. }
  25. public bool ValidateVersion(string encoderAppPath, bool logOutput)
  26. {
  27. string output = string.Empty;
  28. try
  29. {
  30. output = GetProcessOutput(encoderAppPath, "-version");
  31. }
  32. catch (Exception ex)
  33. {
  34. if (logOutput)
  35. {
  36. _logger.LogError(ex, "Error validating encoder");
  37. }
  38. }
  39. if (string.IsNullOrWhiteSpace(output))
  40. {
  41. return false;
  42. }
  43. _logger.LogInformation("ffmpeg info: {0}", output);
  44. if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
  45. {
  46. return false;
  47. }
  48. output = " " + output + " ";
  49. for (var i = 2013; i <= 2015; i++)
  50. {
  51. var yearString = i.ToString(CultureInfo.InvariantCulture);
  52. if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1)
  53. {
  54. return false;
  55. }
  56. }
  57. return true;
  58. }
  59. private List<string> GetDecoders(string encoderAppPath)
  60. {
  61. string output = null;
  62. try
  63. {
  64. output = GetProcessOutput(encoderAppPath, "-decoders");
  65. }
  66. catch (Exception ex)
  67. {
  68. _logger.LogError(ex, "Error detecting available decoders");
  69. }
  70. if (string.IsNullOrWhiteSpace(output))
  71. {
  72. return new List<string>();
  73. }
  74. var required = new[]
  75. {
  76. "mpeg2video",
  77. "h264_qsv",
  78. "hevc_qsv",
  79. "mpeg2_qsv",
  80. "vc1_qsv",
  81. "h264_cuvid",
  82. "hevc_cuvid",
  83. "dts",
  84. "ac3",
  85. "aac",
  86. "mp3",
  87. "h264",
  88. "hevc"
  89. };
  90. var found = new List<string>();
  91. foreach (var codec in required)
  92. {
  93. var srch = " " + codec + " ";
  94. if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
  95. {
  96. found.Add(codec);
  97. }
  98. }
  99. _logger.LogInformation("Available decoders: {Codecs}", found);
  100. return found;
  101. }
  102. private List<string> GetEncoders(string encoderAppPath)
  103. {
  104. string output = null;
  105. try
  106. {
  107. output = GetProcessOutput(encoderAppPath, "-encoders");
  108. }
  109. catch (Exception ex)
  110. {
  111. _logger.LogError(ex, "Error getting encoders");
  112. }
  113. if (string.IsNullOrWhiteSpace(output))
  114. {
  115. return new List<string>();
  116. }
  117. var required = new[]
  118. {
  119. "libx264",
  120. "libx265",
  121. "mpeg4",
  122. "msmpeg4",
  123. "libvpx",
  124. "libvpx-vp9",
  125. "aac",
  126. "libmp3lame",
  127. "libopus",
  128. "libvorbis",
  129. "srt",
  130. "h264_nvenc",
  131. "hevc_nvenc",
  132. "h264_qsv",
  133. "hevc_qsv",
  134. "h264_omx",
  135. "hevc_omx",
  136. "h264_vaapi",
  137. "hevc_vaapi",
  138. "ac3"
  139. };
  140. var found = new List<string>();
  141. foreach (var codec in required)
  142. {
  143. var srch = " " + codec + " ";
  144. if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) != -1)
  145. {
  146. found.Add(codec);
  147. }
  148. }
  149. _logger.LogInformation("Available encoders: {Codecs}", found);
  150. return found;
  151. }
  152. private string GetProcessOutput(string path, string arguments)
  153. {
  154. var process = _processFactory.Create(new ProcessOptions
  155. {
  156. CreateNoWindow = true,
  157. UseShellExecute = false,
  158. FileName = path,
  159. Arguments = arguments,
  160. IsHidden = true,
  161. ErrorDialog = false,
  162. RedirectStandardOutput = true
  163. });
  164. _logger.LogInformation("Running {path} {arguments}", path, arguments);
  165. using (process)
  166. {
  167. process.Start();
  168. try
  169. {
  170. return process.StandardOutput.ReadToEnd();
  171. }
  172. catch
  173. {
  174. _logger.LogInformation("Killing process {path} {arguments}", path, arguments);
  175. // Hate having to do this
  176. try
  177. {
  178. process.Kill();
  179. }
  180. catch (Exception ex)
  181. {
  182. _logger.LogError(ex, "Error killing process");
  183. }
  184. throw;
  185. }
  186. }
  187. }
  188. }
  189. }