AudioHandler.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using MediaBrowser.Api.Transcoding;
  6. using MediaBrowser.Common.Configuration;
  7. using MediaBrowser.Common.Net.Handlers;
  8. using MediaBrowser.Controller;
  9. using MediaBrowser.Model.Entities;
  10. namespace MediaBrowser.Api.HttpHandlers
  11. {
  12. public class AudioHandler : StaticFileHandler
  13. {
  14. private Audio _LibraryItem;
  15. /// <summary>
  16. /// Gets the library item that will be played, if any
  17. /// </summary>
  18. private Audio LibraryItem
  19. {
  20. get
  21. {
  22. if (_LibraryItem == null)
  23. {
  24. string id = QueryString["id"];
  25. if (!string.IsNullOrEmpty(id))
  26. {
  27. _LibraryItem = Kernel.Instance.GetItemById(Guid.Parse(id)) as Audio;
  28. }
  29. }
  30. return _LibraryItem;
  31. }
  32. }
  33. public override string Path
  34. {
  35. get
  36. {
  37. return TranscodedPath;
  38. }
  39. }
  40. private string _TranscodedPath;
  41. /// <summary>
  42. /// Gets the library item that will be played, if any
  43. /// </summary>
  44. private string TranscodedPath
  45. {
  46. get
  47. {
  48. if (_TranscodedPath == null)
  49. {
  50. string originalMediaPath = LibraryItem == null ? base.Path : LibraryItem.Path;
  51. if (!RequiresTranscoding())
  52. {
  53. _TranscodedPath = originalMediaPath;
  54. }
  55. else
  56. {
  57. string outputPath = GetOutputFilePath(originalMediaPath);
  58. // Find the job in the list
  59. TranscodingJob job = ApiService.GetTranscodingJob(outputPath);
  60. if (job == null && !File.Exists(outputPath))
  61. {
  62. job = GetNewTranscodingJob(originalMediaPath, outputPath);
  63. job.Start();
  64. }
  65. if (job != null)
  66. {
  67. job.WaitForExit();
  68. }
  69. _TranscodedPath = outputPath;
  70. }
  71. }
  72. return _TranscodedPath;
  73. }
  74. }
  75. public string AudioFormat
  76. {
  77. get
  78. {
  79. string val = QueryString["audiobitrate"];
  80. if (string.IsNullOrEmpty(val))
  81. {
  82. return "mp3";
  83. }
  84. return val;
  85. }
  86. }
  87. public int? AudioBitRate
  88. {
  89. get
  90. {
  91. string val = QueryString["audiobitrate"];
  92. if (string.IsNullOrEmpty(val))
  93. {
  94. return null;
  95. }
  96. return int.Parse(val);
  97. }
  98. }
  99. public int? NumAudioChannels
  100. {
  101. get
  102. {
  103. string val = QueryString["audiochannels"];
  104. if (string.IsNullOrEmpty(val))
  105. {
  106. return null;
  107. }
  108. return int.Parse(val);
  109. }
  110. }
  111. public int? AudioSampleRate
  112. {
  113. get
  114. {
  115. string val = QueryString["audiosamplerate"];
  116. if (string.IsNullOrEmpty(val))
  117. {
  118. return 44100;
  119. }
  120. return int.Parse(val);
  121. }
  122. }
  123. private static string _StreamsDirectory = null;
  124. /// <summary>
  125. /// Gets the folder path to where transcodes will be cached
  126. /// </summary>
  127. public static string StreamsDirectory
  128. {
  129. get
  130. {
  131. if (_StreamsDirectory == null)
  132. {
  133. _StreamsDirectory = System.IO.Path.Combine(ApplicationPaths.ProgramDataPath, "streams");
  134. if (!Directory.Exists(_StreamsDirectory))
  135. {
  136. Directory.CreateDirectory(_StreamsDirectory);
  137. }
  138. }
  139. return _StreamsDirectory;
  140. }
  141. }
  142. private static string _FFMpegDirectory = null;
  143. /// <summary>
  144. /// Gets the folder path to ffmpeg
  145. /// </summary>
  146. public static string FFMpegDirectory
  147. {
  148. get
  149. {
  150. if (_FFMpegDirectory == null)
  151. {
  152. _FFMpegDirectory = System.IO.Path.Combine(ApplicationPaths.ProgramDataPath, "ffmpeg");
  153. if (!Directory.Exists(_FFMpegDirectory))
  154. {
  155. Directory.CreateDirectory(_FFMpegDirectory);
  156. // Extract ffmpeg
  157. using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Api.ffmpeg.ffmpeg.exe"))
  158. {
  159. using (FileStream fileStream = new FileStream(FFMpegPath, FileMode.Create))
  160. {
  161. stream.CopyTo(fileStream);
  162. }
  163. }
  164. }
  165. }
  166. return _FFMpegDirectory;
  167. }
  168. }
  169. private static string FFMpegPath
  170. {
  171. get
  172. {
  173. return System.IO.Path.Combine(FFMpegDirectory, "ffmpeg.exe");
  174. }
  175. }
  176. private string GetOutputFilePath(string input)
  177. {
  178. string hash = Kernel.GetMD5(input).ToString();
  179. if (AudioBitRate.HasValue)
  180. {
  181. hash += "_ab" + AudioBitRate;
  182. }
  183. if (NumAudioChannels.HasValue)
  184. {
  185. hash += "_ac" + NumAudioChannels;
  186. }
  187. if (AudioSampleRate.HasValue)
  188. {
  189. hash += "_ar" + AudioSampleRate;
  190. }
  191. string filename = hash + "." + AudioFormat.ToLower();
  192. return System.IO.Path.Combine(StreamsDirectory, filename);
  193. }
  194. /// <summary>
  195. /// Determines whether or not the original file requires transcoding
  196. /// </summary>
  197. private bool RequiresTranscoding()
  198. {
  199. // Only support skipping transcoding for library items
  200. if (LibraryItem == null)
  201. {
  202. return true;
  203. }
  204. // If it's not in the same format, we need to transcode
  205. if (!LibraryItem.Path.EndsWith(AudioFormat, StringComparison.OrdinalIgnoreCase))
  206. {
  207. return true;
  208. }
  209. // If the bitrate is greater than our desired bitrate, we need to transcode
  210. if (AudioBitRate.HasValue)
  211. {
  212. if (AudioBitRate.Value < LibraryItem.BitRate)
  213. {
  214. return true;
  215. }
  216. }
  217. // If the number of channels is greater than our desired channels, we need to transcode
  218. if (NumAudioChannels.HasValue)
  219. {
  220. if (NumAudioChannels.Value < LibraryItem.Channels)
  221. {
  222. return true;
  223. }
  224. }
  225. // If the sample rate is greater than our desired sample rate, we need to transcode
  226. if (AudioSampleRate.HasValue)
  227. {
  228. if (AudioSampleRate.Value < LibraryItem.SampleRate)
  229. {
  230. return true;
  231. }
  232. }
  233. // Yay
  234. return false;
  235. }
  236. /// <summary>
  237. /// Creates a new transcoding job
  238. /// </summary>
  239. private TranscodingJob GetNewTranscodingJob(string input, string output)
  240. {
  241. return new TranscodingJob()
  242. {
  243. InputFile = input,
  244. OutputFile = output,
  245. TranscoderPath = FFMpegPath,
  246. Arguments = GetAudioArguments(input, output)
  247. };
  248. }
  249. /// <summary>
  250. /// Creates arguments to pass to ffmpeg
  251. /// </summary>
  252. private string GetAudioArguments(string input, string output)
  253. {
  254. List<string> audioTranscodeParams = new List<string>();
  255. if (AudioBitRate.HasValue)
  256. {
  257. audioTranscodeParams.Add("-ab " + AudioBitRate.Value);
  258. }
  259. if (NumAudioChannels.HasValue)
  260. {
  261. audioTranscodeParams.Add("-ac " + NumAudioChannels.Value);
  262. }
  263. if (AudioSampleRate.HasValue)
  264. {
  265. audioTranscodeParams.Add("-ar " + AudioSampleRate.Value);
  266. }
  267. audioTranscodeParams.Add("-f " + AudioFormat);
  268. return "-i \"" + input + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " \"" + output + "\"";
  269. }
  270. }
  271. }