/src/Logic/VideoPlayers/MPlayer.cs

https://gitlab.com/minaz922/subtitleedit · C# · 390 lines · 332 code · 47 blank · 11 comment · 63 complexity · 3d415b75ff7a6fbe85489687526d48af MD5 · raw file

  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. namespace Nikse.SubtitleEdit.Logic.VideoPlayers
  5. {
  6. public class MPlayer : VideoPlayer
  7. {
  8. private Process _mplayer;
  9. private System.Windows.Forms.Timer timer;
  10. private TimeSpan _lengthInSeconds;
  11. private TimeSpan _lastLengthInSeconds = TimeSpan.FromDays(0);
  12. private bool _paused;
  13. private bool _loaded = false;
  14. private bool _ended = false;
  15. private string _videoFileName;
  16. private bool _waitForChange = false;
  17. public int Width { get; private set; }
  18. public int Height { get; private set; }
  19. public float FramesPerSecond { get; private set; }
  20. public string VideoFormat { get; private set; }
  21. public string VideoCodec { get; private set; }
  22. private double? _pausePosition = null; // Hack to hold precise seeking when paused
  23. private int _pauseCounts = 0;
  24. private double _speed = 1.0;
  25. public override string PlayerName
  26. {
  27. get { return "MPlayer"; }
  28. }
  29. float _volume;
  30. public override int Volume
  31. {
  32. get
  33. {
  34. return (int)_volume;
  35. }
  36. set
  37. {
  38. if (value >= 0 && value <= 100)
  39. {
  40. _volume = value;
  41. SetProperty("volume", value.ToString(), true);
  42. }
  43. }
  44. }
  45. public override double Duration
  46. {
  47. get { return _lengthInSeconds.TotalSeconds; }
  48. }
  49. double _timePosition;
  50. public override double CurrentPosition
  51. {
  52. get
  53. {
  54. if (_paused && _pausePosition != null)
  55. {
  56. if (_pausePosition < 0)
  57. return 0;
  58. return _pausePosition.Value;
  59. }
  60. return _timePosition;
  61. }
  62. set
  63. {
  64. // NOTE: FOR ACCURATE SEARCH USE MPlayer2 - http://www.mplayer2.org/)
  65. _timePosition = value;
  66. if (IsPaused && value <= Duration)
  67. _pausePosition = value;
  68. _mplayer.StandardInput.WriteLine(string.Format("pausing_keep seek {0:0.0} 2", value));
  69. }
  70. }
  71. public override double PlayRate
  72. {
  73. get
  74. {
  75. return _speed;
  76. }
  77. set
  78. {
  79. if (value >= 0 && value <= 2.0)
  80. {
  81. _speed = value;
  82. SetProperty("speed", value.ToString(System.Globalization.CultureInfo.InvariantCulture), true);
  83. }
  84. }
  85. }
  86. public override void Play()
  87. {
  88. _mplayer.StandardInput.WriteLine("pause");
  89. _pauseCounts = 0;
  90. _paused = false;
  91. _pausePosition = null;
  92. }
  93. public override void Pause()
  94. {
  95. if (!_paused)
  96. _mplayer.StandardInput.WriteLine("pause");
  97. _pauseCounts = 0;
  98. _paused = true;
  99. }
  100. public override void Stop()
  101. {
  102. CurrentPosition = 0;
  103. Pause();
  104. _mplayer.StandardInput.WriteLine(string.Format("pausing_keep_force seek 0 2", 0));
  105. _pauseCounts = 0;
  106. _paused = true;
  107. _lastLengthInSeconds = _lengthInSeconds;
  108. _pausePosition = null;
  109. }
  110. public override bool IsPaused
  111. {
  112. get { return _paused; }
  113. }
  114. public override bool IsPlaying
  115. {
  116. get { return !_paused; }
  117. }
  118. public override void Initialize(System.Windows.Forms.Control ownerControl, string videoFileName, EventHandler onVideoLoaded, EventHandler onVideoEnded)
  119. {
  120. _loaded = false;
  121. _videoFileName = videoFileName;
  122. string mplayerExeName = GetMPlayerFileName;
  123. if (!string.IsNullOrEmpty(mplayerExeName))
  124. {
  125. _mplayer = new Process();
  126. _mplayer.StartInfo.FileName = mplayerExeName;
  127. //vo options: gl, gl2, directx:noaccel
  128. if (Utilities.IsRunningOnLinux() || Utilities.IsRunningOnMac())
  129. _mplayer.StartInfo.Arguments = "-nofs -quiet -slave -idle -loop 0 -osdlevel 0 -vsync -wid " + ownerControl.Handle.ToInt32() + " \"" + videoFileName + "\" ";
  130. else
  131. _mplayer.StartInfo.Arguments = "-nofs -quiet -slave -idle -loop 0 -osdlevel 0 -vo direct3d -wid " + (int)ownerControl.Handle + " \"" + videoFileName + "\" ";
  132. _mplayer.StartInfo.UseShellExecute = false;
  133. _mplayer.StartInfo.RedirectStandardInput = true;
  134. _mplayer.StartInfo.RedirectStandardOutput = true;
  135. _mplayer.StartInfo.CreateNoWindow = true;
  136. _mplayer.OutputDataReceived += new DataReceivedEventHandler(MPlayerOutputDataReceived);
  137. try
  138. {
  139. _mplayer.Start();
  140. }
  141. catch
  142. {
  143. System.Windows.Forms.MessageBox.Show("Unable to start MPlayer - make sure MPlayer is installed!");
  144. throw;
  145. }
  146. _mplayer.StandardInput.NewLine = "\n";
  147. _mplayer.BeginOutputReadLine(); // Async reading of output to prevent deadlock
  148. // static properties
  149. GetProperty("width", true);
  150. GetProperty("height", true);
  151. GetProperty("fps", true);
  152. GetProperty("video_format", true);
  153. GetProperty("video_codec", true);
  154. GetProperty("length", true);
  155. // semi static variable
  156. GetProperty("volume", true);
  157. // start timer to collect variable properties
  158. timer = new System.Windows.Forms.Timer();
  159. timer.Interval = 1000;
  160. timer.Tick += new EventHandler(timer_Tick);
  161. timer.Start();
  162. OnVideoLoaded = onVideoLoaded;
  163. OnVideoEnded = onVideoEnded;
  164. }
  165. }
  166. void timer_Tick(object sender, EventArgs e)
  167. {
  168. // variable properties
  169. _mplayer.StandardInput.WriteLine("pausing_keep_force get_property time_pos");
  170. _mplayer.StandardInput.WriteLine("pausing_keep_force get_property pause");
  171. if (!_ended && OnVideoEnded != null && _lengthInSeconds.TotalSeconds == Duration)
  172. {
  173. // _ended = true;
  174. // OnVideoEnded.Invoke(this, null);
  175. }
  176. else if (_lengthInSeconds.TotalSeconds < Duration)
  177. {
  178. _ended = false;
  179. }
  180. if (OnVideoLoaded != null && _loaded == true)
  181. {
  182. timer.Stop();
  183. _loaded = false;
  184. OnVideoLoaded.Invoke(this, null);
  185. timer.Interval = 100;
  186. timer.Start();
  187. }
  188. if (_lengthInSeconds != _lastLengthInSeconds)
  189. _paused = false;
  190. _lastLengthInSeconds = _lengthInSeconds;
  191. }
  192. void MPlayerOutputDataReceived(object sender, DataReceivedEventArgs e)
  193. {
  194. if (e.Data == null)
  195. return;
  196. System.Diagnostics.Debug.WriteLine("MPlayer: " + e.Data);
  197. if (e.Data.StartsWith("Playing "))
  198. {
  199. _loaded = true;
  200. return;
  201. }
  202. if (e.Data.StartsWith("Exiting..."))
  203. {
  204. _ended = true;
  205. if (_loaded)
  206. {
  207. _mplayer.StandardInput.WriteLine("loadfile " + _videoFileName);
  208. if (OnVideoEnded != null)
  209. OnVideoEnded.Invoke(this, null);
  210. }
  211. return;
  212. }
  213. int indexOfEqual = e.Data.IndexOf("=");
  214. if (indexOfEqual > 0 && indexOfEqual + 1 < e.Data.Length && e.Data.StartsWith("ANS_"))
  215. {
  216. string code = e.Data.Substring(0, indexOfEqual);
  217. string value = e.Data.Substring(indexOfEqual + 1);
  218. switch (code)
  219. {
  220. // Examples:
  221. // ANS_time_pos=8.299958, ANS_width=624, ANS_height=352, ANS_fps=23.976025, ANS_video_format=1145656920, ANS_video_format=1145656920, ANS_video_codec=ffodivx,
  222. // ANS_length=1351.600213, ANS_volume=100.000000
  223. case "ANS_time_pos":
  224. _timePosition = Convert.ToDouble(value.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
  225. break;
  226. case "ANS_width":
  227. Width = Convert.ToInt32(value);
  228. break;
  229. case "ANS_height":
  230. Height = Convert.ToInt32(value);
  231. break;
  232. case "ANS_fps":
  233. double d;
  234. if (double.TryParse(value, out d))
  235. FramesPerSecond = (float)Convert.ToDouble(value.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
  236. else
  237. FramesPerSecond = 25.0f;
  238. break;
  239. case "ANS_video_format":
  240. VideoFormat = value;
  241. break;
  242. case "ANS_video_codec":
  243. VideoCodec = value;
  244. break;
  245. case "ANS_length":
  246. _lengthInSeconds = TimeSpan.FromSeconds(Convert.ToDouble(value.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture));
  247. break;
  248. case "ANS_volume":
  249. _volume = (float)Convert.ToDouble(value.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);
  250. break;
  251. case "ANS_pause":
  252. if (value == "yes" || value == "1")
  253. _pauseCounts++;
  254. else
  255. _pauseCounts--;
  256. if (_pauseCounts > 3)
  257. _paused = true;
  258. else if (_pauseCounts < -3)
  259. {
  260. _paused = false;
  261. _pausePosition = null;
  262. }
  263. else if (Math.Abs(_pauseCounts) > 10)
  264. _pauseCounts = 0;
  265. break;
  266. }
  267. _waitForChange = false;
  268. }
  269. }
  270. public static string GetMPlayerFileName
  271. {
  272. get
  273. {
  274. if (Utilities.IsRunningOnLinux() || Utilities.IsRunningOnMac())
  275. return "mplayer";
  276. string fileName = Path.Combine(Configuration.BaseDirectory, "mplayer2.exe");
  277. if (File.Exists(fileName))
  278. return fileName;
  279. fileName = Path.Combine(Configuration.BaseDirectory, "mplayer.exe");
  280. if (File.Exists(fileName))
  281. return fileName;
  282. fileName = @"C:\Program Files (x86)\SMPlayer\mplayer\mplayer.exe";
  283. if (File.Exists(fileName))
  284. return fileName;
  285. fileName = @"C:\Program Files (x86)\mplayer\mplayer.exe";
  286. if (File.Exists(fileName))
  287. return fileName;
  288. fileName = @"C:\Program Files\mplayer\mplayer.exe";
  289. if (File.Exists(fileName))
  290. return fileName;
  291. fileName = @"C:\Program Files\SMPlayer\mplayer\mplayer.exe";
  292. if (File.Exists(fileName))
  293. return fileName;
  294. return null;
  295. }
  296. }
  297. public static bool IsInstalled
  298. {
  299. get
  300. {
  301. return GetMPlayerFileName != null;
  302. }
  303. }
  304. private void GetProperty(string propertyName, bool keepPause)
  305. {
  306. if (keepPause)
  307. _mplayer.StandardInput.WriteLine("pausing_keep get_property " + propertyName);
  308. else
  309. _mplayer.StandardInput.WriteLine("get_property " + propertyName);
  310. }
  311. private void SetProperty(string propertyName, string value, bool keepPause)
  312. {
  313. if (keepPause)
  314. _mplayer.StandardInput.WriteLine("pausing_keep set_property " + propertyName + " " + value);
  315. else
  316. _mplayer.StandardInput.WriteLine("set_property " + propertyName + " " + value);
  317. UglySleep();
  318. }
  319. private void UglySleep()
  320. {
  321. _waitForChange = true;
  322. int i = 0;
  323. while (i < 100 && _waitForChange)
  324. {
  325. System.Windows.Forms.Application.DoEvents();
  326. System.Threading.Thread.Sleep(2);
  327. i++;
  328. }
  329. _waitForChange = false;
  330. }
  331. public override void DisposeVideoPlayer()
  332. {
  333. timer.Stop();
  334. if (_mplayer != null)
  335. {
  336. _mplayer.OutputDataReceived -= MPlayerOutputDataReceived;
  337. _mplayer.StandardInput.WriteLine("quit");
  338. }
  339. }
  340. public override event EventHandler OnVideoLoaded;
  341. public override event EventHandler OnVideoEnded;
  342. }
  343. }